<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael Solati</title>
    <description>The latest articles on DEV Community by Michael Solati (@michaelsolati).</description>
    <link>https://dev.to/michaelsolati</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2585%2F712e1551-ecda-443f-9bf2-2082b7a8b49e.jpg</url>
      <title>DEV Community: Michael Solati</title>
      <link>https://dev.to/michaelsolati</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/michaelsolati"/>
    <language>en</language>
    <item>
      <title>How 129KB of Whitespace (and a Recursive Loop) Broke the Web</title>
      <dc:creator>Michael Solati</dc:creator>
      <pubDate>Sat, 13 Dec 2025 04:00:00 +0000</pubDate>
      <link>https://dev.to/michaelsolati/how-129kb-of-whitespace-and-a-recursive-loop-broke-the-web-lf7</link>
      <guid>https://dev.to/michaelsolati/how-129kb-of-whitespace-and-a-recursive-loop-broke-the-web-lf7</guid>
      <description>&lt;p&gt;It’s been about one week since the disclosure of &lt;strong&gt;React2Shell (CVE-2025-55182)&lt;/strong&gt;. The initial "drop everything" panic has mostly subsided, and hopefully, your PagerDuty alerts have stopped screaming. Now that the smoke has cleared, we can actually take a breath and look at the wreckage to understand what just happened to the React ecosystem.&lt;/p&gt;

&lt;p&gt;For me, the reality of the situation really hit home when I got &lt;strong&gt;8&lt;/strong&gt; emails from GCP (Google Cloud). It wasn't the usual billing alert warning (the other type of email that causes panic). It looked like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;New Advisory Notification&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Dear Google Cloud customer,&lt;/p&gt;

&lt;p&gt;You've received an important Google Cloud notification affecting your resource...&lt;/p&gt;

&lt;p&gt;Notification Title: &lt;strong&gt;Important Security Information Regarding React &amp;amp; Next.js Vulnerability (CVE-2025-55182)&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When your cloud provider starts sending out a bunch of "Advisory Notification" emails naming a JavaScript framework, you know it’s not just a bug; it’s &lt;a href="https://www.cve.org/CVERecord?id=CVE-2025-55182" rel="noopener noreferrer"&gt;an event!&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This wasn't just a bad week for Next.js developers; it was a wake-up call for the entire industry. So, with the benefit of hindsight, and some unfortunate new developments regarding a "Second Wave" of vulnerabilities; let's dissect exactly how a CVSS 10.0 vulnerability slipped into the default config of the world's most popular React framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  How We Got Here
&lt;/h2&gt;

&lt;p&gt;To understand the exploit, you have to look at the architecture. For years, we pushed for a "seamless" integration between client and server. We wanted &lt;strong&gt;React Server Components (RSC)&lt;/strong&gt; to fetch data directly on the backend and stream it to the frontend.&lt;/p&gt;

&lt;p&gt;But here is the trade-off we don't talk about enough: &lt;strong&gt;Trust Boundaries.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the old days (literally 12 months ago!), we mostly sent JSON back and forth. JSON is safe because it's dumb... It’s just data. But RSC needs to transport "execution context" (think Promises, Symbols, and Server Actions). JSON couldn't handle that, so React built the Flight protocol.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fatal Flaw in Flight
&lt;/h3&gt;

&lt;p&gt;The vulnerability lies in how the &lt;code&gt;react-server-dom-*&lt;/code&gt; packages handle the Flight protocol. By design, Flight allows the server to deserialize complex objects sent by the client.&lt;/p&gt;

&lt;p&gt;If you've studied security history, the word "deserialize" should make you flinch. &lt;a href="https://dev.to/cheetah100/lessons-from-react2shell-1m8b#violation-of-security-principles"&gt;Java (Struts), PHP, and Python have all suffered catastrophic failures here&lt;/a&gt;. React2Shell proved that JavaScript is not immune.&lt;/p&gt;

&lt;p&gt;The vulnerability allowed an unauthenticated attacker to send a specially crafted HTTP request, specifically manipulating &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#thenables" rel="noopener noreferrer"&gt;Promise-like objects known as "thenables"&lt;/a&gt; to the server. React's internal logic would aggressively try to "resolve" this malicious object, allowing the attacker to &lt;a href="https://securitylabs.datadoghq.com/articles/cve-2025-55182-react2shell-remote-code-execution-react-server-components/" rel="noopener noreferrer"&gt;hijack the execution flow and run arbitrary code&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The WAF Bypass (Why the Email Came Too Late)
&lt;/h2&gt;

&lt;p&gt;One of the most annoying parts of this week was watching what we thought were our security defenses fail. We assumed our Web Application Firewalls (WAFs) would catch this. They didn't.&lt;/p&gt;

&lt;p&gt;Attackers realized that most WAFs optimize for speed by only inspecting the first 8KB to 128KB of a request body.&lt;/p&gt;

&lt;p&gt;So, the attackers used a stupid simple technique: &lt;strong&gt;padding.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;They simply added ~129KB of "junk" data (whitespace, comments) to the beginning of their malicious payloads. The WAF would scan the junk, see nothing wrong, and pass the request to the Next.js server. The server, which &lt;em&gt;does&lt;/em&gt; read the whole body, would then deserialize the payload and trigger the remote code execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Second Wave: It Wasn't Just RCE
&lt;/h2&gt;

&lt;p&gt;And just as soon as you think you can pat yourself on the back for patching the RCE vulnerability, the security researchers (and the React team) found that the rabbit hole went deeper.&lt;/p&gt;

&lt;p&gt;On December 11, we learned that the parser wasn't just vulnerable to code execution; it was fragile to structural abuse. This led to two new CVEs that you need to know about right now.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Infinite Loop (CVE-2025-55184 &amp;amp; CVE-2025-67779)
&lt;/h3&gt;

&lt;p&gt;The Flight protocol deserializer is recursive by nature, it has to be to resolve references within references. It turns out, if you send a payload where a chunk references itself in a specific loop, the Node.js process enters a synchronous infinite loop.&lt;/p&gt;

&lt;p&gt;Because Node.js is single-threaded, this is catastrophic. &lt;a href="https://vercel.com/kb/bulletin/security-bulletin-cve-2025-55184-and-cve-2025-55183" rel="noopener noreferrer"&gt;Your CPU spikes to 100%, and the server becomes instantly unresponsive to &lt;em&gt;all&lt;/em&gt; users&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the serverless world (Vercel, AWS Lambda), this leads to what we call &lt;strong&gt;"Denial of Wallet"&lt;/strong&gt;. An attacker can force your functions to run until they time out, spinning up thousands of maxed-out instances and racking up a massive bill for compute time you didn't actually use.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Spy in the Reflection (CVE-2025-55183)
&lt;/h3&gt;

&lt;p&gt;This one is a bit spookier. &lt;a href="https://react.dev/blog/2025/12/11/denial-of-service-and-source-code-exposure-in-react-server-components" rel="noopener noreferrer"&gt;It allows an attacker to trick the server into revealing its own source code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If your Server Actions use &lt;code&gt;toString()&lt;/code&gt; on arguments (or implicitly convert them), an attacker can pass a crafted reference object that serializes the internal state of the closure back to the client.&lt;/p&gt;

&lt;p&gt;If you follow best practices and use environment variables (&lt;code&gt;process.env.DB_PASS&lt;/code&gt;), you're mostly okay; the attacker sees the variable name, not the value. But if you hardcoded API keys or secrets directly into your code? Those are now public knowledge.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Patch of the Patch"
&lt;/h2&gt;

&lt;p&gt;Here is the frustrating part that caught a lot of us off guard.&lt;/p&gt;

&lt;p&gt;When the DoS vulnerability was first found, React released version &lt;strong&gt;19.0.2&lt;/strong&gt;. We all updated. We thought we were safe.&lt;/p&gt;

&lt;p&gt;But researchers found a way to bypass &lt;em&gt;that&lt;/em&gt; fix by adding a layer of indirection to the circular reference. This forced a &lt;em&gt;second&lt;/em&gt; patch cycle. If you updated to fix the RCE but stopped there, you are still vulnerable to the DoS and Source Code Exposure flaws.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where We Go From Here
&lt;/h2&gt;

&lt;p&gt;If you haven't patched in the last 24 hours, you are likely living on borrowed time. There is no configuration change that fully mitigates this vulnerability; upgrading dependencies is mandatory, and you need to be on the &lt;strong&gt;final&lt;/strong&gt; safe versions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Upgrade List:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 15.x:&lt;/strong&gt; Update to &lt;strong&gt;15.0.7+&lt;/strong&gt; (Do not stop at 15.0.6)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 14:&lt;/strong&gt; Update to &lt;strong&gt;14.2.35+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React:&lt;/strong&gt; Update to &lt;strong&gt;19.0.3+&lt;/strong&gt; (for 19.0.x branch) or &lt;strong&gt;19.1.4+&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Crucial Step:&lt;/strong&gt; You must &lt;strong&gt;rebuild&lt;/strong&gt; (&lt;code&gt;next build&lt;/code&gt;) and &lt;strong&gt;redeploy&lt;/strong&gt; your application. The vulnerable code is bundled into your server artifacts; a simple restart won't save you.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Hindsight Perspective
&lt;/h3&gt;

&lt;p&gt;React2Shell and its "offspring" vulnerabilities are going to change the conversation around "Full Stack" frameworks. We traded strict separation of concerns for developer convenience, and we got burned.&lt;/p&gt;

&lt;p&gt;Does this mean RSC is dead? No. But the days of assuming the server-side code in your Next.js app is "safe by default" are over. We need to treat our frontend-backend hybrids with the same security rigor we apply to backend-only services.&lt;/p&gt;

&lt;p&gt;Time to patch up (&lt;strong&gt;again&lt;/strong&gt;) and get back to building. Happy Friday!&lt;/p&gt;

</description>
      <category>react</category>
      <category>security</category>
      <category>nextjs</category>
      <category>node</category>
    </item>
    <item>
      <title>I Built an AI-Powered TTRPG Adventure Generator (Because Generic Hallucinations Are Boring)</title>
      <dc:creator>Michael Solati</dc:creator>
      <pubDate>Wed, 03 Dec 2025 16:00:00 +0000</pubDate>
      <link>https://dev.to/michaelsolati/i-built-an-ai-powered-ttrpg-adventure-generator-because-generic-hallucinations-are-boring-362m</link>
      <guid>https://dev.to/michaelsolati/i-built-an-ai-powered-ttrpg-adventure-generator-because-generic-hallucinations-are-boring-362m</guid>
      <description>&lt;p&gt;I grew up reading the gripping and petrifying &lt;a href="https://michaelsolati.com/blog/telling-a-typeform-story-on-the-google-assistant" rel="noopener noreferrer"&gt;narratives of R.L. Stine&lt;/a&gt; and spending way too much time playing story-driven video games. Now that I'm older, I've gotten into the TTRPG space because it hits a certain &lt;em&gt;je ne sais quoi&lt;/em&gt; that tickles my lizard brain.&lt;/p&gt;

&lt;p&gt;I've also found that I'm not as good at coming up with ideas for adventures as I used to be. For anyone who has ever sat behind you laptop screen and keyboard, staring into the blinking cursor, you know the struggle: you have a cool concept, like "a Cyberpunk heist in a floating city," but when you try to flesh it out, you hit a wall.&lt;/p&gt;

&lt;p&gt;Naturally, we now can turn to AI for help. But here's the problem: standard LLMs are great at hallucinating &lt;em&gt;generic&lt;/em&gt; tropes. You ask for a "scary forest," and you get the same old "twisted trees and whispering winds." It lacks soul. It lacks... &lt;em&gt;planning&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I wanted a tool that didn't just make things up but actually &lt;em&gt;researched&lt;/em&gt; real-world lore, wikis, what other people have done, and forums to generate grounded, creative adventures. So, in true developer fashion, I stopped prepping a campaign and spent the weekend building a tool to do it for me.&lt;/p&gt;

&lt;p&gt;Meet &lt;strong&gt;Adventure Weaver&lt;/strong&gt;, an application built with &lt;a href="https://exa.ai/" rel="noopener noreferrer"&gt;Exa&lt;/a&gt; that helps TTRPG Game Masters, and writers in general, overcome their writer's block by turning the entire internet into a procedurally generated library of inspiration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Research-then-Generate" Workflow
&lt;/h2&gt;

&lt;p&gt;Unlike a standard chatbot that just spits out text, we are going to build something sophisticated. Our app follows a "Research-then-Generate" workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;User Prompt&lt;/strong&gt;: You describe the vibe (e.g., "A city built on the back of a dying god").&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Agent&lt;/strong&gt;: We dispatch an AI agent via Exa. It doesn't just search for keywords; it understands &lt;em&gt;concepts&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Streaming Updates&lt;/strong&gt;: We stream the agent's actions ("Crawling wiki...", "Reading blog...") to the user in realtime, because loading spinners are boring.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Inspiration Graph&lt;/strong&gt;: We visualize the web of inspiration using D3.js, so you can see exactly where that creepy villain idea came from.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is how I built it, and how you can get it running on your machine right now.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;p&gt;We are keeping it modern and fast:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js&lt;/strong&gt;: The framework for production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exa&lt;/strong&gt;: The search engine made for AIs that powers our research.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tailwind CSS&lt;/strong&gt;: Because I don't want to spend 3 hours centering a div.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D3.js&lt;/strong&gt;: For that "exploration board" visualization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsvq48b3rftzs8ax316t7.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsvq48b3rftzs8ax316t7.webp" alt="Fun graphic of technology used" width="800" height="339"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;h2&gt;
  
  
  Step 1: Defining the Adventure Schema
&lt;/h2&gt;

&lt;p&gt;To ensure the AI gives us usable data (and not just a wall of text), we need to define a strict JSON schema. This acts as a contract, telling the AI exactly what fields we need: titles, plot hooks, NPCs, and locations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/api/generate/route.ts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;adventureSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;adventure_title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;summary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;plot_hooks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;npcs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;locations&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;adventure_title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;plot_hooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;array&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
    &lt;span class="na"&gt;npcs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;array&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;locations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;array&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: The "Secret Sauce" (Exa)
&lt;/h2&gt;

&lt;p&gt;This is where the magic happens. We aren't just matching strings; we are doing &lt;strong&gt;Neural Search&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you search for "realistic dragon biology" on a normal engine, you might get a movie listicle. Exa's neural model understands you are looking for &lt;em&gt;speculative biology&lt;/em&gt; and can find niche blog posts or StackExchange threads that discuss the actual physics of fire-breathing.&lt;/p&gt;

&lt;p&gt;We create an API route to kick off the research. Note that we aren't awaiting the result here because research takes time! We start it and return a &lt;code&gt;taskId&lt;/code&gt; immediately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/api/generate/route.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Exa&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exa-js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;exa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Exa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EXA_API_KEY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ... adventureSchema definition ...&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instructions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`You are a creative assistant for a TTRPG Game Master. 
  Use the user's prompt to find ideas from blogs, forums, and wikis to generate a compelling adventure.
  Please generate a title, a summary, a few plot hooks, some interesting NPCs, and some key locations for the adventure.
  Each one of the story components should be put into their respective schema.
  Something like the summary should not have the title, plot hooks, NPCs, etc... Those should be in their own schemas.
  For context, here is the user's prompt: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Create the research task, but don't wait for it to complete.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;researchTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;exa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;research&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;outputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;adventureSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Immediately return the task ID to the client.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;researchTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;researchId&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Streaming the "Vibes"
&lt;/h2&gt;

&lt;p&gt;Waiting 30 seconds for a response feels like an eternity. To fix this, we use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events" rel="noopener noreferrer"&gt;&lt;strong&gt;Server-Sent Events (SSE)&lt;/strong&gt;&lt;/a&gt; to stream the agent's progress.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fun fact! Did you know eBay uses Server-Sent events to countdown those final seconds before a sale ends?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This endpoint listens to the task stream. When Exa says "I'm searching for medieval castles," we push that message to the frontend instantly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/api/adventure/[taskId]/route.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Exa&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exa-js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;CitationProcessor&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/lib/citation-processor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;exa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Exa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EXA_API_KEY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;taskId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// We'll stream the progress from Exa.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReadableStream&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextEncoder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;citationProcessor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CitationProcessor&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;taskStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;exa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;research&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;taskStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;citationProcessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;processEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;task-operation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
          &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;plan-operation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Searching: "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
              &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;crawl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Crawling: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
              &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;think&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
              &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Starting an unknown journey...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="s2"&gt;`data: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;})}&lt;/span&gt;&lt;span class="s2"&gt;\n\n`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;research-output&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
          &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;finalResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deduplicatedCitations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;citationProcessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCitations&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resultData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;finalResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;citations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;deduplicatedCitations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;};&lt;/span&gt;

          &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="s2"&gt;`data: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;result&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resultData&lt;/span&gt;&lt;span class="p"&gt;})}&lt;/span&gt;&lt;span class="s2"&gt;\n\n`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/event-stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keep-alive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ddt2veuchhg2m4brpz3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ddt2veuchhg2m4brpz3.gif" alt="Server-Sent Events (SSE) in action" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;h3&gt;
  
  
  Citation Mapping
&lt;/h3&gt;

&lt;p&gt;One of the coolest things about this setup is citation mapping. We can actually track which specific sub-task (like "Search for NPC names") produced which URL results. This lets us tag the generated NPCs with the actual folklore blog post that inspired them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/lib/citation-processor.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CitationProcessor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;taskIdToSection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;citationsBySection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Citation&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

  &lt;span class="nf"&gt;processEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Identify which section (NPCs, Plot Hooks, etc.) the agent is working on&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;task-definition&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instructions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;npc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;taskIdToSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;npcs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="c1"&gt;// ... map other sections (locations, plot hooks, etc.)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="c1"&gt;// 2. Capture search results and assign them to that section&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;section&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;taskIdToSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;section&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Add the found URLs to the specific section's citation list&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;citationsBySection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;section&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpmw5pf25lxhvwei0n91u.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpmw5pf25lxhvwei0n91u.gif" alt="Citation mapping in action via D3.js" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;h2&gt;
  
  
  🚀 Quickstart: Run It Locally
&lt;/h2&gt;

&lt;p&gt;Enough theory... Let's get this running on your machine so you can start generating campaigns for your next session!&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;You'll need &lt;strong&gt;Node.js&lt;/strong&gt; (v20 or higher) and &lt;strong&gt;npm&lt;/strong&gt;. You will also need an &lt;strong&gt;Exa API key&lt;/strong&gt; (for the research) and an &lt;strong&gt;OpenAI API key&lt;/strong&gt; (or compatible provider) for the generation.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Clone the Repository
&lt;/h3&gt;

&lt;p&gt;Open up your terminal and grab the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/MichaelSolati/adventure-weaver.git
&lt;span class="nb"&gt;cd &lt;/span&gt;adventure-weaver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Install Dependencies
&lt;/h3&gt;

&lt;p&gt;Let npm do its thing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Set Up Environment Variables
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;.env.local&lt;/code&gt; file in the root of your project. This keeps your secrets safe. Add your keys here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EXA_API_KEY=your_exa_api_key_here
LLM_API_KEY=your_openai_api_key_here
LLM_MODEL=gpt-4o  # or your preferred model
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Run the Development Server
&lt;/h3&gt;

&lt;p&gt;Fire it up!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to &lt;code&gt;http://localhost:3000&lt;/code&gt;, enter a prompt like "A cyberpunk scorched earth assault to liberate the digital ghost of your former lover from a megacorp's tower," (IYKYK) and watch the magic happen!&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;At the end of the day, the tools we use are just a means to an end. The real magic here is about shifting how we interact with AI. By moving from simple prompting to a "Research-then-Generate" workflow with Exa, we stop the AI from hallucinating generic tropes and start grounding it in actual creativity. It respects the nuance of capturing a specific "vibe," rather than just matching keywords.&lt;/p&gt;

&lt;p&gt;The result is richer, grounded content that feels less like a robot wrote it and more like a curated creative work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyrt5b5h4cwhiefrdgpm.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyrt5b5h4cwhiefrdgpm.gif" alt="Full Exa research and generation workflow running" width="760" height="427"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;If you want to try weaving your own adventures, or if you're thinking, &lt;em&gt;"Show me the code!"&lt;/em&gt;, you can find the full source code on &lt;a href="https://github.com/MichaelSolati/adventure-weaver" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>nextjs</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Visualizing the Event Loop: A Guide to Microtasks, Macros, and Timers</title>
      <dc:creator>Michael Solati</dc:creator>
      <pubDate>Tue, 25 Nov 2025 16:00:00 +0000</pubDate>
      <link>https://dev.to/michaelsolati/visualizing-the-event-loop-a-guide-to-microtasks-macros-and-timers-2l22</link>
      <guid>https://dev.to/michaelsolati/visualizing-the-event-loop-a-guide-to-microtasks-macros-and-timers-2l22</guid>
      <description>&lt;p&gt;I LOVE digging into the "weird" parts of JavaScript. When prepping for technical interviews, or just trying to debug why a UI update isn't rendering when I expect it to, I believe it's critical to understand not just &lt;em&gt;what&lt;/em&gt; the language does, but &lt;em&gt;how&lt;/em&gt; it schedules it.&lt;/p&gt;

&lt;p&gt;An interesting scenario I often throw at folks (and have been thrown at me) is the classic "predict the output" game. It seems simple on the surface, but it quickly reveals if you have a solid mental model of the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Execution_model" rel="noopener noreferrer"&gt;JavaScript execution model&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;Imagine looking at this snippet. What order do the numbers print in?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1. Start&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2. Timeout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3. Promise&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;4. End&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When faced with this as a junior developer, I used to think: "Okay, code runs top to bottom. But &lt;code&gt;setTimeout&lt;/code&gt; is asynchronous, so it waits. &lt;code&gt;Promise&lt;/code&gt; is also async. So maybe 'Start', 'End', then... whichever one is faster?"&lt;/p&gt;

&lt;p&gt;If you guessed: &lt;code&gt;Start&lt;/code&gt; -&amp;gt; &lt;code&gt;End&lt;/code&gt; -&amp;gt; &lt;code&gt;Timeout&lt;/code&gt; -&amp;gt; &lt;code&gt;Promise&lt;/code&gt;, you'd be following logical intuition, but you'd be wrong.&lt;/p&gt;

&lt;p&gt;The actual output is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;1. Start&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;4. End&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;3. Promise&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;2. Timeout&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Wait, why? &lt;code&gt;setTimeout&lt;/code&gt; has a delay of &lt;code&gt;0&lt;/code&gt;, so shouldn't it run immediately after the main code finishes?&lt;/p&gt;

&lt;p&gt;After the anxiety settles, remember something your friend (me) said: "It's important to understand the capabilities and data structures in any given language." In this case, we need to talk about the &lt;a href="https://html.spec.whatwg.org/multipage/webappapis.html#event-loops" rel="noopener noreferrer"&gt;&lt;strong&gt;Event Loop&lt;/strong&gt;&lt;/a&gt;, and specifically, the difference between &lt;strong&gt;Macrotasks&lt;/strong&gt; and &lt;strong&gt;Microtasks&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Model: Visualizing the Traffic
&lt;/h2&gt;

&lt;p&gt;To understand why the Promise beats the Timeout, we have to look at the architecture. JavaScript utilizes a single main thread of execution coupled with a sophisticated mechanism known as the Event Loop. It can only do one thing at a time.&lt;/p&gt;

&lt;p&gt;Here is the flow you need to visualize:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Call Stack:&lt;/strong&gt; This is where your code runs. "Start" and "End" happen here immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Web APIs:&lt;/strong&gt; When the browser sees &lt;code&gt;setTimeout&lt;/code&gt;, it hands that timer off to the Web APIs (or &lt;code&gt;libuv&lt;/code&gt; in Node.js). Even with a delay of &lt;code&gt;0&lt;/code&gt;, it doesn't go back to the stack; it goes to a Queue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Queues:&lt;/strong&gt; This is where the magic (and confusion) happens. There isn't just one queue.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Macrotask Queue (Task Queue):&lt;/strong&gt; This holds things like &lt;code&gt;setTimeout&lt;/code&gt;, &lt;code&gt;setInterval&lt;/code&gt;, and I/O operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Microtask Queue:&lt;/strong&gt; This holds &lt;code&gt;Promise&lt;/code&gt; callbacks (&lt;code&gt;.then&lt;/code&gt;, &lt;code&gt;.catch&lt;/code&gt;, &lt;code&gt;.finally&lt;/code&gt;), &lt;code&gt;queueMicrotask&lt;/code&gt;, and &lt;code&gt;MutationObserver&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Breakdown: The VIP Lane
&lt;/h2&gt;

&lt;p&gt;Here is the golden rule that solves the puzzle: &lt;strong&gt;The Event Loop performs a &lt;a href="https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint" rel="noopener noreferrer"&gt;Microtask Checkpoint&lt;/a&gt; immediately after the Call Stack empties.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Microtasks are like VIPs at a club. They get to cut the line. But more importantly, the Event Loop processes the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide/In_depth" rel="noopener noreferrer"&gt;Microtask Queue exhaustively&lt;/a&gt;. If a microtask schedules &lt;em&gt;another&lt;/em&gt; microtask, it gets added to the same queue and processed in the same cycle. The runtime will not move on to the next Macrotask (or even update the UI!) until the VIP section is completely empty.&lt;/p&gt;

&lt;p&gt;Let's trace our code again with this model:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;console.log('1. Start')&lt;/code&gt;: Pushed to Call Stack. Executed. Popped.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; &lt;code&gt;Start&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;setTimeout(..., 0)&lt;/code&gt;: Pushed to Stack. The engine sees it's a timer, hands it to Web APIs. The Web API sees 0ms delay, so it queues the callback into the &lt;strong&gt;Macrotask Queue&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Promise.resolve().then(...)&lt;/code&gt;: Pushed to Stack. The engine sees a Promise resolution. It queues the &lt;code&gt;.then()&lt;/code&gt; callback into the &lt;strong&gt;Microtask Queue&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;console.log('4. End')&lt;/code&gt;: Pushed to Stack. Executed. Popped.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; &lt;code&gt;End&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Now, the global code is done. The Call Stack is empty. The Event Loop wakes up and asks: &lt;em&gt;"Is there anything in the Microtask Queue?"&lt;/em&gt; Yes, there is! The Promise callback.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The Event Loop moves the Promise callback to the Call Stack. Executed.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; &lt;code&gt;Promise&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Now the stack is empty again. The Event Loop asks: &lt;em&gt;"Any more Microtasks?"&lt;/em&gt; No. &lt;em&gt;"Okay, let's move on."&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rendering (Browser Only):&lt;/strong&gt; At this point, the browser decides if it needs to update the rendering Layout/Paint. This happens &lt;em&gt;after&lt;/em&gt; microtasks but &lt;em&gt;before&lt;/em&gt; the next Macrotask.&lt;/li&gt;
&lt;li&gt;The Event Loop moves the Timeout callback to the Call Stack. Executed.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; &lt;code&gt;Timeout&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Solution: One Loop to Rule Them All
&lt;/h2&gt;

&lt;p&gt;So, next time you're faced with a snippet like this—whether in an interview or a tricky debugging session—you don't need to rely on intuition. You just need to trust the hierarchy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eventLoopCheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Script Start&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// 1. Synchronous&lt;/span&gt;

  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;setTimeout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// 3. Macrotask (Low Priority)&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Promise&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// 2. Microtask (High Priority)&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Cheat Sheet
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Synchronous Code:&lt;/strong&gt; Runs first (Call Stack).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Microtasks (Promises):&lt;/strong&gt; Run immediately after the stack clears, &lt;em&gt;before&lt;/em&gt; rendering or new tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rendering:&lt;/strong&gt; (Browser only) Happens after Microtasks but &lt;em&gt;before&lt;/em&gt; the next Macrotask.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Macrotasks (Timers):&lt;/strong&gt; Run only when the stack AND the Microtask queue are empty.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;h3&gt;
  
  
  A Note for Node.js Developers
&lt;/h3&gt;

&lt;p&gt;If you are running this in Node.js, there is a "Super VIP" lane called &lt;code&gt;process.nextTick&lt;/code&gt;. This runs even before Promises!.&lt;/p&gt;

&lt;p&gt;In Node.js, &lt;a href="https://nodejs.org/en/learn/asynchronous-work/understanding-processnexttick" rel="noopener noreferrer"&gt;&lt;code&gt;process.nextTick&lt;/code&gt;&lt;/a&gt; is technically not part of the Event Loop phases; it is processed immediately after the current operation completes. This means &lt;code&gt;nextTick&lt;/code&gt; can actually starve your I/O if you aren't careful!&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;I love these visual mental models! Hopefully, you found this helpful. If you have any interesting or clever ways you visualize the Event Loop, I'd love to hear them! Or if you have a trickier code snippet that stumps people, I'd love to see that too.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>node</category>
    </item>
    <item>
      <title>LeetCode vs. Vibe Coding: The Reality of Interviewing in 2025</title>
      <dc:creator>Michael Solati</dc:creator>
      <pubDate>Thu, 20 Nov 2025 16:00:00 +0000</pubDate>
      <link>https://dev.to/michaelsolati/leetcode-vs-vibe-coding-the-reality-of-interviewing-in-2025-2582</link>
      <guid>https://dev.to/michaelsolati/leetcode-vs-vibe-coding-the-reality-of-interviewing-in-2025-2582</guid>
      <description>&lt;p&gt;I LOVE a good technical challenge. There is something satisfying about solving a complex problem, optimizing a data structure, or shaving off a few milliseconds of latency. But this past summer, I found myself back in the job market, and let me tell you… Things have changed.&lt;/p&gt;

&lt;p&gt;The industry has split into two distinct realities.&lt;/p&gt;

&lt;p&gt;On the one hand, I interviewed with established enterprise giants whose processes were like bread and butter to me. I was solving algorithmic puzzles and showcasing my understanding of the DOM and general web development skills. On the other hand, I interviewed with startups where the interviewer essentially handed me the keys to GitHub Copilot and said, "Build this feature. You have 45 minutes. Go."&lt;/p&gt;

&lt;p&gt;It made me feel a bit like an "old man yelling at the sky," but it also raised some serious questions about where our industry is headed. Are we testing for engineering skills, or for subscription tiers?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Great Bifurcation
&lt;/h2&gt;

&lt;p&gt;If you've been interviewing lately, you've probably felt this whiplash. The data backs it up; we are seeing a &lt;a href="https://landing.underdog.io/blog/reality-of-tech-interviews-2025" rel="noopener noreferrer"&gt;dramatic bifurcation in how companies hire&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Enterprise Players
&lt;/h3&gt;

&lt;p&gt;The big players are committed to the LeetCode style interview. Why? Because they are terrified of AI impostors.&lt;/p&gt;

&lt;p&gt;When I sat for these interviews, it was all about "Proof of Work". They wanted to know that &lt;em&gt;I&lt;/em&gt; possessed the raw cognitive bandwidth to manipulate logic without a robot whispering the answer in my ear. And honestly? I get it. With tools like ChatGPT, it's easier than ever to fake competence. In fact, &lt;a href="https://medium.com/fonzi-ai/how-ai-is-transforming-tech-interviews-in-2025-b591de563b5d" rel="noopener noreferrer"&gt;81% of interviewers at Big Tech companies suspect candidates of using AI to cheat&lt;/a&gt; during remote sessions.&lt;/p&gt;

&lt;p&gt;So, the LeetCode grind isn't going anywhere. It's the safety against the "AI-powered poser."&lt;/p&gt;

&lt;h3&gt;
  
  
  The Startups
&lt;/h3&gt;

&lt;p&gt;Then there are the startups. These folks have embraced our new machine overlords. They aren't looking for someone who can write a linked list from memory; &lt;a href="https://www.finalroundai.com/blog/software-developer-skills-ctos-want-in-2025" rel="noopener noreferrer"&gt;they want "AI Editors" and "Sense-Makers"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In these interviews, the constraints weren't syntax or memory; they were my ability to prompt, debug, and integrate. It was eventually fun, but also frantic. I had to get over the mindset of “showing" or “explaining" my work. I wasn't there to show I know the native functions and behaviors of the DOM. Instead, there was this massive expectation for speed. Because AI tools are &lt;em&gt;supposed&lt;/em&gt; to make us 10x developers, the interview pacing was often set to "warp speed," expecting me to blaze through boilerplate code.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Hidden Barrier to Entry&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here is the part that really worries me, and it's something we aren't talking about enough: &lt;strong&gt;THE COST.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the traditional interview, all you needed was a brain and a whiteboard (or a laptop). Today, for these startup interviews, you are often expected to bring your own AI tools.&lt;/p&gt;

&lt;p&gt;The startups aren't always providing the enterprise seats for these AI tools during the interview process. So, are you on ChatGPT's free tier? Or are you shelling out $20/month for Plus? Do you have a personal GitHub Copilot subscription?&lt;/p&gt;

&lt;p&gt;It introduces a subtle but real economic barrier. Is your success in an interview dependent on whether you can afford Claude Code Max 20x ($200/month) vs. Cursor Pro ($20/month)? If my model hallucinates because I'm on a lower tier, and I didn't catch it, did I fail the interview?&lt;/p&gt;

&lt;p&gt;Startups are essentially asking candidates to pay for the privilege of being efficient enough to get hired.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Home Court Advantage" Problem
&lt;/h2&gt;

&lt;p&gt;Some companies, like Meta, are addressing this inequality by &lt;a href="https://www.businessinsider.com/meta-job-candidates-use-ai-coding-interviews-2025-7" rel="noopener noreferrer"&gt;introducing standardized interviews that use the same AI tool for everyone&lt;/a&gt;. On paper, this sounds fair. It levels the playing field and removes the cost barrier.&lt;/p&gt;

&lt;p&gt;But does it?&lt;/p&gt;

&lt;p&gt;We all have our "fine-tuned" setups. You may have a &lt;code&gt;CLAUDE.md&lt;/code&gt; or &lt;code&gt;AGENTS.md&lt;/code&gt; file. We are "pros" with &lt;em&gt;our&lt;/em&gt; tools. Throwing a developer into a standardized AI environment, like &lt;a href="https://coderpad.io/blog/hiring-developers/ai-in-the-interview-is-not-cheating-it-is-the-job-according-to-meta/" rel="noopener noreferrer"&gt;Meta's CoderPad setup&lt;/a&gt;, is like handing a race car driver a rental sedan and asking them to set a lap record. You might know how to drive, but you don't know &lt;em&gt;this&lt;/em&gt; car.&lt;/p&gt;

&lt;p&gt;From my own experience working at Meta, I found that even their internal tool, Metamate, struggled to handle complex tasks on the actual Facebook codebase. It often felt like I was correcting everything it output rather than it actually making life better. Anyway, if you've never used their specific flavor of AI before, are you ready to use it like a pro in a high-stakes 60-minute interview? Probably not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try Before You Buy
&lt;/h2&gt;

&lt;p&gt;Finally, this brings me to the &lt;strong&gt;Paid Work Trial&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Startups are increasingly asking candidates to join the team for a few days to work on actual production tickets. In theory, there is some merit to this. It gives you a really good idea of how well you'll click with a team, and it mitigates the risk of a "false positive" hire.&lt;/p&gt;

&lt;p&gt;But let's look at the logistics. &lt;strong&gt;What if you already have a job?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Can you really take 3 to 5 days off to work somewhere else? That's a massive time commitment that privileges people who are currently unemployed or have incredibly flexible schedules. Hell, does your current employer allow you to moonlight at another job? Most employment contracts have clauses that prevent us from working for another entity, even for a short "trial." The Paid Work Trial requires us to breach our current contract to potentially secure a new one.&lt;/p&gt;

&lt;h2&gt;
  
  
  tl;dr Everything is Awful
&lt;/h2&gt;

&lt;p&gt;I wish I had a magic answer, but the reality is that for the foreseeable future, we're going to have to be bilingual.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Keep your fundamentals sharp:&lt;/strong&gt; The "old way" isn't dead. We still need to prove we understand the code well enough to audit the AI's output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Master (Generic) Prompts:&lt;/strong&gt; Don't just rely on your custom configs. Get good at "Prompt Engineering" in a vanilla environment, because you might not get to bring your own configs to the interview.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protect your time:&lt;/strong&gt; If a company asks for a multiday trial, ensure it is &lt;strong&gt;paid&lt;/strong&gt; at market rates. Don't work for free.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's a weird time to be interviewing, caught between the LeetCode grinders and the vibe-coding speedrunners. But hey, at least it keeps things interesting, right?&lt;/p&gt;

&lt;p&gt;I'd love to hear your horror stories (or success stories!) from the 2025 interview circuit. Are you seeing more work trials? More AI? Let me know!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>discuss</category>
      <category>interview</category>
    </item>
    <item>
      <title>I'm Getting Serious Déjà Vu... But This Time It's Different</title>
      <dc:creator>Michael Solati</dc:creator>
      <pubDate>Tue, 18 Nov 2025 16:00:00 +0000</pubDate>
      <link>https://dev.to/michaelsolati/im-getting-serious-deja-vu-but-this-time-its-different-17f4</link>
      <guid>https://dev.to/michaelsolati/im-getting-serious-deja-vu-but-this-time-its-different-17f4</guid>
      <description>&lt;p&gt;I've been getting the strangest sense of déjà vu lately.&lt;/p&gt;

&lt;p&gt;I'm based in the Bay Area, and as I look at the tech landscape today, I'm transported back to somewhere around 2014 or 2015. I was just starting my own journey as a developer, and the coding bootcamps were &lt;em&gt;exploding&lt;/em&gt;. It wasn't just a feeling: &lt;a href="https://www.coursereport.com/reports/2015-coding-bootcamp-market-size-study" rel="noopener noreferrer"&gt;the market grew by 138% in 2015&lt;/a&gt; alone, with graduates jumping from just over 6,700 to more than 16,000 in a single year. I would know, I was one of them! (Shoutout to the A100 team.) Every tech meetup, every "hack night," you'd meet a fresh-faced new developer, portfolio in hand, bright-eyed and ready to build.&lt;/p&gt;

&lt;p&gt;The hiring pool was flooded, and we all had to claw our way into this industry, reading every blog post, breaking (and fixing) side projects, and slowly building careers. Our path, which felt so personal, was actually just a &lt;a href="https://www.coursereport.com/reports/2015-coding-bootcamp-market-size-study#tuition" rel="noopener noreferrer"&gt;packaged and sold 12-week "sprint."&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I remember feeling this perplexing mix of curiosity and concern. Was my job, my hard-won knowledge, or just a commodity?&lt;/p&gt;

&lt;p&gt;Fast forward to today. I've had the opportunity to build a successful career for myself and feel comfortable and knowledgeable in my domain, and yet I'm getting that same feeling. But this time, the "bootcamp" isn't a school. It's GitHub Copilot. It's ChatGPT. It's the entire era of AI-vibe-coding.&lt;/p&gt;

&lt;p&gt;I see demos on Twitter (not calling it X) and LinkedIn of people building entire, complex applications with a few well-worded prompts. And that old, familiar question is back: Are our skills about to be commoditized... again?&lt;/p&gt;

&lt;p&gt;My initial instinct was to say, "It's the same story, just a new tool." But the more I looked at the data, the more I realized my déjà vu was misleading. The 2010s "bootcamp boom" and the 2020s "AI wave" are fundamentally different beasts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0x0xkf3188w25s1aefxd.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0x0xkf3188w25s1aefxd.webp" alt="The 'glitch in the Matrix' scene: An animated GIF where Neo observes a black cat walk past him twice in the exact same way, a repeating anomaly that makes him say 'déjà vu.'" width="600" height="338"&gt;&lt;/a&gt;&lt;br&gt;Whoa. Déjà vu.
  &lt;/p&gt;

&lt;h2&gt;
  
  
  The Old "Filter": Horizontal Saturation
&lt;/h2&gt;

&lt;p&gt;The bootcamp boom was, at its heart, a promise of access. It promised that anyone with the drive (and the tuition) could become a developer. It was a "gold rush" for skills, and it minted many developers who were fantastic at the "how." They knew how to build a to-do list, a simple blog, or a full-stack project with the MERN/MEAN stack.&lt;/p&gt;

&lt;p&gt;But "how" is only half the story.&lt;/p&gt;

&lt;p&gt;The challenge back then was one of &lt;strong&gt;horizontal saturation&lt;/strong&gt;. The market saw a massive influx of new, similarly-skilled junior talent, all competing for a rapidly &lt;em&gt;expanding&lt;/em&gt; pool of junior-level roles.&lt;/p&gt;

&lt;p&gt;After that initial wave, the market &lt;em&gt;calibrated&lt;/em&gt;. A "Great Filter" emerged. It turned out that getting a job and &lt;em&gt;keeping&lt;/em&gt; a job were two very different things.&lt;/p&gt;

&lt;p&gt;The filter wasn't "Can you code?"&lt;/p&gt;

&lt;p&gt;It became:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can you read, understand, and debug someone &lt;em&gt;else's&lt;/em&gt; code?&lt;/li&gt;
&lt;li&gt;Can you sit in a planning meeting and translate a vague business need into a concrete technical spec?&lt;/li&gt;
&lt;li&gt;Can you explain &lt;em&gt;why&lt;/em&gt; you chose PostgreSQL over MongoDB for this specific feature, and can you defend that trade-off?&lt;/li&gt;
&lt;li&gt;Can you write a meaningful test?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The filter separated &lt;em&gt;coding&lt;/em&gt; from &lt;em&gt;engineering&lt;/em&gt;. Coding is the act of writing code. Engineering is the discipline of designing, building, maintaining, and owning systems in the real world.&lt;/p&gt;

&lt;p&gt;The market wasn't saturated with &lt;em&gt;engineers&lt;/em&gt;. It was saturated with people who could &lt;em&gt;code&lt;/em&gt;. The "cream" that rose to the top were the folks who understood that the 12-week program was just the starting line, not the finish.&lt;/p&gt;

&lt;h2&gt;
  
  
  The New "Filter": Vertical Compression &amp;amp; Pipeline Elimination
&lt;/h2&gt;

&lt;p&gt;This is where the parallel breaks down. The 2020s market isn't just saturated; it's being squeezed from two directions by forces we didn't have in 2015.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Squeeze from the Top: "Vertical Compression"
&lt;/h3&gt;

&lt;p&gt;Today's market is defined by &lt;strong&gt;vertical compression&lt;/strong&gt;. The 2022-2024 period didn't just see a hiring slowdown; it saw &lt;a href="https://www.wearedevelopers.com/en/magazine/418/software-engineering-over-saturated" rel="noopener noreferrer"&gt;over 660,000 tech workers lose their jobs&lt;/a&gt;. Crucially, these layoffs weren't confined to junior staff. They included "top-paid and most experienced developers in the industry" from elite companies like Google, Meta, and Amazon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fop2wm68q6w4w8pc778di.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fop2wm68q6w4w8pc778di.webp" alt="An illustration of an office layoff, showing four diverse employees with sad expressions holding boxes of their belongings in front of empty office cubicles." width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;660k+ developers lost their jobs from 2022 to 2024
  &lt;/p&gt;

&lt;p&gt;This creates a "domino effect" that is crushing the job market from the top down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Laid-off senior architects compete for senior developer roles.&lt;/li&gt;
&lt;li&gt;Displaced senior developers compete for mid-level roles.&lt;/li&gt;
&lt;li&gt;Displaced mid-level developers compete for junior roles.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This leaves entry-level and new "AI wave" graduates in an almost impossible position. In 2015, I was competing with other bootcamp grads on a relatively even playing field. In 2025, new grads are competing with laid-off FAANG (MANGA?) engineers with years of experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Squeeze from the Bottom: "Pipeline Elimination"
&lt;/h3&gt;

&lt;p&gt;At the &lt;em&gt;exact&lt;/em&gt; same time, AI is automating the very tasks that new developers used to cut their teeth on.&lt;/p&gt;

&lt;p&gt;The real, immediate impact of AI is not replacing senior engineers. In fact, one study found that, for complex, real-world tasks, current AI tools &lt;a href="https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/" rel="noopener noreferrer"&gt;&lt;em&gt;slowed&lt;/em&gt; experienced developers&lt;/a&gt;, taking them 19% &lt;em&gt;longer&lt;/em&gt; to complete their work.&lt;/p&gt;

&lt;p&gt;The actual danger is at the very &lt;em&gt;bottom&lt;/em&gt; of the pipeline. A &lt;a href="https://stackoverflow.blog/2025/09/10/ai-vs-gen-z/" rel="noopener noreferrer"&gt;2024 survey&lt;/a&gt; found &lt;strong&gt;70% of hiring managers&lt;/strong&gt; believe AI can do the jobs of &lt;em&gt;interns&lt;/em&gt;, and &lt;strong&gt;37% of employers&lt;/strong&gt; stated they would rather "hire" AI than a recent graduate.&lt;/p&gt;

&lt;p&gt;So, new developers are squeezed from the top by "vertical compression" and from the bottom by AI-driven "task automation," all while fighting for a job pool that continues to shrink.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Story: The "Great Capital Reallocation"
&lt;/h2&gt;

&lt;p&gt;It's easy to look at this and say, "See! AI is taking the jobs!" But the data shows that's, at best, a "smokescreen".&lt;/p&gt;

&lt;p&gt;Classic economic forces drove the vast majority of layoffs: correcting "post-pandemic overhiring" and reacting to a harsh macro-economic shift (like rising interest rates) that made capital expensive. In fact, &lt;a href="https://nearshoreamericas.com/u-s-layoffs-surge-and-blaming-ai-is-part-of-the-smokescreen/" rel="noopener noreferrer"&gt;only 3-4% of job cuts&lt;/a&gt; through September 2025 were &lt;em&gt;explicitly&lt;/em&gt; tied to AI.&lt;/p&gt;

&lt;p&gt;So why are CEOs at companies like Amazon and Google &lt;em&gt;publicly&lt;/em&gt; blaming AI for "reorganizations" and "efficiency"?&lt;/p&gt;

&lt;p&gt;It's a strategic narrative for Wall Street. "AI Washing" allows a company to frame a "weak" narrative ("We made a mistake and overhired") as a "strong" one ("We are a lean, AI-first company pivoting to the future").&lt;/p&gt;

&lt;p&gt;This narrative obscures the &lt;em&gt;real&lt;/em&gt;, long-term shift: &lt;strong&gt;The Great Capital Reallocation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is the "great tech paradox": CEOs hand out pink slips with one hand while signing billion-dollar AI investments with the other. For the first time, companies are strategically reallocating billions of dollars, shifting spending from Operational Expenditures, like our salaries, to Capital Expenditures, like massive, expensive GPU clusters.&lt;/p&gt;

&lt;p&gt;The layoffs are not just savings; they are the &lt;em&gt;source of funding&lt;/em&gt; for this new, capital-intensive arms race.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amazon:&lt;/strong&gt; Announced &lt;a href="https://www.crn.com/news/cloud/2025/amazon-confirms-14-000-layoffs-says-ai-innovation-reason-for-reducing-roles" rel="noopener noreferrer"&gt;14,000 corporate layoffs in October 2025&lt;/a&gt;, citing AI-driven "efficiency". At the &lt;em&gt;exact same time&lt;/em&gt;, they announced a &lt;strong&gt;$100 billion&lt;/strong&gt; capital expenditure plan for 2025 to expand AI and cloud data centers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Meta:&lt;/strong&gt; Cut &lt;a href="https://www.mindandmetrics.com/blog/tech-layoffs-ai-google-job-cuts" rel="noopener noreferrer"&gt;over 21,000 jobs&lt;/a&gt; under its "Year of Efficiency". Simultaneously, it &lt;em&gt;raised&lt;/em&gt; its 2025 capital expenditure guidance to &lt;a href="https://www.theguardian.com/technology/2025/oct/29/meta-earnings-report" rel="noopener noreferrer"&gt;&lt;strong&gt;$70-72 billion&lt;/strong&gt;&lt;/a&gt; to acquire a staggering &lt;a href="https://www.pcmag.com/news/zuckerberg-looks-to-double-metas-gpu-stock-to-13-million-for-ai-training" rel="noopener noreferrer"&gt;1.3 million+ GPUs&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the new "Great Filter." The challenge is not just "Can you code?" It's "Can you justify your $150k salary to a company that is 'strategically reallocating' its budget to a $100 billion hardware plan?"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcictkh569i5ffan3ppmz.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcictkh569i5ffan3ppmz.webp" alt="A bar chart from Omdia Research of Nvidia H100 GPU Shipments by Customer showing estimated 2023 shipments." width="800" height="797"&gt;&lt;/a&gt;&lt;br&gt;These 65,000 GPUs, priced from $25k-$40k, represent a staggering $1.6 to $2.6 BILLION investment by these tech giants. &lt;a href="https://www.theverge.com/2023/12/4/23987953/the-gpu-haves-and-have-nots" rel="noopener noreferrer"&gt;(chart source)&lt;/a&gt;
  &lt;/p&gt;

&lt;h2&gt;
  
  
  Don't Fear the Tool, Master the Craft (Now More Than Ever)
&lt;/h2&gt;

&lt;p&gt;This isn't a post to scare anyone. And it's &lt;em&gt;definitely&lt;/em&gt; not meant to be elitist or to gatekeep. On the contrary, this is a pragmatic call to action.&lt;/p&gt;

&lt;p&gt;The "chaff," then and now, are those who look for a shortcut and mistake it for the entire journey. The "cream" will be the same as it ever was: the curious, the pragmatic, the problem-solvers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"History Doesn't Repeat Itself, but It Often Rhymes"&lt;/p&gt;

&lt;p&gt;~ Mark Twain&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But the "filter" &lt;em&gt;is&lt;/em&gt; different, and it's harsher.&lt;/p&gt;

&lt;p&gt;The new filter won't be, "Can you write a function to sort an array?" (AI will do that).&lt;/p&gt;

&lt;p&gt;The new filter will be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can you validate that the 1,000+ lines of code the AI just generated are secure, performant, and (most importantly) &lt;em&gt;correct&lt;/em&gt;?&lt;/li&gt;
&lt;li&gt;Can you architect a system of prompts, validations, and tests to get the output you need, every time, reliably?&lt;/li&gt;
&lt;li&gt;Can you debug a subtle logic error when the AI 'hallucinates' a solution that &lt;em&gt;looks&lt;/em&gt; right but is fundamentally wrong?&lt;/li&gt;
&lt;li&gt;Can you take full, systems-level ownership when the AI-generated part fails at 3 AM?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The new "cream" will be the &lt;strong&gt;AI-Assisted Engineer&lt;/strong&gt;, not the &lt;strong&gt;Prompt-Jockey&lt;/strong&gt;. The "chaff" will be those who trust the black box, copy and paste the output, and call it a day.&lt;/p&gt;

&lt;p&gt;This new wave won't make good engineers obsolete; it will make them more potent than ever. But the path &lt;em&gt;into&lt;/em&gt; the industry is undeniably harder. New developers have to compete on three fronts: against other grads, against laid-off veterans, and against AI automating the entry-level pipeline.&lt;/p&gt;

&lt;p&gt;So I'm not perplexed anymore. I'm focused. The answer is the same as it was in 2015, only more so: Don't just learn the "how." Master the "why." Understand the &lt;em&gt;craft&lt;/em&gt;. Be the one who can build the &lt;em&gt;whole&lt;/em&gt; system, not just one function.&lt;/p&gt;

&lt;p&gt;I'm curious, what do you all think? What skills are &lt;em&gt;you&lt;/em&gt; focusing on to make sure you're on the "engineering" side of this new, very different line? Let me know!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>career</category>
      <category>learning</category>
    </item>
    <item>
      <title>The Production Readiness Checklist</title>
      <dc:creator>Michael Solati</dc:creator>
      <pubDate>Thu, 13 Nov 2025 17:30:00 +0000</pubDate>
      <link>https://dev.to/michaelsolati/the-production-readiness-checklist-1922</link>
      <guid>https://dev.to/michaelsolati/the-production-readiness-checklist-1922</guid>
      <description>&lt;p&gt;I was tracking a package on the official USPS website on my phone this morning. Like many modern sites, it's a Progressive Web App (PWA). A small prompt appeared at the top of the screen asking if I wanted to install the app. I didn't tap "Install," but I did see something that caused a double-take.&lt;/p&gt;

&lt;p&gt;The confirmation prompt didn't say "Install USPS Tracking."&lt;/p&gt;

&lt;p&gt;It said: &lt;strong&gt;"Install Create React App Sample."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmichaelsolati.com%2Fblog%2Fthe-production-readiness-checklist%2Fusps-screenshot.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmichaelsolati.com%2Fblog%2Fthe-production-readiness-checklist%2Fusps-screenshot.webp" alt="A mobile screenshot of the USPS tracking page showing package status updates, overlaid with an unexpected 'Install Create React App Sample' browser prompt at the top of the screen." width="800" height="704"&gt;&lt;/a&gt;&lt;br&gt;When my government tells me to "Install Create React App Sample" I "Install Create React App Sample"
  &lt;/p&gt;

&lt;p&gt;I had to laugh. The official United States Postal Service website, a critical piece of infrastructure used by millions, was prompting me to install a default placeholder.&lt;/p&gt;

&lt;p&gt;My first thought wasn't, "Oh, some developer is in trouble." My first thought was, &lt;em&gt;"That's a process failure."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This isn't about blaming a single person who forgot to update a manifest.json file. This is a sign of a gap in the deployment process. It's a classic case of focusing so hard on the core features (the tracking logic) that we forget the "last 5%," the polish.&lt;/p&gt;

&lt;p&gt;But here's the secret: to a user, that "last 5%" is the product. It's the first thing they see and the main thing that builds or breaks their trust. How can you trust a site with your package details if it still has the default boilerplate text?&lt;/p&gt;

&lt;p&gt;This is why every team needs a "Production Readiness Checklist."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Production Readiness Checklist
&lt;/h2&gt;

&lt;p&gt;This isn't some 100-page document nobody reads. It's a set of automated guards and manual sign-offs that protect your team's professionalism. Here's what I recommend for every project.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The "Chrome" is Correct
&lt;/h3&gt;

&lt;p&gt;I'm going to call this stuff the "chrome," it's the frame around your application. It's &lt;strong&gt;everything but the features&lt;/strong&gt;, and it's the first thing a user sees.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;manifest.json&lt;/code&gt;:&lt;/strong&gt; Are the &lt;code&gt;short_name&lt;/code&gt; and &lt;code&gt;name&lt;/code&gt; correct? Are the icons pointing to the right, non-default assets? (This is what the USPS missed.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;index.html&lt;/code&gt;:&lt;/strong&gt; Is the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; tag correct? Is the &lt;code&gt;&amp;lt;meta name="description"&amp;gt;&lt;/code&gt; meaningful?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Favicons:&lt;/strong&gt; Do you have them? Are they loading? Don't be the broken image icon or the default React/Angular/Vue logo in the browser tab.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Pages:&lt;/strong&gt; Are your 404 Not Found and 500 Server Error pages branded? Or do they just show a default Nginx or server error that scares your users?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Automate Your Professionalism (The CI Gate)
&lt;/h3&gt;

&lt;p&gt;This is the most important part. &lt;strong&gt;Humans forget. Computers don't.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your CI/CD pipeline (e.g., GitHub Actions, GitLab CI, Jenkins) shouldn't just run unit tests; it should serve as your quality gatekeeper. This is something I've championed on my teams. We add various tests to our CI/CD pipeline that run before any deployment.&lt;/p&gt;

&lt;p&gt;For this situation, you can do this with a simple &lt;code&gt;grep&lt;/code&gt; command. Here's a simple "fail-if-found" shell script you can tinker with and add to your pipeline today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# Define placeholder strings to check for&lt;/span&gt;
&lt;span class="c"&gt;# Add your own framework's defaults!&lt;/span&gt;
&lt;span class="nv"&gt;PLACEHOLDERS&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
  &lt;span class="s2"&gt;"Create React App Sample"&lt;/span&gt;
  &lt;span class="s2"&gt;"React App"&lt;/span&gt;
  &lt;span class="s2"&gt;"lorem ipsum"&lt;/span&gt;
  &lt;span class="s2"&gt;"TODO"&lt;/span&gt;
  &lt;span class="s2"&gt;"FIXME"&lt;/span&gt;
  &lt;span class="s2"&gt;"powered by"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Files to check in your build output&lt;/span&gt;
&lt;span class="nv"&gt;FILES_TO_CHECK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"dist/index.html dist/manifest.json"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Checking for placeholder text in production files..."&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;file &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$FILES_TO_CHECK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ℹ️ Skipping check for non-existent file: &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;continue
  fi

  for &lt;/span&gt;placeholder &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PLACEHOLDERS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c"&gt;# Use grep to search for the placeholder&lt;/span&gt;
    &lt;span class="c"&gt;# -i: case-insensitive&lt;/span&gt;
    &lt;span class="c"&gt;# -q: quiet mode, just returns exit status&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$placeholder&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ ERROR: Found placeholder text '&lt;/span&gt;&lt;span class="nv"&gt;$placeholder&lt;/span&gt;&lt;span class="s2"&gt;' in '&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;'."&lt;/span&gt;
      &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deployment aborted. Please remove all placeholder text."&lt;/span&gt;
      &lt;span class="nb"&gt;exit &lt;/span&gt;1
    &lt;span class="k"&gt;fi
  done
done

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ No placeholder text found. Good to go!"&lt;/span&gt;
&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tiny script, added to your deployment process, just saved you from a world of embarrassment. You're now treating your configuration as code, which is a key DevOps principle.&lt;/p&gt;

&lt;p&gt;Don't be the dev with &lt;a href="https://www.youtube.com/watch?v=oPk8d1jA34k&amp;amp;t=8s" rel="noopener noreferrer"&gt;concepts of a plan&lt;/a&gt;, ADD TESTS!&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Review the "Scaffolding"
&lt;/h3&gt;

&lt;p&gt;When we review pull requests, we're often trained to look at the &lt;code&gt;*.ts&lt;/code&gt; or &lt;code&gt;*.js&lt;/code&gt; files, the logic. We skim right past the 'scaffolding' files like &lt;code&gt;index.html&lt;/code&gt;, and the various JSON or YAML files.&lt;/p&gt;

&lt;p&gt;This needs to change. It's a mistake I still make, but these config files are part of the PR and deserve the same level of rigor. Ask in your PR reviews: "Did we update the title? Did we check the manifest?"&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Last 5%" is 100% of Your Reputation
&lt;/h2&gt;

&lt;p&gt;Back when I was a Developer Advocate (🥑), I worked a lot with PWAs and building sample apps. I would use tools like Firebase Hosting to get them up and to the public quickly and easily. But that simplicity can be a trap! It's so easy to run &lt;code&gt;firebase deploy&lt;/code&gt; that you can forget to check the &lt;code&gt;manifest.json&lt;/code&gt; that your framework scaffolding (like the Angular CLI) generated for you.&lt;/p&gt;

&lt;p&gt;Using your CI pipeline (say, GitHub Actions) to deploy to your hosting provider and adding a linting step is the perfect way to combine ease of use with professional-grade quality.&lt;/p&gt;

&lt;p&gt;Look, everyone ships bugs. Even massive organizations like the USPS. It's not about being perfect; it's about building robust systems that catch our simple, human mistakes.&lt;/p&gt;

&lt;p&gt;These little details (the "chrome"), like your PWA name, the favicon, aren't "nice-to-haves." They are your digital storefront. They're the first impression you make, and they're critical for building the user trust that your application depends on.&lt;/p&gt;

&lt;p&gt;So, my challenge to you is: go look at your app's &lt;code&gt;manifest.json&lt;/code&gt; or &lt;code&gt;index.html&lt;/code&gt; in your &lt;code&gt;main&lt;/code&gt; branch. Right now. You might be surprised at what you find.&lt;/p&gt;

&lt;p&gt;What's the most embarrassing placeholder you've ever seen slip into production?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>devops</category>
      <category>pwa</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Fighting Framework Jank (What's Not in the Docs)</title>
      <dc:creator>Michael Solati</dc:creator>
      <pubDate>Wed, 12 Nov 2025 05:00:00 +0000</pubDate>
      <link>https://dev.to/michaelsolati/fighting-framework-jank-whats-not-in-the-docs-mj5</link>
      <guid>https://dev.to/michaelsolati/fighting-framework-jank-whats-not-in-the-docs-mj5</guid>
      <description>&lt;p&gt;I’ve been there. We’ve &lt;em&gt;all&lt;/em&gt; been there. You've just shipped a new dashboard. It’s got charts, it’s got tables, it’s got pizazz ✨. And on your fancy, company issued, 16" MacBook Pro, it flies. Buttery smooth But then the first bug report comes in:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Dashboard is laggy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or maybe you see a "it feels slow," or my personal favorite, "the page is janky."&lt;/p&gt;

&lt;p&gt;You open it on a different machine, like your cell phone, and your heart sinks... Those smooth animations are stuttering. The clicks feel... off. And then creeps in that moment of dread, "Is React (or Vue, or Angular) just... slow?"&lt;/p&gt;

&lt;p&gt;After going through an existential crisis (doubting my years as a software developer and realizing that my imposter syndrome is very justified) I then decided to blame the framework or some library I was using. But after profiling the &lt;em&gt;very&lt;/em&gt; janky dashboard I realized the problem wasn't the framework at all.&lt;/p&gt;

&lt;p&gt;The problem was me. I was so focused on the "framework way" of doing things that I was ignoring the most powerful performance tool I had: the browser itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Framework-Pure" Problem
&lt;/h2&gt;

&lt;p&gt;Let's look at a simplified version of my janky component. It had two main jobs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Render a massive, complex, but totally static SVG icon.&lt;/li&gt;
&lt;li&gt;Fire off an analytics event as soon as it rendered to track that it was visible.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The "pure React" way to write this looked something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Imagine this component returns a &amp;lt;svg&amp;gt; with hundreds of &amp;lt;path&amp;gt; elements&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MyHugeStaticChartIcon&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./MyHugeStaticChartIcon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sendAnalyticsEvent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./analytics&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;JankyWidget&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Fire this off as soon as we mount&lt;/span&gt;
    &lt;span class="nf"&gt;sendAnalyticsEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;widget_visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"widget"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;My Janky Widget&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MyHugeStaticChartIcon&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code &lt;em&gt;looks&lt;/em&gt; right, but it's a performance nightmare. Here's why:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Hydration Cost:&lt;/strong&gt; React has to create a Virtual DOM node for every single one of those hundreds of &lt;code&gt;&amp;lt;path&amp;gt;&lt;/code&gt; elements inside the SVG. That’s a ton of JavaScript objects to create and memory to allocate for something that &lt;em&gt;will never change&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Main Thread Blockage:&lt;/strong&gt; The &lt;code&gt;useEffect&lt;/code&gt; fires right after mount. That &lt;code&gt;sendAnalyticsEvent&lt;/code&gt; function, even if it's quick, is still work that's happening on the main thread. It's competing for resources with the browser, which is still trying to paint the screen and respond to the user's scroll.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This combination is what creates the "jank." The main thread is just too busy.&lt;/p&gt;

&lt;p&gt;

&lt;iframe src="https://stackblitz.com/edit/vitejs-vite-segdqqbc?embed=1&amp;amp;ctl=1&amp;amp;hidedevtools=1&amp;amp;file=src%2Fpages%2FJankyWidget.tsx&amp;amp;initialpath=janky" width="100%" height="500"&gt;
&lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;You can play with the janky version above!&lt;/p&gt;

&lt;h2&gt;
  
  
  The "One Weird Trick": Offload It to the Browser
&lt;/h2&gt;

&lt;p&gt;After hours of profiling, the "Aha!" moment hit me. The fix isn't a new library. It's to &lt;strong&gt;stop&lt;/strong&gt; asking the framework to do things the browser is already amazing at.&lt;/p&gt;

&lt;p&gt;This "trick" has two parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Offload &lt;strong&gt;parsing&lt;/strong&gt; with the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; tag.&lt;/li&gt;
&lt;li&gt;Offload &lt;strong&gt;execution&lt;/strong&gt; with &lt;code&gt;requestIdleCallback&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Part 1: The &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; Tag for Heavy Lifting
&lt;/h2&gt;

&lt;p&gt;First, that massive SVG. It's static. So why are we making JavaScript build it?&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; tag is a native HTML element that is completely inert. The browser parses its content, but it doesn't render it, run scripts in it, or download images. It's just a chunk of DOM waiting to be used.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Put your static HTML into your &lt;code&gt;index.html&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"my-chart-icon-template"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"100"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 100 100"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;g&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"...a-very-complex-path..."&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"...another-complex-path..."&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/g&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Tweak your component to clone this content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;FastWidget&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chartContainerRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Find the template&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-chart-icon-template&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// 2. Clone its content (this is super fast)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cloneNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// 3. Stamp it into our component&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chartContainerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;chartContainerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ... analytics call will go here ...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"widget"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;My Fast Widget&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* This is now just an empty container */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;chartContainerRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Boom. We just saved React from having to manage hundreds of virtual DOM nodes. We offloaded all that parsing work to the browser, which it does much more efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: &lt;code&gt;requestIdleCallback&lt;/code&gt; for the "Nice-to-Haves"
&lt;/h2&gt;

&lt;p&gt;Okay, the component renders faster, but that analytics call is still blocking the main thread in &lt;code&gt;useEffect&lt;/code&gt;. This is where the second part of our "trick" comes in.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;requestIdleCallback&lt;/code&gt; is a browser API that's like saying, "Hey browser, I know you're busy. When you get a free second and you're not busy with user input or animations, could you please run this function for me?"&lt;/p&gt;

&lt;p&gt;It's &lt;em&gt;perfect&lt;/em&gt; for non-critical tasks like analytics.&lt;/p&gt;

&lt;p&gt;Let's add it to our &lt;code&gt;useEffect&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ... inside our FastWidget component ...&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// --- Template code from above ---&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-chart-icon-template&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cloneNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chartContainerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;chartContainerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// --- Our new, non-blocking analytics call ---&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;requestIdleCallback&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestIdleCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;sendAnalyticsEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;widget_visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Fallback for older browsers&lt;/span&gt;
      &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;sendAnalyticsEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;widget_visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Payoff
&lt;/h2&gt;

&lt;p&gt;And just like that, the jank is gone.&lt;/p&gt;

&lt;p&gt;Our component now renders instantly. The state update (if we had one) happens immediately. The heavy-lifting of parsing the SVG is handled by the browser. And the non-critical analytics call waits politely for its turn when the main thread is free.&lt;/p&gt;

&lt;p&gt;I love this kind of solution! It's not about "React vs. Vanilla JS." It's about remembering that your framework is a guest in the browser's house. By respecting the browser and using the native tools it provides, you can make your framework based apps feel infinitely faster.&lt;/p&gt;

&lt;p&gt;So next time you're facing down some "jank," don't just reach for a new library. Ask yourself, "Can I just offload this to the browser?"&lt;/p&gt;

&lt;p&gt;

&lt;iframe src="https://stackblitz.com/edit/vitejs-vite-segdqqbc?embed=1&amp;amp;ctl=1&amp;amp;hidedevtools=1&amp;amp;file=src%2Fpages%2FFastWidget.tsx&amp;amp;initialpath=fast" width="100%" height="500"&gt;
&lt;/iframe&gt;


&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>performance</category>
      <category>react</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Meta's Llama API: Open Models, Meet Developer Convenience</title>
      <dc:creator>Michael Solati</dc:creator>
      <pubDate>Thu, 01 May 2025 18:00:00 +0000</pubDate>
      <link>https://dev.to/michaelsolati/metas-llama-api-open-models-meet-developer-convenience-2odd</link>
      <guid>https://dev.to/michaelsolati/metas-llama-api-open-models-meet-developer-convenience-2odd</guid>
      <description>&lt;p&gt;We keep seeing headlines about new LLMs reaching state-of-the-art performance and dominating benchmarks, which is genuinely incredible progress! But the gap between that and actually getting these powerful models deployed effectively within an application... that's often where the rubber meets the road, and frankly, where things can get pretty messy. Sure, downloading huge model weights is part of it, but creating a reliable, smooth operational workflow around them? That's the harder part.&lt;/p&gt;

&lt;p&gt;That's why Meta's announcement at LlamaCon 2025 wasn't just another model drop (though they keep doing that, too, bless their open source ❤️). They unveiled the official Llama API. This is a significant shift. Meta, the champions of open weights you can download and run yourself, is now stepping firmly into the hosted API game.&lt;/p&gt;

&lt;p&gt;Why should you, as a developer, care? This move is about bridging that gap between incredibly capable open source models and making them radically easier for us to use in our projects. Think about getting the flexibility and transparency we love about open models combined with the kind of developer experience and convenience we've (sometimes grudgingly) come to expect from the closed-source, &lt;a href="https://ai.meta.com/blog/llamacon-llama-news/" rel="noopener noreferrer"&gt;API-first players&lt;/a&gt;. Plus, Meta made some very interesting decisions with this API, like building in OpenAI compatibility from the get-go. Seriously.&lt;/p&gt;

&lt;p&gt;And from where I sit, over here thinking about real-time interactions all day at LiveKit, easier access to faster, cheaper, more capable models? That starts to unlock some really exciting possibilities. Let's dig in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet the Llama Family (Served via API)
&lt;/h2&gt;

&lt;p&gt;First, this isn't just an API for some legacy model. Meta is directly putting some of its latest and greatest Llama iterations into &lt;a href="https://llama.developer.meta.com/docs/overview/" rel="noopener noreferrer"&gt;its hosted service&lt;/a&gt;. When it first peaked in preview, it featured the then-new Llama 4 Scout and Maverick models alongside Llama 3.3 8B. Looking at the official docs now, the lineup includes optimized FP8 versions of those Llama 4 models, plus the Llama 3.3 series in both 70B and 8B parameter sizes. (&lt;a href="https://llama.developer.meta.com/docs/models" rel="noopener noreferrer"&gt;See here&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Let's break down the current herd available via the official API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Llama 4 Scout (Llama-4-Scout-17B-16E-Instruct-FP8):&lt;/strong&gt; This isn't your grandpa's text-only LLM. Scout is natively multimodal, meaning it understands text and images right out of the box. It uses a Mixture-of-Experts (MoE) architecture (17 billion active parameters, 16 'experts') which &lt;a href="https://ai.meta.com/blog/llama-4-multimodal-intelligence/" rel="noopener noreferrer"&gt;helps make it efficient&lt;/a&gt;. Think more intelligent routing of your requests to specialized parts of the model. The API version uses FP8 precision for efficiency.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Llama 4 Maverick (Llama-4-Maverick-17B-128E-Instruct-FP8):&lt;/strong&gt; Scout's sibling, also natively multimodal and running on an MoE architecture, but with way more experts (128 of them!) packed into its 17 billion active parameters. This suggests it might handle more complex or nuanced multimodal tasks. Benchmarks show Maverick punching well above its weight, often outperforming much larger models, especially in image understanding and coding. Again, the API serves an efficient FP8 version.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Llama 3.3 70B (Llama-3.3-70B-Instruct):&lt;/strong&gt; The latest iteration of Meta's 70B text-only model line. It boasts improved reasoning, coding chops, multilingual support, and a beefy 128k token context window. Meta positions it as delivering performance comparable to the earlier massive Llama 3.1 405B for text-based tasks, but faster and cheaper.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Llama 3.3 8B (Llama-3.3-8B-Instruct):&lt;/strong&gt; The lightweight, speedy sibling to the 70B. It still gets the 128k context window and multilingual capabilities. Still, it is designed for scenarios where you need quick responses and lower resource usage. This was also one of the first models for fine-tuning via the API preview.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a quick look at the models currently listed in the &lt;a href="https://llama.developer.meta.com/docs/models" rel="noopener noreferrer"&gt;official API docs&lt;/a&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model ID&lt;/th&gt;
&lt;th&gt;Input Modalities&lt;/th&gt;
&lt;th&gt;Output Modalities&lt;/th&gt;
&lt;th&gt;Context Length (API)&lt;/th&gt;
&lt;th&gt;Key Architecture&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Llama-4-Scout-17B-16E-Instruct-FP8&lt;/td&gt;
&lt;td&gt;Text, image&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;128k&lt;/td&gt;
&lt;td&gt;MoE (16 Experts)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Llama-4-Maverick-17B-128E-Instruct-FP8&lt;/td&gt;
&lt;td&gt;Text, image&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;128k&lt;/td&gt;
&lt;td&gt;MoE (128 Experts)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Llama-3.3-70B-Instruct&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;128k&lt;/td&gt;
&lt;td&gt;Transformer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Llama-3.3-8B-Instruct&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;128k&lt;/td&gt;
&lt;td&gt;Transformer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;What's really interesting here isn't just the specs but the strategy. Meta isn't just incrementally improving text generation. They're adding fundamental new capabilities like &lt;a href="https://ai.meta.com/blog/llama-4-multimodal-intelligence/" rel="noopener noreferrer"&gt;native multimodality&lt;/a&gt; and architectural innovations like MoE that directly target limitations of older models and compete feature-for-feature with the big closed-source players like Google's Gemini and OpenAI's GPT-4 series. Offering these advanced models through an easy-to-use API signals Meta wants developers to have frictionless access to their cutting-edge, not just the older stuff.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flvqo5q4lguk1utvcuhsv.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flvqo5q4lguk1utvcuhsv.webp" alt="An image showing benchmark comparisons for Llama 4 models against other models like Gemini, Mistral, GPT, and DeepSeek on various tasks like MMLU, MathVista, ChartQA, DocQA, LiveCodeBench, and more." width="800" height="740"&gt;&lt;/a&gt;&lt;br&gt;Benchmarks of Llama 4 (&lt;a href="https://apidog.com/blog/llama-4-api/" rel="noopener noreferrer"&gt;source&lt;/a&gt;)
  &lt;/p&gt;

&lt;p&gt;The increased context windows across the board (up to 128k standard in the API, a massive leap from Llama 2/3's initial 8k) also unlock more sophisticated applications, from deeper conversations to analyzing larger documents.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Developer Experience: Less Yak Shaving, More Building
&lt;/h2&gt;

&lt;p&gt;Okay, powerful models are cool. But how easy is it to use them via this new API? This is where Meta has seriously considered reducing friction for developers.&lt;/p&gt;

&lt;p&gt;We're talking easy, one-click API key generation and interactive "playgrounds" to quickly test prompts and models. These days, this is standard fare for APIs, but nailing these basics is crucial for getting developers up and running quickly.&lt;/p&gt;

&lt;p&gt;As you'd hope, they've rolled out official &lt;a href="https://llama.developer.meta.com/docs/sdks" rel="noopener noreferrer"&gt;SDKs for Python and TypeScript&lt;/a&gt;. Installation looks super simple (for Python, it's just &lt;code&gt;pip install llama-api-client&lt;/code&gt;). The SDK examples lay out ways to use it for chat completion. Plus, you get support for async and streaming responses, which is great.&lt;/p&gt;

&lt;p&gt;Now, pay attention, because this next part is the real game-changer and tells you a lot about Meta's thinking: it &lt;a href="https://llama.developer.meta.com/docs/features/compatibility" rel="noopener noreferrer"&gt;works with the OpenAI API&lt;/a&gt;! Yep, you heard right. They've actually included a dedicated compatibility endpoint at &lt;code&gt;https://api.llama.com/compat/v1/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What does this mean? You can take your existing code that uses the official OpenAI client libraries, point it to Meta's base URL, and swap in your Llama API key. It should just work for core functionalities like listing models, chat completions (sync and streaming), and even image understanding with Llama 4. Meta explicitly provides examples showing how to do this. This is a massive olive branch for developers already invested in the OpenAI ecosystem. It dramatically lowers the barrier to trying out or switching to Llama. Meta removes the "but I'd have to rewrite my integration" excuse. It's a genius, pragmatic move acknowledging OpenAI's current de facto standard status while leveraging Llama's strengths (like cost and openness) as a compelling reason to make that tiny configuration change.&lt;/p&gt;

&lt;p&gt;The API preview wasn't just for running inferences; it also packed tools for fine-tuning and evaluating models. They first showed this off with the Llama 3.3 8B model, letting developers build custom versions right there in the hosted API. This means you could tailor models to your specific needs without wrestling with complex training setups. It really signals that Meta gets that serious AI work often needs more than just a one-size-fits-all model – it needs specialization. Putting these tools in the API turns it from a basic inference point into a much more complete development platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Arms vs. Walled Gardens: Llama's Place in the AI Bazaar
&lt;/h2&gt;

&lt;p&gt;Meta's entire philosophy with Llama has been centered around openness. They release the model weights, allowing anyone (with the right hardware and expertise) to run, modify, and build upon them. The Llama API fits into this by providing an easier access point to these open models, but it doesn't lock you in. Meta explicitly states that models fine-tuned via the API are yours to &lt;a href="https://ai.meta.com/blog/llamacon-llama-news/" rel="noopener noreferrer"&gt;take and host elsewhere&lt;/a&gt; if you want. This is in contrast to the proprietary API-only approach of competitors like OpenAI, Anthropic, and Google, where the models remain firmly within their walled gardens.&lt;/p&gt;

&lt;p&gt;And let's be clear: these open models aren't just "good for open source," they are competitive on performance. Llama 3 models showed significant improvements over Llama 2. They outperformed other open models of similar size on various benchmarks like MMLU, HumanEval, and GSM-8K. The larger Llama 3.1 405B was positioned as rivaling top closed-source models. The newer Llama 4 models, like Maverick, are showing impressive results, even surpassing GPT-4o and Gemini 2 in areas like image understanding (ChartQA, DocVQA) and long-context tasks, according to &lt;a href="https://apidog.com/blog/llama-4-api/#standard-benchmark-performance-metrics" rel="noopener noreferrer"&gt;some benchmarks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But where Llama really throws down the gauntlet is cost. While Meta hasn't published official pricing for their own hosted API preview (it was mentioned as a free limited preview initially), the pricing from ecosystem partners who offer Llama models via API sets a clear and dramatic precedent. Meta themselves have highlighted affordability as a key benefit, and external analyses confirm Llama often offers some of the &lt;a href="https://artificialanalysis.ai/" rel="noopener noreferrer"&gt;lowest costs per token in the industry&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safety in the Open: Enter Purple Llama
&lt;/h2&gt;

&lt;p&gt;With great power comes great responsibility, right? Meta isn't just tossing powerful models over the wall and hoping for the best. Along with the models and the API, they've invested significantly in building and openly sharing tools for trust and safety under the &lt;a href="https://github.com/meta-llama/PurpleLlama" rel="noopener noreferrer"&gt;Purple Llama project umbrella&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The name comes from cybersecurity's "purple teaming," which combines offensive (red team) and defensive (blue team) approaches to finding and fixing vulnerabilities. Purple Llama aims to bring this collaborative, proactive security mindset to generative AI.&lt;/p&gt;

&lt;p&gt;Key components include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Llama Guard:&lt;/strong&gt; A family of models (like Llama Guard 3 and 4) specifically designed for content moderation. These can filter both the inputs sent to your main Llama model and the outputs it generates, checking for harmful, unethical, or policy-violating content based on taxonomies like the MLCommons standard. Llama Guard models are available via the API, supporting multiple languages and image reasoning (&lt;a href="https://www.llama.com/llama-protections/" rel="noopener noreferrer"&gt;in Guard 4&lt;/a&gt;).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CyberSec Eval:&lt;/strong&gt; They've put together a set of benchmarks and tools to check if an LLM is likely to churn out insecure code or help someone with cyber mischief. This is super useful for developers because it lets them measure and lower the cybersecurity risks of using LLMs, especially when those models write code.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt Guard:&lt;/strong&gt; A tool focused on catching and blocking prompt injection attacks and jailbreaking. These are the kinds of bad inputs folks use specifically to try and bypass a model's built-in safety controls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why does any of this matter? Well, it's a strong sign that Meta is taking the safety concerns around AI models seriously. What's crucial is that they're putting these tools out there openly (with flexible licenses), letting everyone in the community use them, poke around, improve them, and help standardize safety practices. This open safety strategy is a good move, positioning Meta not just as a developer of powerful open AI but as a champion for responsible development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: Let the Llamas Loose
&lt;/h2&gt;

&lt;p&gt;My final thoughts on the Llama API? It's clear this is more than just Meta playing catch-up in the API world. It feels like a deliberate step designed to make their strong open source models much simpler for developers to adopt and work with. It brings together the ease of using a hosted API with all the good stuff people already love about Llama: its performance, affordability, and open nature.&lt;/p&gt;

&lt;p&gt;Meta's push with the API, the raw performance/cost advantages of the models, and the explicit focus on speed through partnerships like Cerebras and Groq make a strong argument for the momentum behind open(ly accessible) AI. And from a real-time perspective? The possibilities got a lot more interesting and, crucially, affordable.&lt;/p&gt;

&lt;p&gt;Is it worth experimenting with the Llama API? Absolutely. Head over to the &lt;a href="https://llama.developer.meta.com/docs/overview" rel="noopener noreferrer"&gt;Llama Developer Portal&lt;/a&gt;, check out the models and SDKs, get on the &lt;a href="https://llama.developer.meta.com/join_waitlist" rel="noopener noreferrer"&gt;waitlist&lt;/a&gt; if the preview is still limited, and see how it slots into your stack. I'd love to hear what you build with it!&lt;/p&gt;

</description>
      <category>llama</category>
      <category>llm</category>
      <category>ai</category>
      <category>agents</category>
    </item>
    <item>
      <title>Voice Assistants Evolving? Perplexity on iOS Attempts True Task Integration</title>
      <dc:creator>Michael Solati</dc:creator>
      <pubDate>Mon, 28 Apr 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/michaelsolati/voice-assistants-evolving-perplexity-on-ios-attempts-true-task-integration-86a</link>
      <guid>https://dev.to/michaelsolati/voice-assistants-evolving-perplexity-on-ios-attempts-true-task-integration-86a</guid>
      <description>&lt;p&gt;Okay, real talk: how often has Siri left you hanging with a "Here's what I found on the web" when you needed something done? 🙋‍♂️ It happens to the best of us. For years, it feels like we've been waiting for our iPhone assistants to get genuinely smart, especially with the AI revolution happening all around us.&lt;/p&gt;

&lt;p&gt;Well, buckle up because &lt;a href="https://www.perplexity.ai/" rel="noopener noreferrer"&gt;Perplexity AI&lt;/a&gt;, the "answer engine" known for giving you sourced answers instead of just links, just brought its conversational voice assistant to &lt;a href="https://apps.apple.com/us/app/perplexity-ask-anything/id1668000334" rel="noopener noreferrer"&gt;iOS&lt;/a&gt; just last week! And the timing? Let's say it's very interesting, given the reported delays with Apple's own "Apple Intelligence" and the smarter Siri we were promised. Perplexity is sliding right into that gap.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, What's the Big Deal? It's an Agent
&lt;/h2&gt;

&lt;p&gt;This isn't just about talking to your search engine. Perplexity is pushing into &lt;strong&gt;agentic AI&lt;/strong&gt;. What does that mean? It means the AI doesn't just find information; it aims to do things for you by interacting with other apps. It's trying to be the assistant that bridges the gap between knowing what you want and actually making it happen. They cleverly used standard &lt;a href="https://www.macstories.net/stories/what-siri-isnt-perplexitys-voice-assistant-and-the-potential-of-llms-integrated-with-ios/" rel="noopener noreferrer"&gt;iOS developer tools&lt;/a&gt; to make this happen, connecting their AI brain to the apps we use daily.&lt;/p&gt;

&lt;h2&gt;
  
  
  What kind of stuff can it actually do?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Bookings: Ask for a table tonight, and it can pop open OpenTable with the details mostly filled in. Need a ride? It can tee up an Uber request.&lt;/li&gt;
&lt;li&gt;Emails: Dictate an email, and it'll draft it in the Mail app, which is ready for your final check.&lt;/li&gt;
&lt;li&gt;Scheduling: Add events to your calendar or set reminders (with permission, of course).&lt;/li&gt;
&lt;li&gt;Media: Find specific songs on Apple Music or pull up videos on YouTube.&lt;/li&gt;
&lt;li&gt;Navigation: Get directions started in Apple Maps.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Perplexity vs. Siri: The Showdown
&lt;/h2&gt;

&lt;p&gt;So, is it time to kick Siri to the curb? Well, not entirely. Siri still has exclusive access to core iPhone functions – setting system alarms, changing settings like Wi-Fi or Do Not Disturb, and sending texts directly via Messages. Perplexity can't touch those.&lt;/p&gt;

&lt;p&gt;But where they overlap, Perplexity often feels like it's playing a different game:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Action, Not Just Links: This is huge. Ask Perplexity to book that table; it opens OpenTable and fills in the details. Ask Siri the same thing, and you're likely getting... web links. Perplexity tries to initiate the task, making it feel genuinely helpful.&lt;/li&gt;
&lt;li&gt;Smoother Conversations: Perplexity seems much better at understanding natural language and keeping track of the conversation flow. It feels less like issuing commands and more like having a dialogue.&lt;/li&gt;
&lt;li&gt;Works on Older iPhones: Big plus, this runs on devices like the iPhone 12/13, unlike Apple Intelligence, which demands newer hardware.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Perplexity makes you feel more capable and less frustrated with everyday assistant tasks. It's closer to the helpful AI companion many of us envisioned years ago.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Take: Worth Your Time? Heck Yes
&lt;/h2&gt;

&lt;p&gt;Perplexity's voice assistant on iOS is a genuinely exciting development. It's fast, conversational, and surprisingly good at orchestrating tasks across different apps (often better than Siri).&lt;/p&gt;

&lt;p&gt;No, it can't replace Siri for everything due to those system limitations. However, it offers a glimpse of a more powerful, proactive AI assistant for a wide range of common requests. It shows what's possible even within Apple's constraints. It pressures Apple to deliver on that "smarter Siri" promise.&lt;/p&gt;

&lt;p&gt;If you're curious, download the app, tap that waveform icon, and take it for a spin. You might find yourself reaching for it more often than you expect.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>assistant</category>
    </item>
    <item>
      <title>Google's Cookie Conundrum</title>
      <dc:creator>Michael Solati</dc:creator>
      <pubDate>Tue, 30 Jul 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/michaelsolati/googles-cookie-conundrum-4ii5</link>
      <guid>https://dev.to/michaelsolati/googles-cookie-conundrum-4ii5</guid>
      <description>&lt;p&gt;Last Monday (July 22nd, 2024, for those of you reading in the future), Google made headlines by announcing a major policy change regarding its plans to phase out third-party tracking cookies in Chrome. This decision marks a pivot from Google's initial commitment to increase user privacy and calls into question the future of web privacy standards.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Big Announcement
&lt;/h2&gt;

&lt;p&gt;In the original proposal introduced several years ago as part of the Privacy Sandbox, Google aimed to gradually phase out third-party cookies, a powerful tool many advertisers use to track users across sites. However, the latest announcement encapsulated a rather unexpected turn of events. Anthony Chavez, the vice president of the Privacy Sandbox, stated, "Instead of deprecating third-party cookies, we would introduce a new experience in Chrome that lets people make an informed choice that applies across their web browsing." &lt;a href="https://privacysandbox.com/news/privacy-sandbox-update/" rel="noopener noreferrer"&gt;(source)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While Chrome plans to roll out a user-choice prompt for users to decide about third-party cookies, this change underscores a growing reality: achieving privacy in a digital landscape saturated with tracking technologies is a tough battle.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Under the Surface?
&lt;/h2&gt;

&lt;p&gt;Industry experts have expressed skepticism regarding Google's decision to maintain third-party cookies under the guise of user choice. Safari and Firefox have already taken a hard stance against these tracking mechanisms, locking users out of third-party cookies since early 2020. Google's decision to backtrack highlights its predicament: balancing user privacy with its advertising revenue model.&lt;/p&gt;

&lt;p&gt;Apple, a fierce critic of Google's Topics API—a core component of Privacy Sandbox that categorizes user interests based on browsing history—added fuel to the fire. As Apple's WebKit team pointed out, "The user doesn't get told upfront which topics Chrome has tagged them with." &lt;a href="https://webkit.org/blog/15697/private-browsing-2-0/" rel="noopener noreferrer"&gt;(source)&lt;/a&gt; This raises legitimate questions about users being able to manage their data and maintain anonymity.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Regulatory Landscape
&lt;/h2&gt;

&lt;p&gt;This Google pivot isn't just a matter of corporate policy; it's a broader commentary on regulatory pressures shaping tech giants' approaches to privacy. The UK's Competition and Markets Authority (CMA) is intensely monitoring this new approach. They are evaluating the impact of Chrome's user-choice status on cookies and its implications for user privacy across the internet.&lt;/p&gt;

&lt;p&gt;Stephen Bonner, deputy commissioner at the Information Commissioner's Office (ICO), expressed disappointment in Google's adjustments, emphasizing the need for a more privacy-friendly internet. The ICO continues to advocate for the digital advertising industry to transition to more private alternatives to third-party cookies rather than veering into less transparent tracking forms. &lt;a href="https://ico.org.uk/about-the-ico/media-centre/news-and-blogs/2024/07/ico-statement-in-response-to-google-announcing-it-will-no-longer-block-third-party-cookies/" rel="noopener noreferrer"&gt;(source)&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Implications
&lt;/h2&gt;

&lt;p&gt;So, what does this mean for everyone involved?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;For Users:&lt;/strong&gt; Those who value online privacy may see this pivot as a step backward, reducing their control over personal data and how it's shared across the web.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;For Advertisers:&lt;/strong&gt; The existing system that generates revenue through third-party tracking remains intact, leaving advertisers to navigate the older, more convoluted advertising landscape. This could lead to a shift in advertising strategies, focusing on first-party data and contextual advertising rather than relying on third-party tracking.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;For Regulators:&lt;/strong&gt; This shift prompts an ongoing dialogue around privacy legislation and the tech industry's accountability in safeguarding user data. Regulators are now faced with the challenge of protecting user privacy while allowing for innovation and competition in the digital advertising space.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The conclusion? Google's about-face on third-party cookies leaves everyone in a state of pause. While intended as a measure of user choice, it muddies the waters of privacy protection, sparking ethical debates about consent in the ever-evolving digital landscape.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;As this saga unfolds, the situation serves as a reminder that the trajectory toward a more transparent, privacy-centric internet is fraught with challenges. While Google plans to roll out user-choice prompts, significant trust issues remain within the ecosystem. Whether this initiative will empower users by giving them more control over their data or serve as a façade of choice depends on execution and ongoing engagement with privacy advocates and regulators.&lt;/p&gt;

&lt;p&gt;As we navigate the future of online privacy, one question looms: can companies genuinely prioritize user data, or will advertising revenue continue to dictate the rules of the game?&lt;/p&gt;




&lt;p&gt;Do I post on &lt;a href="https://twitter.com/MichaelSolati" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, no I gave up on it, but I plan on getting active on &lt;a href="https://www.threads.net/@michaelsolati" rel="noopener noreferrer"&gt;Threads&lt;/a&gt;, so follow me there!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>chrome</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Why Netflix Took a Bet on GraphQL</title>
      <dc:creator>Michael Solati</dc:creator>
      <pubDate>Tue, 27 Jun 2023 05:37:14 +0000</pubDate>
      <link>https://dev.to/amplication/why-netflix-took-a-bet-on-graphql-56i2</link>
      <guid>https://dev.to/amplication/why-netflix-took-a-bet-on-graphql-56i2</guid>
      <description>&lt;p&gt;So you may have missed it, but about two weeks ago, the streaming giant Netflix shared the details of its massive leap forward by embracing GraphQL as its preferred API architecture. Let's dive into what Netflix did, why it made this bold move, how it executed the migration, and why other companies should seriously consider following suit.&lt;/p&gt;

&lt;p&gt;If you're interested in reading about their experience, be sure to check out the &lt;a href="https://netflixtechblog.com/migrating-netflix-to-graphql-safely-8e1e4d4f1e72" rel="noopener noreferrer"&gt;blog post&lt;/a&gt; on the Netflix Technology Blog. But do that after you read this blog post to get our opinion first!&lt;/p&gt;

&lt;h2&gt;
  
  
  Netflix's Big Move
&lt;/h2&gt;

&lt;p&gt;In 2022, Netflix migrated their iOS and Android apps to a GraphQL backend. Until then, they had used their own home-baked API framework, &lt;a href="https://netflix.github.io/falcor/" rel="noopener noreferrer"&gt;Falcor&lt;/a&gt;, to power their mobile apps. Interestingly, the Falcor Java implementation team also managed the API server.&lt;/p&gt;

&lt;p&gt;Netflix's decision to adopt GraphQL was driven by its ambition to create a more flexible, efficient, and developer-friendly API architecture. Moving away from its monolithic REST API server, Netflix sought to empower its development teams, optimize the data transfer, and enhance the overall user experience.&lt;/p&gt;

&lt;p&gt;By breaking up their Falcor monolith, they allowed every team to manage their own GraphQL API thanks to a federated GraphQL gateway; removing a burden from the Falcor Java team while empowering the other teams with ownership of the code they were writing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Power of GraphQL
&lt;/h2&gt;

&lt;p&gt;So, what makes GraphQL so compelling? First and foremost, it offers unparalleled flexibility. Unlike REST APIs, where clients are constrained by fixed data structures, GraphQL empowers clients to request the data they need, eliminating over-fetching and under-fetching data. This leads to faster load times, improved performance, and, ultimately, happier users.&lt;/p&gt;

&lt;p&gt;GraphQL's declarative nature and powerful tooling also provide an enhanced developer experience. It simplifies data fetching and eliminates the need for multiple API endpoints, making development more efficient and productive. With GraphQL, developers can focus on delivering value without being hindered by rigid API structures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Netflix's Migration Journey
&lt;/h2&gt;

&lt;p&gt;Now, let's look at how Netflix executed its migration to GraphQL. They approached the transition in two phases, ensuring a smooth and seamless integration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1 - Creating a GraphQL Shim Service
&lt;/h3&gt;

&lt;p&gt;Netflix's first step involved creating a GraphQL shim service on their monolithic Falcor API. This allowed client engineers to swiftly adopt GraphQL and explore client-side concerns without disrupting the server-side infrastructure. To launch this phase safely, Netflix employed AB testing, evaluating the impact of GraphQL versus the legacy Falcor stack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmichaelsolati.com%2Fblog%2Fwhy-netflix-took-a-bet-on-graphql%2F0.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmichaelsolati.com%2Fblog%2Fwhy-netflix-took-a-bet-on-graphql%2F0.webp" alt="Diagram of GraphQL Shim Service in front of Legacy API Monolith." width="800" height="298"&gt;&lt;/a&gt;&lt;br&gt;Diagram of GraphQL Shim Service in front of Legacy API Monolith.
  &lt;/p&gt;



&lt;h3&gt;
  
  
  Phase 2 - Replacing Falcor with a GraphQL Gateway
&lt;/h3&gt;

&lt;p&gt;Then, Netflix deprecated the GraphQL shim service and the legacy Falcor API in favor of federated GraphQL services owned by domain teams. This decentralized approach enabled independent management and ownership of specific sections of the API. To ensure the correctness and functional accuracy of the migration, Netflix employed replay testing, comparing results between the GraphQL Shim and the new Video API service. They also leveraged sticky canaries, infrastructure experiments that assessed performance, and business metrics, to build confidence in the new GraphQL services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmichaelsolati.com%2Fblog%2Fwhy-netflix-took-a-bet-on-graphql%2F1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmichaelsolati.com%2Fblog%2Fwhy-netflix-took-a-bet-on-graphql%2F1.webp" alt="Diagram of Federated GraphQL Gateway replacing Shim Service." width="800" height="413"&gt;&lt;/a&gt;&lt;br&gt;Diagram of Federated GraphQL Gateway replacing Shim Service.
  &lt;/p&gt;



&lt;h2&gt;
  
  
  Why You Should Consider GraphQL?
&lt;/h2&gt;

&lt;p&gt;Netflix's successful migration is a powerful example for other companies evaluating their API strategies. Here are some compelling reasons why GraphQL should be seriously considered:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Increased Efficiency&lt;/strong&gt;: GraphQL optimizes data retrieval by allowing clients to request only the required data, eliminating unnecessary network overhead. This leads to faster load times, improved performance, and optimized resource utilization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility and Adaptability&lt;/strong&gt;: With GraphQL, companies can quickly iterate and innovate, responding to changing business needs and user demands. Its flexible nature allows seamless additions, modifications, and deprecations without breaking existing client implementations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer-Friendly&lt;/strong&gt;: GraphQL's declarative nature, comprehensive documentation, and robust tooling make it a developer's dream. It simplifies data fetching and enhances productivity, empowering developers to deliver value more effectively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Future-Proofing and Scalability&lt;/strong&gt;: GraphQL's flexibility and adaptability future-proof API infrastructures. It enables long-term scalability, forward compatibility, and easy integration with evolving technologies.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Want to Make the Jump?
&lt;/h2&gt;

&lt;p&gt;If you found this blog post interesting and are considering switching to GraphQL, first &lt;a href="https://github.com/amplication/amplication" rel="noopener noreferrer"&gt;give us a 🌟 on GitHub&lt;/a&gt;, and also be sure to check out &lt;a href="https://amplication.com/" rel="noopener noreferrer"&gt;Amplication&lt;/a&gt;. Besides excellent content like this, we also build the premiere open-source developer tool for generating scalable, secure, and extensible backends using technologies like GraphQL.&lt;/p&gt;

&lt;p&gt;We're making it even faster than ever to build on our platform with our new Database Schema Import functionality, allowing you to seamlessly import your existing database schema into Amplication. This helps reduce the transition time from your old backend to your new one by preserving your underlying database so you can work on enhancing your services.&lt;/p&gt;

&lt;p&gt;You can sign up for the &lt;a href="https://amplication.com/db-import-beta" rel="noopener noreferrer"&gt;beta here&lt;/a&gt;, migrate your backend, and build something as impressive as Netflix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Netflix's migration to GraphQL is a testament to the power and benefits of this revolutionary API architecture. By adopting GraphQL, Netflix achieved increased efficiency, flexibility, and developer-friendliness, while ensuring a seamless user experience. Other companies should take note of Netflix's success and seriously consider embracing GraphQL to take advantage of these benefits for themselves.&lt;/p&gt;

&lt;p&gt;Adapting and staying ahead of the curve is critical as the technological landscape evolves. GraphQL presents a paradigm shift in API architecture, reimagining how data is exchanged between clients and servers. So, whether you're a small startup or a tech giant, you should explore the possibilities of GraphQL and join the ranks of companies (like Netflix and Amplication) embracing this technology.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>api</category>
      <category>graphql</category>
      <category>news</category>
    </item>
    <item>
      <title>Amplication Partners With GitHub to Provide Students With Intern Opportunities</title>
      <dc:creator>Michael Solati</dc:creator>
      <pubDate>Thu, 15 Jun 2023 07:24:47 +0000</pubDate>
      <link>https://dev.to/amplication/amplication-partners-with-github-to-provide-students-with-intern-opportunities-4813</link>
      <guid>https://dev.to/amplication/amplication-partners-with-github-to-provide-students-with-intern-opportunities-4813</guid>
      <description>&lt;p&gt;Are you a student ready to take your skills to the next level? Well, look no further! We want to introduce you to the exciting GitHub Octernships program. This groundbreaking paid internship program connects students with industry partners, like Amplication, where they gain paid professional experiences and mentorship on open-source development projects. We'll dive into the Octernships program, why students should jump on this incredible opportunity, and why Amplication's involvement will add excitement to the mix.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Octernships?
&lt;/h2&gt;

&lt;p&gt;Imagine a world where you can work on real-world projects, get paid for them, and enhance your skills as a student. That's only a part of what Octernships offer. This program provides students with hands-on experience and the chance to work on diverse projects, helping them hone various software development skills. Octernships expose students to the full spectrum of software development endeavors, from open-source projects to documentation, design, and testing.&lt;/p&gt;

&lt;p&gt;One of the most valuable parts of Octernships is the meaningful mentorship industry experts provide. This mentorship helps students navigate their projects and facilitates personal and professional growth. By working alongside seasoned professionals, students gain insights, advice, and guidance that can significantly impact their career trajectory.&lt;/p&gt;

&lt;p&gt;Additionally, Octernships allow students to collaborate with other developers, expanding their network and forging connections that may prove key in their future endeavors. The program also provides a curated list of registered partners, allowing students to connect with potential employers and open doors to exciting career opportunities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Should Students Apply?
&lt;/h2&gt;

&lt;p&gt;Now let's discuss what an Octernship entails and why students should jump on this remarkable opportunity.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hands-On Experience: Octernships offer students the chance to gain practical, real-world experience in software development. This experience-based learning is invaluable in bridging the gap between the classroom and the real world. It equips students with the skills and confidence necessary to thrive in their future careers.&lt;/li&gt;
&lt;li&gt;Mentorship from Industry Experts: The mentorship provided through Octernships is a big deal. Being guided by experienced professionals allows students to learn from the best, receive personalized feedback, and gain insights into industry best practices. This mentorship fosters personal growth and accelerates students' learning curves.&lt;/li&gt;
&lt;li&gt;Building a Professional Network: The connections students make during Octernships can impact their careers. Collaborating with fellow developers and connecting with potential employers broaden their network and open doors to future collaborations and employment opportunities.&lt;/li&gt;
&lt;li&gt;Competitive Student Stipend: GitHub and its partners understand the importance of recognizing students' efforts and dedication. Participants receive a competitive stipend for each project, acknowledging their hard work and providing financial support.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Amplication and Octernships: A Dynamic Duo
&lt;/h2&gt;

&lt;p&gt;Octernships have recently taken an exciting turn with the involvement of Amplication. This leading software development platform empowers developers to build robust and scalable backends. Amplication's participation in Octernships adds extra excitement and opportunity for students. Octerns will get the chance to &lt;a href="https://github.com/Amplication-Octernships/Amplication-Plugin-Task#information" rel="noopener noreferrer"&gt;work on our plugin ecosystem&lt;/a&gt;, adding new functionality and features to Amplication that developers worldwide will use.&lt;/p&gt;

&lt;p&gt;So here's why Amplication's involvement is something to take note of:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cutting-Edge Technology: Amplication brings cutting-edge technology to the Octernships program. Students participating in Amplication-related projects can work on building innovative tools that make developers' lives easier and help drive the industry forward. This experience will help prepare students for the ever-evolving landscape of software development.&lt;/li&gt;
&lt;li&gt;Meaningful Contributions to Open Source: Amplication is committed to its' open-source origin, and through its involvement in the Octernship program, students can help the open-source community. By contributing to Amplication's open-source initiatives, students become part of a global collaborative effort, leaving a lasting mark on the software development ecosystem.&lt;/li&gt;
&lt;li&gt;Enhanced Learning Opportunities: Amplication's expertise in software development improves the learning experience for the Octerns. Students will benefit from Amplication's knowledge and resources, expanding their skill sets and exposing them to real professional development experiences.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;GitHub Octernship offers an exceptional opportunity for students to gain hands-on experience, receive mentorship from industry experts, build their professional networks, and make a meaningful impact on the open-source community. With Amplication's involvement, students can further elevate their learning experiences and embrace cutting-edge technology in their Octernship journey.&lt;/p&gt;

&lt;p&gt;If you are a student passionate about software development, &lt;a href="https://education.github.com/students/octernships" rel="noopener noreferrer"&gt;take advantage of this incredible opportunity&lt;/a&gt;. GitHub Octernships are your gateway to unleashing your potential, kick-starting your tech career, and becoming part of the next generation of developers driving software innovation.&lt;/p&gt;

&lt;p&gt;Finally, Amplication's VP of Research &amp;amp; Development, Muly Gottlieb, will join GitHub to share about our Octernship project and answer your questions! Check out &lt;a href="https://www.youtube.com/watch?v=uhHgBuJfBB0" rel="noopener noreferrer"&gt;the stream&lt;/a&gt; on June 21st at 3:55 PM UTC.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/uhHgBuJfBB0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>opensource</category>
      <category>github</category>
      <category>news</category>
    </item>
  </channel>
</rss>
