<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Quizizz Engineering]]></title><description><![CDATA[From the engineering desk, building an all-in-one assessment platform powered with AI]]></description><link>https://eng.quizizz.com</link><image><url>https://substackcdn.com/image/fetch/$s_!Qz0S!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F448397b8-e9c8-4f85-b73b-26f6917b8cc0_512x512.png</url><title>Quizizz Engineering</title><link>https://eng.quizizz.com</link></image><generator>Substack</generator><lastBuildDate>Tue, 14 Apr 2026 23:40:00 GMT</lastBuildDate><atom:link href="https://eng.quizizz.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Quizizz Engineering]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[quizizzengineering@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[quizizzengineering@substack.com]]></itunes:email><itunes:name><![CDATA[Quizizz Engineering]]></itunes:name></itunes:owner><itunes:author><![CDATA[Quizizz Engineering]]></itunes:author><googleplay:owner><![CDATA[quizizzengineering@substack.com]]></googleplay:owner><googleplay:email><![CDATA[quizizzengineering@substack.com]]></googleplay:email><googleplay:author><![CDATA[Quizizz Engineering]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Behind the UX: Creating student delight with our QBit]]></title><description><![CDATA[Introduction: Why Signup Pages Need Personality]]></description><link>https://eng.quizizz.com/p/behind-the-animation-enhancing-ux</link><guid isPermaLink="false">https://eng.quizizz.com/p/behind-the-animation-enhancing-ux</guid><dc:creator><![CDATA[Abhi Kumar]]></dc:creator><pubDate>Fri, 07 Feb 2025 05:30:52 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/cd556cfb-6a44-45c5-83fa-ef377c4d60c4_1456x1000.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3><strong>Introduction: Why Signup Pages Need Personality</strong></h3><p>Signup pages are the first impression of any platform, and for students, they need to be more than just functional&#8212;they should be fun, engaging, and relatable. Imagine this: you&#8217;re excited to join a game or a new learning adventure, but the first thing you face is a dull, lifeless form. In today&#8217;s digital-first world, that can feel boring or even intimidating.</p><p>This is where personality makes all the difference. By making the signup process interactive and enjoyable, we can turn a routine task into a moment of delight. A friendly, playful touch not only keeps students engaged but also sets the tone for their entire journey.</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;78d838d6-e993-46f4-b783-592aad9ba2b6&quot;,&quot;duration&quot;:null}"></div><h3><strong>Designing the Interaction: From Idea to Execution</strong></h3><h4><strong>The Idea: Engaging Students with QBit</strong></h4><p>We started by putting ourselves in the shoes of our primary users: students. We asked a simple question&#8212;how can we make the often mundane task of filling out a signup form feel exciting? The answer was clear: create a playful, interactive element that could entertain and guide students simultaneously.</p><p>Enter QBit, From the moment students arrive at the signup page, QBit springs to life:</p><ul><li><p><strong>Curious Eyes:</strong> QBit tracks the cursor with his eyes, giving students a sense of connection and playful interaction.</p></li><li><p><strong>Hiding Act:</strong> When students click on the password field, Qbit shyly ducks out of view, respecting their privacy charmingly and humorously.</p></li><li><p><strong>Motivational Prompts:</strong> QBit nudges students with friendly reminders, like &#8220;Sign up now and join the activity!&#8221;</p></li></ul><p>These small touches create a sense of delight while subtly guiding students through the signup process.<br><br><em><strong>Drumroll &#129345;, Please...</strong></em><strong> "And Now, the Moment You&#8217;ve Been Waiting For: Let&#8217;s Build This Magic!"<br><br>1. Understanding the Math Behind the Magic</strong></p><p>The first step in making QBit's eyes follow the cursor is calculating the angle of sight between the cursor position and the center of QBit&#8217;s eyes. Here's how:<br><br><strong>Step 1.1:</strong> Capture the coordinates of the cursor<strong> </strong><code>(x1, y1)</code> and the coordinates of QBit's eye center <code>(x2, y2)</code><br><br><strong>Step 1.2:</strong> Use the slope calculation formula to find the angle &#952;. </p><pre><code><code>tan&#952; = (y2&#8203;&#8722;y1&#8203;) (x2&#8203;&#8722;x1&#8203;&#8203;)</code></code></pre><p><strong>Step 1.3:</strong> Convert this slope into an angle using the arctangent function:</p><pre><code><code>&#952; = arctan ((y2&#8203;&#8722;y1) (&#8203;x2&#8203;&#8722;x1&#8203;&#8203;))</code></code></pre><p>This angle tells us the direction in which QBit's eyes should move relative to the cursor.<br><br><strong>2. Calculating New Eye Coordinates</strong></p><p>Now, let&#8217;s move QBit&#8217;s eyes along the calculated line of sight by a fixed distance, say <code>10px</code>. To do this, we'll use basic trigonometry:</p><p><strong>Step 2.1:</strong> Use the angle <code>&#952;</code> to calculate the new x and y offsets:</p><pre><code><code> offsetX &#8203;= sin&#952; 10
 offsetY &#8203;= cos&#952; 10 </code></code></pre><p><strong>Step 2.2:</strong> Adjust these offsets by adding them to QBit&#8217;s initial eye coordinates (x2, y2) to get the final position of the eyes:</p><pre><code><code>newX&#8203; = x2&#8203; + offsetX&#8203;
newY&#8203; = y2&#8203; + offsetY&#8203;</code></code></pre><p><strong>Putting It All Together</strong></p><p>Here&#8217;s a pseudocode example for implementation:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KEkC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779bfca1-c410-442b-b921-252940c911d1_2048x1676.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KEkC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779bfca1-c410-442b-b921-252940c911d1_2048x1676.png 424w, https://substackcdn.com/image/fetch/$s_!KEkC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779bfca1-c410-442b-b921-252940c911d1_2048x1676.png 848w, https://substackcdn.com/image/fetch/$s_!KEkC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779bfca1-c410-442b-b921-252940c911d1_2048x1676.png 1272w, https://substackcdn.com/image/fetch/$s_!KEkC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779bfca1-c410-442b-b921-252940c911d1_2048x1676.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!KEkC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779bfca1-c410-442b-b921-252940c911d1_2048x1676.png" width="1456" height="1192" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/779bfca1-c410-442b-b921-252940c911d1_2048x1676.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1192,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:434044,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!KEkC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779bfca1-c410-442b-b921-252940c911d1_2048x1676.png 424w, https://substackcdn.com/image/fetch/$s_!KEkC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779bfca1-c410-442b-b921-252940c911d1_2048x1676.png 848w, https://substackcdn.com/image/fetch/$s_!KEkC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779bfca1-c410-442b-b921-252940c911d1_2048x1676.png 1272w, https://substackcdn.com/image/fetch/$s_!KEkC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779bfca1-c410-442b-b921-252940c911d1_2048x1676.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Conclusion</strong></p><p>Delightful UX isn&#8217;t just about aesthetics&#8212;it&#8217;s about creating meaningful, engaging moments for users. QBit&#8217;s playful interaction transformed a routine signup process into an enjoyable experience, proving that small, thoughtful details can make a big impact. By adding personality and fun, we&#8217;ve not only enhanced usability but also built a stronger connection with our student users, making their journey on Quizizz more memorable and exciting.</p><p>In a world where digital interactions often feel impersonal, adding a touch of personality can set your platform apart. QBit&#8217;s playful interaction reminded us that UX design is not just about solving problems but also about creating joy. By embracing the power of delightful UX, we&#8217;re not only enhancing functionality but also building meaningful connections with our users&#8212;one small smile at a time.</p><p>If you want to build similar or better experiences, we&#8217;re looking out for people like you. Head over to our careers page and apply to our open positions.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://quizizz.com/careers&quot;,&quot;text&quot;:&quot;We're hiring!&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://quizizz.com/careers"><span>We're hiring!</span></a></p>]]></content:encoded></item><item><title><![CDATA[3PCD - Third-Party Cookie Deprecation: A Technical Deep Dive]]></title><description><![CDATA[Navigating the Cookie Crumble: Adapting to a Privacy-First web]]></description><link>https://eng.quizizz.com/p/3pcd-third-party-cookie-deprecation</link><guid isPermaLink="false">https://eng.quizizz.com/p/3pcd-third-party-cookie-deprecation</guid><dc:creator><![CDATA[Arvind Manikandan]]></dc:creator><pubDate>Tue, 24 Dec 2024 06:01:20 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!nj2z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5c3c09-70ac-4f7a-ab05-f6fb270dedab_612x408.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>The Changing Landscape of Web Cookies</h2><p>Web browsers, particularly Chrome are finally taking a stand to reshape how cross-site tracking and authentication work. Third-party cookies, once a ubiquitous method for maintaining user sessions across different domains, are being phased out due to privacy concerns. Most privacy focused browsers already sandbox the session data like Cookies, Storage APIs to within the origin domain. But with Chrome also set to enable this, it requires web applications to adopt more privacy-conscious approaches to user authentication and data management. Though the deadline for this deprecation is delayed until early 2025, it is already rolled-out to 1% of users and the exposure is set to increase to 100% by the time the deadline is finalized.</p><p>Just to give a quick rundown of how unregulated 3rd party cookies can jeopardize your privacy, here&#8217;s what can happen,</p><ul><li><p>You visited your favourite social media XYZ, it sets a cookie</p></li><li><p>That same social media XYZ also sells Ads</p></li><li><p>Now any other site that you visit, if it has XYZ ads/trackers/any kind of integration, that XYZ cookie will get sent</p></li><li><p>This way XYZ keeps track of anything and everything you do.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nj2z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5c3c09-70ac-4f7a-ab05-f6fb270dedab_612x408.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nj2z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5c3c09-70ac-4f7a-ab05-f6fb270dedab_612x408.jpeg 424w, https://substackcdn.com/image/fetch/$s_!nj2z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5c3c09-70ac-4f7a-ab05-f6fb270dedab_612x408.jpeg 848w, https://substackcdn.com/image/fetch/$s_!nj2z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5c3c09-70ac-4f7a-ab05-f6fb270dedab_612x408.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!nj2z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5c3c09-70ac-4f7a-ab05-f6fb270dedab_612x408.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nj2z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5c3c09-70ac-4f7a-ab05-f6fb270dedab_612x408.jpeg" width="612" height="408" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bf5c3c09-70ac-4f7a-ab05-f6fb270dedab_612x408.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:408,&quot;width&quot;:612,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Mean Girls Third Party Cookies &quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Mean Girls Third Party Cookies " title="Mean Girls Third Party Cookies " srcset="https://substackcdn.com/image/fetch/$s_!nj2z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5c3c09-70ac-4f7a-ab05-f6fb270dedab_612x408.jpeg 424w, https://substackcdn.com/image/fetch/$s_!nj2z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5c3c09-70ac-4f7a-ab05-f6fb270dedab_612x408.jpeg 848w, https://substackcdn.com/image/fetch/$s_!nj2z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5c3c09-70ac-4f7a-ab05-f6fb270dedab_612x408.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!nj2z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5c3c09-70ac-4f7a-ab05-f6fb270dedab_612x408.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I&#8217;m sure nobody wants that to happen, so 3PCD is definitely a need to avoid this from happening, but the collateral damage for doing that is benevolent actors who use 3rd party cookies for legitimate use cases.</p><p>Quizizz is one of those, where the content embedded in 3rd party websites is a critical use case for us, so we decided to pre-empt and prepare for this deprecation well ahead of the timeline. While there are many approaches to handle this deprecation, we decided to implement CHIPS and use Storage Access APIs to enable our integrations to work seamlessly in a 3rd party context.</p><h2>CHIPS: Cookies with Independent Partitioned States</h2><h3>What is CHIPS?</h3><p>Cookies Having Independent Partitioned States (CHIPS) is a browser mechanism that isolates cookies for different top-level sites. Think of it like creating separate, secure compartments for cookies from different websites, preventing cross-site tracking while maintaining necessary functionality. This would ensure there are separate cookie jars for each origin and 3rd party domain combinations so that there is no cross site cookie access. The following is a simplified implementation of how to set CHIPS from the backend.</p><h3>Core Implementation</h3><pre><code>public generatePartitionedCookie (response, params) {
  const { userId, domain, expiresIn = 86400 } = params;

  // Generate your session cookie value here
  const cookieValue = this.generateSecureToken(userId);

  // Create a CHIPS-compatible cookie configuration
  return response.cookie({
    name: 'quizizz_session_id',
    value: cookieValue,
    domain,
    maxAge: expiresIn,
    sameSite: 'none',  // Allows cross-site access
    secure: true,      // HTTPS only
    partitioned: true  // Enable CHIPS
  });
}</code></pre><h3>Advantages</h3><ul><li><p>Prevents cross-site tracking</p></li><li><p>Maintains session continuity for a given origin and 3rd party domain combination</p></li><li><p>Provides granular control over which context the cookie is stored</p></li><li><p>Enhances user privacy</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Y_lI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d75b964-5990-4ee4-b5ad-7e828ca0f9d9_1768x872.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Y_lI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d75b964-5990-4ee4-b5ad-7e828ca0f9d9_1768x872.png 424w, https://substackcdn.com/image/fetch/$s_!Y_lI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d75b964-5990-4ee4-b5ad-7e828ca0f9d9_1768x872.png 848w, https://substackcdn.com/image/fetch/$s_!Y_lI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d75b964-5990-4ee4-b5ad-7e828ca0f9d9_1768x872.png 1272w, https://substackcdn.com/image/fetch/$s_!Y_lI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d75b964-5990-4ee4-b5ad-7e828ca0f9d9_1768x872.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Y_lI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d75b964-5990-4ee4-b5ad-7e828ca0f9d9_1768x872.png" width="1456" height="718" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4d75b964-5990-4ee4-b5ad-7e828ca0f9d9_1768x872.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:718,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:780694,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Y_lI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d75b964-5990-4ee4-b5ad-7e828ca0f9d9_1768x872.png 424w, https://substackcdn.com/image/fetch/$s_!Y_lI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d75b964-5990-4ee4-b5ad-7e828ca0f9d9_1768x872.png 848w, https://substackcdn.com/image/fetch/$s_!Y_lI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d75b964-5990-4ee4-b5ad-7e828ca0f9d9_1768x872.png 1272w, https://substackcdn.com/image/fetch/$s_!Y_lI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d75b964-5990-4ee4-b5ad-7e828ca0f9d9_1768x872.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Storage Access API: Explicit Cross-Site Permission</h2><h3>Understanding the Storage Access API</h3><p>The Storage Access API provides a transparent mechanism for websites to request explicit permission to access storage across different domains. Unlike traditional third-party cookies, this approach gives users more control and visibility into how their data is accessed.</p><h3>Implementation Strategy</h3><pre><code>class StorageAccessManager {
  public async requestStorageAccess(): Promise&lt;boolean&gt; {
    try {
      // Check API availability
      if (!document.hasStorageAccess) {
        return false;
      }

      // Request cross-site storage access
      const accessGranted = await document.requestStorageAccess({
        types: ['cookies', 'localStorage', 'sessionStorage']
      });

      return accessGranted;
    } catch (error) {
      // Handle access request failures
      return false;
    }
  }
}</code></pre><p>This provides an explicit consent mechanism where the user can decide to choose to give access to cross site data. In most cases, critical information wouldn&#8217;t be kept in browser storage but accessing these stores when consent is not given is a problem as it will throw an error like below,</p><pre><code>Access to localStorage failed: SecurityError: Failed to read the 'localStorage' property from 'Window': Access is denied for this document.</code></pre><h2>Alternative Approaches to Cross-Site Authentication and Storage access</h2><p>While Quizizz has primarily implemented CHIPS, several alternative strategies exist for managing cross-site authentication and storage in the era of third-party cookie deprecation.</p><h3>1. Token based authentication</h3><p>Token-based authentication is a common way to overcome third-party cookie deprecation (3PCD) restrictions, particularly in web applications that rely on being able to access inside a 3rd party context. Here's a concise explanation of how it would work</p><ul><li><p>Token Issuance:</p><ul><li><p>The authentication server validates the user provided credentials.</p></li><li><p>If valid, it generates a JSON Web Token (JWT) or other token types (e.g., OAuth access token) and sends it back to the client in the response.</p></li></ul></li><li><p>The client stores the token securely, typically in:</p><ul><li><p>LocalStorage (persists across tabs but is prone to XSS).</p></li><li><p>SessionStorage (limited to the tab but less persistent).</p></li><li><p>In-Memory (best against XSS but cleared on refresh).</p></li></ul></li><li><p>For each subsequent request to a protected resource, the client includes the token in the Authorization header</p></li></ul><pre><code>import * as jwt from 'jsonwebtoken';
import * as crypto from 'crypto';

class SimpleJWT {
  private readonly SECRET_KEY;
  private readonly ISSUER: string = 'quizizz.com';

  constructor() {
    // Use a secure key for production instead
    this.SECRET_KEY = crypto.randomBytes(64).toString('hex');
  }

  public generateCrossDomainToken(options) {
    const { userId, userRole, domains } = options;

    return jwt.sign({
      sub: userId,           // Subject (user identifier)
      role: userRole,        // User role/permissions
      aud: domains,          // Intended audience (domains)
      iss: this.ISSUER,      // Token issuer
      exp: this.getExpiration(), // Expiration time
      iat: Date.now(),       // Issued at time
      jti: this.generateJTI() // Unique token identifier
    }, this.SECRET_KEY, {
      algorithm: 'HS256'
    });
  }
}</code></pre><p>Along with a custom authentication implementation, we can also leverage OpenID Connect (OIDC) which provides a cookie-free, token-based approach to user authentication. It relies on OAuth 2.0 to handle secure token exchange, enabling applications to authenticate users without relying on third-party cookies.</p><p>Here&#8217;s how you can integrate your application with <a href="https://developers.google.com/identity/openid-connect/openid-connect">Google OIDC</a> </p><pre><code>const { Issuer } = require('openid-client');

// Discover the OIDC provider's configuration
Issuer.discover('https://accounts.google.com') // Replace with your provider's URL
  .then((googleIssuer) =&gt; {
    const client = new googleIssuer.Client({
      client_id: 'YOUR_CLIENT_ID',
      client_secret: 'YOUR_CLIENT_SECRET',
      redirect_uris: ['http://localhost:3000/callback'], // Adjust to your setup
      response_types: ['code'],
    });

    // Redirect users to the authorization URL
    const authUrl = client.authorizationUrl({
      scope: 'openid email profile',
    });

    // Exchange the authorization code for tokens (e.g., in a callback route)
    app.get('/callback', async (req, res) =&gt; {
      const params = client.callbackParams(req);
      const tokenSet = await client.callback('http://localhost:3000/callback', params);
      console.log('Tokens received:', tokenSet);

      const userInfo = await client.userinfo(tokenSet.access_token);
   // Handle user session with the information obtained using the open ID token
      res.send('Authentication successful');
    });
  })
  .catch((err) =&gt; console.error('Error with OIDC setup:', err));</code></pre><p>Similar to google, we can use any OIDC identity providers like Microsoft, Okta, Auth0 etc., we can refer to all available certified OIDC implementations <a href="https://openid.net/certification/#OPENID-OP-P">here</a>.</p><p>On top of OIDC being a tested standard for authentication, once we&#8217;ve added support for authenticating users through OIDC in an application, it makes it trivial to support more SSOs going forward.</p><h3>2. Federated Identity Management (FedCM)</h3><p>FedCM is a privacy-preserving alternative to third-party cookies that enables federated identity login flows without relying on cross-site cookies. It facilitates authentication through identity providers (IdPs) while adhering to modern web privacy standards.</p><h3>How FedCM Overcomes 3PCD Restrictions</h3><ul><li><p>No third-party cookies: It uses browser-mediated communication between Relying Parties (RPs) and IdPs.</p></li><li><p>User consent: The browser ensures that users are aware of and consent to sharing their identity.</p></li><li><p>Reduced tracking: Only essential data is shared, minimizing potential user tracking across sites.</p></li></ul><h3>Basic Workflow</h3><ol><li><p>User initiates login on the Relying Party (RP) website.</p></li><li><p>RP requests identity from the browser using FedCM.</p></li><li><p>Browser contacts the IdP to fetch identity information.</p></li><li><p>Users consent to sharing their identity.</p></li><li><p>IdP returns a token (e.g., OpenID token).</p></li><li><p>RP verifies the token and logs in the user.</p></li></ol><h4>1. Client-Side: Request Identity</h4><p>The RP (your web app) initiates the authentication flow using the FedCM API.</p><pre><code>async function loginWithIdentityProvider() {
  try {
    const result = await navigator.identity.get({
      provider: 'https://idp.example.com',
      // Optional: Specify a nonce for additional security
      nonce: 'random-string-for-verification',
    });

    if (result) {
      // Send the token to your server for verification
      await fetch('/api/auth/callback', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ token: result.token }),
      });

      console.log('User successfully logged in.');
    }
  } catch (error) {
    console.error('Login failed:', error);
  }
}</code></pre><h4>2. IdP Implementation</h4><p>The identity provider serves a configuration endpoint for the browser and handles authentication.</p><ul><li><p>/.well-known/fedcm.json (IdP metadata configuration):</p></li></ul><pre><code>{
  "id_assertion_endpoint": "https://idp.example.com/assertion",
  "accounts_endpoint": "https://idp.example.com/accounts",
  "branding": {
    "background_color": "#FFFFFF",
    "icon_url": "https://idp.example.com/logo.png"
  }
}</code></pre><ul><li><p>an Endpoint for identity assertion</p></li></ul><pre><code>app.post('/assertion', (req, res) =&gt; {
  const { clientId, nonce } = req.body;

  // Validate client and nonce, then issue a token
  const token = generateOpenIDToken({
    sub: 'user123',
    aud: clientId,
    nonce,
  });

  res.json({ token });
});</code></pre><h4>3. Server-Side: Validate Token</h4><p>The RP&#8217;s backend validates the token received from the client.</p><pre><code>const jwt = require('jsonwebtoken');

app.post('/api/auth/callback', (req, res) =&gt; {
  const { token } = req.body;

  try {
    const decoded = jwt.verify(token, 'your-public-key', {
      audience: 'your-client-id',
      issuer: 'https://idp.example.com',
    });

    // User is authenticated
    req.session.user = decoded.sub;
    res.status(200).json({ message: 'Login successful' });
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
});</code></pre><h3>Key Benefits of FedCM</h3><ul><li><p>User privacy: Reduces tracking by limiting shared information.</p></li><li><p>Improved UX: Native browser consent flow simplifies login.</p></li><li><p>No reliance on third-party cookies: Secure and privacy-focused federated login.</p></li></ul><h3>Limited Support</h3><p>FedCM is still under development and adoption. It works in Chromium-based browsers with certain flags enabled (chrome://flags/#fedcm). But other major browsers like Firefox and Safari do not support it.</p><h3>3. Related Website sets</h3><p>Related Website Sets is a privacy-preserving mechanism introduced by Google to help websites that are part of the same organizational group maintain cross-site functionality after third-party cookies are phased out. Think of it like a family pass for websites owned by the same company.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!e8QW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F271915c1-6b27-4b81-8989-e24a5810b65e_2048x891.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!e8QW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F271915c1-6b27-4b81-8989-e24a5810b65e_2048x891.png 424w, https://substackcdn.com/image/fetch/$s_!e8QW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F271915c1-6b27-4b81-8989-e24a5810b65e_2048x891.png 848w, https://substackcdn.com/image/fetch/$s_!e8QW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F271915c1-6b27-4b81-8989-e24a5810b65e_2048x891.png 1272w, https://substackcdn.com/image/fetch/$s_!e8QW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F271915c1-6b27-4b81-8989-e24a5810b65e_2048x891.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!e8QW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F271915c1-6b27-4b81-8989-e24a5810b65e_2048x891.png" width="1456" height="633" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/271915c1-6b27-4b81-8989-e24a5810b65e_2048x891.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:633,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Diagram showing brandx.com, fly-brandx.com and drive-brandx.com as one group and example.com, example.rs, example.co.uk as another group.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Diagram showing brandx.com, fly-brandx.com and drive-brandx.com as one group and example.com, example.rs, example.co.uk as another group." title="Diagram showing brandx.com, fly-brandx.com and drive-brandx.com as one group and example.com, example.rs, example.co.uk as another group." srcset="https://substackcdn.com/image/fetch/$s_!e8QW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F271915c1-6b27-4b81-8989-e24a5810b65e_2048x891.png 424w, https://substackcdn.com/image/fetch/$s_!e8QW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F271915c1-6b27-4b81-8989-e24a5810b65e_2048x891.png 848w, https://substackcdn.com/image/fetch/$s_!e8QW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F271915c1-6b27-4b81-8989-e24a5810b65e_2048x891.png 1272w, https://substackcdn.com/image/fetch/$s_!e8QW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F271915c1-6b27-4b81-8989-e24a5810b65e_2048x891.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Key Concepts:</p><ul><li><p>Allows a set of related websites to share limited cross-site access</p></li><li><p>Helps maintain functionality for sites with related domains</p></li><li><p>Requires explicit declaration and verification by site owners</p></li></ul><p>Here&#8217;s a practical example to illustrate how to implement Related Website Sets:</p><pre><code>// Registering a Related Website Set
async function registerRelatedWebsiteSet() {
  if ('relatedWebsites' in navigator) {
    try {
      // Declare primary site and associated sites
      const registrationResult = await navigator.relatedWebsites.register({
        primary: 'https://main-site.com',
        associatedSites: [
          'https://analytics.main-site.com',
          'https://payments.main-site.com',
        ]
      });

      // Check registration status
      if (registrationResult.status === 'success') {
        console.log('Related Website Set registered successfully');
      }
    } catch (error) {
      console.error('Related Website Set registration failed:', error);
    }
  }
}

// Check if a site is part of a Related Website Set
async function checkRelatedWebsiteSet() {
  if ('relatedWebsites' in navigator) {
    const isRelated = await navigator.relatedWebsites.isRelatedWebsite();
    console.log('Is this site part of a Related Website Set?', isRelated);
  }
}</code></pre><p>Implementation Steps:</p><ol><li><p>Site owners must declare their Related Website Set through Google Search Console by hosting a file at /.well-known/related-website-set.json similar to the following under your main domain,</p></li></ol><pre><code>{
  "primary": "https://main-site.com",
  "associatedSites": ["https://analytics.main-site.com", "https://payments.main-site.com"],
  "description": "Example Related Websites"
}</code></pre><ol start="2"><li><p>Websites must meet specific criteria (same organization, related purpose)</p></li><li><p>Use the navigator.relatedWebsites API to register and check set membership</p></li></ol><p>Limitations:</p><ul><li><p>Limited to a maximum of 5 associated sites</p></li><li><p>Requires explicit registration and verification</p></li><li><p>Not a complete replacement for third-party cookies</p></li></ul><h2>Quick Compatibility Testing</h2><p>Follow these steps to quickly see if your web application is compatible with the guidelines or not. Run these diagnostics when your web application loads inside an iFrame or is embedded within another website.</p><h3>1. Simulate Third-Party Cookie Restrictions</h3><p>Use Chrome&#8217;s built-in flags to simulate third-party cookie restrictions, you can also find the same flag if you visit chrome://flags</p><pre><code># Chrome launch command with third-party cookies blocked
google-chrome --disable-third-party-cookies</code></pre><h3>2. Basic Compatibility Diagnostic Script</h3><pre><code>function diagnoseThirdPartyCookieImpact() {
  try {
    document.hasStorageAccess().then((hasAccess) =&gt; {
      if (hasAccess) {
    &#9;// storage access has been granted already.
      } else {
    &#9;// storage access hasn't been granted already;
    &#9;// you may want to call requestStorageAccess().
      }
    });
  } catch (error) {
    console.error('Cookie storage test failed', error);
  }
}

window.addEventListener('load', diagnoseThirdPartyCookieImpact);</code></pre><h2>Conclusion</h2><p>Adhering to third-party cookie deprecation guidelines requires a thorough examination of cookie and storage usage of web applications. By implementing one of these authentication strategies, platforms can ensure they maintain a predictable user experience across different domains.</p><p>If you found this interesting and want to solve similar interesting, deep technical problems, we&#8217;re looking for you at Quizizz. We&#8217;re looking for folks across Frontend, Backend, DevOps roles. Apply below!</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://quizizz.com/careers&quot;,&quot;text&quot;:&quot;See Open Roles&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://quizizz.com/careers"><span>See Open Roles</span></a></p><h2>References</h2><ul><li><p><a href="https://developers.google.com/privacy-sandbox/cookies">https://developers.google.com/privacy-sandbox/cookies</a></p></li><li><p><a href="https://developers.google.com/privacy-sandbox/cookies/prepare/audit-cookies">https://developers.google.com/privacy-sandbox/cookies/prepare/audit-cookies</a></p></li><li><p><a href="https://developers.google.com/privacy-sandbox/cookies/prepare/test-for-breakage">https://developers.google.com/privacy-sandbox/cookies/prepare/test-for-breakage</a></p></li><li><p><a href="https://developers.google.com/privacy-sandbox/cookies/chips">https://developers.google.com/privacy-sandbox/cookies/chips</a></p></li><li><p><a href="https://developers.google.com/privacy-sandbox/cookies/storage-access-api">https://developers.google.com/privacy-sandbox/cookies/storage-access-api</a></p></li><li><p><a href="https://developer.mozilla.org/en-US/docs/Web/API/FedCM_API">https://developer.mozilla.org/en-US/docs/Web/API/FedCM_API</a></p></li><li><p><a href="https://github.com/GoogleChrome/related-website-sets/blob/main/RWS-Submission_Guidelines.md#set-formation-requirements">https://github.com/GoogleChrome/related-website-sets/blob/main/RWS-Submission_Guidelines.md#set-formation-requirements</a></p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://eng.quizizz.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Quizizz Engineering! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[How we built our Data Platform at Quizizz]]></title><description><![CDATA[Every day, millions of teachers and learners on Quizizz generate a goldmine of data.]]></description><link>https://eng.quizizz.com/p/building-a-robust-data-platform</link><guid isPermaLink="false">https://eng.quizizz.com/p/building-a-robust-data-platform</guid><dc:creator><![CDATA[Parth]]></dc:creator><pubDate>Fri, 22 Nov 2024 05:01:22 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/d7cbc41a-089a-4424-bba4-77ffb6c843c7_1154x649.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every day, millions of teachers and learners on Quizizz generate a goldmine of data. But how do we turn all that data into meaningful, actionable insights that continually improve our platform for them? Enter our new Data Platform Team, tasked with bringing data to life and driving impactful learning across classrooms worldwide. Here&#8217;s a peek behind the curtain at the foundation we&#8217;re building for anyone on a similar journey either as a lone analyst in a young startup or an analytics leader at a company looking to overhaul its data stack.</p><p>As you read through, if you have any feedback for us, we&#8217;d love to hear from you in the comments below!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Y5B2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc5174b5-a9ed-478b-949f-a9a9fef522f3_500x567.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Y5B2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc5174b5-a9ed-478b-949f-a9a9fef522f3_500x567.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Y5B2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc5174b5-a9ed-478b-949f-a9a9fef522f3_500x567.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Y5B2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc5174b5-a9ed-478b-949f-a9a9fef522f3_500x567.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Y5B2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc5174b5-a9ed-478b-949f-a9a9fef522f3_500x567.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Y5B2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc5174b5-a9ed-478b-949f-a9a9fef522f3_500x567.jpeg" width="500" height="567" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cc5174b5-a9ed-478b-949f-a9a9fef522f3_500x567.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:567,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Y5B2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc5174b5-a9ed-478b-949f-a9a9fef522f3_500x567.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Y5B2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc5174b5-a9ed-478b-949f-a9a9fef522f3_500x567.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Y5B2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc5174b5-a9ed-478b-949f-a9a9fef522f3_500x567.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Y5B2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc5174b5-a9ed-478b-949f-a9a9fef522f3_500x567.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">This is how we we were before we revamped our Data Platform</figcaption></figure></div><h1>Data Sources</h1><p>Let&#8217;s start off with our data sources at Quizizz. Primarily, there are two categories of data sources that we consume:</p><ul><li><p>Internal Data: This contains all the data generated by Quizizz, primarily derived from usage of the product</p></li><li><p>External Data: This is the data we source from all the 3rd party tools we use</p></li></ul><h2>Internal Data</h2><p>Our platform&#8217;s data backbone is MongoDB, with additional layers from:</p><ul><li><p>Cassandra: Stores in-product reporting data</p></li><li><p>Redis: Temporary store for caching as well as other use cases</p></li><li><p>ElasticSearch: Serves search results and recommendations</p></li><li><p>Clickhouse: Serve real-time analytics to our end users</p></li><li><p>Pinecone: Recent addition to power AI features</p></li></ul><p>Most of our internal data is ETL'd from MongoDB. The system reads chunks of Mongo Operations log (<strong>oplog</strong>) once every 30 minutes and writes to BigQuery. The data is not modeled in any way, and lands in bigQuery as a JSON object in a single column.</p><h2>External Data</h2><p>Beyond our own platform, we leverage a powerhouse lineup of external tools that keep our data ecosystem dynamic and robust.&nbsp; We use:</p><ul><li><p><a href="https://stripe.com">Stripe</a></p></li><li><p><a href="https://salesforce.com">Salesforce</a></p></li><li><p><a href="https://hubspot.com">Hubspot</a></p></li><li><p><a href="https://braze.com">Braze</a></p></li><li><p><a href="https://zendesk.com">Zendesk</a>, just to name a few</p></li></ul><p>To bring all this data together, we rely on <strong><a href="https://fivetran.com">Fivetran</a></strong> for data integration. It&#8217;s our go-to for ETL, for pulling data into our warehouse from these various sources, though we have had some twists along the way like migrating from Stitch to address model completeness (for instance, we always struggled to get full Stripe subscription data).</p><p>One heads-up: Fivetran&#8217;s billing based on Monthly Active Rows (MARs) keeps us on our toes with its dynamic pricing, making it tough to nail down monthly costs. We&#8217;re learning as we go, and here&#8217;s a quick tip we picked up:&nbsp;</p><blockquote><p><strong>Tip:</strong> Fivetran doesn&#8217;t charge for the first 2 weeks of a new sync, so explore every table during this grace period! Once you see what&#8217;s useful, refine your selections to avoid unnecessary costs &#8212; selecting all tables could mean a surprise bill if unchecked; a possible bankruptcy, too!</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Oam6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23410ca8-7a77-4a6e-86a2-aa935b8872d3_691x201.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Oam6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23410ca8-7a77-4a6e-86a2-aa935b8872d3_691x201.png 424w, https://substackcdn.com/image/fetch/$s_!Oam6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23410ca8-7a77-4a6e-86a2-aa935b8872d3_691x201.png 848w, https://substackcdn.com/image/fetch/$s_!Oam6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23410ca8-7a77-4a6e-86a2-aa935b8872d3_691x201.png 1272w, https://substackcdn.com/image/fetch/$s_!Oam6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23410ca8-7a77-4a6e-86a2-aa935b8872d3_691x201.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Oam6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23410ca8-7a77-4a6e-86a2-aa935b8872d3_691x201.png" width="691" height="201" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/23410ca8-7a77-4a6e-86a2-aa935b8872d3_691x201.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:201,&quot;width&quot;:691,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Oam6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23410ca8-7a77-4a6e-86a2-aa935b8872d3_691x201.png 424w, https://substackcdn.com/image/fetch/$s_!Oam6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23410ca8-7a77-4a6e-86a2-aa935b8872d3_691x201.png 848w, https://substackcdn.com/image/fetch/$s_!Oam6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23410ca8-7a77-4a6e-86a2-aa935b8872d3_691x201.png 1272w, https://substackcdn.com/image/fetch/$s_!Oam6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23410ca8-7a77-4a6e-86a2-aa935b8872d3_691x201.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>For certain elements which cannot be synced directly via Fivetran, we have our own custom scripts to fill in the gaps.</p><h1>Transformations</h1><p>Once data lands in our warehouse, the magic really begins!</p><p>Using <strong><a href="https://www.getdbt.com/">dbt</a></strong> (Data Build Tool), we streamline and structure our data so it&#8217;s not only just useful but also impactful. With dbt, our team handles transformations in BigQuery without relying on engineering for every change. This flexibility means we can adjust and optimize data models quickly, ensuring the freshest insights are just a query away.&nbsp;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SZ79!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ecbeeca-11a3-43ec-af39-017b59a460a7_500x500.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SZ79!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ecbeeca-11a3-43ec-af39-017b59a460a7_500x500.jpeg 424w, https://substackcdn.com/image/fetch/$s_!SZ79!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ecbeeca-11a3-43ec-af39-017b59a460a7_500x500.jpeg 848w, https://substackcdn.com/image/fetch/$s_!SZ79!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ecbeeca-11a3-43ec-af39-017b59a460a7_500x500.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!SZ79!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ecbeeca-11a3-43ec-af39-017b59a460a7_500x500.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SZ79!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ecbeeca-11a3-43ec-af39-017b59a460a7_500x500.jpeg" width="500" height="500" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3ecbeeca-11a3-43ec-af39-017b59a460a7_500x500.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:500,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!SZ79!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ecbeeca-11a3-43ec-af39-017b59a460a7_500x500.jpeg 424w, https://substackcdn.com/image/fetch/$s_!SZ79!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ecbeeca-11a3-43ec-af39-017b59a460a7_500x500.jpeg 848w, https://substackcdn.com/image/fetch/$s_!SZ79!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ecbeeca-11a3-43ec-af39-017b59a460a7_500x500.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!SZ79!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ecbeeca-11a3-43ec-af39-017b59a460a7_500x500.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>As of late 2024, our pipeline boasts about 800 data models, organized into two core "layers":</p><p>- <strong>The Base Layer</strong>: This is where raw data gets its first polish. Think of it as our prep station for everything that enters BigQuery. It&#8217;s also where we tackle JSON files, ensuring naming consistency, data types, and streamlined formats to support smooth downstream analysis.&nbsp;</p><p>- <strong>The Consumption Layer</strong>: This is where each team can access insights tailored to their specific needs. With datasets aligned to pods and team use cases, this layer empowers analysts across Quizizz to find the data they need and build reports that drive real results.</p><p>Both layers refresh within the same command in the same dbt job, and their refreshes are sequential.</p><p>The cost for refreshing both layers is about $150/day, a small investment for the clarity it brings across teams.</p><h1>Self-Serve Analytics</h1><p>Now, we can make more sense with the data we have and start doing analytics on top of it. We use a variety of tools to make it seamless for people from different functions in the company to be able to get answers to their questions.</p><p>To make this possible, we host a self-managed instance of <strong><a href="https://metabase.com">Metabase</a></strong>, chosen for its intuitive query builder that anyone can use. After trialing Superset and Redash, we landed on Metabase based on its user-friendly design and internal user feedback. Today, teams create and curate dashboards, pull insights on demand, and answer many of their data questions without waiting on analysts. With enriched fields and ready-to-use aggregates in our clean dataset, we&#8217;re seeing more autonomy and more impactful decisions driven directly by data.</p><blockquote><p><strong>Fun fact:</strong> Self-hosting Metabase via AWS Elastic Beanstalk costs us just $70/month. With the average querying cost around $25/day, it&#8217;s been a budget-friendly way to democratize data across the org.</p></blockquote><h2>Custom Tools</h2><p>For those projects needing a bit more power, we turn to Hex, a platform that combines the versatility of a Python environment with an interactive dashboard setup. This lets us create internal tools that respond to user input, like our:&nbsp;</p><h3>Funnel Builder</h3><p>Built atop the modeled events data, this is an easy-to-use dashboard that helps Product Managers and Designers visualize product funnels. They can filter on various user/event attributes. This has greatly reduced the dependency of product stakeholders on product analysts to understand our user funnels.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yVtj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Face328db-917d-4ead-9568-4c5597a74c3e_1159x1226.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yVtj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Face328db-917d-4ead-9568-4c5597a74c3e_1159x1226.png 424w, https://substackcdn.com/image/fetch/$s_!yVtj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Face328db-917d-4ead-9568-4c5597a74c3e_1159x1226.png 848w, https://substackcdn.com/image/fetch/$s_!yVtj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Face328db-917d-4ead-9568-4c5597a74c3e_1159x1226.png 1272w, https://substackcdn.com/image/fetch/$s_!yVtj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Face328db-917d-4ead-9568-4c5597a74c3e_1159x1226.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yVtj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Face328db-917d-4ead-9568-4c5597a74c3e_1159x1226.png" width="1159" height="1226" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ace328db-917d-4ead-9568-4c5597a74c3e_1159x1226.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1226,&quot;width&quot;:1159,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!yVtj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Face328db-917d-4ead-9568-4c5597a74c3e_1159x1226.png 424w, https://substackcdn.com/image/fetch/$s_!yVtj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Face328db-917d-4ead-9568-4c5597a74c3e_1159x1226.png 848w, https://substackcdn.com/image/fetch/$s_!yVtj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Face328db-917d-4ead-9568-4c5597a74c3e_1159x1226.png 1272w, https://substackcdn.com/image/fetch/$s_!yVtj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Face328db-917d-4ead-9568-4c5597a74c3e_1159x1226.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Experimentation Dashboard</h3><p>This dashboard lets product stakeholders easily access A/B test results. We&#8217;ve curated our L0 metrics and given the flexibility to perform A/B tests on conversion rates between any 2 events or page views. The dashboard also renders the underlying SQL queries for others to augment them for their special use cases.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5Qvk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9034437-6211-4b45-bdf4-fcc6c6723ea9_1600x938.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5Qvk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9034437-6211-4b45-bdf4-fcc6c6723ea9_1600x938.png 424w, https://substackcdn.com/image/fetch/$s_!5Qvk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9034437-6211-4b45-bdf4-fcc6c6723ea9_1600x938.png 848w, https://substackcdn.com/image/fetch/$s_!5Qvk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9034437-6211-4b45-bdf4-fcc6c6723ea9_1600x938.png 1272w, https://substackcdn.com/image/fetch/$s_!5Qvk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9034437-6211-4b45-bdf4-fcc6c6723ea9_1600x938.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5Qvk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9034437-6211-4b45-bdf4-fcc6c6723ea9_1600x938.png" width="1456" height="854" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c9034437-6211-4b45-bdf4-fcc6c6723ea9_1600x938.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:854,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!5Qvk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9034437-6211-4b45-bdf4-fcc6c6723ea9_1600x938.png 424w, https://substackcdn.com/image/fetch/$s_!5Qvk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9034437-6211-4b45-bdf4-fcc6c6723ea9_1600x938.png 848w, https://substackcdn.com/image/fetch/$s_!5Qvk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9034437-6211-4b45-bdf4-fcc6c6723ea9_1600x938.png 1272w, https://substackcdn.com/image/fetch/$s_!5Qvk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9034437-6211-4b45-bdf4-fcc6c6723ea9_1600x938.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>Reverse ETL</h1><p>With rich user data now in our warehouse, we can also now power data flowing back into the 3rd party tools we use, empowering multiple functions like GTM, sales, and success teams with powerful metrics to engage with our users more effectively. This way, our teams have up-to-date insights right where they need them, keeping communication targeted and relevant.</p><p>The logic for the enrichment is maintained in dbt, in our <strong>reverse_ETL </strong>dataset. While tools like Hightouch and Census offer reverse ETL solutions, we found their pricing models a bit priced for the value they provide. Instead, we handle this with Python and Golang scripts customized to our specific needs.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_swi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb81a23e1-aa61-40d6-b5b9-2e71a78bd4a2_800x449.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_swi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb81a23e1-aa61-40d6-b5b9-2e71a78bd4a2_800x449.jpeg 424w, https://substackcdn.com/image/fetch/$s_!_swi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb81a23e1-aa61-40d6-b5b9-2e71a78bd4a2_800x449.jpeg 848w, https://substackcdn.com/image/fetch/$s_!_swi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb81a23e1-aa61-40d6-b5b9-2e71a78bd4a2_800x449.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!_swi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb81a23e1-aa61-40d6-b5b9-2e71a78bd4a2_800x449.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_swi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb81a23e1-aa61-40d6-b5b9-2e71a78bd4a2_800x449.jpeg" width="493" height="276.69625" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b81a23e1-aa61-40d6-b5b9-2e71a78bd4a2_800x449.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:449,&quot;width&quot;:800,&quot;resizeWidth&quot;:493,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_swi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb81a23e1-aa61-40d6-b5b9-2e71a78bd4a2_800x449.jpeg 424w, https://substackcdn.com/image/fetch/$s_!_swi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb81a23e1-aa61-40d6-b5b9-2e71a78bd4a2_800x449.jpeg 848w, https://substackcdn.com/image/fetch/$s_!_swi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb81a23e1-aa61-40d6-b5b9-2e71a78bd4a2_800x449.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!_swi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb81a23e1-aa61-40d6-b5b9-2e71a78bd4a2_800x449.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Haven&#8217;t we all gone through this?</figcaption></figure></div><h1>What&#8217;s next?</h1><p>We&#8217;re just getting started. As we look ahead, our focus is on taking our data capabilities to the next level, adding both depth and efficiency to our processes:</p><h2>More Product Analytics Tools</h2><p>We&#8217;re working on releasing more interval analytics tools such as cohort builder, trend builder, and product flow builder to empower all functions to be able to do more.</p><h2>Orchestration Framework</h2><p>The warehouse is becoming more ubiquitous, with pipelines making a stop via it. Managing cron jobs is becoming more difficult and we want to move away from the current model.</p><p>With these projects in the pipeline, we're gearing up for an exciting close to 2024, strengthening Quizizz into a more data-powered platform for our teams, educators, and students worldwide!</p><div><hr></div><p>We&#8217;re on the lookout for people who can help us build the next version of our Data Platform as well as our core product. If you are passionate about building great consumer products, have a knack for tinkering with newer technologies and you like to work with great minds, Quizizz is the place to be.</p><p>We&#8217;re hiring across all functions and levels within engineering and we want you to be part of this amazing journey ahead.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://quizizz.com/home/careers&quot;,&quot;text&quot;:&quot;Our Careers page&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://quizizz.com/home/careers"><span>Our Careers page</span></a></p>]]></content:encoded></item><item><title><![CDATA[Building AI powered products | Part 1: Getting Started]]></title><description><![CDATA[To AI and beyond!]]></description><link>https://eng.quizizz.com/p/building-ai-powered-products-part</link><guid isPermaLink="false">https://eng.quizizz.com/p/building-ai-powered-products-part</guid><dc:creator><![CDATA[Gaurav Chandak]]></dc:creator><pubDate>Mon, 04 Nov 2024 07:25:04 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/26933f3b-78c0-4495-9ecd-9b5acbae1234_1920x1080.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The current landscape of advanced AI models has created a huge shift in what is possible through this technology. Almost every company is trying to add AI to their products or pivoting to AI-native products. While getting started with these new technologies is much easier than traditional ML solutions, it still requires some understanding of how these models work to effectively use them to build products.</p><p>In this blog, we'll discuss how we started building AI-based products at Quizizz. We will start with a basic understanding of how these models work followed by some simple and effective prompt engineering techniques.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RoMz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe09d1b-d0be-46e4-841b-916a2ba910c5_500x500.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RoMz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe09d1b-d0be-46e4-841b-916a2ba910c5_500x500.jpeg 424w, https://substackcdn.com/image/fetch/$s_!RoMz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe09d1b-d0be-46e4-841b-916a2ba910c5_500x500.jpeg 848w, https://substackcdn.com/image/fetch/$s_!RoMz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe09d1b-d0be-46e4-841b-916a2ba910c5_500x500.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!RoMz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe09d1b-d0be-46e4-841b-916a2ba910c5_500x500.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RoMz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe09d1b-d0be-46e4-841b-916a2ba910c5_500x500.jpeg" width="500" height="500" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fbe09d1b-d0be-46e4-841b-916a2ba910c5_500x500.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:500,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Building AI Products = Calling GPT APIs&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Building AI Products = Calling GPT APIs" title="Building AI Products = Calling GPT APIs" srcset="https://substackcdn.com/image/fetch/$s_!RoMz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe09d1b-d0be-46e4-841b-916a2ba910c5_500x500.jpeg 424w, https://substackcdn.com/image/fetch/$s_!RoMz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe09d1b-d0be-46e4-841b-916a2ba910c5_500x500.jpeg 848w, https://substackcdn.com/image/fetch/$s_!RoMz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe09d1b-d0be-46e4-841b-916a2ba910c5_500x500.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!RoMz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe09d1b-d0be-46e4-841b-916a2ba910c5_500x500.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>Understanding Large Language Models (LLMs)</strong></h3><p>LLMs, such as GPT-4o or Claude 3.5 Sonnet, are language models trained on vast amounts of data. Their primary job is to predict the next word (token) in a sequence, much like an advanced autocomplete system.</p><p>LLMs analyze the context provided by previous words to predict what should come next. This predictive ability makes them effective at understanding language and following instructions or answering questions.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aOp3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc336701e-55cc-4943-8785-2955f7237725_831x190.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aOp3!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc336701e-55cc-4943-8785-2955f7237725_831x190.gif 424w, https://substackcdn.com/image/fetch/$s_!aOp3!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc336701e-55cc-4943-8785-2955f7237725_831x190.gif 848w, https://substackcdn.com/image/fetch/$s_!aOp3!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc336701e-55cc-4943-8785-2955f7237725_831x190.gif 1272w, https://substackcdn.com/image/fetch/$s_!aOp3!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc336701e-55cc-4943-8785-2955f7237725_831x190.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aOp3!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc336701e-55cc-4943-8785-2955f7237725_831x190.gif" width="831" height="190" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c336701e-55cc-4943-8785-2955f7237725_831x190.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:190,&quot;width&quot;:831,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;How does an LLM predict the next word&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="How does an LLM predict the next word" title="How does an LLM predict the next word" srcset="https://substackcdn.com/image/fetch/$s_!aOp3!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc336701e-55cc-4943-8785-2955f7237725_831x190.gif 424w, https://substackcdn.com/image/fetch/$s_!aOp3!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc336701e-55cc-4943-8785-2955f7237725_831x190.gif 848w, https://substackcdn.com/image/fetch/$s_!aOp3!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc336701e-55cc-4943-8785-2955f7237725_831x190.gif 1272w, https://substackcdn.com/image/fetch/$s_!aOp3!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc336701e-55cc-4943-8785-2955f7237725_831x190.gif 1456w" sizes="100vw"></picture><div></div></div></a></figure></div><p>Most LLMs undergo fine-tuning on specific datasets, which enhances their abilities for various tasks. For example, models used by ChatGPT are further trained on conversation-based datasets to make them proficient in handling interactive dialogues.</p><h3><strong>Choosing the Right Model</strong></h3><p>Our primary use case at Quizizz was to reduce the amount of time and effort a teacher would have to spend in creating their assessment. We wanted to make sure that we use the right model that was lightweight, fast, inexpensive and is effective at generating the right content.</p><p>Here are some common options:</p><ul><li><p><strong>OpenAI&#8217;s GPT Models</strong></p></li><li><p><strong>Anthropic&#8217;s Claude Models</strong></p></li><li><p><strong>Google&#8217;s Gemini Models</strong></p></li><li><p><strong>Meta&#8217;s Llama Models</strong></p></li></ul><p>Smaller models are <strong>faster and cheaper</strong> than larger models but are generally <strong>less capable</strong> than larger models in complex tasks</p><p>We chose OpenAI&#8217;s GPT models while we were experimenting with integrating AI with Quizizz. GPT-3.5 was one of the first models we integrated with and gave us decent results. But as we started getting better models like GPT-4 and then 4o, we were quick to experiment and switch. We kept using GPT-3.5 for simpler tasks. We wanted to always optimize for cost without compromising on quality ensuring that teachers still had a great experience with AI.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ey59!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b8fe662-5b15-4840-8c40-33749d80e7a0_500x561.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ey59!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b8fe662-5b15-4840-8c40-33749d80e7a0_500x561.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Ey59!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b8fe662-5b15-4840-8c40-33749d80e7a0_500x561.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Ey59!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b8fe662-5b15-4840-8c40-33749d80e7a0_500x561.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Ey59!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b8fe662-5b15-4840-8c40-33749d80e7a0_500x561.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ey59!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b8fe662-5b15-4840-8c40-33749d80e7a0_500x561.jpeg" width="500" height="561" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0b8fe662-5b15-4840-8c40-33749d80e7a0_500x561.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:561,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Ey59!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b8fe662-5b15-4840-8c40-33749d80e7a0_500x561.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Ey59!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b8fe662-5b15-4840-8c40-33749d80e7a0_500x561.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Ey59!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b8fe662-5b15-4840-8c40-33749d80e7a0_500x561.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Ey59!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b8fe662-5b15-4840-8c40-33749d80e7a0_500x561.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>Getting the Most from Your Model: Prompt Engineering</strong></h3><p><strong>Prompt engineering</strong> is the art of designing effective prompts to get the best response from an LLM. A well-crafted prompt can significantly enhance the quality of the output.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Wds1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840aba40-138c-4cfe-b1a6-73da754afec5_600x321.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Wds1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840aba40-138c-4cfe-b1a6-73da754afec5_600x321.gif 424w, https://substackcdn.com/image/fetch/$s_!Wds1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840aba40-138c-4cfe-b1a6-73da754afec5_600x321.gif 848w, https://substackcdn.com/image/fetch/$s_!Wds1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840aba40-138c-4cfe-b1a6-73da754afec5_600x321.gif 1272w, https://substackcdn.com/image/fetch/$s_!Wds1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840aba40-138c-4cfe-b1a6-73da754afec5_600x321.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Wds1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840aba40-138c-4cfe-b1a6-73da754afec5_600x321.gif" width="600" height="321" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/840aba40-138c-4cfe-b1a6-73da754afec5_600x321.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:321,&quot;width&quot;:600,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:506448,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Wds1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840aba40-138c-4cfe-b1a6-73da754afec5_600x321.gif 424w, https://substackcdn.com/image/fetch/$s_!Wds1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840aba40-138c-4cfe-b1a6-73da754afec5_600x321.gif 848w, https://substackcdn.com/image/fetch/$s_!Wds1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840aba40-138c-4cfe-b1a6-73da754afec5_600x321.gif 1272w, https://substackcdn.com/image/fetch/$s_!Wds1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840aba40-138c-4cfe-b1a6-73da754afec5_600x321.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h4><strong>Be Direct and Specific</strong></h4><p>At Quizizz, we started with a simple prompt to create questions based on the subject/topic entered by the teacher. This was one of the earliest versions we used which gave us great feedback from the teachers on how we could improve it.</p><p>It&#8217;s important to keep the prompt as simple as possible for the model to understand and then act upon. Few tips on how to be direct and specific:</p><ul><li><p>Avoid ambiguous words and phrases</p></li><li><p>Specify the desired response format, the expected length, and the tone of the prompt.</p></li><li><p>Be as objective as possible.</p></li><li><p>Do not mention redundant details in the prompt.</p></li><li><p>Break down the task into smaller and easier sub-tasks.</p></li><li><p>Do not ask leading questions if you don&#8217;t want leading answers.</p></li><li><p>Instead of saying what not to do, say what to do.</p></li></ul><h4><strong>Few-shot Prompting</strong></h4><p>One of the things that teachers want their students to have is mastery over what they are learning. And they usually do that by providing answer explanations after a problem is attempted. We started generating answer explanations given a question based on the techniques mentioned above. While that worked well, the answer explanations were not very consistent and sometimes had mistakes.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!m_G9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de91f9-9923-4b19-8732-31dd5c896be6_600x302.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!m_G9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de91f9-9923-4b19-8732-31dd5c896be6_600x302.gif 424w, https://substackcdn.com/image/fetch/$s_!m_G9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de91f9-9923-4b19-8732-31dd5c896be6_600x302.gif 848w, https://substackcdn.com/image/fetch/$s_!m_G9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de91f9-9923-4b19-8732-31dd5c896be6_600x302.gif 1272w, https://substackcdn.com/image/fetch/$s_!m_G9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de91f9-9923-4b19-8732-31dd5c896be6_600x302.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!m_G9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de91f9-9923-4b19-8732-31dd5c896be6_600x302.gif" width="600" height="302" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a4de91f9-9923-4b19-8732-31dd5c896be6_600x302.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:302,&quot;width&quot;:600,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2336074,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!m_G9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de91f9-9923-4b19-8732-31dd5c896be6_600x302.gif 424w, https://substackcdn.com/image/fetch/$s_!m_G9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de91f9-9923-4b19-8732-31dd5c896be6_600x302.gif 848w, https://substackcdn.com/image/fetch/$s_!m_G9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de91f9-9923-4b19-8732-31dd5c896be6_600x302.gif 1272w, https://substackcdn.com/image/fetch/$s_!m_G9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de91f9-9923-4b19-8732-31dd5c896be6_600x302.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>That&#8217;s where <strong>Few-shot Prompting</strong> came in. It involves providing the model with input-output pairs that it can use to infer the structure and tone of the desired response.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wsXV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe148ef48-659f-4040-8aa8-ea3068600787_1456x521.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wsXV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe148ef48-659f-4040-8aa8-ea3068600787_1456x521.jpeg 424w, https://substackcdn.com/image/fetch/$s_!wsXV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe148ef48-659f-4040-8aa8-ea3068600787_1456x521.jpeg 848w, https://substackcdn.com/image/fetch/$s_!wsXV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe148ef48-659f-4040-8aa8-ea3068600787_1456x521.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!wsXV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe148ef48-659f-4040-8aa8-ea3068600787_1456x521.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wsXV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe148ef48-659f-4040-8aa8-ea3068600787_1456x521.jpeg" width="1456" height="521" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e148ef48-659f-4040-8aa8-ea3068600787_1456x521.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:521,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wsXV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe148ef48-659f-4040-8aa8-ea3068600787_1456x521.jpeg 424w, https://substackcdn.com/image/fetch/$s_!wsXV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe148ef48-659f-4040-8aa8-ea3068600787_1456x521.jpeg 848w, https://substackcdn.com/image/fetch/$s_!wsXV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe148ef48-659f-4040-8aa8-ea3068600787_1456x521.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!wsXV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe148ef48-659f-4040-8aa8-ea3068600787_1456x521.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>For our specific use case, our in-house experts categorized different types of concepts and manually created examples for those. Whenever an answer explanation is supposed to be created, we retrieve the relevant examples and pass them to the model for generating an answer explanation of the new question.</p><p>One-shot prompting is a technique where one input-output pair is provided as an example.</p><h4><strong>Chain of Thought Prompting</strong></h4><p>At Quizizz, we deal with a lot of Math-related use-cases, and one of the biggest problems with that it requires reasoning. Asking an LLM to answer a Math question often results in incorrect answers. Chain of Thought Prompting is a technique that tries to induce reasoning in the output of the model thereby helping it break the complex task into multiple smaller tasks that it is good at.</p><p>Asking a model to &#8220;Think step-by-step&#8221; or &#8220;Explain the reason before answering&#8221; are a few ways to do Chain of Thought Prompting. We can also use Few-shot Chain of Thought Prompting where the examples help the model on how the thought process should look like.</p><h3><strong>Conclusion</strong></h3><p>The first step to building an AI-based product is building a basic understanding of the foundational technologies and learning how to utilize them effectively. This article should provide a great starting point, and with careful prompt engineering, you can leverage these models to create compelling AI features. Everything boils down to experimentation and figuring out the right model and prompt which works for your use case.</p><p>In the next part of this series, we will go beyond the basics of prompting to explore advanced techniques that make AI products more capable and reliable.</p><p>If you&#8217;re interested in leveraging AI to build products that improve the daily lives of educators and learners, we want you! We&#8217;re looking for backend and frontend engineers who have a high sense of ownership, are curious, and are willing to step into the world of a fast-paced startup. Apply to the open roles using the link below.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://quizizz.com/home/careers&quot;,&quot;text&quot;:&quot;Join Us!&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://quizizz.com/home/careers"><span>Join Us!</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[Our Journey of Building a Gateway Service]]></title><description><![CDATA[Learn how Quizizz tackled DoS attacks by implementing a custom gateway service using Kong API Gateway. while solving for performance, reduced costs, and enhanced security with rate limiting, and IP blacklisting.]]></description><link>https://eng.quizizz.com/p/our-journey-of-building-a-gateway</link><guid isPermaLink="false">https://eng.quizizz.com/p/our-journey-of-building-a-gateway</guid><dc:creator><![CDATA[Quizizz Engineering]]></dc:creator><pubDate>Mon, 16 Sep 2024 12:06:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/505022a4-411b-4afb-b7da-3e122eafe591_1400x647.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>Originally published on July 10, 2023</em></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_jCB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac13ee3-7533-4eb7-9992-6ee5e410574e_1400x647.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_jCB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac13ee3-7533-4eb7-9992-6ee5e410574e_1400x647.webp 424w, https://substackcdn.com/image/fetch/$s_!_jCB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac13ee3-7533-4eb7-9992-6ee5e410574e_1400x647.webp 848w, https://substackcdn.com/image/fetch/$s_!_jCB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac13ee3-7533-4eb7-9992-6ee5e410574e_1400x647.webp 1272w, https://substackcdn.com/image/fetch/$s_!_jCB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac13ee3-7533-4eb7-9992-6ee5e410574e_1400x647.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_jCB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac13ee3-7533-4eb7-9992-6ee5e410574e_1400x647.webp" width="1400" height="647" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eac13ee3-7533-4eb7-9992-6ee5e410574e_1400x647.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:647,&quot;width&quot;:1400,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:61004,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_jCB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac13ee3-7533-4eb7-9992-6ee5e410574e_1400x647.webp 424w, https://substackcdn.com/image/fetch/$s_!_jCB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac13ee3-7533-4eb7-9992-6ee5e410574e_1400x647.webp 848w, https://substackcdn.com/image/fetch/$s_!_jCB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac13ee3-7533-4eb7-9992-6ee5e410574e_1400x647.webp 1272w, https://substackcdn.com/image/fetch/$s_!_jCB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac13ee3-7533-4eb7-9992-6ee5e410574e_1400x647.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Introduction</h2><p>At Quizizz, our mission is to empower educators to engage learners to help improve their learning outcomes. A core part of our platform is the ability for teachers to conduct live gamified quizzes in classrooms. Since these activities are happening live, it is essential to have&nbsp;no disruptions and a smooth and frictionless user experience for every student.</p><p>One of the most pressing problems that we have been facing was DoS (Denial of Service) attacks. In this blog post, I&#8217;m going to talk about what we did in order to negate the consequences of DoS attacks and our learnings from this effort.&nbsp;</p><h2>Our current system</h2><p>As we have been growing and amassing new users, the frequency of such attacks is also growing. In our initial days, we found quicker and cost-efficient solutions to manage these attacks. Our quick solutions were to use AWS WAF (Web Application Firewall) and custom application-level rate limiters in each service.</p><p>As the frequency and complexity of these attacks increased, and our infrastructure became more complex, we had to find a permanent solution. Our current solutions still let DoS attacks through, mostly due to WAF&#8217;s 5-minute window being too long for detecting DoS attacks before they propagate to our services. Additionally, WAF has started becoming prohibitively expensive as our traffic increases as well.</p><h2>Problems</h2><h4>Failure to block all DoS attacks</h4><p>We have been facing DoS attacks for some time now, AWS WAF (Web Application Firewall, a managed DoS protection service by AWS) wasn&#8217;t fully effective in stopping these attacks and quite a few of them would leak into our system, which would cause scaling up of our services, high resource/CPU usage on individual containers, and would have cascading effects into our entire system, generally resulting in instability to users.&nbsp;</p><p>Application level rate limiters and quick fixes proved ineffective since attackers would change strategy and start attacking different endpoints/services. We needed an effective single service in front of all our services to handle DoS attacks by rate limiting based on multiple attributes, such as login state, IP address, HTTP method/path/headers, etc.</p><h4>Duplication of logic in multiple services</h4><p>In our current infrastructure, traffic is routed to different load balancers behind our CDN.&nbsp;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GTw_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d06969-5241-41d1-b64d-5cebc54cf9db_1024x375.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GTw_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d06969-5241-41d1-b64d-5cebc54cf9db_1024x375.png 424w, https://substackcdn.com/image/fetch/$s_!GTw_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d06969-5241-41d1-b64d-5cebc54cf9db_1024x375.png 848w, https://substackcdn.com/image/fetch/$s_!GTw_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d06969-5241-41d1-b64d-5cebc54cf9db_1024x375.png 1272w, https://substackcdn.com/image/fetch/$s_!GTw_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d06969-5241-41d1-b64d-5cebc54cf9db_1024x375.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GTw_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d06969-5241-41d1-b64d-5cebc54cf9db_1024x375.png" width="1024" height="375" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/37d06969-5241-41d1-b64d-5cebc54cf9db_1024x375.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:375,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!GTw_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d06969-5241-41d1-b64d-5cebc54cf9db_1024x375.png 424w, https://substackcdn.com/image/fetch/$s_!GTw_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d06969-5241-41d1-b64d-5cebc54cf9db_1024x375.png 848w, https://substackcdn.com/image/fetch/$s_!GTw_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d06969-5241-41d1-b64d-5cebc54cf9db_1024x375.png 1272w, https://substackcdn.com/image/fetch/$s_!GTw_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d06969-5241-41d1-b64d-5cebc54cf9db_1024x375.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Current Architecture</figcaption></figure></div><p>While this is a simpler way of building our platform, this has a huge downside. We have little to no control over how requests are routed, and we have no place to write business logic, such as authentication, which is common to all services.</p><p>The result of this is that over time individual microservices started implementing their own versions of the same business logic. Changes to the common layers require similar changes in all services which can over time cause bugs and increase tech debt. These bugs can have severe consequences such as security vulnerabilities.&nbsp;</p><p>The solution is to have a common entry point, the gateway service, to the system which serves as a middleware to all our requests.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WlXr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb984f26d-eadd-4a56-a24d-f4d1f5e47e9f_1024x343.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WlXr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb984f26d-eadd-4a56-a24d-f4d1f5e47e9f_1024x343.png 424w, https://substackcdn.com/image/fetch/$s_!WlXr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb984f26d-eadd-4a56-a24d-f4d1f5e47e9f_1024x343.png 848w, https://substackcdn.com/image/fetch/$s_!WlXr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb984f26d-eadd-4a56-a24d-f4d1f5e47e9f_1024x343.png 1272w, https://substackcdn.com/image/fetch/$s_!WlXr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb984f26d-eadd-4a56-a24d-f4d1f5e47e9f_1024x343.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WlXr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb984f26d-eadd-4a56-a24d-f4d1f5e47e9f_1024x343.png" width="1024" height="343" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b984f26d-eadd-4a56-a24d-f4d1f5e47e9f_1024x343.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:343,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!WlXr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb984f26d-eadd-4a56-a24d-f4d1f5e47e9f_1024x343.png 424w, https://substackcdn.com/image/fetch/$s_!WlXr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb984f26d-eadd-4a56-a24d-f4d1f5e47e9f_1024x343.png 848w, https://substackcdn.com/image/fetch/$s_!WlXr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb984f26d-eadd-4a56-a24d-f4d1f5e47e9f_1024x343.png 1272w, https://substackcdn.com/image/fetch/$s_!WlXr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb984f26d-eadd-4a56-a24d-f4d1f5e47e9f_1024x343.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">New Architecture with the Gateway service</figcaption></figure></div><h4>Heavy reliance on AWS restricting customizability, ballooning costs</h4><p>We use AWS Application Load Balancers for a number of interesting platform features that enhance developer experience and provide developers with ways to test and experiment with their code without releasing it to actual users. This strategy has worked out so far but as we continue to grow quickly, we have been facing issues with AWS services quota limits, and costs.&nbsp;</p><p>We realized that to continue forward in our growth, providing speed to developers, and ensuring Quizizz remains a reliable and highly available platform while also ensuring cost efficiency, we&#8217;d need to move parts of our platform away from AWS services towards cloud-native solutions.</p><p>We decided to move forward with building a custom gateway service with our own implementation of a rate limiter, deploying it on Kubernetes while using a cloud-native tech stack.</p><h2>Gateways, Kong: What are these?</h2><p>An application gateway is a networking component that acts as an intermediary between users and web applications. It provides secure and scalable access to web applications by offering functionalities such as load balancing, SSL/TLS termination, and web application firewall (WAF) capabilities. It serves as a central entry point, routing incoming requests to the appropriate backend servers, and can also provide features like session affinity and URL-based routing. Overall, an application gateway helps improve performance, reliability, and security for web applications by managing traffic and enforcing policies at the application layer.</p><p><a href="https://konghq.com/">Kong Gateway</a> is an open-source, cloud-native API gateway that acts as a central point of control for managing and securing API traffic. It enables organizations to efficiently manage their APIs by providing capabilities such as authentication, rate limiting, request/response transformations, and traffic control. Kong Gateway acts as a middleware layer between clients and backend services, ensuring reliable and secure communication. With its extensive plugin ecosystem, it offers flexibility and extensibility to customize and enhance API functionality. By simplifying API management tasks, Kong Gateway empowers developers to focus on building robust and scalable applications while ensuring high performance and security for their APIs. </p><p>There is a lot more to say about Kong than what I have summarized and if you&#8217;re interested, <a href="https://www.baeldung.com/kong">this is a great start</a>.</p><p>Kong needs a persistent data store where it can store rate limits, while there are many options for these, one of the more popular options is <a href="https://redis.io/">Redis</a>, which we used.</p><h2>Why we built our own rate limiter</h2><p>Kong provides a default rate limiter, but it didn&#8217;t fill our use case completely. There were many features we wanted that required custom implementation, such as:</p><h4>Redis Cluster Support</h4><p>Quizizz gets ~750k RPM peak traffic, and DoS attacks can go into 10s of millions of RPM. This meant we can&#8217;t rely on a single Redis node to guarantee high availability and performance. We wanted a large Redis cluster to ensure Redis didn&#8217;t crash during DoS attacks.</p><h4>Configurable Time Windows</h4><p>The default rate limiter provides only a small set of time windows on which rate limits can be configured. For our use case, we realized that a one-minute window is too long, and within that period of time, an attacker can cause harm to our system, and hamper other user&#8217;s experience. A one-second window, while important, would not be enough since we&#8217;d like to block attacks for a longer period of time. This meant we wanted configurability on n second windows.&nbsp;</p><h4>IP Blacklisting/whitelisting</h4><p>Blacklisting and whitelisting IP addresses is an important part of our rate limiter that can power multiple features. For example, we need to whitelist our own custom IPs. We may also whitelist IPs for certain users if they have special cases that require rate limits to be relaxed for a small period of time. Furthermore, we are working on improving our system so that it provides a captcha instead of an error when a certain IP is rate limited. If the IP completes the captcha, we can then whitelist it for a short period of time. Another interesting feature is to blacklist certain IPs which we know are malicious.</p><h4>Custom logic for different rate limits</h4><p>While we want to block DoS attacks, we also want to ensure that this doesn&#8217;t hamper the experience of legitimate users. Legitimate user requests follow certain patterns that DoS attacks generally don&#8217;t, for example, DoS attacks generally use unauthenticated requests, hit the same endpoint, etc.</p><p>There is no definitive way to tell whether certain activity is legitimate or not, and we cannot write a rate limiter that would never block a legitimate user request, but we can understand how users use our platform, and build a rate limiter that minimizes the probability that a user request is returned with a 429 response.</p><h4>Fault tolerance</h4><p>One of our core emphasis while building this service was to ensure that requests don&#8217;t get blocked in the case of a failure in Gateway service. To put it simply, we understand that code can and will fail, but it shouldn&#8217;t impact how users experience on our platform.</p><p>This meant we had to ensure fallbacks and write custom code to handle anything and everything that can happen. So for example, if Redis crashes, requests shouldn&#8217;t fail and instead start routing without the rate-limiting logic. The default rate limiter wouldn&#8217;t handle all such cases and hence we had the need to build our own.</p><h2>Architecture</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!te2c!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b53862-2672-4482-9402-abcfaceaab7d_1024x334.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!te2c!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b53862-2672-4482-9402-abcfaceaab7d_1024x334.png 424w, https://substackcdn.com/image/fetch/$s_!te2c!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b53862-2672-4482-9402-abcfaceaab7d_1024x334.png 848w, https://substackcdn.com/image/fetch/$s_!te2c!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b53862-2672-4482-9402-abcfaceaab7d_1024x334.png 1272w, https://substackcdn.com/image/fetch/$s_!te2c!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b53862-2672-4482-9402-abcfaceaab7d_1024x334.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!te2c!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b53862-2672-4482-9402-abcfaceaab7d_1024x334.png" width="1024" height="334" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/26b53862-2672-4482-9402-abcfaceaab7d_1024x334.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:334,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!te2c!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b53862-2672-4482-9402-abcfaceaab7d_1024x334.png 424w, https://substackcdn.com/image/fetch/$s_!te2c!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b53862-2672-4482-9402-abcfaceaab7d_1024x334.png 848w, https://substackcdn.com/image/fetch/$s_!te2c!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b53862-2672-4482-9402-abcfaceaab7d_1024x334.png 1272w, https://substackcdn.com/image/fetch/$s_!te2c!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b53862-2672-4482-9402-abcfaceaab7d_1024x334.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Complete architecture for the current system</figcaption></figure></div><p>We created staging deployments based on a custom header using Cloudfront&#8217;s continuous deployment feature. This allowed us to initially start sending our own traffic to Kong. Once we were confident, we started sending small amounts of user traffic based on percentage on a single route, gradually increasing traffic percentage. All this while, we started doing small load and simulation tests on the gateway service while monitoring it to see how it would react.</p><p>Cloudfront staging distribution only allows up to 15% of traffic to be routed to the staging deployment. To increase it beyond 15%, we used Lambda@Edge. We wrote a lambda function that would change the origin of the request using custom logic. Using this, we were able to configure request routing very granularly, constantly updating it based on our requirements and testing. After gradual increases over a long period of time, we finally released gateway service to 100% of traffic with the older CDN distribution&nbsp;</p><p>All this time, we had enabled shadow mode on our custom rate limiter, where it would not block any requests, instead updating a metric when a certain IP crosses the rate limit. Using this, we were able to fine-tune our rate limiter logic and decide on time windows and rules. Eventually, we disabled shadow mode once we had finalized the rate limiter rules.</p><p>To scale pods, we used <a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/">Horizontal Pod Autoscaler(HPA),</a> and to scale nodes, we used <a href="https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/cloudprovider/aws/README.md">Cluster Autoscaler(CAS)</a></p><h2>Roadblocks</h2><h4>Out Of Memory (OOM) Restarts</h4><p>During testing Kong, gateway service pods failed to restart after crashing due to OOM errors. This was mostly caused by <a href="https://github.com/Kong/kong/pull/10468">a bug in Kong</a> due to which containers wouldn&#8217;t delete a socket connection file after dying, and hence new containers couldn&#8217;t spin up since the ports wouldn&#8217;t be open. This bug was fixed in the later versions of Kong.&nbsp;</p><h2>Conclusion</h2><p>In summary, our current system was vulnerable to DoS attacks and regular attacks causing degradation of user experience while increasing infrastructure costs. AWS WAF was proving to be ineffective against larger attacks due to its long 5-minute time window. We implemented a common gateway layer using Kong which could rate limit requests on configurable time windows. This also allowed us to move common business logic from multiple services to a single service, therefore cleaning up our architecture while reducing the occurrence of DoS attacks. While this has not completely solved the problem for us, it was a good first step in the right direction.</p><h2>Come Join Us!</h2><p>If you&#8217;re passionate about solving similar problems, have experience in building platform systems, reach out to us and help us the next challenging problem. Reach out to us, and let&#8217;s build the best learning platform in the world!</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://quizizz.com/home/careers&quot;,&quot;text&quot;:&quot;We're hiring&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://quizizz.com/home/careers"><span>We're hiring</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[Building Scalable Data Ingestion Pipelines at Quizizz]]></title><description><![CDATA[Explore how Quizizz optimized data syncing from MongoDB to BigQuery with a new CDC-based ingestion pipeline using Kafka and Debezium. Learn how this scalable solution ensures real-time analytics, data accuracy, fault tolerance, and unlocks advanced use cases for business growth.]]></description><link>https://eng.quizizz.com/p/building-scalable-data-ingestion</link><guid isPermaLink="false">https://eng.quizizz.com/p/building-scalable-data-ingestion</guid><dc:creator><![CDATA[Gaurav Chandak]]></dc:creator><pubDate>Sun, 16 Jun 2024 12:11:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!M5Tj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c3a365-4482-4531-bf28-a7e2eaf57a1f_1600x625.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Introduction</h2><p>At Quizizz, we have been using MongoDB as our primary database and <a href="https://cloud.google.com/bigquery">Google BigQuery</a> as our analytics data warehouse. The ability to effectively sync data from MongoDB to BigQuery has played a vital role in driving product and business decisions. Our data ingestion pipeline has been syncing MongoDB data to BigQuery for the past few years. In this article, we will explore the challenges we faced with our previous ingestion pipeline, discuss the decisions we made while building our new data ingestion pipeline, and delve into the details of how this pipeline has empowered us to make data-driven decisions at Quizizz.</p><h2>Old Data Ingestion Pipeline</h2><h3>Architecture</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!M5Tj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c3a365-4482-4531-bf28-a7e2eaf57a1f_1600x625.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!M5Tj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c3a365-4482-4531-bf28-a7e2eaf57a1f_1600x625.png 424w, https://substackcdn.com/image/fetch/$s_!M5Tj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c3a365-4482-4531-bf28-a7e2eaf57a1f_1600x625.png 848w, https://substackcdn.com/image/fetch/$s_!M5Tj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c3a365-4482-4531-bf28-a7e2eaf57a1f_1600x625.png 1272w, https://substackcdn.com/image/fetch/$s_!M5Tj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c3a365-4482-4531-bf28-a7e2eaf57a1f_1600x625.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!M5Tj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c3a365-4482-4531-bf28-a7e2eaf57a1f_1600x625.png" width="1456" height="569" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e8c3a365-4482-4531-bf28-a7e2eaf57a1f_1600x625.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:569,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Architecture of our old data ingestion pipeline&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Architecture of our old data ingestion pipeline" title="Architecture of our old data ingestion pipeline" srcset="https://substackcdn.com/image/fetch/$s_!M5Tj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c3a365-4482-4531-bf28-a7e2eaf57a1f_1600x625.png 424w, https://substackcdn.com/image/fetch/$s_!M5Tj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c3a365-4482-4531-bf28-a7e2eaf57a1f_1600x625.png 848w, https://substackcdn.com/image/fetch/$s_!M5Tj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c3a365-4482-4531-bf28-a7e2eaf57a1f_1600x625.png 1272w, https://substackcdn.com/image/fetch/$s_!M5Tj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c3a365-4482-4531-bf28-a7e2eaf57a1f_1600x625.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Image 1: Architecture of our old data ingestion pipeline</figcaption></figure></div><p>Our old data ingestion pipeline had the following architecture:</p><ul><li><p>Events were triggered from code after DB changes (Create, Update, and Delete).</p></li><li><p>These events were added to <a href="https://aws.amazon.com/sqs/">SQS</a> (Amazon&#8217;s managed queuing service) so that they could be asynchronously processed without affecting our live flow.</p></li><li><p>Consumers would read events from SQS and populate temporary BigQuery (BQ) tables. There are temporary tables for each entity, with one table for each 6-hour block.</p></li><li><p>Every 24 hours, a cron job would merge the data from the temporary BQ tables for each entity into the main BQ table for that entity. This batch-processing method aimed to optimize costs by reducing the frequency of table updates.</p></li></ul><h2>Challenges with Old Data Ingestion Pipeline</h2><p><em><strong>tl;dr:</strong> Data Loss and Unreliable Data</em></p><h3>Code-triggered Events</h3><p>In our old pipeline, events were manually triggered from code during create, update, and delete operations. However, this approach had limitations, primarily, the risk of missed events due to code bugs.</p><h3>Lack of fault tolerance</h3><p>Our old pipeline was based on SQS. The system was fairly simple without a DLQ resulting in the absence of a robust retry mechanism in case of failures. This lack of fault tolerance made us vulnerable to data loss during transient failures and/or intermittent network issues. To enhance the reliability of our pipeline, we prioritized the implementation of a comprehensive retry mechanism (covered later).</p><h3>Manual Schema Updates</h3><p>Another challenge we encountered was the manual synchronization of schema changes between our primary database and BigQuery. This process required human intervention and was prone to errors, leading to data loss. We aimed to streamline this process and ensure data consistency by automating schema updates or removing the need for&nbsp;schema updates.</p><h3>Backfill and Evolving Data Requirements</h3><p>We do not index documents in any of the collections on the created date in MongoDB. Accommodating evolving data requirements and performing backfill operations proved to be challenging in our previous pipeline as querying based on dates would be very expensive. Due to changing business needs, we had to change the data type of certain fields in the past. Having strict data types for fields in BQ would result in errors when backfilling older events. We needed a more flexible solution to adapt to changing data needs without disrupting workflows.</p><h3>Loss of Historical Data</h3><p>In the previous architecture, we only stored the current state of each entity, disregarding the change events. This lack of historical data limited our ability to perform comprehensive analysis and gain insights into the evolution of our data over time.</p><h2>Introducing the New Data Ingestion Pipeline</h2><p><strong>tl;dr</strong>: <em>Change Data Capture-based Kafka Connect pipeline</em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qau4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc93e0249-3e58-48d8-a491-3f9f1e0a6c1b_1600x650.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qau4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc93e0249-3e58-48d8-a491-3f9f1e0a6c1b_1600x650.png 424w, https://substackcdn.com/image/fetch/$s_!qau4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc93e0249-3e58-48d8-a491-3f9f1e0a6c1b_1600x650.png 848w, https://substackcdn.com/image/fetch/$s_!qau4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc93e0249-3e58-48d8-a491-3f9f1e0a6c1b_1600x650.png 1272w, https://substackcdn.com/image/fetch/$s_!qau4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc93e0249-3e58-48d8-a491-3f9f1e0a6c1b_1600x650.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qau4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc93e0249-3e58-48d8-a491-3f9f1e0a6c1b_1600x650.png" width="1456" height="592" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c93e0249-3e58-48d8-a491-3f9f1e0a6c1b_1600x650.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:592,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Architecture of our new data ingestion pipeline&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Architecture of our new data ingestion pipeline" title="Architecture of our new data ingestion pipeline" srcset="https://substackcdn.com/image/fetch/$s_!qau4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc93e0249-3e58-48d8-a491-3f9f1e0a6c1b_1600x650.png 424w, https://substackcdn.com/image/fetch/$s_!qau4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc93e0249-3e58-48d8-a491-3f9f1e0a6c1b_1600x650.png 848w, https://substackcdn.com/image/fetch/$s_!qau4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc93e0249-3e58-48d8-a491-3f9f1e0a6c1b_1600x650.png 1272w, https://substackcdn.com/image/fetch/$s_!qau4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc93e0249-3e58-48d8-a491-3f9f1e0a6c1b_1600x650.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Image 2: Architecture of our new data ingestion pipeline</figcaption></figure></div><h3>Change Data Capture</h3><p>To address the limitations of our old pipeline, we decided to use <a href="https://en.wikipedia.org/wiki/Change_data_capture">Change Data Capture (CDC)</a> for data ingestion. CDC is a widely adopted standard for syncing data from a primary data source to a <a href="https://en.wikipedia.org/wiki/Data_lake">data lake</a>, <a href="https://en.wikipedia.org/wiki/Data_warehouse">data warehouse</a>, or any other secondary data source.</p><p><a href="https://debezium.io/">Debezium</a> (an open-source CDC framework) captures change events (creates, updates, deletes) from various databases and adds the change events to Kafka. Consumers can read from Kafka and react to those change events. In our case, Debezium captures change events from MongoDB in real time, allowing us to keep our data warehouse, BigQuery up to date. We used <a href="https://debezium.io/documentation/reference/stable/architecture.html">Debezium Kafka Connector</a> for this.</p><p>Using CDC helped us solve the problems related to triggering events from code.</p><h3>Fault tolerance using Kafka</h3><p>Because of Debezium, we started using Kafka instead of SQS. Because of Kafka, we got more <a href="https://www.confluent.io/blog/kafka-connect-deep-dive-error-handling-dead-letter-queues/">control in terms of error handling</a>. Consumers could now implement retries with exponential backoff. If a message could not be processed due to non-retriable exceptions, Kafka Connect tasks would fail and we get <a href="https://docs.confluent.io/platform/current/connect/monitoring.html">alerts</a>. This helped us make the pipeline fault-tolerant.</p><p>Every component in the pipeline is decoupled and can be scaled independently. Offsets are maintained for every component (including the change stream offset) in Kafka making it easy to restart any of the components in case of issues.</p><h3>Agile Data Storage with JSON State</h3><p>As part of our new pipeline, we made the strategic decision to store the state data as a JSON string within a field called &#8220;state&#8221; for each event row. This approach allows us to store the entire state of each entity instead of specific fields at a given time. By doing so, we eliminate the need for backfilling operations as the data is always complete. Furthermore, this approach helps us avoid schema updates, enhancing our agility and reducing disruptions.</p><h3>Streaming Change Events to BigQuery</h3><p>In our new data ingestion pipeline, we have introduced a more efficient approach. Each collection in MongoDB now has a dedicated events table in BigQuery. The change events are streamed directly to BigQuery and stored as rows in the corresponding table. The new tables follow an append-only model, ensuring an efficient and reliable data flow. This also helped us store historical data and make the analytics data available in real time. We used BigQuery Kafka Connector for this.</p><h3>Easier onboarding of new tables</h3><p>In the new pipeline, onboarding a new table requires just adding the name of the collection in the configuration and data would start flowing ASAP. Backfilling data for any table would require adding an entry in a signal table and everything gets handled.</p><h2>Other Use Cases</h2><p>Given the reliable and fault-tolerant nature of Kafka and Kafka Connect as well as the usefulness of CDC events, this change opens up possibilities for expanding use cases beyond syncing data to BigQuery. Some potential use cases that can benefit from a similar pipeline include:</p><ul><li><p>Syncing Data to Elasticsearch: With our pipeline&#8217;s scalability and reliability, we can seamlessly sync data from MongoDB to Elasticsearch by adding a <a href="https://docs.confluent.io/kafka-connectors/elasticsearch/current/overview.html">new sink connector for ES</a>. This eliminates the sync issues often encountered with code-based triggers in our current Elasticsearch ingestion pipeline.</p></li></ul><ul><li><p>Triggering Real-time Campaigns: The flexibility of our data ingestion pipeline allows us to sync data to platforms like Braze or other similar systems. By capturing and streaming change events, we can trigger real-time emails or campaigns based on user interactions and events, delivering personalized experiences to our users.</p></li></ul><ul><li><p>Building a Data Lake with S3: Our new pipeline sets the foundation for building a data lake by syncing data to Amazon S3. Storing the complete state as a JSON string enables seamless integration of MongoDB change events into our data lake. This empowers our machine learning pipelines, leveraging the scalability and processing capabilities of S3.</p></li></ul><h2>Conclusion</h2><p>Building a scalable and agile data ingestion pipeline is crucial for organizations aiming to make data-driven decisions effectively. At Quizizz, we embarked on a journey to transform our pipeline, overcome challenges, and leverage industry-standard solutions.</p><p>Our new data ingestion pipeline empowers us to capture and sync change events from MongoDB in real-time, ensuring data accuracy and eliminating missed events. This new pipeline is playing a pivotal role in empowering Quizizz to make data-driven decisions that drive our product and business growth.</p><p>If you&#8217;re interested in solving similar problems at high scale, reach out to us and help us build a more robust pipeline.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://quizizz.com/home/careers&quot;,&quot;text&quot;:&quot;We're hiring&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://quizizz.com/home/careers"><span>We're hiring</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[Building Engineering Teams → Interviewing]]></title><description><![CDATA[Building teams is hard.]]></description><link>https://eng.quizizz.com/p/building-engineering-teams-interviewing</link><guid isPermaLink="false">https://eng.quizizz.com/p/building-engineering-teams-interviewing</guid><dc:creator><![CDATA[Sandeep Bantia]]></dc:creator><pubDate>Thu, 08 Feb 2024 06:37:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/477b339b-5d92-4755-9350-8c1080cf59fd_1200x667.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Building teams is hard. Building engineering teams is harder. Building engineering teams for a startup is the hardest. There are many pieces to this puzzle of building engineering teams. I hope to cover some of these in the upcoming months. This post is about one of the many pieces &#8211; <strong>interviewing</strong>. Having varying degrees of interviewer experience on my team, we laid out some guidelines on interviewing at <a href="https://quizizz.com/">Quizizz</a>, so we can all learn together and do our part to ensure we are able to share our journey with high-quality talent. We went over this post as a team (some parts that may not be relevant are redacted).&nbsp;</p><p>Interviewing is a two-way street. We want to provide the best candidate experience to every person who walks through our doors. We want to be consistent and thorough in evaluating our candidates. And we want to make candidates comfortable, so they can ask us questions too. As much as we evaluate candidates, candidates are evaluating us with every interaction. Interviews form everlasting memories of such interactions and come up while talking to their friends; friends who could be potential candidates in the future. As someone famous once said, <em>&#8220;People will forget what you said, people will forget what you did, but people will never forget how you made them feel!&#8221;</em>&nbsp; Remember the times when you were a candidate and had a pleasant interview experience? Remember to consciously <em>pay</em> it forward!&nbsp;</p><h2>Expectations</h2><p>Whether you are a candidate, interviewer or recruiter, interviews are stressful. Our goal is to make it as stress-free as possible, so we can get the candidate to showcase the best version of themselves and for interviewers to make the best decisions, all within a short time frame.</p><p>What are our expectations from you as an<strong> interviewer</strong>?</p><ul><li><p><em><strong>Treat candidates with respect. </strong></em>Be on time. 100% attention, nothing less. Don&#8217;t code, don&#8217;t respond to Slack messages, or WhatsApp. Don&#8217;t multitask. People can tell when you get distracted and feel let down. Ensure to turn off your notifications.&nbsp;</p></li><li><p><em><strong>Make candidates feel comfortable:</strong> </em>You will be able to bring the best out of them. The goal is not to create pressure situations and stump them with our questions, the goal is to encourage them to feel supported &#8211;&nbsp; treat an interview as a working session with a co-worker.&nbsp;</p></li><li><p><em><strong>Let candidates impress you.</strong></em><strong> </strong>&nbsp;Talk less and listen more. Listen and ask insightful questions with genuine curiosity and ensure to not come across as judgmental. A good rule of thumb for a great interview is when a candidate talks &gt; 70% of the time.&nbsp;</p></li><li><p><em><strong>Answer questions candidly</strong>.</em> Leave time at the end of an interview to answer questions. Interviewing is a two-way street. Candidates are evaluating us as much as we are. Do not provide answers if you are not sure and do not overcommit in order to come across as knowing something.&nbsp;&nbsp;</p></li><li><p><em><strong>Provide next steps</strong> </em>to candidates before wrapping up the call. Do not give out the result of the interview. Leave that to the Recruiting team.&nbsp;</p></li><li><p><em><strong>Make clear decisions.</strong></em><strong> </strong>A candidate is one of 3 &#8211; Strong Yes, Yes, No. There are no Maybes. Gather enough information and signals to make your decision. If you are in doubt, talk to your peers or leads, and convert the maybe to either Yes or No.</p></li><li><p><em><strong>Leave thorough feedback in Workable (our ATS)</strong></em>. There&#8217;s no expectation to write essays. Write down key talking points that are essential to discuss during the debrief.&nbsp;</p></li><li><p><strong>Attend Debriefs. </strong>Notes are sometimes not sufficient. Providing context can help us better understand feedback and make good decisions collectively.&nbsp;</p></li></ul><h3>What should you expect as an interviewer?</h3><ul><li><p><em><strong>A clear description of role and level.</strong></em> Candidates will be considered for a certain level based on past experience. Treat this as a starting point, not the final one.</p></li><li><p><em><strong>Competencies and skills to evaluate. </strong></em>There is no expectation on you to evaluate all aspects. Every member in the panel will have primary areas to cover during the interview.&nbsp;</p></li></ul><h3>What should you expect from candidates?</h3><p>These will evolve over time and that&#8217;s natural. At this time, here&#8217;s what matters:</p><ul><li><p><em><strong>Coding and Problem-Solving.</strong></em> Are candidates strong with reasoning skills about their solutions and are they competent in converting their ideas to code?&nbsp;</p></li><li><p><em><strong>System Design / Domain Knowledge. Are </strong></em>candidates able to articulate high-level designs and are they able to deep-dive in explaining trade-offs?</p></li><li><p><em><strong>Effective Communication. </strong></em>&nbsp;Are candidates able to share ideas, explain technical topics effectively, listen, answer and ask questions?&nbsp;</p></li><li><p><em><strong>Growth Mindset. </strong>Given that </em>technologies are evolving at a fast rate and problems can be solved in more than one way, are candidates able to consistently learn, relearn and unlearn?&nbsp;</p></li><li><p><em><strong>No &#8220;brilliant&#8221; jerks. </strong></em>Regardless of how skilled they are, we do not want jerks.</p></li><li><p>Integrity. Last but not least &#8211; Do what you say and say what you do. This will help us build trust.</p></li></ul><h3>What should you expect from our recruiting team?</h3><ul><li><p><em><strong>Integrity. </strong></em>We say things the way they are, whether it is with candidates or interviewers. No false promises.</p></li><li><p><em><strong>Communicate decisions with respect. </strong></em>We let candidates know about our decision, regardless of what it is. Rejections are as important as acceptances in our books.</p></li><li><p><em><strong>Responsive. </strong></em>Candidates are anxious. We respond to all candidate communications as quickly as possible.</p></li><li><p><em><strong>Set clear expectations with candidates and interviewers. </strong></em>About the candidates&#8217; role, level, and other relevant information.&nbsp;</p></li><li><p><em><strong>Track performance, analyze, and report numbers.</strong></em> Sourcing and Conversions are our friends. We continue to improve when we find out wh<em>at works and what does not. </em>&nbsp;Data is king.</p></li></ul><h2>Feedback</h2><p>The last 5 mins after every interview should be used to write feedback in Workable. This not only helps us go fast, but also allows you to capture thorough feedback as the conversation will be fresh in your mind.&nbsp;</p><ul><li><p><em><strong>Capture important points about your conversation.</strong></em> You won&#8217;t remember the conversation in a few days when we do the debrief. Do not overestimate what you can remember.</p></li><li><p><em><strong>Paste the code/link in Workable.</strong></em> This is important for 2 reasons a) we don&#8217;t repeat questions b) helpful during debriefs for others to see.&nbsp;</p></li><li><p><em><strong>Leave notes for subsequent interviews. </strong></em>We don&#8217;t want everyone asking candidates the same questions. It is boring and a loss of signal.</p></li></ul><h2>Becoming an Interviewer</h2><h3>As an interviewer, that seems like a lot of effort. I&#8217;d rather build products. Why do it?</h3><p>The biggest reason &#8594; You are learning! Interviews teach you a lot about yourself. Some concrete ones:&nbsp;&nbsp;&nbsp;</p><ul><li><p>Interviews help improve communication skills. Communication is not only about speaking, but also listening.</p></li><li><p>Interviews help you understand the business, product, and technology better. Answering candidate questions requires a deeper understanding.&nbsp;</p></li><li><p>You will get help when the candidate joins! <em>They&#8217;ll remember how you made them feel, and pay it back.</em>&nbsp;</p></li><li><p>You are likely to spend more time with your co-workers than with your friends and family. You might as well have a say in the decision. &#128578;&nbsp;</p></li></ul><h3>How do I become one?</h3><ol><li><p><em><strong>Shadow interviews. </strong></em>Shadow experienced interviewers to learn the art of interviewing. At the end of the interview, spend 5 mins with the interviewer to discuss the candidate&#8217;s performance.&nbsp;</p></li></ol><ol start="2"><li><p><em><strong>Reverse Shadow Interviews. </strong>Take the lead </em>after you&#8217;ve shadowed 3 interviews. The interview will still have the experienced interviewer, but they will shadow you. After the interview, discuss the observations of a candidate&#8217;s performance with the experienced interviewer. They will provide feedback and approve you to lead interviews independently.&nbsp;</p></li></ol><ol start="3"><li><p><em><strong>Go solo!</strong></em>&nbsp;</p></li></ol><p>Take ownership when there is a conflict of schedules. Be proactive about asking for feedback, and discussing candidates. Stalk people&#8217;s calendars for interviews and ask to shadow. Be creative!&nbsp;</p><h2>FAQs</h2><p><em><strong>When do I have to submit feedback?&nbsp;</strong></em></p><p>Hiring is competitive. We need to move fast so we can land the best candidates. In most cases, you are able to submit feedback within 5 mins after the interview. Stuff happens and we are overbooked sometimes. In such cases, ensure to submit feedback within 24 hours of the completion of the interview, at the latest.&nbsp;</p><p><strong>Do I need to attend Interview Debriefs?</strong></p><p>Yes, it is mandatory for every interviewer to attend the debrief session. We are all interviewing different facets and it is important to provide all the information/context so we can arrive at a good decision with high confidence.&nbsp;</p><p><em><strong>How many interviews should I do in a week?&nbsp;</strong></em></p><p>In an ideal situation, an interviewer would cap the number of interviews at 5 per week. There will be weeks when we will do more given our ambitious goals. Please pay attention to how you feel about taking more interviews and let us know &#8211; Interview burnout is real.&nbsp;</p><p>Some interviewers prefer batching all of them in 1 day, others prefer 1-2 a day. Priorities will be captured and scheduling will take into account interviewer preferences. But remember, the schedule has too many variables. As we know, scheduling is an NP-hard problem :-).&nbsp;</p><p><em><strong>Will it look bad on me as an interviewer if I said yes, and everyone else says no, or vice versa?&nbsp;</strong></em></p><p>No. We are all evaluating different skills for candidates as a panel. It is reasonable for candidates to be strong in some areas and weak in others. Every interviewer can probe different facets.&nbsp;</p><p><em><strong>Can I give a strong YES when I have not evaluated all skills of a candidate?</strong></em></p><p>Absolutely. Your strong yes is for areas that you have evaluated. For example, you will find a candidate who is strong at system design and you should give a strong yes despite not knowing their strength in coding. Bring all interviewers together in a debrief and discuss all aspects.&nbsp;</p><p><em><strong>Should we have candidates work on home assignments / multi-day projects?</strong></em></p><p>Projects can be a good idea for interviews. They provide a window into the thought process, execution, and communication of a candidate. Home assignments are more realistic when compared to operating under the gun in an hour-long conversation. However, candidates may not be able to afford the time to complete projects when juggling jobs, families, etc.,&nbsp;&nbsp;</p><p><em><strong>Too many interviews will burn me out. What can we do?</strong></em></p><p>In addition to capping the number of interviews/week, as an organization, we practice NO-interview days on Wednesdays.&nbsp;&nbsp;</p><h2>Conclusion</h2><p>Interviewing is both an art and science. It is important to be yourself, throughout. It may be challenging to trust your process as you start and it will be tempting to follow these steps to the T. However, over time, you will rely a lot more on your own than the guidelines provided. And that&#8217;s a natural evolution!&nbsp;</p>]]></content:encoded></item><item><title><![CDATA[Three Reasons to Work at Quizizz]]></title><description><![CDATA[Building Tech for Every Learner on the Planet]]></description><link>https://eng.quizizz.com/p/three-reasons-to-work-at-quizizz</link><guid isPermaLink="false">https://eng.quizizz.com/p/three-reasons-to-work-at-quizizz</guid><dc:creator><![CDATA[Sandeep Bantia]]></dc:creator><pubDate>Mon, 16 Oct 2023 11:49:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!mcTy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d9c9174-8aee-4967-a210-d554348ed1b0_1000x1000.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>Originally published on February 12, 2022. Copied from from the previous blog</em></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mcTy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d9c9174-8aee-4967-a210-d554348ed1b0_1000x1000.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mcTy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d9c9174-8aee-4967-a210-d554348ed1b0_1000x1000.png 424w, https://substackcdn.com/image/fetch/$s_!mcTy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d9c9174-8aee-4967-a210-d554348ed1b0_1000x1000.png 848w, https://substackcdn.com/image/fetch/$s_!mcTy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d9c9174-8aee-4967-a210-d554348ed1b0_1000x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!mcTy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d9c9174-8aee-4967-a210-d554348ed1b0_1000x1000.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mcTy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d9c9174-8aee-4967-a210-d554348ed1b0_1000x1000.png" width="1000" height="1000" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1d9c9174-8aee-4967-a210-d554348ed1b0_1000x1000.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1000,&quot;width&quot;:1000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!mcTy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d9c9174-8aee-4967-a210-d554348ed1b0_1000x1000.png 424w, https://substackcdn.com/image/fetch/$s_!mcTy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d9c9174-8aee-4967-a210-d554348ed1b0_1000x1000.png 848w, https://substackcdn.com/image/fetch/$s_!mcTy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d9c9174-8aee-4967-a210-d554348ed1b0_1000x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!mcTy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d9c9174-8aee-4967-a210-d554348ed1b0_1000x1000.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I joined&nbsp;<a href="https://quizizz.com/">Quizizz</a>&nbsp;a couple of weeks ago and wrote about it&nbsp;<a href="https://sandeepbantia.medium.com/hello-quizizz-52887aa0178d">here</a>. I have also been working closely with the team over the last few months. In this post, I want to talk about our vision and mission, plans for building the tech organization, and a sneak peek into what to expect if you choose to work with us.</p><p><strong>What&#8217;s Quizizz?</strong></p><blockquote><p><em>We are on a mission to&nbsp;</em><strong>motivate every learner on the planet</strong><em>.</em></p></blockquote><p>Teachers in the United States get an hour a day outside classroom time for preparation. You can imagine it&#8217;s probably worse in India given the difference in population and class sizes. Teachers barely have any time to consider new teaching methodologies to rethink their instructions and learn modern practices (ex: incorporating games, flipped classrooms, etc.,) to make classroom learning more effective. Students are bored in their classes and end up supplementing with tuition / after-school classes. While other EdTech companies are trying to solve this problem using async learning, tuition, and sharing extra content, we at Quizizz are on a mission to empower our teachers to use their classroom time more effectively using technology and modern pedagogy involving gameplay flipped classrooms and interactive content.</p><p>Quizizz is a platform that has been empowering teachers to motivate and engage learners around the globe. We started out by building products to gamify and make different types of assessments engaging and fun. We have grown to&nbsp;<strong>75M</strong>&nbsp;monthly active users and&nbsp;<strong>2B&nbsp;</strong>responses recorded every month in the last year. These games take up the first and last 10 mins of a typical class. We have the remaining 40 mins in a classroom that is ripe for innovation. Building on this foundation, we are on the journey to becoming the preferred OS for every teacher to create and deliver engaging content, irrespective of the mode of learning.</p><p>We always talk about students being at the center of a triangle of stakeholders &#8212; teachers, parents, and peers. So far, we have had success in exploring the link between the teacher and the student via classroom content, gameplay, assessments, and homework. Not only are we planning to go deeper in exploring the student-teacher link, but we also intend to go broader in exploring the other 2 links.</p><p>So what will it take to achieve this mission?</p><h3>Building a World-Class Technology Team</h3><p>We have gotten to this scale with a nimble team of&nbsp;<em><strong>20</strong></em>. As we continue to grow, we will preserve the small team culture. Having a direct channel with users and speed will be our biggest moat. We don&#8217;t want the bureaucracy of big companies. We want to be able to delight our teachers and students at speed. To fulfill our ambitions for growing the business, we will hire not only some of the best engineers across the board from Infrastructure, Data, ML, Distributed Systems, Frontend, and Mobile but product managers and designers.</p><p>At the end of the day, we can guarantee this &#8212; there will always be more problems to solve than people. The idea is to relentlessly prioritize building the most impactful products and platforms, eliminate bureaucracy, and create opportunities for everyone to learn, lead, and experience exponential growth in their careers. To that end, we organize ourselves as small, autonomous, decentralized, and mission-oriented teams with high levels of ownership and accountability. Reducing the number of decision-makers is the only way we can move fast. Read more about how we organize ourselves&nbsp;<a href="https://sandeepbantia.medium.com/organizing-engineering-teams-for-speed-7601743c7058">here</a>.</p><h3>What challenges can you expect as an engineer?</h3><p>We have the scale of users that every engineer craves. Our real-time experiences can support&nbsp;<em><strong>3K</strong></em>&nbsp;concurrent users in a session and we have&nbsp;<em><strong>500K</strong></em>&nbsp;active connections across all sessions on our servers. You can imagine the number of broadcasts per second, and the optimizations required in packaging data and rendering. A flavor of some of the challenges you can expect are &#8211;</p><ul><li><p>Geographically distributed scale that will require us to build multi-datacenter infrastructure, storage technologies optimized for different workloads, and streaming technologies to power real-time experiences</p></li><li><p>We have collected massive amounts of data over the last five years, which enables us to now launch products that require solving deep technical problems in Machine Learning (ML) (discovery, recommendations, personalization, etc.,).</p></li><li><p>Modern Frontend platforms will require scaling the number of concurrent users for our real-time interactive experiences. For comparison, most frontends scale horizontally (ex: Google search, Facebook newsfeed, etc.,), but at Quizizz, we need to scale both horizontally&nbsp;<em><strong>and</strong></em>&nbsp;vertically.</p></li></ul><p>This is a non-exhaustive list of technologies used to build our products. Most engineers on our team used these technologies for the first time in their careers at Quizizz. More than having a knowledge of these technologies, willingness to learn has helped us build with an eye toward the future.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3Tt3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fdaf4bb-0b3a-4fa8-9470-d1e7e2e76d9e_1400x803.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3Tt3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fdaf4bb-0b3a-4fa8-9470-d1e7e2e76d9e_1400x803.png 424w, https://substackcdn.com/image/fetch/$s_!3Tt3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fdaf4bb-0b3a-4fa8-9470-d1e7e2e76d9e_1400x803.png 848w, https://substackcdn.com/image/fetch/$s_!3Tt3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fdaf4bb-0b3a-4fa8-9470-d1e7e2e76d9e_1400x803.png 1272w, https://substackcdn.com/image/fetch/$s_!3Tt3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fdaf4bb-0b3a-4fa8-9470-d1e7e2e76d9e_1400x803.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3Tt3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fdaf4bb-0b3a-4fa8-9470-d1e7e2e76d9e_1400x803.png" width="1400" height="803" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1fdaf4bb-0b3a-4fa8-9470-d1e7e2e76d9e_1400x803.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:803,&quot;width&quot;:1400,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!3Tt3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fdaf4bb-0b3a-4fa8-9470-d1e7e2e76d9e_1400x803.png 424w, https://substackcdn.com/image/fetch/$s_!3Tt3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fdaf4bb-0b3a-4fa8-9470-d1e7e2e76d9e_1400x803.png 848w, https://substackcdn.com/image/fetch/$s_!3Tt3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fdaf4bb-0b3a-4fa8-9470-d1e7e2e76d9e_1400x803.png 1272w, https://substackcdn.com/image/fetch/$s_!3Tt3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fdaf4bb-0b3a-4fa8-9470-d1e7e2e76d9e_1400x803.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Technologies in use at Quizizz</figcaption></figure></div><h3>What is Our Culture like?</h3><p>Tim Ferris once defined culture as &#8212; &#8220;what happens when people are left to their own devices.&#8221; As part of my onboarding over the last few weeks, I&#8217;ve done ~35 1:1s and thought I&#8217;d ask people what they like about working at Quizizz. It was heartwarming to hear people talk about what they inherently feel rather than perks like freebies, free food, snacks, etc., (which by the way, we have!). I&#8217;ll list a few responses verbatim &#8211;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6L05!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27003e1-3e34-42ff-901a-670bd154512f_600x365.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6L05!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27003e1-3e34-42ff-901a-670bd154512f_600x365.png 424w, https://substackcdn.com/image/fetch/$s_!6L05!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27003e1-3e34-42ff-901a-670bd154512f_600x365.png 848w, https://substackcdn.com/image/fetch/$s_!6L05!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27003e1-3e34-42ff-901a-670bd154512f_600x365.png 1272w, https://substackcdn.com/image/fetch/$s_!6L05!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27003e1-3e34-42ff-901a-670bd154512f_600x365.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6L05!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27003e1-3e34-42ff-901a-670bd154512f_600x365.png" width="600" height="365" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d27003e1-3e34-42ff-901a-670bd154512f_600x365.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:365,&quot;width&quot;:600,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!6L05!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27003e1-3e34-42ff-901a-670bd154512f_600x365.png 424w, https://substackcdn.com/image/fetch/$s_!6L05!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27003e1-3e34-42ff-901a-670bd154512f_600x365.png 848w, https://substackcdn.com/image/fetch/$s_!6L05!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27003e1-3e34-42ff-901a-670bd154512f_600x365.png 1272w, https://substackcdn.com/image/fetch/$s_!6L05!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27003e1-3e34-42ff-901a-670bd154512f_600x365.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">What&#8217;s it like to work at Quizizz?</figcaption></figure></div><p>We will continue to stay true to these values. We want to bring people that are excited by working alongside peers like the above. Staying true to our product, our focus is on creating an environment where everyone has the opportunity to learn and grow in their careers. To that end, we expect our team members to learn and adapt themselves. People who thrive here don&#8217;t expect to be micromanaged. As someone famously said, &#8220;It doesn&#8217;t make sense to hire smart people and then tell them what to do; we hire smart people so they can tell us what to do&#8221;.</p><p>If you love taking higher levels of ownership, are comfortable with things not being defined to the T, and have an endless curiosity to learn, relearn and unlearn, then this&nbsp;<em><strong>is</strong></em>&nbsp;the place for you. We will provide you with problems, a bit of chaos, and lots of opportunities, (Chaos creates opportunity!) and empower you to make some truly magical strides in defining the future of Edtech.</p><p>Does this sound like you? If so, apply directly&nbsp;<a href="https://quizizz.com/home/careers">here</a>. If you don&#8217;t see yourself fit the descriptions, but are excited to contribute to our mission, hit me up at sandeep [at] quizizz [dot] com. We are hiring for frontend, backend, mobile, and data roles.</p><p>We are building a global platform from India. More than one in two schools in the US use Quizizz. We&#8217;re rapidly growing in South East Asia and India. It is a rare opportunity to build a global product from the ground up with a brand as loved as ours.</p><p>Thanks to&nbsp;<em><a href="https://www.linkedin.com/in/haripriyakrao/">Haripriya</a></em>&nbsp;for editing and&nbsp;<em><a href="https://www.instagram.com/spreefirit/">Vijaya</a></em>&nbsp;for the beautiful illustrations!</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://quizizz.com/home/careers&quot;,&quot;text&quot;:&quot;We're hiring!&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://quizizz.com/home/careers"><span>We're hiring!</span></a></p>]]></content:encoded></item><item><title><![CDATA[Spicing up the Authorization Layer at Quizizz]]></title><description><![CDATA[Discover how Quizizz scaled its authorization system by adopting SpiceDB, an access control solution inspired by Google&#8217;s Zanzibar]]></description><link>https://eng.quizizz.com/p/spicing-up-the-authorization-layer</link><guid isPermaLink="false">https://eng.quizizz.com/p/spicing-up-the-authorization-layer</guid><dc:creator><![CDATA[Anirudh Singh]]></dc:creator><pubDate>Fri, 09 Jun 2023 11:56:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/13e0cdcf-691a-4e7f-9250-7edb4ceafcc7_801x602.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>Originally published on June 9, 2023</em></p></blockquote><h2>Introduction</h2><p>Here at Quizizz, we&#8217;ve been continuously improving our product and scaling our operations, serving an ever-growing number of teachers and schools globally. As our platform expanded, so did the complexity of our authorization needs, especially with the addition of more collaborative features and the management of diverse user roles and permissions.</p><p>This led us to SpiceDB, an authorization solution inspired by Google&#8217;s Zanzibar. SpiceDB provides a robust, scalable, and flexible framework for fine-grained, relationship-based access control, which perfectly suited our requirements.</p><p>In this blog post, we&#8217;re going to discuss our adoption of SpiceDB, including our reasons for choosing it, how we went about the integration, and the improvements we&#8217;ve observed since.</p><h2>Authentication vs Authorization</h2><p>Authentication and authorization are layers of an application that serve as barriers of defense to stop actors from performing actions that they shouldn&#8217;t be able to.</p><p>Authentication is the process of verifying who a user is and establishing their identity. This usually plays out in a &#8220;login&#8221; experience where the user enters their username/password or uses an SSO (single sign-on) provider in order to prove their identity to you.</p><p>On the other hand, authorization kicks in after a user is authenticated. It&#8217;s about permissions and determining what an authenticated user is allowed to do. It answers the question &#8220;Can this user perform a particular action (like edit or delete) on this resource?&#8221;.</p><p>In a nutshell, authentication is about confirming identity, while authorization is about granting access based on that identity.</p><h2>Authorization at Quizizz</h2><p>Authorization at Quizizz is critical to our collaborative features. Let&#8217;s look at how it&#8217;s utilized in different scenarios:</p><ul><li><p><strong>Sharing Quizzes</strong>: Teachers often need to share quizzes with colleagues. Our authorization system manages who can access these quizzes and what they can do with them &#8211; view, edit, or share.</p></li></ul><ul><li><p><strong>Co-Teaching</strong>: Classrooms often have more than one teacher instructing students. We want to facilitate collaboration among these teachers, and allow them to work together on creating assignments, assessing their students and making data informed adjustments to their lesson plans.&nbsp;</p></li></ul><ul><li><p><strong>Sharing Reports</strong>: Student reports are valuable for collaboration between teachers. Teachers can make data-driven decisions about the pace of instruction and assessment that their students require. Authorization ensures these sensitive data can only be accessed by authorized individuals.</p></li></ul><p>In short, authorization plays a crucial role in enabling collaboration securely and effectively.</p><h2>The need for change</h2><p>Our existing system had chugged along quite well for the last few years, but we saw that it was becoming a limiting factor in the kind of capabilities we wanted to build into Quizizz.</p><p>A couple of key areas that were proving to be difficult were:</p><ul><li><p><strong>Many-to-many</strong> <strong>relationships: Collections </strong>are logical groupings (like a folder) of quizzes. A quiz can be part of many collections, and a collection can have many quizzes. Managing permissions between entities which don&#8217;t have a strict one-to-one relationship like quizzes and collections was becoming complicated at our scale of data and latency requirements</p></li></ul><ul><li><p><strong>Transitive permissions</strong>: A Class in Quizizz is a logical grouping of students. Teachers can create and assign assessments directly to a class instead of having to do that to each individual student. We struggled with transitive permissions, such as granting access from a class to a co-teacher and then to other entities that were part of the class. Bringing these together in an efficient and maintainable manner was proving to be difficult.</p></li></ul><p>In addition, as we built out our B2B product, we required a way to model hierarchical structures where every level would have permissions for the level below it (district-school, enterprise-branch etc.)</p><p>We started re-thinking our current architecture, debating between other out-of-the-box solutions and building it all ourselves, when we stumbled upon Zanzibar, including a couple of blogs by Airbnb and Carta detailing their experience with in-house implementations of Zanzibar.</p><h2>Zanzibar</h2><p><a href="https://research.google/pubs/pub48190/">Zanzibar</a> is based on &#8220;relation tuples&#8221; to define access rules, which it uses to create an entity graph. It then uses set operations to resolve permission checks by traversing the graph of relations.&nbsp;</p><p>These tuples, which could represent entities or other tuples, allow transitive relations. Permissions are defined through userset_rewrite clauses in a tuple, determining access based on whether the entity falls within the permitted tuple set.&nbsp;</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rjol!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1c35ef7-1dc0-4ff7-8c44-cb5c7604b382_400x193.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rjol!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1c35ef7-1dc0-4ff7-8c44-cb5c7604b382_400x193.png 424w, https://substackcdn.com/image/fetch/$s_!rjol!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1c35ef7-1dc0-4ff7-8c44-cb5c7604b382_400x193.png 848w, https://substackcdn.com/image/fetch/$s_!rjol!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1c35ef7-1dc0-4ff7-8c44-cb5c7604b382_400x193.png 1272w, https://substackcdn.com/image/fetch/$s_!rjol!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1c35ef7-1dc0-4ff7-8c44-cb5c7604b382_400x193.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rjol!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1c35ef7-1dc0-4ff7-8c44-cb5c7604b382_400x193.png" width="400" height="193" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d1c35ef7-1dc0-4ff7-8c44-cb5c7604b382_400x193.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:193,&quot;width&quot;:400,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;media&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="media" title="media" srcset="https://substackcdn.com/image/fetch/$s_!rjol!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1c35ef7-1dc0-4ff7-8c44-cb5c7604b382_400x193.png 424w, https://substackcdn.com/image/fetch/$s_!rjol!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1c35ef7-1dc0-4ff7-8c44-cb5c7604b382_400x193.png 848w, https://substackcdn.com/image/fetch/$s_!rjol!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1c35ef7-1dc0-4ff7-8c44-cb5c7604b382_400x193.png 1272w, https://substackcdn.com/image/fetch/$s_!rjol!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1c35ef7-1dc0-4ff7-8c44-cb5c7604b382_400x193.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">Fig 1. Structure of a relation tuple</figcaption></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GCB5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53639202-c659-4b2d-8e21-7cc5603a017e_400x479.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GCB5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53639202-c659-4b2d-8e21-7cc5603a017e_400x479.png 424w, https://substackcdn.com/image/fetch/$s_!GCB5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53639202-c659-4b2d-8e21-7cc5603a017e_400x479.png 848w, https://substackcdn.com/image/fetch/$s_!GCB5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53639202-c659-4b2d-8e21-7cc5603a017e_400x479.png 1272w, https://substackcdn.com/image/fetch/$s_!GCB5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53639202-c659-4b2d-8e21-7cc5603a017e_400x479.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GCB5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53639202-c659-4b2d-8e21-7cc5603a017e_400x479.png" width="400" height="479" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/53639202-c659-4b2d-8e21-7cc5603a017e_400x479.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:479,&quot;width&quot;:400,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;media&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="media" title="media" srcset="https://substackcdn.com/image/fetch/$s_!GCB5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53639202-c659-4b2d-8e21-7cc5603a017e_400x479.png 424w, https://substackcdn.com/image/fetch/$s_!GCB5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53639202-c659-4b2d-8e21-7cc5603a017e_400x479.png 848w, https://substackcdn.com/image/fetch/$s_!GCB5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53639202-c659-4b2d-8e21-7cc5603a017e_400x479.png 1272w, https://substackcdn.com/image/fetch/$s_!GCB5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53639202-c659-4b2d-8e21-7cc5603a017e_400x479.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Fig 2. Rewrite clauses define the operations required to resolve permissions</figcaption></figure></div><p>Continuing our research on Zanzibar, we found <a href="https://authzed.com/">SpiceDB by AuthZed</a>, an open-source Zanzibar-inspired relation-based access-control service that tries to stay true to the principles outlined in the paper.</p><p>On further evaluation, we felt like this was a good fit for us</p><ul><li><p>The community and the developers behind it were active</p><ul><li><p>The discord server has a lot of people talking about how they&#8217;re working with SpiceDB in their organizations</p></li><li><p>Their developers gave us a custom patch to help with backfilling relations into SpiceDB at scale, which was incredibly helpful in getting us started</p></li></ul></li><li><p>They had a solid roadmap of features, and fairly frequent release cycles. One thing that was of quite a lot of interest to us was the <a href="https://github.com/authzed/spicedb/issues/207">Tiger cache proposal</a>, which would help solve for ACL-aware filtering (think full-text search across resources that a user has access to) in a performant manner.</p></li><li><p>The tooling was pretty mature (SDKs in multiple languages, the <a href="https://play.authzed.com/schema">playground</a> etc.).&nbsp;</p></li><li><p>We were able to model our current and expected features in the SpiceDB schema DSL in a very simple and straightforward way, making it extremely easy to grok and maintain for people new to the system.</p></li></ul><p>With this, we started tinkering with it and working on a proof-of-concept to see if it would be able to support the scale and latency requirements at Quizizz, which has hundreds of millions of responses every day.</p><h2>SpiceDB at Quizizz</h2><p>Authzed has a good series of <a href="https://authzed.com/blog">blogs</a> that break down different components of SpiceDB in quite a lot of <a href="https://authzed.com/blog/spicedb-architecture">detail</a>, which I would encourage you to read! But in this blog, I&#8217;ll go through our process of adopting SpiceDB into our stack.</p><p>SpiceDB keeps a record of relation tuples in a selected datastore (we used Postgres on RDS). It processes permission checks by dividing the query into subproblems and <a href="https://authzed.com/blog/consistent-hash-load-balancing-grpc">distributing them across multiple instances</a>. SpiceDB achieves this via <a href="https://github.com/sercand/kuberesolver">kuberesolver</a>, which monitors K8s endpoints for other instances. To streamline this and other SpiceDB tasks, we utilized the <a href="https://authzed.com/products/spicedb-operator">SpiceDB Operator</a> to manage our cluster.</p><p>We built out a new service that would own the domain of Identity and Access management (IAM) in Quizizz, hooked it up to SpiceDB and started running some tests. After being satisfied with the kind of performance and flexibility it offered us in our controlled load tests, we decided to put this into production in &#8220;shadow&#8221; mode, i.e, all requests going to our existing authorization system would also be duplicated to the new service, and we would compare the results between the two to verify correctness and performance.</p><p>Other services interact with the IAM service to check whether an entity has the permissions to perform some action on a resource that is specific to their domain.&nbsp;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!86Aq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ee9806c-f027-45a8-8f8c-2b57282f4652_1600x861.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!86Aq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ee9806c-f027-45a8-8f8c-2b57282f4652_1600x861.png 424w, https://substackcdn.com/image/fetch/$s_!86Aq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ee9806c-f027-45a8-8f8c-2b57282f4652_1600x861.png 848w, https://substackcdn.com/image/fetch/$s_!86Aq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ee9806c-f027-45a8-8f8c-2b57282f4652_1600x861.png 1272w, https://substackcdn.com/image/fetch/$s_!86Aq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ee9806c-f027-45a8-8f8c-2b57282f4652_1600x861.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!86Aq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ee9806c-f027-45a8-8f8c-2b57282f4652_1600x861.png" width="1456" height="784" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3ee9806c-f027-45a8-8f8c-2b57282f4652_1600x861.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:784,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!86Aq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ee9806c-f027-45a8-8f8c-2b57282f4652_1600x861.png 424w, https://substackcdn.com/image/fetch/$s_!86Aq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ee9806c-f027-45a8-8f8c-2b57282f4652_1600x861.png 848w, https://substackcdn.com/image/fetch/$s_!86Aq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ee9806c-f027-45a8-8f8c-2b57282f4652_1600x861.png 1272w, https://substackcdn.com/image/fetch/$s_!86Aq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ee9806c-f027-45a8-8f8c-2b57282f4652_1600x861.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Fig 3. Authorization Flow</figcaption></figure></div><h2>Results</h2><ul><li><p>We saw a tiny latency increase (as one would expect with adding a couple of network hops), but very little of that was attributable to SpiceDB itself</p></li><li><p>We experienced <strong>&lt;20-30ms p99 latencies</strong> for the <strong>CheckPermission </strong>API (which tells us whether an accessor has the required permission for a particular resource)</p></li><li><p>Due to some optimizations that we had done in our internal layers, not every call to access a quiz goes through to SpiceDB. So the most frequent call was actually to list all the relationships for a particular resource, which had a <strong>p99 of &lt;10ms </strong>which was excellent.</p></li><li><p>When we checked for correctness comparisons across a couple of weeks, we found very few instances where there was a mismatch between our existing system and SpiceDB. In fact, a couple of times SpiceDB was correct when our internal system wasn&#8217;t!</p></li><li><p>When we tried to build out something new which wasn&#8217;t part of our existing authorization model, it took less than a couple of days to have that layer up and running in SpiceDB.</p></li></ul><p>However, we did run into a few issues that arose due to the way some of our existing flows worked and how SpiceDB works internally. We were using the TOUCH operation in SpiceDB, which ensures idempotency and does not throw an error if a relation already exists, to create relations. The implementation of TOUCH (at the time), would delete the older relation (if it already existed), and then create a new one. It would create a transaction, and then perform these operations within that transaction.&nbsp;</p><p>This worked fine under normal load, but in a particular case, when we were on-boarding one of our larger clients (the State of Paran&#225;, Brazil), we were sending one WriteRelationships call for each teacher that we onboarded. This resulted in extremely high contention in these transactions, resulting in a massive spike in our Postgres CPU utilization and SpiceDB API latency (graphs below).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-coT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8bf83d-8be8-4d75-a839-1b4896b93f2c_914x433.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-coT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8bf83d-8be8-4d75-a839-1b4896b93f2c_914x433.png 424w, https://substackcdn.com/image/fetch/$s_!-coT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8bf83d-8be8-4d75-a839-1b4896b93f2c_914x433.png 848w, https://substackcdn.com/image/fetch/$s_!-coT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8bf83d-8be8-4d75-a839-1b4896b93f2c_914x433.png 1272w, https://substackcdn.com/image/fetch/$s_!-coT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8bf83d-8be8-4d75-a839-1b4896b93f2c_914x433.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-coT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8bf83d-8be8-4d75-a839-1b4896b93f2c_914x433.png" width="914" height="433" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1c8bf83d-8be8-4d75-a839-1b4896b93f2c_914x433.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:433,&quot;width&quot;:914,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!-coT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8bf83d-8be8-4d75-a839-1b4896b93f2c_914x433.png 424w, https://substackcdn.com/image/fetch/$s_!-coT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8bf83d-8be8-4d75-a839-1b4896b93f2c_914x433.png 848w, https://substackcdn.com/image/fetch/$s_!-coT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8bf83d-8be8-4d75-a839-1b4896b93f2c_914x433.png 1272w, https://substackcdn.com/image/fetch/$s_!-coT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8bf83d-8be8-4d75-a839-1b4896b93f2c_914x433.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Fig 4. Postgres CPU utilization</figcaption></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!EB8Q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d9acddb-dcd8-496f-8dcb-76299f78e81f_1235x348.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EB8Q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d9acddb-dcd8-496f-8dcb-76299f78e81f_1235x348.png 424w, https://substackcdn.com/image/fetch/$s_!EB8Q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d9acddb-dcd8-496f-8dcb-76299f78e81f_1235x348.png 848w, https://substackcdn.com/image/fetch/$s_!EB8Q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d9acddb-dcd8-496f-8dcb-76299f78e81f_1235x348.png 1272w, https://substackcdn.com/image/fetch/$s_!EB8Q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d9acddb-dcd8-496f-8dcb-76299f78e81f_1235x348.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!EB8Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d9acddb-dcd8-496f-8dcb-76299f78e81f_1235x348.png" width="1235" height="348" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5d9acddb-dcd8-496f-8dcb-76299f78e81f_1235x348.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:348,&quot;width&quot;:1235,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!EB8Q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d9acddb-dcd8-496f-8dcb-76299f78e81f_1235x348.png 424w, https://substackcdn.com/image/fetch/$s_!EB8Q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d9acddb-dcd8-496f-8dcb-76299f78e81f_1235x348.png 848w, https://substackcdn.com/image/fetch/$s_!EB8Q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d9acddb-dcd8-496f-8dcb-76299f78e81f_1235x348.png 1272w, https://substackcdn.com/image/fetch/$s_!EB8Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d9acddb-dcd8-496f-8dcb-76299f78e81f_1235x348.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Fig 5. SpiceDB Latencies</figcaption></figure></div><p>The fix in this case was fairly simple: we modified our flow to batch relationships (which SpiceDB allows as part of a single API call) across the onboarding process and made a single call instead of many.&nbsp;</p><h2>Conclusion</h2><p>We realized we needed to upgrade our authorization system to keep up with Quizizz&#8217;s growth and the complexity of our platform.&nbsp;</p><p>The impact of introducing SpiceDB into our systems has been quite significant.&nbsp;</p><ul><li><p>In less than a day, we were able to build and deliver a feature that our enterprise client (State of Paran&#225;) requested, which had a strong dependency on authorization.</p></li><li><p>We continued building highly collaborative capabilities (like the co-teaching experience) into Quizizz with minimal time spent on modeling the authorization layer, and instead were able to focus all our time on building the best possible experience for our teachers.</p></li></ul><div><hr></div><h3>Come Join Us!</h3><p><br>If you&#8217;re interested in working on building an identity management platform, dealing with high scale authorizations, and tinkering with databases &#8212;Quizizz offers a fertile ground for engineering talent to thrive and make an impact. Reach out to us, and let&#8217;s build the future of learning together!</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://quizizz.com/home/careers&quot;,&quot;text&quot;:&quot;We're hiring!&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://quizizz.com/home/careers"><span>We're hiring!</span></a></p><p></p>]]></content:encoded></item></channel></rss>