<?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: Playful Programming</title>
    <description>The latest articles on DEV Community by Playful Programming (playfulprogramming).</description>
    <link>https://dev.to/playfulprogramming</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.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F3314%2Ffd92caab-2014-431e-a19e-8ab47f2bf5ab.png</url>
      <title>DEV Community: Playful Programming</title>
      <link>https://dev.to/playfulprogramming</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/playfulprogramming"/>
    <language>en</language>
    <item>
      <title>GitHub Universe 2026: My recap from San Francisco</title>
      <dc:creator>Emanuele Bartolesi</dc:creator>
      <pubDate>Tue, 03 Nov 2026 10:23:54 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/github-universe-2026-my-recap-from-san-francisco-23hd</link>
      <guid>https://dev.to/playfulprogramming/github-universe-2026-my-recap-from-san-francisco-23hd</guid>
      <description>&lt;p&gt;I'll write another post in the coming days covering the announcements. This one focuses on GitHub Universe as an event and on the two keynotes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;My updates from San Francisco&lt;/li&gt;
&lt;li&gt;The numbers behind the changes&lt;/li&gt;
&lt;li&gt;The Copilot app&lt;/li&gt;
&lt;li&gt;Projects across repositories&lt;/li&gt;
&lt;li&gt;Session continuity between tools&lt;/li&gt;
&lt;li&gt;Copilot Automations&lt;/li&gt;
&lt;li&gt;Review and merge&lt;/li&gt;
&lt;li&gt;Smaller platform updates&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Enterprise access and budgets&lt;/li&gt;
&lt;li&gt;Rebuilding Git&lt;/li&gt;
&lt;li&gt;Octoverse: how developers use the tools&lt;/li&gt;
&lt;li&gt;Closing keynote: developer joy and hardware&lt;/li&gt;
&lt;li&gt;Protecting open-source maintainers&lt;/li&gt;
&lt;li&gt;DevOps as a prerequisite for agents&lt;/li&gt;
&lt;li&gt;Human judgment and speed&lt;/li&gt;
&lt;li&gt;Chopin and collaborative planning&lt;/li&gt;
&lt;li&gt;Science and the next generation&lt;/li&gt;
&lt;li&gt;The closing message&lt;/li&gt;
&lt;li&gt;What I’m taking back&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I attended GitHub Universe 2026 in person in San Francisco. Between the opening keynote that laid out the architectural overhaul of GitHub and the closing keynote dedicated to the open-source community, maintainers, and developer joy, there was far too much substance to absorb from memory alone. I watched both the &lt;a href="https://www.youtube.com/watch?v=EQeJj0hoPS8" rel="noopener noreferrer"&gt;Day 1 opening keynote&lt;/a&gt; and the &lt;a href="https://www.youtube.com/watch?v=ZSMFrM54H2Q" rel="noopener noreferrer"&gt;Day 2 closing keynote&lt;/a&gt; again to take detailed notes.&lt;/p&gt;

&lt;p&gt;The second watch helped me connect the announcements across both days. In the Copilot app, Projects coordinates work across repositories while Fusion handles model selection. Automations respond to production signals, code review checks the result, and GitHub is rebuilding the Git infrastructure underneath all of it. But the closing keynote provided the essential counterweight: how open-source maintainers protect their repositories from AI pull-request spam, why DevOps maturity is the real prerequisite for coding agents, and how collaborative multiplayer planning can replace isolated single-player prompts.&lt;/p&gt;

&lt;p&gt;My takeaway is that GitHub wants to connect the whole delivery workflow, including the parts that happen before an agent writes code and long after it opens a pull request. That is the angle I care about as a technical lead: how a feature reaches production, who decides whether it is ready, how we protect maintainers, and what the platform needs to support that 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw9n470avbi777uehmq83.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw9n470avbi777uehmq83.jpg" alt="Jay Parikh opening the GitHub Universe 2026 keynote" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Two updates I shared from San Francisco
&lt;/h2&gt;

&lt;p&gt;The trip also gave me a chance to share two things I had been working on myself. Alongside the keynote announcements, these were my own small Copilot updates from San Francisco.&lt;/p&gt;

&lt;p&gt;The first was &lt;strong&gt;Copilot Insights 5.5.0&lt;/strong&gt;, my VS Code extension. I added a Model Costs explorer and a token-cost estimator so developers can compare model rates and explore how different choices affect estimated AI credit usage. The estimates stay separate from actual account consumption. Hearing so much about model choice at Universe made that question feel especially relevant: what does a particular choice mean for the work in front of me and the credits it uses?&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%2Fmedia.licdn.com%2Fdms%2Fimage%2Fv2%2FD4E22AQG1z__kvUeeQQ%2Ffeedshare-image-high-res%2FB4EaF7no6QJEAU-%2F0%2F1793075351438%3Fe%3D2147483647%26v%3Dbeta%26t%3Drtd56rAuE-_NDmVtZmSIYEiHf9it4Zg8MGOOdkI4Yrc" 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%2Fmedia.licdn.com%2Fdms%2Fimage%2Fv2%2FD4E22AQG1z__kvUeeQQ%2Ffeedshare-image-high-res%2FB4EaF7no6QJEAU-%2F0%2F1793075351438%3Fe%3D2147483647%26v%3Dbeta%26t%3Drtd56rAuE-_NDmVtZmSIYEiHf9it4Zg8MGOOdkI4Yrc" alt="Promotional visual from my LinkedIn update about Copilot Insights 5.5.0" width="1086" height="1448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/posts/bartolesiemanuele_hello-from-san-francisco-a-quick-update-share-7520703120881152000-cgwS/" rel="noopener noreferrer"&gt;Read my original Copilot Insights update on LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I also shared that I had earned the &lt;strong&gt;Microsoft Pro Badge: GitHub Copilot - Proficient&lt;/strong&gt;. I am not usually a big badge-sharing person, but I was happy about this one. What I like is its focus on deliberate, practical Copilot use: refactoring that preserves behavior, tests that check something useful, and less time spent on repetitive logic. That connects with what I took from both keynotes: the result still needs our attention and judgment.&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%2Fmedia.licdn.com%2Fdms%2Fimage%2Fv2%2FD4E22AQGuHvk7HenZzg%2Ffeedshare-image-high-res%2FB4EaGBP8.WIwAU-%2F0%2F1793169805472%3Fe%3D2147483647%26v%3Dbeta%26t%3D6mvRGvVDu5viaxXBUXtOeu6qeDn-ae4ODpxEGTa6U8Q" 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%2Fmedia.licdn.com%2Fdms%2Fimage%2Fv2%2FD4E22AQGuHvk7HenZzg%2Ffeedshare-image-high-res%2FB4EaGBP8.WIwAU-%2F0%2F1793169805472%3Fe%3D2147483647%26v%3Dbeta%26t%3D6mvRGvVDu5viaxXBUXtOeu6qeDn-ae4ODpxEGTa6U8Q" alt="Visual from my LinkedIn post about the GitHub Copilot Proficient badge" width="1122" height="1402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/feed/update/urn:li:activity:7521099296109240321/" rel="noopener noreferrer"&gt;Read my original badge update on LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers explain why GitHub is changing the stack
&lt;/h2&gt;

&lt;p&gt;Jay Parikh opened with the scale of activity on GitHub. The platform now has more than 225 million developers, and a new developer joined every second over the past year, for the second consecutive year.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.blog/news-insights/octoverse/octoverse-commits-pushes-reach-new-highs-in-2026-as-developers-build-with-agents/" rel="noopener noreferrer"&gt;2026 Octoverse report&lt;/a&gt; puts the rest of that activity into perspective:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Reported activity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Code pushes&lt;/td&gt;
&lt;td&gt;17.9 billion, more than 2024 and 2025 combined&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commits across public and private repositories&lt;/td&gt;
&lt;td&gt;34.3 billion, more than three times the level two years earlier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merged pull requests&lt;/td&gt;
&lt;td&gt;1.3 billion, averaging around 3.5 million a day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public and open-source repositories&lt;/td&gt;
&lt;td&gt;566 million, up more than 30% year over year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public contributions&lt;/td&gt;
&lt;td&gt;2.5 billion, up from 1.12 billion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Actions jobs&lt;/td&gt;
&lt;td&gt;1,440 a second, or about 124 million a day, more than three times the previous year's volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merged Dependabot pull requests&lt;/td&gt;
&lt;td&gt;More than 30 million, roughly twice the previous year's pace&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The reporting window matters: the Octoverse year runs from October 1, 2025, through September 30, 2026. These are not totals for the full 2026 calendar year, and Actions jobs are different from Actions minutes.&lt;/p&gt;

&lt;p&gt;Activity climbed from January and accelerated sharply around June. GitHub connects that timing with better models, stronger agent harnesses, persistent sessions that can continue across days, and more work running in cloud environments.&lt;/p&gt;

&lt;p&gt;One comparison deserves attention: among repositories created this year, those with an agent configuration file averaged eight times as many commits as those without one. That measures activity, with some limits: commit counts do not measure useful software, and a repository without an agent file may still use AI.&lt;/p&gt;

&lt;p&gt;Jay also called out three projects that give those numbers some purpose: Ersilia's open-source AI tools for drug discovery, OpenQuake's earthquake hazard and risk assessment software, and Matt Pocock's AI Skills for sharing instructions across coding agents. Those examples range from scientific work to improving the tools developers use every day.&lt;/p&gt;

&lt;h2&gt;
  
  
  From an issue to working software in the Copilot app
&lt;/h2&gt;

&lt;p&gt;Jay described a delivery process where a colleague's request, a code change, or a production signal can start work at any point. Developers set direction and make decisions while agents take on implementation and verification. He organized that vision around velocity, intelligence, and quality: completing work efficiently, carrying context and learning forward, and catching defects before shipping.&lt;/p&gt;

&lt;p&gt;Cassidy Williams made it concrete with the GitHub Copilot app and a coffee-shop application.&lt;/p&gt;

&lt;p&gt;Her task was a Coffee Passport feature: customers visit different locations, collect stamps, and receive a reward when they complete the passport. Before implementing it, the team needed answers about what counted as a visit and what happened on repeat visits.&lt;/p&gt;

&lt;p&gt;That is a useful place to start a demo. An agent cannot implement the right behavior if the team has not agreed on the rules.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnk6fxa12p1hjn2xotcyu.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnk6fxa12p1hjn2xotcyu.jpg" alt="Cassidy Williams demonstrating the GitHub Copilot app" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The app brings agent sessions, issues, external tools, and project work into one desktop surface. Its canvases include a browser, a terminal, and custom views. Cassidy showed the agent interacting with the running application in a browser canvas to test the passport feature, then opened the changes in VS Code to inspect the code.&lt;/p&gt;

&lt;p&gt;The useful part for me is that implementation and inspection remain connected. A working preview helps answer whether the feature behaves correctly; the diff still needs attention.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connectors bring the missing context into the session
&lt;/h3&gt;

&lt;p&gt;The answers to Cassidy's requirements questions came through connected services. Copilot Connectors use GitHub-managed, verified MCP connections so you can authenticate to a supported service once and use its tools across supported Copilot surfaces.&lt;/p&gt;

&lt;p&gt;The keynote and announcement materials included services such as Slack, Work IQ, Sentry, Vercel, Netlify, and Cloudflare. GitHub also emphasized the wider ecosystem, including Datadog and Figma. The point is access to requirements, discussions, deployment information, and production signals where the agent is doing the work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/copilot/concepts/customizations/copilot-connectors" rel="noopener noreferrer"&gt;Connectors are currently in public preview&lt;/a&gt;. They work across supported app, CLI, VS Code, mobile, and cloud surfaces, but are not yet supported in sessions running on GitHub Actions, such as assigning an issue to Copilot. That distinction matters when adding them to an existing workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Copilot Fusion chooses how to use the models
&lt;/h3&gt;

&lt;p&gt;Cassidy's next question was which model to use. Copilot Fusion moves that decision into a router and orchestrator that evaluates the task and chooses an execution pattern.&lt;/p&gt;

&lt;p&gt;The three patterns explained in the keynote were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Single:&lt;/strong&gt; one model handles the task.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cascade:&lt;/strong&gt; a model drafts an answer, a judge evaluates it, and the system can escalate to a stronger model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Critique:&lt;/strong&gt; other models review a draft, and their feedback informs a revision.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You choose an effort level: Standard, High, or Max. Standard targets everyday work, High adds orchestration for more substantial tasks, and Max prioritizes deeper reasoning. That is more useful to me than manually switching models for every step of a feature.&lt;/p&gt;

&lt;p&gt;GitHub reported that Fusion Max scored 60 on the Artificial Analysis Coding Agent Index, within two points of Opus 5.5 Max, at half the cost and 40% less time per task. Those are benchmark results, so I would evaluate the same trade-off on my own repositories before assuming equivalent savings.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.github.com/en/copilot/concepts/models/copilot-fusion" rel="noopener noreferrer"&gt;Fusion documentation&lt;/a&gt; also clarifies that it respects plan and administrator model policies, bills for the models it uses, and does not itself launch subagents. Projects orchestration and model orchestration are separate capabilities. Users on paid Copilot plans receive a 30% discount on Fusion model costs through November 30, 2026.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff82h8gxdin0nqg6ozaw4.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff82h8gxdin0nqg6ozaw4.jpg" alt="Copilot Fusion's Single, Cascade, and Critique patterns presented during the keynote" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Copilot Dreaming addresses instructions that have gone stale
&lt;/h3&gt;

&lt;p&gt;One detail I wanted to keep from the second watch was Copilot Dreaming.&lt;/p&gt;

&lt;p&gt;Cassidy explained that the coffee application had moved from Java to SvelteKit, while some instructions still described the old implementation. Dreaming proposed a pull request to update that guidance. She reviewed the diff as she would any other change, and also showed the memories Copilot had learned about her preferences and the team's practices. The &lt;a href="https://www.youtube.com/watch?v=EQeJj0hoPS8&amp;amp;t=760s" rel="noopener noreferrer"&gt;Dreaming demo starts around 12:40&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I like this example because custom instructions require maintenance. An instruction file that describes last month's architecture can make an agent consistently do the wrong thing. Proposing an update through a reviewable change is a practical way to handle that maintenance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/copilot/concepts/prompting-and-context/copilot-dreaming" rel="noopener noreferrer"&gt;Dreaming is in public preview&lt;/a&gt;, with plan and model restrictions: it requires GPT-6.1 Sol access, excludes Copilot Pro and data-residency environments, and consumes AI credits when it runs. Instruction-file changes arrive as pull requests; learned memories can update automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Projects coordinates a feature across repositories
&lt;/h2&gt;

&lt;p&gt;Burke Holland continued with Copilot Projects, using the Fourth Coffee repositories to build a feature that lets a customer reorder their usual drink with one tap.&lt;/p&gt;

&lt;p&gt;He added the repositories as context, then supplied design mockups, a Slack discussion, and a Teams conversation with legal and compliance. The latter mattered because this was a personalization feature. He could keep adding context while work was underway, without waiting for an individual agent loop to finish.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fev90l4lube1x3oiw7gd2.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fev90l4lube1x3oiw7gd2.jpg" alt="Burke Holland selecting repositories for a Copilot project" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Projects provides an orchestrator that coordinates agents across one or more repositories while keeping the goal and context together. Sessions can run locally or in the cloud, and resulting pull requests appear in the project overview. The developer can follow progress and redirect the work.&lt;/p&gt;

&lt;p&gt;The demo also went beyond code. Burke showed short marketing videos generated with a skill, stored in the library, and described an automation to create them as new features ship. That connects implementation with another piece of delivery work that someone normally has to organize separately.&lt;/p&gt;

&lt;p&gt;Projects was announced as available that day, in &lt;a href="https://docs.github.com/en/copilot/how-tos/github-copilot-app/working-with-projects" rel="noopener noreferrer"&gt;public preview&lt;/a&gt;. For a team with several repositories, the shared view of context, sessions, and resulting pull requests gives the feature a home beyond any individual agent's work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same session can follow you between tools
&lt;/h2&gt;

&lt;p&gt;Damian Brady started his demo on a phone. The order-ahead feature stopped accepting orders before a cafe closed, as intended, but failed to explain that to customers.&lt;/p&gt;

&lt;p&gt;He started a Copilot session to fix it in a cloud sandbox, then picked up the same session in VS Code. Session continuity also spans the Copilot app, CLI, and web, so changing surfaces does not require restarting the task.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd2g77let6vt2lnh0edeu.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd2g77let6vt2lnh0edeu.jpg" alt="Damian Brady starting a cloud agent session from his phone" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Jay explained that this uses the same Copilot harness, now rolled out across Microsoft, and announced that developers could use the harness to build agents through Microsoft Foundry as well.&lt;/p&gt;

&lt;p&gt;Damian's observation about being short on attention and time captured the practical constraint. Once the agent has a clear direction, the developer should be able to focus elsewhere and return when a decision is needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automations connect production signals to repeatable work
&lt;/h2&gt;

&lt;p&gt;The next demo began with a production error. The coffee site's information button returned a 500, and Damian opened the Sentry details in a custom canvas.&lt;/p&gt;

&lt;p&gt;He could create an issue or ask Copilot to investigate. Copilot Automations turns that response into reusable work that can run manually, through an API, on a schedule, or in response to an event. Cloud execution means the workflow can continue after the laptop closes.&lt;/p&gt;

&lt;p&gt;The controls were a substantial part of the demonstration: choose the available tools, allow an automation to create pull requests without allowing it to merge them, and set an AI credit limit. Shared automations let the team reuse that setup.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Far4uli22ypf26njm067q.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Far4uli22ypf26njm067q.jpg" alt="Shared Copilot Automations listed in the subscriptions repository" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Damian also showed a group of specialist automations in a subscriptions repository. One watched for signals and created issues; others triaged them, reproduced the problem with screenshots, implemented a fix, and verified it against the original request.&lt;/p&gt;

&lt;p&gt;That separation gives each automation a specific job and an output the next one can use. The announcements also described automations learning from outcomes and proposing improvements for review. The &lt;a href="https://docs.github.com/en/copilot/how-tos/github-copilot-app/using-automations" rel="noopener noreferrer"&gt;automation documentation&lt;/a&gt; confirms support for self-improving groups and configurable tools and spending limits. Changes to those workflows deserve the same review as changes to the application.&lt;/p&gt;

&lt;p&gt;For a pilot, I would also account for the &lt;a href="https://docs.github.com/en/copilot/concepts/agent-capabilities/agent-powered-workflows-and-operations/about-automations" rel="noopener noreferrer"&gt;current cloud-automation requirements&lt;/a&gt;: an eligible paid plan, a private or internal repository, and both AI credit and GitHub Actions usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review and merge have to keep up
&lt;/h2&gt;

&lt;p&gt;GitHub reported that Copilot Code Review has surfaced 200 million findings since its launch. At Universe, it introduced Code Review Max, alongside Lite and Balanced, for changes that need deeper inspection.&lt;/p&gt;

&lt;p&gt;According to GitHub, Max achieved the highest F1 score on ReviewBench for high-severity issues. My interest is in the defects a quick review can miss, such as interactions across changed files or a subtle security problem. Testing Max against a team's own review history would help establish where that deeper inspection pays off.&lt;/p&gt;

&lt;p&gt;Damian showed Copilot reviewing a coffee-service pull request before the human review. Code Review is also available to organization members without a Copilot subscription when the organization enables paid usage; that route is currently on GitHub.com. Reviews can consume AI credits and GitHub Actions minutes. &lt;a href="https://docs.github.com/en/copilot/concepts/agent-capabilities/agent-powered-workflows-and-operations/code-review" rel="noopener noreferrer"&gt;GitHub's documentation&lt;/a&gt; covers the review levels and access requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stacked pull requests make related changes easier to inspect
&lt;/h3&gt;

&lt;p&gt;The Coffee Passport work was split into a stack: data changes, a service endpoint, and the UI that depended on them. Each part could be reviewed separately, and the stack could be merged together or in contiguous groups, starting from the bottom.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7uiigptxhca7o686x3io.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7uiigptxhca7o686x3io.jpg" alt="The Coffee Passport feature organized as stacked pull requests" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This gives reviewers smaller diffs while keeping the dependency between changes visible. Native support matters when agents can produce a large feature faster than a person can comfortably review it. The &lt;a href="https://docs.github.com/en/pull-requests/how-tos/stacked-pull-requests" rel="noopener noreferrer"&gt;stacked pull request docs&lt;/a&gt; cover the workflow and the &lt;code&gt;gh stack&lt;/code&gt; extension. Octoverse reports that the preview launched on July 30 and had been used by more than 264,000 developers by September. Stacked PRs reached &lt;a href="https://github.blog/changelog/2026-10-06-stacked-pull-requests-generally-available/" rel="noopener noreferrer"&gt;general availability on October 6&lt;/a&gt;, before Universe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent Merge helps a pull request reach completion
&lt;/h3&gt;

&lt;p&gt;Damian returned to the fix he had started on his phone and enabled Agent Merge. In the demo, it handled follow-up work such as review feedback, CI failures, and conflicts in the background, with merging enabled once the requirements were met. The &lt;a href="https://www.youtube.com/watch?v=EQeJj0hoPS8&amp;amp;t=1720s" rel="noopener noreferrer"&gt;Agent Merge demo starts around 28:40&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That closes a gap I see in agent workflows: opening a pull request is only one stage of completing the task. Branches keep changing, checks fail, and reviewers ask for adjustments. Agent Merge takes on those follow-up changes, with repository checks and merge permissions defining when the work can land.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.github.com/en/copilot/how-tos/copilot-on-github/use-copilot-agents/watch-and-fix-pull-requests" rel="noopener noreferrer"&gt;current GitHub.com watch-and-fix workflow&lt;/a&gt; documents failed Actions checks and merge conflicts, excludes fork and stacked pull requests, and treats auto-merge as a separate setting. I would check those boundaries before expecting the whole stack to run unattended.&lt;/p&gt;

&lt;h2&gt;
  
  
  The smaller platform updates belong in this recap too
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/events/universe/recap" rel="noopener noreferrer"&gt;Universe announcement page&lt;/a&gt; includes several changes that fit into the same workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub CLI attachments:&lt;/strong&gt; repeatable &lt;code&gt;--attach&lt;/code&gt; flags add local images and videos inline to issues, pull requests, and comments. A reproduction video or architecture diagram can travel with work created from the terminal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issue Artifacts:&lt;/strong&gt; plans, research, links, and other artifacts stay attached to the issue, making the reasoning behind the implementation easier to find.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repository pull request views:&lt;/strong&gt; the updated view helps you scan open work, understand its status, and find what needs attention next.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, the &lt;a href="https://docs.github.com/en/github-cli/github-cli/attaching-files-with-github-cli" rel="noopener noreferrer"&gt;CLI attachment workflow&lt;/a&gt; supports attaching more than one file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh &lt;span class="nb"&gt;pr &lt;/span&gt;create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--title&lt;/span&gt; &lt;span class="s2"&gt;"Explain when order-ahead is unavailable"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--body&lt;/span&gt; &lt;span class="s2"&gt;"Adds a customer-facing explanation before the cafe closes."&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--attach&lt;/span&gt; ./order-ahead-demo.mp4 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--attach&lt;/span&gt; ./order-flow.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With the files saved locally, an agent-generated screenshot or recording can become part of the review evidence, especially when it shows whether a fix satisfies the original request.&lt;/p&gt;
&lt;h2&gt;
  
  
  Security needs to be part of the agent workflow
&lt;/h2&gt;

&lt;p&gt;Damian demonstrated code scanning findings with the location, vulnerability type, and remediation context available to Agentic Autofix. Assigning a finding produced a pull request with a proposed fix.&lt;/p&gt;

&lt;p&gt;The broader security announcements cover different stages of the workflow, from local changes to dependency installation and pushing code.&lt;/p&gt;
&lt;h3&gt;
  
  
  A Security canvas for investigating findings
&lt;/h3&gt;

&lt;p&gt;The Copilot app's security extension brings code scanning alerts into a Security canvas, where you can ask for an explanation, request a fix, and inspect the resulting changes. It can also review local changes before a pull request is opened.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.github.com/en/copilot/how-tos/github-copilot-app/view-and-fix-security-alerts" rel="noopener noreferrer"&gt;extension documentation&lt;/a&gt; makes two useful distinctions: it is in public preview, and it displays code scanning alerts rather than Dependabot or secret scanning alerts. A requested fix changes the local working copy; the usual review and scanning process still determines whether the alert is resolved.&lt;/p&gt;
&lt;h3&gt;
  
  
  AI Scan extends coverage beyond CodeQL
&lt;/h3&gt;

&lt;p&gt;GitHub presented whole-repository scanning and assigning a backlog of findings to Copilot, as well as AI Scan coverage for pull requests in languages and frameworks CodeQL does not cover.&lt;/p&gt;

&lt;p&gt;For the pull request feature, &lt;a href="https://docs.github.com/en/code-security/concepts/code-scanning/ai-powered-security-detections" rel="noopener noreferrer"&gt;the docs&lt;/a&gt; list examples including PHP, Shell/Bash, Terraform, Dockerfiles, JSP, and Blazor. The findings are advisory and currently cannot enforce a merge requirement through a ruleset. That is a material detail for a team planning its security gates.&lt;/p&gt;
&lt;h3&gt;
  
  
  Push protection catches unstructured secrets
&lt;/h3&gt;

&lt;p&gt;Damian pushed a local commit containing a secret and showed AI detection blocking it before it reached the repository. This extends protection to unstructured secrets such as passwords that do not match a known provider pattern.&lt;/p&gt;

&lt;p&gt;AI-detected secret push protection is in public preview. Both push protection and AI secret detection must be enabled. &lt;a href="https://docs.github.com/en/code-security/how-tos/secure-your-secrets/prevent-future-leaks/enable-ai-push-protection" rel="noopener noreferrer"&gt;GitHub documents&lt;/a&gt; the security licensing requirements and additional AI credit consumption, which belong in the rollout plan.&lt;/p&gt;
&lt;h3&gt;
  
  
  Package firewall and reachability analysis address dependency risk
&lt;/h3&gt;

&lt;p&gt;Package firewall blocks malicious or risky npm packages before agents, developer tools, or CI runners install them. The &lt;a href="https://docs.github.com/en/code-security/how-tos/secure-your-supply-chain/secure-your-dependencies/protecting-endpoints-with-package-firewall" rel="noopener noreferrer"&gt;public preview&lt;/a&gt; evaluates downloads against policies such as package age and malware advisories.&lt;/p&gt;

&lt;p&gt;Reachability analysis helps prioritize existing Dependabot alerts by looking for call paths from application code to vulnerable dependency functions. It currently supports JavaScript and TypeScript repositories and is also &lt;a href="https://docs.github.com/en/code-security/concepts/supply-chain-security/about-reachability-analysis-for-dependabot-alerts" rel="noopener noreferrer"&gt;in public preview&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The static analysis gives teams a call path to investigate and helps order the remediation queue. Reachability alone establishes neither runtime execution nor exploitability, so severity and other evidence still inform the priority.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7epurjakso5ro4z86n1h.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7epurjakso5ro4z86n1h.jpg" alt="Package firewall settings for blocking malware and packages with security advisories" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Enterprise access changes, but usage still needs a budget
&lt;/h2&gt;

&lt;p&gt;GitHub Enterprise customers can now access Copilot features without purchasing a separate Copilot subscription and pay for the AI credits they use. The announcement includes the app, code review, automations, and the wider Copilot feature set.&lt;/p&gt;

&lt;p&gt;The rollout details in &lt;a href="https://github.blog/news-insights/company-news/github-universe-2026-evolving-every-layer-of-the-stack/" rel="noopener noreferrer"&gt;the announcement&lt;/a&gt; matter: the new route is available through GitHub Sales, self-service is coming later, and existing Copilot Business and Copilot Enterprise customers can keep their plans or move when ready.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Access or rollout detail to retain&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Copilot app and Projects&lt;/td&gt;
&lt;td&gt;App access spans Copilot plans; Projects is in public preview&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copilot Fusion&lt;/td&gt;
&lt;td&gt;Generally available in supported clients; model and effort access follows plan and policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copilot Automations&lt;/td&gt;
&lt;td&gt;Eligible paid plans; private/internal repositories for cloud execution; AI credits and Actions usage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code Review, including Max&lt;/td&gt;
&lt;td&gt;Organization policy can enable credit-based review for members without a Copilot subscription&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise consumption route&lt;/td&gt;
&lt;td&gt;Available through Sales; self-service forthcoming; existing plans can continue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security canvas and Connectors&lt;/td&gt;
&lt;td&gt;Public preview&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI Scan&lt;/td&gt;
&lt;td&gt;Repository visibility, security licensing, settings, and usage billing affect access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI-detected secret push protection&lt;/td&gt;
&lt;td&gt;Public preview; requires the security entitlement and detection settings; consumes AI credits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Package firewall and reachability analysis&lt;/td&gt;
&lt;td&gt;Public preview&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stacked PRs and CLI attachments&lt;/td&gt;
&lt;td&gt;Stacked PRs reached GA October 6; follow the documented stack and attachment workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Simpler access should make adoption easier, while tool permissions and AI credit budgets give enterprise teams a way to control how that usage grows.&lt;/p&gt;
&lt;h2&gt;
  
  
  Rebuilding Git was one of the most substantial announcements
&lt;/h2&gt;

&lt;p&gt;Jay acknowledged that GitHub had struggled with performance and reliability at points during the year. Growth explained some of the pressure; he explicitly said it was not an excuse.&lt;/p&gt;

&lt;p&gt;GitHub reported adding four million CPU cores, 160 PB of high-speed storage, and 50 Tb/s of network capacity so far this year. It also expects to complete the broader migration to a multi-region Azure deployment in early 2027.&lt;/p&gt;

&lt;p&gt;April Leonard then explained why adding capacity alone would not solve the Git bottlenecks. Every agent session can create branches, read history, and push changes. In September alone, developers and agents produced more than seven billion commits.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnyij5ab4hed1l88d5z1h.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnyij5ab4hed1l88d5z1h.jpg" alt="April Leonard speaking about GitHub's infrastructure during the keynote" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nearly every repository had already moved to Azure, but for roughly 100 of the busiest repositories, even 25 milliseconds between data centers mattered. An initial improvement changed write coordination from locking a whole repository to locking the branch being updated. That delivered three to four times more write throughput for agents working on different branches.&lt;/p&gt;

&lt;p&gt;The next architecture follows three principles: simplify the primitives, separate storage from compute, and minimize coordination. Azure Blob Storage holds the durable source of truth. Ephemeral serving nodes cache data and can scale with demand. A specialized replicated log service handles the small reference transaction that remains on the critical path.&lt;/p&gt;

&lt;p&gt;The test results were substantial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With 5,000 agents targeting one repository, the system sustained 7,200 writes per second, which GitHub described as 180 times current throughput.&lt;/li&gt;
&lt;li&gt;GitHub replayed 24 hours of traffic from its busiest open-source repository in one minute against the new architecture.&lt;/li&gt;
&lt;li&gt;The read load-testing harness reached its own limit before the new architecture did.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are reported test results, rather than a throughput guarantee for every repository. The keynote included an early-user segment with Peter Steinberger of OpenClaw, and GitHub expects everyone to move to the new architecture by early 2027.&lt;/p&gt;

&lt;p&gt;The infrastructure work also includes an AI gateway processing trillions of tokens, prompt caching and harness improvements, more Actions capacity and control-plane hardening, additional network edge sites, four times the backhaul between legacy data centers and Azure, and TLS optimizations. The &lt;a href="https://www.youtube.com/watch?v=EQeJj0hoPS8&amp;amp;t=1890s" rel="noopener noreferrer"&gt;keynote recording&lt;/a&gt; covers the infrastructure section, alongside &lt;a href="https://github.blog/news-insights/company-news/github-universe-2026-evolving-every-layer-of-the-stack/" rel="noopener noreferrer"&gt;GitHub's written announcement&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is the dependency behind the earlier demos. Coordinating agents becomes much less useful if they spend their time waiting on Git, CI, or platform availability.&lt;/p&gt;
&lt;h2&gt;
  
  
  Octoverse shows how developers are using the tools
&lt;/h2&gt;

&lt;p&gt;The rest of the &lt;a href="https://github.blog/news-insights/octoverse/octoverse-commits-pushes-reach-new-highs-in-2026-as-developers-build-with-agents/" rel="noopener noreferrer"&gt;Octoverse report&lt;/a&gt; adds context that I did not want to lose in a keynote-only recap.&lt;/p&gt;
&lt;h3&gt;
  
  
  Agents are increasingly producing the first draft
&lt;/h3&gt;

&lt;p&gt;By September, more than 40% of pull requests started with a detectable agent's first draft, up from 17% in March. That amounted to around 110 million in September alone.&lt;/p&gt;

&lt;p&gt;About 87% of agent-started pull requests changed multiple files, compared with 79% of those without detected agent involvement. The share of agent-involved PRs merged within two weeks rose from 83% in February to 90% in September; the comparison group remained around 86%.&lt;/p&gt;

&lt;p&gt;Among Copilot's monthly active users, 55% used agentic features, up from 35% a year earlier. Cloud agent usage grew 230% year over year, compared with 85% for agent chat. These figures describe observed workflows, and undetected agent use can still exist in the comparison group.&lt;/p&gt;

&lt;p&gt;Review and security activity grew alongside implementation: AI-assisted review and GitHub Advanced Security usage each increased by more than 90%, while push protection ran 5.8 billion scans, nearly four times the previous year's pace.&lt;/p&gt;
&lt;h3&gt;
  
  
  Model choice is already plural
&lt;/h3&gt;

&lt;p&gt;In September, 61% of Copilot users who explicitly selected models used more than one, up from 50% four months earlier. Developers on paid plans used 47 distinct models, compared with 29 in May, and no model accounted for more than 25% of qualifying requests.&lt;/p&gt;

&lt;p&gt;The report groups examples by their intended role:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model group&lt;/th&gt;
&lt;th&gt;Examples in the report&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Deep reasoning and planning&lt;/td&gt;
&lt;td&gt;Claude Opus 5.5, GPT 5.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;General purpose&lt;/td&gt;
&lt;td&gt;Claude Sonnet 5, Grok 4.7, GPT 6.1 Sol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fast and lightweight&lt;/td&gt;
&lt;td&gt;Claude Haiku 5.5, Gemini 3.8 Flash, GPT 6 Luna&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Of every 100 developer-selected requests, 38 went to general-purpose models, 37 to reasoning and planning models, and 25 to lightweight models. This analysis excludes automatic selection and several other kinds of traffic; it describes usage rather than model quality.&lt;/p&gt;

&lt;p&gt;Separately, 63% of paying Copilot developers used automatic model selection at least once in September. Project HydraFusion, the research preview behind Fusion, routed almost 60,000 sessions. That helps explain why GitHub is investing in choosing models on the developer's behalf while preserving explicit selection.&lt;/p&gt;
&lt;h3&gt;
  
  
  Growth reaches more countries and industries
&lt;/h3&gt;

&lt;p&gt;The ten largest developer populations in September were:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Country&lt;/th&gt;
&lt;th&gt;Developers on GitHub&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;United States&lt;/td&gt;
&lt;td&gt;38.3 million&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;India&lt;/td&gt;
&lt;td&gt;32.0 million&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;China&lt;/td&gt;
&lt;td&gt;12.4 million&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brazil&lt;/td&gt;
&lt;td&gt;10.2 million&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Japan&lt;/td&gt;
&lt;td&gt;7.15 million&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;United Kingdom&lt;/td&gt;
&lt;td&gt;6.47 million&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Indonesia&lt;/td&gt;
&lt;td&gt;6.34 million&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Germany&lt;/td&gt;
&lt;td&gt;6.30 million&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Russia&lt;/td&gt;
&lt;td&gt;5.07 million&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;France&lt;/td&gt;
&lt;td&gt;4.75 million&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The United States and India each added more than ten million developers, and Brazil added 3.28 million. Every country included in last year's projections had already closed at least 20% of the distance to its 2030 projection; the US had closed 38.6%, and India 28.3%.&lt;/p&gt;

&lt;p&gt;For pushes to public repositories, the top ten were the United States, India, China, Brazil, Germany, the United Kingdom, South Korea, France, Japan, and Canada. That is a different ranking from total developer population.&lt;/p&gt;

&lt;p&gt;The new industry analysis uses NAICS classifications of public repositories. Mining, construction, healthcare, and agriculture have grown substantially since 2022. More than 40% of repositories in administrative services, mining, healthcare, and agriculture mentioned AI, but detectable agent-opened pull requests were below 3%. Construction had about 25% mentioning AI and around 9% with agent-opened PRs.&lt;/p&gt;

&lt;p&gt;The distinction between AI mentions and agent-opened PRs keeps two different kinds of adoption visible. The report's wider examples include disaster alerts in Nepal, spacecraft software, research tools, and industrial energy analysis. There is useful software here well beyond developer tooling.&lt;/p&gt;
&lt;h3&gt;
  
  
  Language choices may also reflect agent defaults
&lt;/h3&gt;

&lt;p&gt;TypeScript remained first by contributors, followed by Python and JavaScript. Between September 2025 and September 2026, they added 3.62 million, 3.23 million, and 1.82 million monthly contributors respectively. Rust doubled its contributor base, the fastest percentage growth among the leading languages.&lt;/p&gt;

&lt;p&gt;GitHub's forecast puts TypeScript and Python near 13 million monthly contributors each by September 2027.&lt;/p&gt;

&lt;p&gt;The report also raises the possibility that agent defaults influence language choice. Among new repositories, TypeScript was the primary language in 35% of those with an observed agent signal, compared with 24% without one. For me, that reinforces the value of specifying language and architectural constraints when starting a project with agents.&lt;/p&gt;
&lt;h3&gt;
  
  
  Open source supplies both the foundations and the tools
&lt;/h3&gt;

&lt;p&gt;Octoverse separates community and independent projects from single-vendor or product-led repositories. The distinction is strategic control, not whether the project receives community contributions.&lt;/p&gt;

&lt;p&gt;These are the complete fastest-growing lists by increase in contributing accounts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rank&lt;/th&gt;
&lt;th&gt;Community or independent&lt;/th&gt;
&lt;th&gt;Single vendor or product-led&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;farion1231/cc-switch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;anthropics/claude-code&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;punkpeye/awesome-mcp-servers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;openai/codex&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;omacom/omarchy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;anomalyco/opencode&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;firstcontributions/first-contributions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;microsoft/vscode&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ggml-org/llama.cpp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;github/copilot-cli&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;code&gt;EclipseFdn/open-vsx.org&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;zed-industries/zed&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sgl-project/sglang&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;google-gemini/gemini-cli&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vllm-project/vllm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BerriAI/litellm&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;code&gt;imputnet/helium&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;microsoft/winget-pkgs&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vllm-project/vllm-ascend&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Kilo-Org/kilocode&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The most-engaged ranking combines stars, forks, and distinct pull request and issue creators:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rank&lt;/th&gt;
&lt;th&gt;Community or independent&lt;/th&gt;
&lt;th&gt;Single vendor or product-led&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;openclaw/openclaw&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NousResearch/hermes-agent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vllm-project/vllm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;anomalyco/opencode&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ggml-org/llama.cpp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;earendil-works/pi&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;omacom/omarchy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;stablyai/orca&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;farion1231/cc-switch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pingdotgg/t3code&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sgl-project/sglang&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;microsoft/vscode&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pytorch/pytorch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Graphify-Labs/graphify&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rust-lang/rust&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;paperclipai/paperclip&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;code&gt;home-assistant/core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BerriAI/litellm&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;code&gt;python/cpython&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;can1357/oh-my-pi&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Inference engines, runtimes, and community platforms sit alongside the agents and editors developers use directly. Both depend on people maintaining code that other projects build on.&lt;/p&gt;
&lt;h2&gt;
  
  
  The closing keynote: Developer joy, hackable hardware, and the human side of AI
&lt;/h2&gt;

&lt;p&gt;While Day 1 focused on platform architecture, enterprise governance, and Git throughput, the final keynote of Universe 2026 brought the conversation back to where GitHub began: open source, community maintainers, and the joy of building software.&lt;/p&gt;

&lt;p&gt;Martin Woodward opened the final session by acknowledging the emotional reality of 2026: with new frontier models shipping every few weeks and telemetry breaking records everywhere, developers are feeling the pace. His answer to feeling constantly out of date is simple: learn by doing.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp64m6clm8hu8i7y2dtzr.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp64m6clm8hu8i7y2dtzr.jpg" alt="Martin Woodward opening the GitHub Universe 2026 Day 2 Keynote" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The hackable badge and culture when no one is looking
&lt;/h3&gt;

&lt;p&gt;The centerpiece of that hands-on ethos was the physical conference badge every attendee wore in San Francisco. Built in partnership with Arm, the 2026 badge is an astonishing piece of hardware: a dual-core Raspberry Pi RP2350 Cortex processor packed with an accelerometer, environmental sensors, Bluetooth radio, and a color screen running MicroPython.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvg52l1fer3e7ntty0fwy.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvg52l1fer3e7ntty0fwy.jpg" alt="The hackable Universe badge powered by the RP2350 processor" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Martin shared an unscripted story behind the badge that stuck with me. During the 2025 conference, a typhoon struck a component supplier and global supply shortages delayed screen deliveries until the night before the event. Rather than canceling the badge giveaway, GitHub engineers, managers, and community members spent the entire night in the San Francisco office hand-assembling boards and soldering screens so attendees could pick them up at 8:00 AM.&lt;/p&gt;

&lt;p&gt;As Martin put it: "Culture is what happens when no one is looking." For all the enterprise features and cloud scale discussed this week, that community-first startup spirit is what still makes GitHub distinctive.&lt;/p&gt;
&lt;h3&gt;
  
  
  From web simulation to MicroPython in the Copilot app
&lt;/h3&gt;

&lt;p&gt;To make sure attendees without hardware or developers watching the livestream could participate, GitHub published a complete web simulator for the badge. It was built by a summer intern, Charlie Nicholson, who used the simulator framework and around $95 in Copilot credits to create an emulator capable of running games like &lt;em&gt;Flappy Mona&lt;/em&gt; directly in the browser.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F12gs7vadd91lcjxoz486.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F12gs7vadd91lcjxoz486.jpg" alt="The Universe badge simulator running in the browser" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Martin then took the badge project into the GitHub Copilot app. In a live canvas connected to the badge repository, he showed a Breakout game that he said he had created with a single prompt.&lt;/p&gt;

&lt;p&gt;The technical detail that matters here is not the prompt; it is the harness. Martin explained that the Copilot app canvas could access the MicroPython debugging environment, letting the agent inspect execution and debug the game. That gave it a verification loop beyond simply generating code.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb4qvavk7bztgvaed4vuh.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb4qvavk7bztgvaed4vuh.jpg" alt="GitHub Copilot app canvas interacting with the MicroPython badge environment" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As Martin noted, that tight verification loop is the difference between an agent producing broken syntax and an agent delivering working software.&lt;/p&gt;
&lt;h3&gt;
  
  
  Edge ML on stage: Training a decision tree in the browser
&lt;/h3&gt;

&lt;p&gt;Martin followed the Breakout demo with an edge machine learning application: building a pedometer for the badge without prior ML expertise.&lt;/p&gt;

&lt;p&gt;He streamed raw accelerometer telemetry from the badge over Bluetooth into a browser canvas. On stage, he gathered live training samples across distinct motion states: standing still, walking normally, and swinging his arms with "big keynote energy" (or as he joked, "the walk of someone who just merged to main on a Friday").&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn3r660v7vpfeg7xnbh0j.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn3r660v7vpfeg7xnbh0j.jpg" alt="The audience applauding during Martin's badge demonstration" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using a browser-based training loop, he trained a decision-tree classifier on the accelerometer dataset and deployed the resulting model directly back to the badge firmware over Bluetooth. The badge successfully distinguished actual walking from arbitrary gestures in real time.&lt;/p&gt;

&lt;p&gt;It was a playful demo, but it demonstrated a practical point: agent tooling can give a developer a way into unfamiliar domains—like embedded edge ML—through experimentation. The learning still comes from collecting data, testing the result, and understanding where it fails.&lt;/p&gt;
&lt;h2&gt;
  
  
  Open source under AI pressure: Maintainer fatigue, pull request slop, and new defensive controls
&lt;/h2&gt;

&lt;p&gt;The keynote then transitioned into the hardest conversation happening across the industry: what happens to open-source maintainers when coding agents make creating pull requests essentially free?&lt;/p&gt;

&lt;p&gt;Octoverse reported that over 40% of all pull requests on GitHub now begin with an agent's first draft, up from 17% in March. While that speed accelerates prototyping, Martin acknowledged the painful downside: "Not all those PRs are diamonds. Just because it's easier to create than ever doesn't mean what you have is ready to commit. Some are just bad ideas. Some are barely ideas at all. You know, slop."&lt;/p&gt;

&lt;p&gt;Traditionally, open source functioned because coding had inherent friction. Contributors had to spend time understanding the codebase, learning the project's culture, reading documentation, and following contribution guidelines before submitting code. That friction acted as an organic quality filter.&lt;/p&gt;

&lt;p&gt;Coding agents have largely eliminated that barrier. Well-meaning contributors now use agents to generate pull requests and shallow security reports across dozens of repositories without reading the project guidelines or verifying the fix. For open-source maintainers—most of whom volunteer their free time after work—wading through low-effort AI contributions has become an exhausting, unpaid burden.&lt;/p&gt;
&lt;h3&gt;
  
  
  Maintainer guardrails: WIP limits, contributor exemptions, and PR access
&lt;/h3&gt;

&lt;p&gt;To protect maintainers, Martin highlighted repository controls designed to restore balance. Some predate Universe; the latest update adds more flexibility over who is subject to limits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Work-in-progress (WIP) limits per repository:&lt;/strong&gt; Maintainers can cap open pull requests from users without write access, encouraging contributors to finish an existing change before opening another.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exemptions for established contributors:&lt;/strong&gt; The &lt;a href="https://github.blog/changelog/2026-10-28-pull-request-limits-exempt-contributors-by-age-or-history/" rel="noopener noreferrer"&gt;October 28 update&lt;/a&gt; allows exemptions based on account age or prior merged pull requests. Meeting either configured condition can exempt a contributor. This is separate from temporary interaction limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control over pull request access:&lt;/strong&gt; Since &lt;a href="https://github.blog/changelog/2026-02-13-new-repository-settings-for-configuring-pull-request-access/" rel="noopener noreferrer"&gt;February&lt;/a&gt;, maintainers can restrict PRs to collaborators or disable them. Disabling PRs hides the PR tab, including existing pull requests; re-enabling restores access. It is more than a pause on new submissions.&lt;/li&gt;
&lt;/ol&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg68wjsoyjkskrg1jcpfg.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg68wjsoyjkskrg1jcpfg.jpg" alt="Martin Woodward discussing the pressure on open-source maintainers" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Martin framed this policy change around a clear boundary:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Open source is the freedom to inspect, to remix, and to share without asking for permission. But it's not the freedom to impose your will on others."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As a technical lead who manages both internal shared code and public open-source libraries, I see these controls as long overdue. If platforms do not give maintainers tools to protect their time, maintainers will simply walk away.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why DevOps is the real prerequisite for AI agents
&lt;/h2&gt;

&lt;p&gt;While unguided AI generation strains maintainers, the keynote showed that maintainers with mature operational pipelines are using agents effectively.&lt;/p&gt;

&lt;p&gt;Martin recognized Franck Nijhof from Home Assistant in the audience and reported that the project had merged more than 5,000 pull requests. He also described GitHub's support for Copilot agents across the Cloud Native Computing Foundation (CNCF) ecosystem.&lt;/p&gt;

&lt;p&gt;Martin recounted a telling example from Jorge Castro around OpenPrinting. The OpenPrinting maintainers possess deep, specialized knowledge of printer drivers and protocols, but their primary pain point was infrastructure: managing container refreshes, keeping dependencies current, and maintaining CI/CD pipelines across multiple architectures. He described the &lt;strong&gt;Hive&lt;/strong&gt; automation framework taking on operational maintenance across four OpenPrinting repositories, freeing the domain experts to focus on the driver code they enjoy.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy85m1o4kdvjhialhalyl.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy85m1o4kdvjhialhalyl.jpg" alt="Martin Woodward recounting the OpenPrinting and Hive example" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That example reinforces a foundational truth: &lt;strong&gt;teams that do DevOps well are the best equipped to take advantage of AI.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The automated checks, strict linting, integration suites, and branch protections you build to let human developers ship quickly are the exact same safeguards an autonomous agent needs. An agent running in a repository without automated testing or clean CI is just an engine for accumulating unverified changes.&lt;/p&gt;

&lt;p&gt;Actions continues to be the backbone of that verification. According to the published Octoverse report, Actions executed 1,440 jobs per second (about 124 million daily) over the reporting year—more than three times the previous year's volume.&lt;/p&gt;
&lt;h2&gt;
  
  
  The limit of pure speed: "Why they call it work"
&lt;/h2&gt;

&lt;p&gt;Martin addressed the temptation to treat raw coding speed as the sole metric of success:&lt;/p&gt;

&lt;p&gt;His point was that shipping faster only helps if we are building the right things. Humans still need to understand the problem, direct the work, and reflect on whether the result makes things better or worse. Defining what good looks like remains part of the job; the agent cannot take that responsibility away.&lt;/p&gt;

&lt;p&gt;That judgment also reflects how developers interact with models. The telemetry shows that developers are not relying on a single frontier model. Of every 100 developer-selected requests, 25 went to fast, lightweight models (such as Gemini 3.8 Flash, Claude Haiku 5.5, or GPT-6 Luna), while 37 went to reasoning models and 38 to general-purpose models. These figures exclude automatic selection; they are request shares, not a breakdown of all developer tasks.&lt;/p&gt;

&lt;p&gt;Choice also extends to editor surfaces. Copilot usage spans the terminal, CLI, Copilot app, web, mobile, VS Code, Visual Studio, Eclipse, and IntelliJ. Native Copilot integration has also expanded to Apple's Xcode following announcements at WWDC.&lt;/p&gt;
&lt;h2&gt;
  
  
  GitHub Next and Chopin: Rethinking collaborative planning with agents
&lt;/h2&gt;

&lt;p&gt;One of the most compelling segments of the closing keynote came from Maggie Appleton, designer and engineer at GitHub Next (GitHub's advanced research and prototyping lab).&lt;/p&gt;

&lt;p&gt;Maggie tackled a problem every engineer using coding agents has experienced: agent planning today is broken.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqb3l0cd0tt0dj0qik2pi.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqb3l0cd0tt0dj0qik2pi.jpg" alt="Maggie Appleton critiquing current agent planning limitations" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Her critique targeted four specific failure modes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The multiple-choice interrogation:&lt;/strong&gt; Planning typically happens in a tiny terminal CLI or desktop chat box, subjecting the developer to an endless sequence of multiple-choice questions. By question nine, cognitive fatigue sets in and the developer defaults to clicking "Option A (Recommended)"—what Maggie called &lt;em&gt;vibes-based decision making&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text walls:&lt;/strong&gt; Once the questionnaire concludes, the agent spits out pages of dense markdown. Reading walls of technical text is tiring, and subtle architectural errors get buried.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single-player isolation:&lt;/strong&gt; Today's agent sessions are private 1-on-1 conversations between an individual engineer and an LLM. Teammates cannot see the plans being generated or the architectural trade-offs being accepted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The PR is too late for feedback:&lt;/strong&gt; Because engineers lack collaborative planning tools, they default to letting the agent implement the entire feature and opening a pull request to get feedback from teammates. But a pull request is way too late in the lifecycle to ask: &lt;em&gt;"Should we even build this?"&lt;/em&gt; or &lt;em&gt;"Is this the right backend architecture?"&lt;/em&gt; Rewriting or discarding code after tokens have already been spent is pure waste.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  The Chopin prototype: Multiplayer canvases, proactive research, and decision audit logs
&lt;/h3&gt;

&lt;p&gt;To solve this, GitHub Next built &lt;strong&gt;Chopin&lt;/strong&gt;, an experimental prototype named after the composer Frédéric Chopin.&lt;/p&gt;

&lt;p&gt;Chopin re-imagines agent planning as a real-time collaborative workspace where multiple human developers and an intelligent agent work together inside the same document:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shared context:&lt;/strong&gt; The planning canvas is connected directly to the repository and open to the entire team. Multiple engineers can chat, write requirements, and refine specifications simultaneously.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proactive agent research:&lt;/strong&gt; Rather than waiting passively for instructions, the Chopin agent reads the team's discussion and anticipates needs. When Martin and Maggie discussed using Bluetooth versus Wi-Fi for their multiplayer badge game, Chopin proactively researched Bluetooth LE range limitations, synthesized the technical trade-offs, and generated inline dataflow diagrams explaining how synchronization would work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual prototypes over text walls:&lt;/strong&gt; When debating user interfaces, Chopin generated interactive, clickable mockups showing different states (such as an animated landscape game view versus a large accessible counter). Engineers can click through states before any production code is written.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decision cards with attribution:&lt;/strong&gt; The demo captured choices as decision cards. When Maggie or Martin selected an option, the document recorded who made the decision and when. If a developer asks two months later why a specific approach was chosen, the record gives them someone to ask and the surrounding planning context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct conversion to tasks:&lt;/strong&gt; Once the team agrees on the plan, Chopin breaks the work into modular implementation tasks and opens corresponding pull requests.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9dr651js4jyew5fdtprt.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9dr651js4jyew5fdtprt.jpg" alt="Chopin showing badge-sync research and a prototype with two step counters" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Maggie and Martin then tried the collaborative 10,000-step game on their badges. After moving closer together, the demo reached its shared goal—a suitably hands-on ending to a discussion about planning.&lt;/p&gt;

&lt;p&gt;For engineering teams, this approach feels far healthier than isolated agent prompting. It brings software planning back to what it should be: a collaborative team activity where agents assist research and visual prototyping, while humans make and own the decisions.&lt;/p&gt;
&lt;h2&gt;
  
  
  Software beyond tech: Science, low-level systems, and the next generation
&lt;/h2&gt;

&lt;p&gt;The keynote also explored how open-source practices are transforming disciplines outside traditional commercial software.&lt;/p&gt;

&lt;p&gt;Professional, scientific, and technical services have grown fivefold on GitHub, becoming the leading open-source growth sector outside of core tech. Research repositories have quietly become the verifiable paper trail behind published academic literature.&lt;/p&gt;

&lt;p&gt;To support researchers, GitHub highlighted native &lt;strong&gt;Citation support&lt;/strong&gt; (enabling standard academic citations directly from repositories) and &lt;strong&gt;ORCID ID integration&lt;/strong&gt; to link software artifacts to verified researcher identities.&lt;/p&gt;

&lt;p&gt;The GitHub Campus Program now spans nearly 2,500 academic institutions across 60 countries, supported by more than 100 active Campus Experts and 500 alumni helping students navigate git, AI tools, and open source.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9w3j9tm2hwp96cvku8n2.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9w3j9tm2hwp96cvku8n2.jpg" alt="Jocelyn Velarde discussing BCI robotics and low-level engineering" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Martin invited Jocelyn Velarde, a visiting undergraduate researcher at the UC Davis Neuroprosthetics Lab and an MLH coach, to share her path through open source.&lt;/p&gt;

&lt;p&gt;While still in high school in Monterrey, Mexico, Jocelyn cold-emailed Dr. Miriam Alanis at Tec de Monterrey and built an EEG-controlled brain-computer interface (BCI) robotic hand prosthetic using Linear Discriminant Analysis (LDA) machine learning.&lt;/p&gt;

&lt;p&gt;Today at UC Davis, she works on the "Brain to Voice" project, decoding neural signals to restore real-time speech for ALS patients. Her specific technical focus is low-level systems engineering across heterogeneous compute platforms to minimize audio streaming latency—work that requires deep hardware and systems understanding.&lt;/p&gt;

&lt;p&gt;Her advice to aspiring engineers was simple and practical: set clear goals, find supportive communities (such as Campus Experts and MLH), and do not hesitate to reach out to mentors directly.&lt;/p&gt;
&lt;h2&gt;
  
  
  The closing message: You write the story
&lt;/h2&gt;

&lt;p&gt;The final keynote ended with characteristic hacker playfulness. Martin placed his badge inside a plushie "Ship It" squirrel, trained gesture-recognition models on stage to detect throws and drops, and invited the audience into a live airtime contest, tossing Mona, Copilot, and Octocat plushies across the hall while the badges tracked airtime.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fky0pyhlikaskhvtbmxo9.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fky0pyhlikaskhvtbmxo9.jpg" alt="The closing keynote finale with plushie gesture recognition and audience competition" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Underneath the fun was a serious message for developers:&lt;/p&gt;

&lt;p&gt;Martin said AI makes it easier to create applications we previously lacked the time or skills to build. He asked us to broaden the circle of people we learn from, push the boundaries of what we know, and learn by doing. Creativity, judgment, and taste remain ours to bring.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"In this new era, AI doesn't write the story. You write the story."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  What I am taking back from Universe
&lt;/h2&gt;

&lt;p&gt;Jay Parikh opened Universe with the claim that all the software written so far might amount to just 1% of what humans and agents build over the next five years. Martin Woodward closed it by reminding us that without human judgment and heart, building faster just means accumulating unverified slop.&lt;/p&gt;

&lt;p&gt;Across both keynotes, the real narrative of Universe 2026 was not that agents are replacing developers. It was that GitHub is evolving every layer of the delivery pipeline to make agent-assisted engineering manageable, accountable, and sustainable.&lt;/p&gt;

&lt;p&gt;Day 1 provided the technical machinery:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copilot Projects to coordinate features across repositories.&lt;/li&gt;
&lt;li&gt;Copilot Fusion to handle multi-model routing across reasoning, general, and fast tiers.&lt;/li&gt;
&lt;li&gt;Rebuilding Git from the ground up, with a reported test sustaining 7,200 writes per second from 5,000 agents targeting one repository.&lt;/li&gt;
&lt;li&gt;Automations and Code Review Max to catch regressions and security vulnerabilities before shipping.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Day 2 provided the operational principles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Protecting maintainers from automated spam with WIP limits and interaction gates.&lt;/li&gt;
&lt;li&gt;Recognizing that DevOps and CI/CD maturity are strict prerequisites before deploying agents at scale.&lt;/li&gt;
&lt;li&gt;Moving planning out of private terminal questionnaires into multiplayer collaborative canvases like Chopin.&lt;/li&gt;
&lt;li&gt;Anchoring delivery in tight verification loops and human accountability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For my own teams and clients, I am taking back a clear implementation checklist:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pilot a multi-repo feature with Copilot Projects:&lt;/strong&gt; Choose a bounded feature spanning two or three services. Set explicit AI credit budgets and tool access, and follow the work from initial issue through review and merge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Harden the verification gate before scaling agents:&lt;/strong&gt; If a repository does not have automated linting, appropriate unit and integration tests, and clean CI checks, do not give an agent merge permissions. The OpenPrinting example reinforced why operational maintenance belongs alongside the agent rollout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turn on repository guardrails:&lt;/strong&gt; Configure appropriate limits for outside contributors, with exemptions that fit the community, to encourage focused contributions and prevent maintainer fatigue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bring planning into the open:&lt;/strong&gt; Stop letting engineers plan complex features in private 1-on-1 LLM prompts. Use shared specs, visual mockups, and explicit decision records so the whole team understands what is being built and why.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Tools like Fusion, Automations, and Chopin will continue to evolve. But the fundamental responsibility of an engineering lead remains unchanged: define what good looks like, establish the safety rails, and ensure that every decision that reaches production can be explained by a human.&lt;/p&gt;



&lt;p&gt;Thanks for reading this post, I hope you found it interesting!&lt;/p&gt;

&lt;p&gt;Feel free to follow me to get notified when new articles are out 🙂&lt;/p&gt;


&lt;div class="ltag__user ltag__user__id__79932"&gt;
    &lt;a href="/kasuken" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=150,height=150,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F79932%2Fbe7bcd76-1f69-4336-9155-6017aebb2c53.png" alt="kasuken image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/kasuken"&gt;Emanuele Bartolesi&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/kasuken"&gt;🏆 Microsoft MVP (12x) &amp;amp; GitHub Star (4x) ⭐
👨‍🎓 LinkedIn Technical Instructor 🎓&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>github</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>I Open-Sourced My Web Developer Agent. AI Is Supercharging Open Source.</title>
      <dc:creator>Domenico Tenace</dc:creator>
      <pubDate>Mon, 02 Nov 2026 16:52:29 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/i-open-sourced-my-web-developer-agent-ai-is-supercharging-open-source-379o</link>
      <guid>https://dev.to/playfulprogramming/i-open-sourced-my-web-developer-agent-ai-is-supercharging-open-source-379o</guid>
      <description>&lt;p&gt;I finally did it: my web developer agent is public on GitHub. I named the repo &lt;code&gt;web-dev-agent&lt;/code&gt;, and it lives at &lt;a href="https://github.com/Domenico-Tenace-Open-Labs/web-dev-agent" rel="noopener noreferrer"&gt;github.com/Domenico-Tenace-Open-Labs/web-dev-agent&lt;/a&gt;, for the first time since I started building it, people other than me can run it, break it, and tell me what it should do next.&lt;/p&gt;

&lt;p&gt;Making that call felt less like a release and more like surrendering control, which is exactly why I made it. Every week I read about agents, benchmarks, and new harnesses, and almost all of it is built in the open now. Keeping mine closed would have meant hiding in exactly the place the ecosystem has decided things get better: public repositories, public issues, public pull requests.&lt;/p&gt;

&lt;p&gt;Turns out I picked a decent moment for it. The data from the last twelve months shows something that would have sounded like science fiction in 2023: AI agents are now a structural force in how open source grows, not a nice-to-have at the edge of the dashboard. Here is what I shipped, and here is the evidence that the open source flywheel has a new engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I shipped, and where it sits
&lt;/h2&gt;

&lt;p&gt;A "web developer agent" sounds either trivial or terrifying, and it's neither. Mine is an &lt;a href="https://opencode.ai" rel="noopener noreferrer"&gt;OpenCode&lt;/a&gt; agent configuration: a primary agent, &lt;code&gt;web-developer&lt;/code&gt;, that works like a senior frontend developer for Astro, Vue 3 (+ Nuxt), Vite and strict TypeScript, paired with six specialist subagents (code review, SEO, UI/UX, accessibility, documentation, security), nine skills, two slash commands and three rule sets. It scaffolds a project with &lt;code&gt;/scaffold-frontend-project&lt;/code&gt; (pnpm only, strict TypeScript), injects clean-code, Astro and Bulma conventions into every session, and can audit the result before it ships.&lt;/p&gt;

&lt;p&gt;The point isn't magic, it's leverage. The agent turns the tedious loop of &lt;em&gt;scaffold → implement → check → fix&lt;/em&gt; into something a developer supervises instead of performs. You stay on the decisions: what to build, what's good enough, what ships. The agent does the walking, and because every write is approval-gated (&lt;code&gt;effect: ask&lt;/code&gt;) and the workspace is sandboxed (&lt;code&gt;external_directory: deny&lt;/code&gt;), "supervise" is the operating mode, not a suggestion.&lt;/p&gt;

&lt;p&gt;I'm not entering an empty market. The most striking thing about open source in 2026 is that the agents themselves went open source. &lt;code&gt;opencode&lt;/code&gt;, the model-agnostic agent from Anomaly, passed 200,000 stars by late 2026 and sits near the top of GitHub's fastest-growing &lt;em&gt;and&lt;/em&gt; most-engaged project lists. OpenAI's Codex CLI and Google's Gemini CLI are open too, with six-figure star counts. Aider, Cline, OpenHands, the list goes on. A year and a half ago the category was a handful of tools, most of them closed. Today it's a category where the reference implementations are public, MIT- and Apache-licensed code that anyone can fork. Mine plugs into that same harness, it's an OpenCode configuration, MIT-licensed, and model-agnostic.&lt;/p&gt;

&lt;p&gt;Publishing into that ecosystem is the whole point of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open source is growing faster than ever (and AI is part of the why)
&lt;/h2&gt;

&lt;p&gt;Start with the raw numbers, because they're the least controversial thing I'll say today. According to GitHub's Octoverse report published October 28, 2026, the twelve months ending September 2026 were the biggest in GitHub's history:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric (12 months ending Sep 30)&lt;/th&gt;
&lt;th&gt;2024&lt;/th&gt;
&lt;th&gt;2025&lt;/th&gt;
&lt;th&gt;2026&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pushes&lt;/td&gt;
&lt;td&gt;5.2B&lt;/td&gt;
&lt;td&gt;6.7B&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;17.9B&lt;/strong&gt; (more than 2024 + 2025 combined)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commits&lt;/td&gt;
&lt;td&gt;10.0B&lt;/td&gt;
&lt;td&gt;12.9B&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;34.3B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merged pull requests&lt;/td&gt;
&lt;td&gt;402M&lt;/td&gt;
&lt;td&gt;538M&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1.3B&lt;/strong&gt; (~3.5M/day)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Actions jobs&lt;/td&gt;
&lt;td&gt;8.0B&lt;/td&gt;
&lt;td&gt;14.0B&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;45.4B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Open source holds up its end of that growth: contributions to public repositories reached &lt;strong&gt;2.5 billion in 2026, more than double 2025's 1.12 billion&lt;/strong&gt;. The developer base keeps expanding too: &lt;strong&gt;225 million+ developers&lt;/strong&gt; on GitHub today, 36 million of them joining in 2025 alone, more than one new developer every second. March 2025 also set a record: 255,000 first-time contributors in a single month.&lt;/p&gt;

&lt;p&gt;AI isn't the whole story behind those numbers, but it's woven into them. GitHub reports that about 80% of new developers start using Copilot within their first week, and more than a million public repositories now use an LLM SDK, up 178% year over year (both Octoverse 2025). The barrier to &lt;em&gt;contributing code&lt;/em&gt; has never been lower: the first draft is cheap now, and the bottleneck has moved to review, which is exactly where a human should be.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agents have become the new contributors
&lt;/h2&gt;

&lt;p&gt;The step change in 2026 is that agents stopped being assistants and started being &lt;em&gt;contributors&lt;/em&gt;, with a GitHub identity, a branch, and a pull request.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;More than 40% of pull requests on GitHub now start from an agent's first draft&lt;/strong&gt; (September 2026), up from 17% in March. That's &lt;strong&gt;110 million agent-started PRs in September alone&lt;/strong&gt;, roughly six times March's volume. GitHub notes its detection is a floor, not a ceiling.&lt;/li&gt;
&lt;li&gt;Pull requests started by agents change multiple files more often (87% vs 79%) and, by September, merged within two weeks at a higher rate than the non-agent baseline (90% vs 86%).&lt;/li&gt;
&lt;li&gt;GitHub's Copilot coding agent alone contributes &lt;strong&gt;~1.2 million pull requests per month&lt;/strong&gt; (GitHub, Oct 15, 2025), and paid Copilot subscribers grew 75% year over year to 4.7 million by January 2026.&lt;/li&gt;
&lt;li&gt;Independent academic work agrees. The AIDev dataset measured &lt;strong&gt;~930,000 agent-authored PRs across ~116,000 repositories&lt;/strong&gt; between December 2024 and July 2025, spanning Codex, Devin, Copilot, Cursor, and Claude Code (arXiv:2606.28235).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's also a striking correlation in this year's report: among repositories created in 2026, the average repo &lt;strong&gt;with an agent configuration file logged eight times as many commits&lt;/strong&gt; as the average new repo without one. Correlation, not causation, but when a nearly-free text file (an &lt;code&gt;AGENTS.md&lt;/code&gt; that tells the agent how your project works) coincides with an 8x difference in activity, it's worth treating seriously.&lt;/p&gt;

&lt;p&gt;The language data tells the same story from another angle. The three fastest-growing language communities, Rust (+162% year over year), TypeScript (+134%), Python (+118%), are the ones with the highest share of AI-involved merged PRs (40 to 44%). The languages where agents show up the most are the ones where the communities are growing the fastest.&lt;/p&gt;

&lt;p&gt;This is the ecosystem I just published into. An open-source agent is no longer a hobby artifact; it's infrastructure that other people's repositories read, run, and learn from.&lt;/p&gt;

&lt;h2&gt;
  
  
  The open source economy is finally getting some help
&lt;/h2&gt;

&lt;p&gt;For years, the open source conversation has been about a mismatch: more consumers than contributors, and a shrinking pool of volunteer maintainers. The numbers are grim. Tidelift's 2024 survey found &lt;strong&gt;60% of maintainers are unpaid&lt;/strong&gt;, and roughly &lt;strong&gt;60% have quit or seriously considered quitting&lt;/strong&gt;. The maintainer base is aging, maintainers aged 46 to 65 have doubled since 2021, while contributors under 26 dropped from 25% to 10% of the total (via GitHub, Aug 2025, citing Tidelift 2024).&lt;/p&gt;

&lt;p&gt;AI can't fix funding by itself, but 2026 is the first year the economy around open source has real money moving into it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sovereign Tech Fund&lt;/strong&gt; (Germany): €61.6M commissioned across 2022 to 2026 for critical OSS, funding curl, OpenSSH, the OpenSSF Scorecard, and most recently €1.285M for KDE in May 2026.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Secure Open Source Fund&lt;/strong&gt;: $1.25M across 125 projects at launch (November 2024), with partner commitments since.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Sponsors&lt;/strong&gt;: $60M+ paid out cumulatively (November 2024 figures).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EU Sovereign Tech Fund pilot&lt;/strong&gt;: launched in 2026.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fewer young hands, more code, and new institutional money: the gap between supply and demand of maintainer time is exactly where agents plug in. They won't review as well as a senior maintainer, but they can triage issues, update dependencies (developers merged 30M+ Dependabot PRs in 2026, about double 2025's pace, Octoverse 2026), fix boilerplate, and keep the repository alive while the human does the parts that need judgment. Published under MIT, an agent like mine is a tool any maintainer can run at zero license cost, installation is a single &lt;code&gt;cp -r&lt;/code&gt; into your OpenCode config directory. That's the argument for this repo, and it's the argument for open-sourcing agents generally.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest counterweight
&lt;/h2&gt;

&lt;p&gt;I'd be lying if I told you the story is only upward momentum. The last couple of years produced some sobering findings.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;METR's randomized controlled trial&lt;/strong&gt; (July 2025): 16 experienced OSS maintainers working on 246 real issues in mature repositories were &lt;strong&gt;19% slower with AI tools, while believing they were 20% faster&lt;/strong&gt;. Perception and reality are diverging at the exact moment everyone is making tooling decisions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stack Overflow's 2025 survey&lt;/strong&gt;: 84% of developers use or plan to use AI tools, yet &lt;strong&gt;46% distrust the accuracy&lt;/strong&gt; of the output and only 3% say they highly trust it. The top frustration: results that are "almost right."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: broken access control overtook injection as the most common CodeQL alert, &lt;strong&gt;151,000+ repositories, +172% year over year&lt;/strong&gt; (Octoverse 2025), and GitHub itself ties part of that surge to AI-generated scaffolds that skip critical authorization checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainers remain skeptical of AI contributions&lt;/strong&gt;: 64% of maintainers said they'd be less likely to accept a contribution if they knew it was AI-generated (Tidelift 2024).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why the Octoverse line "review is how developers shape what comes back" isn't marketing, it's the actual operating model. Agents generate volume; humans curate quality. Speed without review is how you get slop in, security holes in, and maintainers out.&lt;/p&gt;

&lt;p&gt;I built my agent with that tension in mind. Its job isn't to maximize lines generated, it's to reduce the &lt;em&gt;review load per merged change&lt;/em&gt;: conventions enforced while writing mean fewer style nits in review, and a specialist subagent for each domain, accessibility, SEO, security, UI/UX, code review, means whole categories of problems get caught before a human ever looks at the diff. If the agent makes my reviews shorter, it's working.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for you
&lt;/h2&gt;

&lt;p&gt;Whether you're a maintainer, a contributor, or someone about to be both, here's a concrete do-list:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add an agent configuration file to your repo.&lt;/strong&gt; An &lt;code&gt;AGENTS.md&lt;/code&gt; that documents structure, conventions, and commands is nearly free, and it's the file that coincides with the 8x commit difference in this year's data. It also helps every human who joins later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat agent-authored PRs as review-first work.&lt;/strong&gt; They merge fast (90% within two weeks); you're the gate. Run CI, require tests, and wire up Dependabot and a security scanner (94% of the top 50 projects use OpenSSF Scorecard, Octoverse 2025).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automate the maintenance backlog, not the architecture.&lt;/strong&gt; Agents are genuinely good at triage, dependency updates, and boilerplate, the exact work that burns maintainers out. Keep humans on design decisions and interface contracts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you build agents, ship them open.&lt;/strong&gt; Public agents get public benchmarks, public bug reports, and public improvements, and every README and config file doubles as training signal for the next generation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure your own loop.&lt;/strong&gt; METR showed that beliefs about AI speed are unreliable. Track review time and revert rates; keep what helps, drop what doesn't.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The flywheel
&lt;/h2&gt;

&lt;p&gt;Here's the loop I'm betting on: open source gives AI agents their training data, their benchmark suites, and their adoption channels. The agents, in turn, give open source more contributors, more merged PRs, and more velocity. Each open-sourced agent adds a gear to that machine.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;web-dev-agent&lt;/code&gt; is mine. It's at &lt;a href="https://github.com/Domenico-Tenace-Open-Labs/web-dev-agent" rel="noopener noreferrer"&gt;github.com/Domenico-Tenace-Open-Labs/web-dev-agent&lt;/a&gt;, clone it, drop it into &lt;code&gt;~/.config/opencode&lt;/code&gt;, and try it on a real project. Open an issue when it does something dumb (it will), and fork it if you want to make it yours. And whether or not you touch my repo: if you've been building something useful with an agent, consider doing what I finally did. Build it in the open, and let the ecosystem do the rest.&lt;/p&gt;




&lt;p&gt;Hi 👋🏻&lt;br&gt;
My name is Domenico, software developer passionate of Open Source, I write article about it for share my knowledge and experience.&lt;br&gt;
Don't forget to visit my social links to discover my links, check out Domenico Tenace Open Labs for my open source projects and subscribe to my newsletter! 🫰🏻&lt;/p&gt;

&lt;p&gt;🌲 Links: &lt;a href="https://links.domenicotenace.dev/" rel="noopener noreferrer"&gt;https://links.domenicotenace.dev/&lt;/a&gt;&lt;br&gt;
🐙 Domenico Tenace Open Labs: &lt;a href="https://github.com/Domenico-Tenace-Open-Labs" rel="noopener noreferrer"&gt;https://github.com/Domenico-Tenace-Open-Labs&lt;/a&gt;&lt;br&gt;
📰 Newsletter: &lt;a href="https://domenicotenace.substack.com/subscribe" rel="noopener noreferrer"&gt;https://domenicotenace.substack.com/subscribe&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you like my content or want to support my work, you can support me with a small donation. I would be grateful 🥹&lt;/p&gt;

&lt;p&gt;☕ Buy me a Coffee: &lt;a href="https://buymeacoffee.com/domenicotenace" rel="noopener noreferrer"&gt;https://buymeacoffee.com/domenicotenace&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.blog/news-insights/octoverse/octoverse-commits-pushes-reach-new-highs-in-2026-as-developers-build-with-agents/" rel="noopener noreferrer"&gt;GitHub: Octoverse 2026, Commits, pushes reach new highs in 2026 as developers build with agents (Oct 28, 2026)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.blog/news-insights/octoverse/octoverse-a-new-developer-joins-github-every-second-as-ai-leads-typescript-to-1/" rel="noopener noreferrer"&gt;GitHub: Octoverse 2025, A new developer joins GitHub every second (Oct 28, 2025)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.blog/ai-and-ml/github-copilot/copilot-faster-smarter-and-built-for-how-you-work-now" rel="noopener noreferrer"&gt;GitHub: Copilot, Faster, smarter, and built for how you work now (Oct 15, 2025)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.blog/open-source/maintainers/who-will-maintain-the-future-rethinking-open-source-leadership-for-a-new-generation" rel="noopener noreferrer"&gt;GitHub: Who will maintain the future? (Aug 20, 2025)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.blog/news-insights/company-news/announcing-github-secure-open-source-fund/" rel="noopener noreferrer"&gt;GitHub: Announcing GitHub Secure Open Source Fund (Nov 19, 2024)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://survey.stackoverflow.co/2025/ai" rel="noopener noreferrer"&gt;Stack Overflow Developer Survey 2025, AI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study" rel="noopener noreferrer"&gt;METR: Early 2025 AI experiences of experienced open-source developers (arXiv:2507.09089)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://arxiv.org/abs/2606.28235" rel="noopener noreferrer"&gt;AIDev: Measuring ecosystem-level risk in AI-native software (arXiv:2606.28235)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sdtimes.com/open-source/the-state-of-open-source-maintainers/" rel="noopener noreferrer"&gt;Tidelift: State of the Open Source Maintainer 2024 (via SD Times, Sep 17, 2024)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.sovereign.tech/" rel="noopener noreferrer"&gt;Sovereign Tech Agency, public reports (2022 to 2026)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kde.org/announcements/sovereign-tech-fund-invests-kde" rel="noopener noreferrer"&gt;KDE: Sovereign Tech Fund invests in KDE (May 13, 2026)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/open-source" rel="noopener noreferrer"&gt;GitHub Open Source programs (Sponsors + Secure Open Source Fund)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/anomalyco/opencode" rel="noopener noreferrer"&gt;opencode, open-source AI coding agent (anomalyco/opencode)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.blog/changelog/2025-09-25-copilot-coding-agent-is-now-generally-available/" rel="noopener noreferrer"&gt;GitHub Changelog: Copilot coding agent is now generally available (Sep 25, 2025)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techcrunch.com/2026/01/29/satya-nadella-insists-people-are-using-microsofts-copilot-ai-a-lot" rel="noopener noreferrer"&gt;Microsoft FY26 Q2 earnings: GitHub Copilot 4.7M paid subscribers (via TechCrunch, Jan 29, 2026)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>github</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Astro Wellness: The First Project Built Start to Finish by My Agent Team 🏋️</title>
      <dc:creator>Domenico Tenace</dc:creator>
      <pubDate>Wed, 28 Oct 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/astro-wellness-the-first-project-built-start-to-finish-by-my-agent-team-3kkn</link>
      <guid>https://dev.to/playfulprogramming/astro-wellness-the-first-project-built-start-to-finish-by-my-agent-team-3kkn</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Hey everyone 👋&lt;/p&gt;

&lt;p&gt;A few days ago I wrote about the agent team I built for OpenCode: a web-developer primary agent and four subagents for UX/UI, code review, SEO and documentation. The obvious question was, does it actually hold up on a real project?&lt;/p&gt;

&lt;p&gt;Meet &lt;a href="https://github.com/DomeT99/astro-wellness" rel="noopener noreferrer"&gt;&lt;strong&gt;Astro Wellness&lt;/strong&gt;&lt;/a&gt;, a modern, dark and energetic gym template built with Astro and Bulma. It's the first project the whole team took from empty folder to finished, and it's now listed among the official Astro templates: &lt;a href="https://portal.astro.build/themes/astro-wellness/" rel="noopener noreferrer"&gt;astro-wellness on the Astro portal&lt;/a&gt;. I'm still a bit surprised typing that.&lt;/p&gt;

&lt;p&gt;Let's dive in! 🤙&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Astro Wellness? 🤔
&lt;/h2&gt;

&lt;p&gt;It's a template for a gym, studio or training brand, ready to adopt and customise. It ships with everything you need to go live:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pages: home (hero, stats, services, testimonials), about, services with pricing plans, blog, contact and a custom 404&lt;/li&gt;
&lt;li&gt;Dark and light mode through Bulma's themes, persisted in &lt;code&gt;localStorage&lt;/code&gt;, following the OS preference on the first visit and applied before first paint&lt;/li&gt;
&lt;li&gt;SPA style page transitions with Astro's &lt;code&gt;ClientRouter&lt;/code&gt;, respecting &lt;code&gt;prefers-reduced-motion&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A Markdown blog on content collections with a typed schema and an auto generated RSS feed&lt;/li&gt;
&lt;li&gt;SEO ready: per page title and description, canonical URLs, Open Graph, JSON-LD (&lt;code&gt;HealthClub&lt;/code&gt; on the home page, &lt;code&gt;BlogPosting&lt;/code&gt; on posts), sitemap and &lt;code&gt;robots.txt&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Strict TypeScript, typed props on every component, and Bulma imported module by module so nothing unused ships
The look is a dark theme with an orange accent plus an adaptive light theme, and every design token lives in two clearly documented places, so re-theming the whole site is a quick job. It's MIT licensed, pnpm only, on Astro 7, with a live demo on Netlify.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How the Team Worked on It 👥
&lt;/h2&gt;

&lt;p&gt;Here's the part I wanted to test. If you missed the previous article, the rule in my setup is simple: nobody edits anything in the first pass. Every agent proposes first, I approve, and only then does it apply changes, with each write still behind an &lt;code&gt;ask&lt;/code&gt; gate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;web-developer&lt;/strong&gt; led the build. It explored the project, proposed the structure (pages, layout, data files, components), and implemented it after my OK, then verified with the type check and the build. The strict TypeScript setup and the clean &lt;code&gt;src/data&lt;/code&gt; folder for services, team, pricing and testimonials come from its rules about typed props and keeping pages free of complex logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ux-ui-expert&lt;/strong&gt; handled the design system: the dark and orange palette, the light theme flipping every token automatically, responsive behaviour at Bulma's breakpoints, and the details like focus states and reduced motion support. Its rule of using the framework's primitives before custom CSS is why the project styles stay small and prefixed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;seo-expert&lt;/strong&gt; audited the head, the structured data, the sitemap, the robots file and the social share image. It's also the reason the README tells you to set your real domain before deploying, since the agent flagged it as something only the template user can decide.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;code-reviewer&lt;/strong&gt; went over the changes with its checklist and reported by severity, with concrete fixes I could approve one by one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;documentation-updater&lt;/strong&gt; kept the repo honest: README, CHANGELOG, CONTRIBUTING, SECURITY and AGENTS.md. Its rule of never documenting what it can't verify against the code is exactly what you want from docs that will be read by strangers.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Worked 👍
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The approval gate paid off.&lt;/strong&gt; On a template that other people will copy, I didn't want surprise edits. Proposals first meant I made the design and architecture calls, and the agents did the heavy lifting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Specialists beat one big prompt.&lt;/strong&gt; Having the SEO and docs passes happen as separate reviews, by agents with their own skill and checklist, made the final result more complete than anything I'd have remembered to ask for at the end of a long conversation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consistency.&lt;/strong&gt; Same structure rules, same naming, same quality bar across the whole codebase, because the conventions live in the agent files and not in my head.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's reusable.&lt;/strong&gt; The same team can now take on the next template without me rebuilding the workflow.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Was Harder 😬
&lt;/h2&gt;

&lt;p&gt;I won't pretend it was effortless. The approval flow means a lot of reading and clicking, and for small tasks it's slower than just doing it. Delegation costs tokens and time. And the agents don't replace judgment: decisions like the visual direction, the tone of the sample content and which sections a gym site really needs were mine.&lt;/p&gt;

&lt;p&gt;The other lesson is that a template has a different bar than a personal site. People will fork it, so documentation, accessibility and clean defaults matter more than usual, and that's where having dedicated agents for those jobs helped the most.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It, Fork It, Break It 🚀
&lt;/h2&gt;

&lt;p&gt;Astro Wellness is open source and free to use for your own gym, studio or training brand:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You'll need Node 22.22 or newer and pnpm 10+. Edit &lt;code&gt;src/data/site.ts&lt;/code&gt; for your identity, swap the sample content, set your real domain in &lt;code&gt;astro.config.mjs&lt;/code&gt;, and deploy the &lt;code&gt;dist/&lt;/code&gt; folder anywhere static. The live demo is at astro-wellness.netlify.app, and issues and PRs are welcome.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts 🌟
&lt;/h2&gt;

&lt;p&gt;Having a template accepted among the official Astro ones was a great moment, but the bigger win for me is the proof that the agent setup works on a real deliverable, not just on demos. One primary agent, four specialists, a lot of small approvals, and a project I'm proud to put my name on.&lt;/p&gt;

&lt;p&gt;If you try the template, or build your own agent team for OpenCode, tell me how it goes.&lt;/p&gt;

&lt;p&gt;Happy coding! ✨&lt;/p&gt;




&lt;p&gt;Hi 👋🏻&lt;br&gt;
My name is Domenico, software developer passionate of Open Source, I write article about it for share my knowledge and experience.&lt;br&gt;
Don't forget to visit my social links to discover my links, check out Domenico Tenace Open Labs for my open source projects and subscribe to my newsletter! 🫰🏻&lt;/p&gt;

&lt;p&gt;🌲 Links: &lt;a href="https://links.domenicotenace.dev/" rel="noopener noreferrer"&gt;https://links.domenicotenace.dev/&lt;/a&gt;&lt;br&gt;
🐙 Domenico Tenace Open Labs: &lt;a href="https://github.com/Domenico-Tenace-Open-Labs" rel="noopener noreferrer"&gt;https://github.com/Domenico-Tenace-Open-Labs&lt;/a&gt;&lt;br&gt;
📰 Newsletter: &lt;a href="https://domenicotenace.substack.com/subscribe" rel="noopener noreferrer"&gt;https://domenicotenace.substack.com/subscribe&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you like my content or want to support my work, you can support me with a small donation. I would be grateful 🥹&lt;/p&gt;

&lt;p&gt;☕ Buy me a Coffee: &lt;a href="https://buymeacoffee.com/domenicotenace" rel="noopener noreferrer"&gt;https://buymeacoffee.com/domenicotenace&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>astro</category>
      <category>javascript</category>
    </item>
    <item>
      <title>One Dev, Four Specialists: My Agent Team for OpenCode 🧑‍💻</title>
      <dc:creator>Domenico Tenace</dc:creator>
      <pubDate>Thu, 22 Oct 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/one-dev-four-specialists-my-agent-team-for-opencode-5f31</link>
      <guid>https://dev.to/playfulprogramming/one-dev-four-specialists-my-agent-team-for-opencode-5f31</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Hey everyone 👋&lt;/p&gt;

&lt;p&gt;For a long time my OpenCode workflow was one agent, one giant conversation, and me asking it to code, then review, then fix the SEO, then update the docs. It works, but the context gets messy, and the agent that just wrote the code is the worst one to judge it.&lt;/p&gt;

&lt;p&gt;So I rebuilt the setup as a small team: one primary agent that builds, and four subagents it delegates to. Each one has its own role, its own boundaries and its own dedicated skill. Every agent is a single markdown file, so the whole thing is easy to read, tweak and version.&lt;/p&gt;

&lt;p&gt;Let's dive in! 🤙&lt;/p&gt;




&lt;h2&gt;
  
  
  The Team 👥
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;web-developer&lt;/strong&gt; is the primary agent, a senior web dev for Astro, Vue, TypeScript, Vite, Bulma and Tailwind. It's the one I talk to, and it owns the implementation.&lt;/p&gt;

&lt;p&gt;Then four subagents, all launched by the web-developer:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ux-ui-expert&lt;/strong&gt; designs and reviews UI, UX and graphics, always respecting the project's design system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;code-reviewer&lt;/strong&gt; reviews changes, a feature or the whole codebase, and reports issues by severity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;seo-expert&lt;/strong&gt; audits on-page, technical and content SEO.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;documentation-updater&lt;/strong&gt; keeps README, changelog and guides in sync with the code.&lt;/p&gt;

&lt;p&gt;The flow is natural: I describe a feature, the web-developer proposes a plan, builds it after my OK, then calls the right specialists before we call it done.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Golden Rule: Propose First, Apply After Approval ✋
&lt;/h2&gt;

&lt;p&gt;This is the heart of the setup, and it's the same in every agent. Nobody edits files in the first pass. They present a concrete proposal (files to change, what changes, why) and wait for my explicit approval. Then, when they do apply something, every single write is gated by an &lt;code&gt;ask&lt;/code&gt; permission, so I confirm it again.&lt;/p&gt;

&lt;p&gt;The relay matters too. When a subagent proposes fixes, the web-developer passes the proposal to me instead of applying it on its own. Two checkpoints, no silent changes, and if something unexpected shows up mid-application, the instruction is to stop and report instead of improvising.&lt;/p&gt;




&lt;h2&gt;
  
  
  Anatomy of an Agent File 🔧
&lt;/h2&gt;

&lt;p&gt;OpenCode has primary agents (the ones you talk to, switched with Tab) and subagents (specialists called by a primary agent, or by you with an @ mention). Agents live in &lt;code&gt;.opencode/agents/&lt;/code&gt; and the file name becomes the agent name. Here's the frontmatter of my code-reviewer, trimmed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Expert code reviewer for the current project, proposes fixes and applies them only after approval.&lt;/span&gt;
&lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;subagent&lt;/span&gt;
&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#f59e0b"&lt;/span&gt;
&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# load and follow the dedicated skill&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;skill&lt;/span&gt;
    &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;code-review&lt;/span&gt;
    &lt;span class="na"&gt;effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;allow&lt;/span&gt;
  &lt;span class="c1"&gt;# approval gate: every write asks for confirmation&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;edit&lt;/span&gt;
    &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
    &lt;span class="na"&gt;effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ask&lt;/span&gt;
  &lt;span class="c1"&gt;# web access for docs and known issues&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;webfetch&lt;/span&gt;
    &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
    &lt;span class="na"&gt;effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;allow&lt;/span&gt;
  &lt;span class="c1"&gt;# connected MCP servers, when necessary&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*_*"&lt;/span&gt;
    &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
    &lt;span class="na"&gt;effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;allow&lt;/span&gt;
  &lt;span class="c1"&gt;# sandbox: nothing outside the workspace&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;external_directory&lt;/span&gt;
    &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
    &lt;span class="na"&gt;effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deny&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details I like. First, the sandbox rule goes last on purpose: for &lt;code&gt;external_directory&lt;/code&gt; the last matching rule wins, so that deny overrides every allow above it, including the MCP one. Agents can't read or write outside the current project, not even through the terminal. Second, web access and MCP servers are always allowed, but the prompt tells the agent not to overuse them and to try project knowledge first.&lt;/p&gt;

&lt;p&gt;Below the frontmatter comes the prompt: role, process, operating boundaries and the report format.&lt;/p&gt;




&lt;h2&gt;
  
  
  One Dedicated Skill Per Subagent 🎯
&lt;/h2&gt;

&lt;p&gt;Each subagent has a global skill it must load first, before doing anything else:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;code-reviewer loads &lt;strong&gt;code-review&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;seo-expert loads &lt;strong&gt;seo-audit&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;ux-ui-expert loads &lt;strong&gt;web-design&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;documentation-updater loads &lt;strong&gt;update-documentation&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The split is simple. The agent file says who the agent is and what it's allowed to do. The skill holds the actual checklist and workflow, loaded on demand so it doesn't clutter the context when it isn't needed. For example, the documentation-updater follows a fixed path: establish repository context, understand the change, inspect the implementation, search for documentation drift, plan, edit, validate, review the diff.&lt;/p&gt;

&lt;p&gt;Because the skills are global, I can reuse them across every project and improve them in one place.&lt;/p&gt;




&lt;h2&gt;
  
  
  How the Delegation Works 🧭
&lt;/h2&gt;

&lt;p&gt;The web-developer prompt has a dedicated section for each specialist, saying exactly when to call it: code reviews, SEO audits and pre-ship checks, UI work and responsive fixes, documentation and changelog updates. It's explicit about what the specialist will bring back, too.&lt;/p&gt;

&lt;p&gt;The reports are structured and that's what makes them useful:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;code-reviewer&lt;/strong&gt; returns findings ordered from critical to info, each with &lt;code&gt;path:line&lt;/code&gt;, why it matters and a concrete fix. It also notes what is good, because a review that is only a list of problems isn't balanced.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;seo-expert&lt;/strong&gt; returns findings by severity grouped as on-page, technical and content, with before and after snippets, a separate list of quick wins, and the items that need input from me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ux-ui-expert&lt;/strong&gt; returns design proposals ranked by impact, the files to edit, asset recommendations and anything it can't decide without brand facts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;documentation-updater&lt;/strong&gt; returns a plan first: files to modify, files to add, files to leave alone and why. It only documents what it can verify against the implementation.&lt;/p&gt;

&lt;p&gt;And one rule I really like, shared by all of them: if a fact only I know (brand info, copy, a release version), flag it and ask, never invent it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Beats a Single Agent 💡
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Fresh eyes.&lt;/strong&gt; The agent that wrote the code isn't the one reviewing it. That alone catches more than any "now review what you just wrote" prompt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cleaner context.&lt;/strong&gt; Each subagent works in its own child session, so the review reasoning doesn't pollute the building conversation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Predictable behavior.&lt;/strong&gt; Same approval gate, same sandbox, same reporting style, across the whole team.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The routine is built in.&lt;/strong&gt; Review, SEO, design and docs checks are part of the setup, not something I have to remember at the end of every task.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Downsides 😬
&lt;/h2&gt;

&lt;p&gt;It's not free magic. More agents means more tokens and more waiting, so for a one line fix it's overkill and I simply stay in the web-developer. The approval flow adds clicks, and with two checkpoints it can feel heavy on small tasks, though I prefer that to surprise edits. Subagent descriptions need care too, because similar descriptions make the primary agent pick the wrong one. And a review only helps if you actually read it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts 🌟
&lt;/h2&gt;

&lt;p&gt;Splitting one do-everything agent into a team looked like a small change, but it changed how I work. I stopped micromanaging prompts at the end of every task, and I got a setup where nothing touches my code without a proposal and a yes from me.&lt;/p&gt;

&lt;p&gt;If you use OpenCode, start small: one primary agent plus one reviewer with the approval gate. Once you see the difference, you'll want the rest of the team.&lt;/p&gt;

&lt;p&gt;Happy coding! ✨&lt;/p&gt;




&lt;p&gt;Hi 👋🏻&lt;br&gt;
My name is Domenico, software developer passionate of Open Source, I write article about it for share my knowledge and experience.&lt;br&gt;
Don't forget to visit my social links to discover my links, check out Domenico Tenace Open Labs for my open source projects and subscribe to my newsletter! 🫰🏻&lt;/p&gt;

&lt;p&gt;🌲 Links: &lt;a href="https://links.domenicotenace.dev/" rel="noopener noreferrer"&gt;https://links.domenicotenace.dev/&lt;/a&gt;&lt;br&gt;
🐙 Domenico Tenace Open Labs: &lt;a href="https://github.com/Domenico-Tenace-Open-Labs" rel="noopener noreferrer"&gt;https://github.com/Domenico-Tenace-Open-Labs&lt;/a&gt;&lt;br&gt;
📰 Newsletter: &lt;a href="https://domenicotenace.substack.com/subscribe" rel="noopener noreferrer"&gt;https://domenicotenace.substack.com/subscribe&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you like my content or want to support my work, you can support me with a small donation. I would be grateful 🥹&lt;/p&gt;

&lt;p&gt;☕ Buy me a Coffee: &lt;a href="https://buymeacoffee.com/domenicotenace" rel="noopener noreferrer"&gt;https://buymeacoffee.com/domenicotenace&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>llm</category>
      <category>programming</category>
    </item>
    <item>
      <title>Closed vs Open Source AI: Who Really Owns Your Data? 🔓</title>
      <dc:creator>Domenico Tenace</dc:creator>
      <pubDate>Mon, 12 Oct 2026 08:18:12 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/closed-vs-open-source-ai-who-really-owns-your-data-1jd3</link>
      <guid>https://dev.to/playfulprogramming/closed-vs-open-source-ai-who-really-owns-your-data-1jd3</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Hey everyone 👋&lt;/p&gt;

&lt;p&gt;When we pick an AI model, we usually look at benchmarks, speed and price. Fair enough. But there's a question most teams skip until it's too late: who owns the data, and who holds the controls?&lt;/p&gt;

&lt;p&gt;Right now the AI world is split in two. On one side, proprietary models, the classic "black boxes". On the other, open weights models you can download and run yourself. Let me walk you through why this choice matters more than it looks. Let's dive in! 🤙&lt;/p&gt;




&lt;h2&gt;
  
  
  Closed vs Open: The Quick Version
&lt;/h2&gt;

&lt;p&gt;Closed models live behind an API. You send text, you get text back, and what happens in between is not your business. They're easy to start with, and that's why so many companies pick them.&lt;/p&gt;

&lt;p&gt;Open weights models (think Llama, Mistral, Qwen, DeepSeek) are different. You get the model itself, so you can run it on your own server, in a private cloud, or even on a beefy laptop. One note: "open weights" is not always the same as "open source" in the strict sense, so always read the license before building a product on top of one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Open Source Is Worth a Look
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Your Data Stays at Home 🔒
&lt;/h3&gt;

&lt;p&gt;With a self-hosted model, nothing leaves your infrastructure. No customer data, no internal documents, no proprietary code sent to someone else's servers. If you work in healthcare, finance, or you just have a nervous legal team, this alone can settle the discussion.&lt;/p&gt;

&lt;h3&gt;
  
  
  No Vendor Lock-in 🔓
&lt;/h3&gt;

&lt;p&gt;Prices change. APIs get deprecated. Terms of service get rewritten on a random Tuesday. If your whole product depends on one provider, every one of those changes is your problem, and you get no vote.&lt;/p&gt;

&lt;p&gt;With open models you control the infrastructure, so you can move, switch or fork whenever you want.&lt;/p&gt;

&lt;h3&gt;
  
  
  You Can Look Inside and Make It Yours 🛠️
&lt;/h3&gt;

&lt;p&gt;No black box means you can inspect what you're running. You can also fine-tune a model for your own domain and keep full ownership of the result. Try doing that with a closed API, and you'll quickly find where the rules stop.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Catch
&lt;/h2&gt;

&lt;p&gt;I'm not here to sell you a dream, so let's be honest about the downsides.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Running models costs real money, in GPUs or cloud instances.&lt;/li&gt;
&lt;li&gt;Someone has to maintain it: updates, scaling, monitoring, security. That someone is you.&lt;/li&gt;
&lt;li&gt;The very top closed models still tend to lead on the hardest tasks, even if the gap keeps shrinking.&lt;/li&gt;
&lt;li&gt;Licenses vary a lot, and some come with restrictions you won't like.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're a small team with no infra experience, a managed API might be the smarter call, at least at the start.&lt;/p&gt;




&lt;h2&gt;
  
  
  So What Should You Do? 🎯
&lt;/h2&gt;

&lt;p&gt;For most teams, the answer isn't "pick a side", it's "keep the exit open".&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start with a closed API if you need to move fast.&lt;/li&gt;
&lt;li&gt;Put a thin abstraction layer between your code and the model, so swapping providers is a config change, not a rewrite.&lt;/li&gt;
&lt;li&gt;Use open models for the sensitive stuff, or for tasks where a smaller model is good enough and much cheaper.&lt;/li&gt;
&lt;li&gt;Test open alternatives regularly. The landscape moves every few weeks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open source fits best when privacy is non negotiable, when you have enough volume to justify your own infra, or when you need deep customization. Closed fits best when you want zero ops and the strongest raw performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts 🌟
&lt;/h2&gt;

&lt;p&gt;This isn't a philosophical debate, it's a practical one. The more AI becomes the core of your product, the more it matters who controls it. Open source is also what keeps a handful of companies from owning the entire cognitive infrastructure we all rely on.&lt;/p&gt;

&lt;p&gt;You don't have to go all in. Just make sure that if the rules change tomorrow, you have somewhere to go.&lt;/p&gt;

&lt;p&gt;Happy coding! ✨&lt;/p&gt;




&lt;p&gt;Hi 👋🏻&lt;br&gt;
My name is Domenico, software developer passionate of Open Source, I write article about it for share my knowledge and experience.&lt;br&gt;
Don't forget to visit my social links to discover my links, check out Domenico Tenace Open Labs for my open source projects and subscribe to my newsletter! 🫰🏻&lt;/p&gt;

&lt;p&gt;🌲 Links: &lt;a href="https://links.domenicotenace.dev/" rel="noopener noreferrer"&gt;https://links.domenicotenace.dev/&lt;/a&gt;&lt;br&gt;
🐙 Domenico Tenace Open Labs: &lt;a href="https://github.com/Domenico-Tenace-Open-Labs" rel="noopener noreferrer"&gt;https://github.com/Domenico-Tenace-Open-Labs&lt;/a&gt;&lt;br&gt;
📰 Newsletter: &lt;a href="https://domenicotenace.substack.com/subscribe" rel="noopener noreferrer"&gt;https://domenicotenace.substack.com/subscribe&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you like my content or want to support my work, you can support me with a small donation. I would be grateful 🥹&lt;/p&gt;

&lt;p&gt;☕ Buy me a Coffee: &lt;a href="https://buymeacoffee.com/domenicotenace" rel="noopener noreferrer"&gt;https://buymeacoffee.com/domenicotenace&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>llm</category>
      <category>programming</category>
    </item>
    <item>
      <title>Seasoned Developer Advice after 25 years in IT</title>
      <dc:creator>Emanuele Bartolesi</dc:creator>
      <pubDate>Fri, 09 Oct 2026 10:09:24 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/seasoned-developer-advice-after-25-years-in-it-5dpg</link>
      <guid>https://dev.to/playfulprogramming/seasoned-developer-advice-after-25-years-in-it-5dpg</guid>
      <description>&lt;p&gt;I've now been working in IT for exactly 25 years.&lt;/p&gt;

&lt;p&gt;My impostor syndrome is still very much present. Writing an article called "seasoned developer advice" makes me hesitate a little. There is always something I don't know, a technology I haven't used, or someone who understands a subject much better than I do.&lt;/p&gt;

&lt;p&gt;But after 25 years, I think I can start sharing a few thoughts.&lt;/p&gt;

&lt;p&gt;These are opinions about how to work, build software, and stay in this profession. Take what helps, question the rest, and adapt it to the team and situation you have in front of you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask the question you are embarrassed to ask
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fib8llx945q6r6c2t5d1i.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fib8llx945q6r6c2t5d1i.png" alt="01-ask-questions" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If a requirement is unclear, ask about it before you turn it into code.&lt;/p&gt;

&lt;p&gt;What does "active customer" mean? Who can approve this operation? What should happen when the external service is unavailable? These questions can feel basic, especially when everyone else in the meeting appears to understand everything.&lt;/p&gt;

&lt;p&gt;I would rather admit that I don't understand a business rule than build an entire feature around my interpretation of it. The same applies to an unfamiliar piece of code. Ask someone to walk you through it, then explain it back in your own words.&lt;br&gt;
This step is even easier with AI nowdays, but asking a colleague still an easier thing to understand maybe something that AI doesn't know behind a business choice or something like that.&lt;/p&gt;

&lt;p&gt;Experience should make it easier to say "I don't know yet." I still have to remind myself of that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give coordination an owner
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwvipn9z9svvlcr92489k.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwvipn9z9svvlcr92489k.png" alt="Plush Emanuele arranging connected task cards on a felt board." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Developers need time to develop. If the lead developer also has to chase every dependency, negotiate the scope, update stakeholders, and organize the release, account for that work in the plan.&lt;/p&gt;

&lt;p&gt;On a larger project, I would want someone explicitly responsible for coordination. That might be a project manager, a delivery lead, or another role that fits the organization. On a small team, it may be a shared responsibility, but the decisions still need an owner.&lt;/p&gt;

&lt;p&gt;A date written in a ticket does not resolve a blocked dependency. Someone has to talk to the other team, agree on what happens next, and adjust the plan when the answer changes.&lt;/p&gt;

&lt;p&gt;When estimating a feature, include that work. A week full of meetings leaves less time for implementation than a week with clear decisions and uninterrupted time.&lt;br&gt;
Actually we are still doing this mistake to "overmeetings" but I hope it will change soon or later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make meetings earn their place
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feyio1e0zl6lpq0bp99up.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feyio1e0zl6lpq0bp99up.png" alt="Plush Emanuele at a meeting table with an hourglass and a decision board." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I don't think every team needs the same meeting schedule.&lt;/p&gt;

&lt;p&gt;A short conversation with the person who understands the business process can be useful while a feature is taking shape. Showing a working screen may reveal that the approval flow is wrong before you spend another week building it.&lt;/p&gt;

&lt;p&gt;A daily meeting where everyone reads out tickets already visible on the board is harder to justify.&lt;/p&gt;

&lt;p&gt;For me, a useful meeting resolves a question, exposes a dependency, or produces a decision. If an update can be written down and read when people have time, try that. If a disagreement needs a conversation, have the conversation.&lt;/p&gt;

&lt;p&gt;Record the outcome where the work lives. A decision made on a call is easy to miss if the developer implementing it was somewhere else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Write code for the person who has to change it
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbwve3fp0ewbg2ayoawrc.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbwve3fp0ewbg2ayoawrc.png" alt="Plush Emanuele showing readable code on a laptop beside connected modules." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Readable code matters most when someone needs to fix it under pressure.&lt;/p&gt;

&lt;p&gt;I prefer a method with a clear name and an obvious flow over an abstraction that takes three files to understand. There are good reasons for abstraction, but I want to know which problem it solves before adding another layer.&lt;/p&gt;

&lt;p&gt;The same caution applies to shared code. Two methods that look similar may represent business rules that will evolve independently. Putting them in one common library creates a relationship you will have to maintain.&lt;/p&gt;

&lt;p&gt;Extract code when the shared behavior is understood. If several applications depend on it, think about compatibility, ownership, and how updates will reach them. Publishing a package creates a responsibility beyond the original project.&lt;br&gt;
Still a valid advice also for we use a lot of AI for writing code.&lt;br&gt;
We need anywayt to review what is written to understand if we can read and understand the code anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leave the explanation you would want to find later
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu66h82yrr9a52g029e9l.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu66h82yrr9a52g029e9l.png" alt="Plush Emanuele writing diagrams in a notebook beside a laptop." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Code can show what happens without explaining why it must happen that way.&lt;/p&gt;

&lt;p&gt;A comment explaining that a file format is required by an external system is useful. A comment saying that the next line increments a counter usually adds little.&lt;/p&gt;

&lt;p&gt;I want documentation that helps someone do the work: how to run the application, which configuration is needed, how to execute the tests, and what to check when a deployment fails. Keep those instructions close to the repository and update them when the workflow changes.&lt;/p&gt;

&lt;p&gt;There is also value in a short decision note. If the team rejected a queue because the current workload did not justify operating one, record that reasoning and the condition that would make you reconsider.&lt;/p&gt;

&lt;p&gt;AI can help draft these explanations. Check them against the code and the actual setup before treating them as documentation. A plausible installation guide that cannot be followed is another problem for the next developer to debug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Think about the application after it leaves your machine
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff01lcapyywv10288tljt.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff01lcapyywv10288tljt.png" alt="Plush Emanuele inspecting a server with a recovery arrow and a shield." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A feature needs more than a successful local run.&lt;/p&gt;

&lt;p&gt;Before shipping, I want to know how we will recognize a failure and what we can do about it. A useful log entry should give enough context to trace an operation without exposing credentials or customer data. An exception with no context can leave you searching through several unrelated requests.&lt;/p&gt;

&lt;p&gt;Be deliberate about tests, too. If a feature moves data between customers, test that boundary. If it sends notifications, consider what happens when an operation is repeated. The failure case should influence the test, rather than becoming an afterthought once the happy path passes.&lt;/p&gt;

&lt;p&gt;And discuss recovery. Reverting code will not undo every effect of that code. A database change or an email already sent needs its own consideration.&lt;/p&gt;

&lt;p&gt;These questions belong in development, while you can still change the design without an incident forcing the decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review the code without making it personal
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7xn1p41u494hx4q59xzb.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7xn1p41u494hx4q59xzb.png" alt="Plush Emanuele reviewing a laptop with a magnifying glass and a friendly feedback bubble." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having someone question your implementation can be uncomfortable. With impostor syndrome already in the room, a review comment can feel larger than it is.&lt;/p&gt;

&lt;p&gt;Try to keep the conversation specific. Explain which behavior concerns you, point to the assumption, and suggest a way to verify it. "What happens if this request is retried?" is a useful opening for a discussion.&lt;/p&gt;

&lt;p&gt;As the author, explain your reasoning and be willing to change it. As the reviewer, distinguish a defect from a preference. A naming choice does not deserve the same treatment as a missing authorization check.&lt;/p&gt;

&lt;p&gt;I also like reviews small enough that a colleague can understand the change properly. If a bug fix includes a folder reorganization and a new framework, consider separating those decisions.&lt;/p&gt;

&lt;p&gt;The goal is a change the team understands well enough to maintain. Being the person who wins the argument contributes very little to that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make maintenance part of the plan
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F80apjn13cfirdcuue8ov.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F80apjn13cfirdcuue8ov.png" alt="Plush Emanuele maintaining a software box beside a calendar." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Framework upgrades, dependency updates, and refactoring need time on the calendar.&lt;/p&gt;

&lt;p&gt;It is easy to postpone them because the application still works. When you do, be explicit about what you are accepting and when you will revisit it. Otherwise, postponement becomes the plan by default.&lt;/p&gt;

&lt;p&gt;A refactoring ticket should name the problem it will address. Perhaps adding a validation rule currently requires changing five different places. That gives the team a concrete improvement to assess and a way to tell whether the work helped.&lt;/p&gt;

&lt;p&gt;I would apply the same discipline to modernization. Explain what the upgrade enables, which risk it addresses, and how you will validate the change. The age of a framework alone is a weak explanation for rewriting a working application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep learning through things you can use
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F01fymquljz45tenzs45n.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F01fymquljz45tenzs45n.png" alt="Plush Emanuele learning from a book and experimenting with a laptop." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You cannot study every language, framework, cloud service, and AI tool that appears.&lt;/p&gt;

&lt;p&gt;Choose something relevant and spend enough time with it to understand where it helps. Build a small application, add a feature to a side project, or investigate a part of your production system that you usually avoid.&lt;/p&gt;

&lt;p&gt;A course can give you structure. Applying the lesson gives you questions the course may never have covered.&lt;/p&gt;

&lt;p&gt;I would also leave room to learn from colleagues. Someone with less experience may know a tool or technique you have never used. Twenty-five years in IT should not make that conversation awkward.&lt;/p&gt;

&lt;p&gt;Your main language is useful expertise. Stay curious about the parts around it: the database query, the deployment pipeline, or the interface the user has to navigate. A problem does not stop at the boundary of your preferred stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose tools you can trust and understand
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsruu8qfgvavlyu41xan2.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsruu8qfgvavlyu41xan2.png" alt="Plush Emanuele choosing tools beside a laptop and a small AI robot." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use an editor that lets you work comfortably. Learn its debugger, navigation, and refactoring tools before assuming a different editor will solve your frustration.&lt;/p&gt;

&lt;p&gt;I would take the same approach to AI. Start with work where you can judge the result: exploring unfamiliar code, preparing a first test draft, or checking an implementation for missed cases.&lt;/p&gt;

&lt;p&gt;Read the output and run the checks. When the tool proposes a design, ask what assumptions it made. You remain responsible for the change you submit.&lt;/p&gt;

&lt;p&gt;Trying another tool or model can be useful when it answers a specific question about your workflow. You don't need a collection of subscriptions just to feel current.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a way of working you can keep doing
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxd8nq266pz72kx45ggv0.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxd8nq266pz72kx45ggv0.png" alt="Plush Emanuele taking a coffee break beside a closed laptop." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After 25 years, I want my advice to include what happens away from the keyboard.&lt;/p&gt;

&lt;p&gt;Take breaks. Get up, walk around, do sports, and give yourself permission to stop staring at the same problem. If you are stuck, ask for help rather than making another exhausted hour the price of proving that you can solve it alone.&lt;/p&gt;

&lt;p&gt;Talk to teammates sometimes without a ticket as the agenda. It is easier to ask someone a difficult question when your only interaction with them has not been a review disagreement.&lt;/p&gt;

&lt;p&gt;And keep something outside work. There will always be another release, article, framework, or project. Closing the laptop does not mean you stopped caring about your profession.&lt;/p&gt;

&lt;p&gt;I still don't feel that 25 years gives me an answer to everything. It does give me enough reason to share these thoughts, even while the impostor syndrome tells me to wait a little longer.&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%2F9x5aklqdjlp32k4xhu06.png" 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%2F9x5aklqdjlp32k4xhu06.png" width="500" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you enjoyed this blog post and want to learn more about C# development, you might be interested in subscribing to my bi-weekly newsletter called Dev Dispatch. By subscribing, you will get access to exclusive content, tips, and tricks, as well as updates on the latest news and trends in the development world. You will also be able to interact with me, and share your feedback and suggestions. To subscribe, simply navigate to &lt;a href="https://buttondown.email/kasuken?tag=devto" rel="noopener noreferrer"&gt;https://buttondown.email/kasuken?tag=devto&lt;/a&gt;, enter your email address and click on the Subscribe button. You can unsubscribe at any time. Thank you for your support!&lt;/p&gt;

</description>
      <category>career</category>
      <category>careerdevelopment</category>
      <category>weeklyretro</category>
    </item>
    <item>
      <title>My Windows 11 Laptop Was Struggling with Claude and Codex. Defender Exclusions Helped.</title>
      <dc:creator>Emanuele Bartolesi</dc:creator>
      <pubDate>Tue, 06 Oct 2026 15:01:47 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/my-windows-11-laptop-was-struggling-with-claude-and-codex-defender-exclusions-helped-2iln</link>
      <guid>https://dev.to/playfulprogramming/my-windows-11-laptop-was-struggling-with-claude-and-codex-defender-exclusions-helped-2iln</guid>
      <description>&lt;p&gt;My Windows 11 laptop sometimes struggled while I was working with Claude and Codex or GitHub Copilot. CPU usage was high, and the fans were running like crazy.&lt;br&gt;
It means also that my battery runs out in 1 hour and 30 minutes 😀&lt;/p&gt;

&lt;p&gt;When I checked Task Manager, Antimalware Service Executable stood out. Disk usage was around 90%, with heavy CPU activity as well, while the two tools were running.&lt;/p&gt;

&lt;p&gt;That process belongs to Microsoft Defender Antivirus. You can find it as &lt;code&gt;Antimalware Service Executable&lt;/code&gt; in Task Manager's Processes tab, or &lt;code&gt;MsMpEng.exe&lt;/code&gt; in the Details tab. &lt;a href="https://learn.microsoft.com/en-us/defender-endpoint/microsoft-defender-antivirus-windows" rel="noopener noreferrer"&gt;Microsoft documents both names here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I suspected Defender's scanning of the tools' local folders was contributing to the slowdown. Adding folder exclusions solved the problem I was seeing on my laptop. This is my experience with one machine, rather than proof that Claude or Codex always causes this behavior.&lt;/p&gt;
&lt;h2&gt;
  
  
  The folders I excluded
&lt;/h2&gt;

&lt;p&gt;These are the four entries in my Windows Security exclusion list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C:\Users\barto\.claude
C:\Users\barto\.codex
C:\Users\barto\.copilot
C:\Users\barto\.github-copilot-cli
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fta5jqbgk3owcewv1jx8o.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fta5jqbgk3owcewv1jx8o.png" alt="Windows Security exclusion list showing the .claude, .codex, .copilot, and .github-copilot-cli folders under my Windows user profile." width="375" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last two entries are for my Copilot tooling. Their presence in this list does not establish that they contributed to the Claude and Codex slowdown.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is a security tradeoff:&lt;/strong&gt; a folder exclusion removes Defender antivirus scanning coverage for that folder and its contents. Files placed there later are also covered by the exclusion. Trusting the tool does not guarantee that every file it writes or downloads is safe. &lt;a href="https://support.microsoft.com/en-us/windows/security/threat-malware-protection/virus-and-threat-protection-in-the-windows-security-app" rel="noopener noreferrer"&gt;Microsoft explains the exclusion options and risks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Treat this list as a record of my setup. For your machine, identify the affected folder first and keep any exclusion as narrow as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to add a folder exclusion in Windows 11
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Start&lt;/strong&gt;, search for &lt;strong&gt;Windows Security&lt;/strong&gt;, and open it.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Virus &amp;amp; threat protection&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Virus &amp;amp; threat protection settings&lt;/strong&gt;, select &lt;strong&gt;Manage settings&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Scroll to &lt;strong&gt;Exclusions&lt;/strong&gt; and select &lt;strong&gt;Add or remove exclusions&lt;/strong&gt;. Approve the administrator prompt if it appears.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Add an exclusion&lt;/strong&gt;, then &lt;strong&gt;Folder&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Browse to the folder you want to exclude, for example &lt;code&gt;C:\Users\YOUR_USERNAME\.claude&lt;/code&gt;, and select &lt;strong&gt;Select Folder&lt;/strong&gt;. Replace &lt;code&gt;YOUR_USERNAME&lt;/code&gt; with your Windows user folder name.&lt;/li&gt;
&lt;li&gt;Confirm that the folder appears in the exclusion list. Repeat for other folders only where needed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These steps follow &lt;a href="https://support.microsoft.com/en-us/windows/security/threat-malware-protection/virus-and-threat-protection-in-the-windows-security-app" rel="noopener noreferrer"&gt;Microsoft's Windows Security guidance&lt;/a&gt;. On a company-managed laptop, your IT team may control these settings.&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%2F9x5aklqdjlp32k4xhu06.png" 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%2F9x5aklqdjlp32k4xhu06.png" width="500" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you enjoyed this blog post and want to learn more about C# development, you might be interested in subscribing to my bi-weekly newsletter called Dev Dispatch. By subscribing, you will get access to exclusive content, tips, and tricks, as well as updates on the latest news and trends in the development world. You will also be able to interact with me, and share your feedback and suggestions. To subscribe, simply navigate to &lt;a href="https://buttondown.email/kasuken?tag=devto" rel="noopener noreferrer"&gt;https://buttondown.email/kasuken?tag=devto&lt;/a&gt;, enter your email address and click on the Subscribe button. You can unsubscribe at any time. Thank you for your support!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>Moving to European Digital Services, One Decision at a Time</title>
      <dc:creator>Emanuele Bartolesi</dc:creator>
      <pubDate>Thu, 24 Sep 2026 16:13:19 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/moving-to-european-digital-services-one-decision-at-a-time-3j68</link>
      <guid>https://dev.to/playfulprogramming/moving-to-european-digital-services-one-decision-at-a-time-3j68</guid>
      <description>&lt;p&gt;My move to European digital services includes Proton, Qwant, and DeepL. When it came to Notion, I went further and replaced it with &lt;a href="https://github.com/kasuken/Brainy" rel="noopener noreferrer"&gt;Brainy&lt;/a&gt;, the product I built for my own notes, projects, and tasks.&lt;/p&gt;

&lt;p&gt;That sounds like a migration, but the word creates the wrong expectation. I did not spend a weekend exporting every account, cancelling every subscription, and declaring myself independent from American technology on Monday morning.&lt;/p&gt;

&lt;p&gt;I am changing one decision at a time.&lt;/p&gt;

&lt;p&gt;I do not want a US provider to be the automatic answer for every digital need simply because it is the most visible option. Europe has credible products. Some are good enough to compete directly, and some fit my needs better. That does not mean I will replace a working service because a company has the wrong flag beside its logo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proton made the change visible
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://proton.me/" rel="noopener noreferrer"&gt;Proton&lt;/a&gt; is where the migration became a system rather than a few unrelated experiments.&lt;/p&gt;

&lt;p&gt;Proton Mail replaced my previous email setup. Proton VPN replaced NordVPN. Proton Pass replaced NordPass. I also use &lt;a href="https://proton.me/lumo" rel="noopener noreferrer"&gt;Lumo&lt;/a&gt; for selected private AI conversations rather than treating every chat as suitable for any general-purpose assistant.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4o2345o1guq4n89951lt.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4o2345o1guq4n89951lt.webp" alt="Proton" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The immediate benefit was consolidation. One privacy-focused Swiss ecosystem replaced several separate accounts and subscriptions. I did not need to find a different European company for email, another for passwords, and another for the VPN before making progress.&lt;/p&gt;

&lt;p&gt;There is an obvious trade-off: consolidation reduces vendor sprawl while increasing dependence on Proton. Moving three services into one company is not diversification. I still prefer the current setup, but it would be dishonest to describe concentration as independence.&lt;/p&gt;

&lt;p&gt;Lumo has a similar boundary. Proton says stored conversations use zero-access encryption, which is why it has a place in my private workflow. I do not pretend that it replaces every AI service or every model. The choice depends on the conversation and the job I need to complete.&lt;br&gt;
For the moment I am using Lumo only for questions related my health and other sensitive information.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqxcbgks6wztacu5edrya.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqxcbgks6wztacu5edrya.webp" alt="Lumo" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The corporate map is more complicated than the logos
&lt;/h2&gt;

&lt;p&gt;Once I started checking where services were based, the simple Europe-versus-USA story stopped working.&lt;/p&gt;

&lt;p&gt;NordVPN is not an American service. &lt;a href="https://support.nordvpn.com/hc/en-us/articles/19441152966161-Where-is-NordVPN-based" rel="noopener noreferrer"&gt;NordVPN says it is based in and operates under Panama's jurisdiction&lt;/a&gt;, while its parent company, Nord Security, is based in the Netherlands. Moving to Proton VPN is a Panama-to-Switzerland change at the service-jurisdiction level, with a European parent company also involved in the old setup.&lt;/p&gt;

&lt;p&gt;WeTransfer is European too. &lt;a href="https://wetransfer.com/documents/WeTransfer_Data_Processing_Agreement.pdf" rel="noopener noreferrer"&gt;WeTransfer B.V. is based in Amsterdam&lt;/a&gt;, so my switch to SwissTransfer was not about escaping an American company. It was about the product.&lt;/p&gt;

&lt;p&gt;For my file-sharing needs, &lt;a href="https://www.swisstransfer.com/" rel="noopener noreferrer"&gt;SwissTransfer&lt;/a&gt; has a privacy model I prefer and a much more useful free allowance for large files. Infomaniak says SwissTransfer accepts transfers up to 50 GB, stores the files in Switzerland, supports password protection, and deletes the files after the transfer period. &lt;a href="https://help.wetransfer.com/hc/en-us/articles/360036942752-What-does-a-free-WeTransfer-account-offer-me" rel="noopener noreferrer"&gt;WeTransfer's current free-account documentation&lt;/a&gt; describes an allowance of up to ten transfers or 3 GB in total over a rolling 30-day period.&lt;/p&gt;

&lt;p&gt;That is a concrete difference. I am not using SwissTransfer because the landing page has a Swiss flag. I am using it because privacy and larger transfers matter in this workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd9mjdcmxowrgxjvto9dj.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd9mjdcmxowrgxjvto9dj.webp" alt="SwissTransfer" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This distinction changed how I evaluate alternatives. I now look separately at the service jurisdiction, parent company, data location, underlying infrastructure, business model, portability, and actual product quality. Those facts do not always point to the same country.&lt;/p&gt;
&lt;h2&gt;
  
  
  Some changes need almost no migration
&lt;/h2&gt;

&lt;p&gt;Qwant and DeepL were easier decisions.&lt;/p&gt;

&lt;p&gt;I use &lt;a href="https://www.qwant.com/" rel="noopener noreferrer"&gt;Qwant&lt;/a&gt; for search. Qwant is a French company, and its current privacy policy says it does not use targeted advertising or retain search history by default. Search is also easy to test: use it, compare the results, and keep another engine available when it does not find what you need.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmuynv4h8ay40sy3xzoh1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmuynv4h8ay40sy3xzoh1.webp" alt="Qwant" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I use &lt;a href="https://www.deepl.com/" rel="noopener noreferrer"&gt;DeepL&lt;/a&gt; for translation. It was founded in Cologne, Germany, and it is a good example of why the word "alternative" can be misleading. I do not need to treat a European translation service as a political compromise. I can use it because it does the work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fovqs2baalve27fkydsim.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fovqs2baalve27fkydsim.webp" alt="DeepL" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Neither choice required a dramatic data migration. There was no archive to restructure and no DNS record that could break a production system. They simply became part of the workflow.&lt;/p&gt;

&lt;p&gt;New domains are another low-risk entry point. I now use &lt;a href="https://www.infomaniak.com/en/domains" rel="noopener noreferrer"&gt;Infomaniak Domains&lt;/a&gt; instead of GoDaddy for new registrations. I am not forcing every existing domain through an immediate transfer. When I need a new domain, the new default is a Swiss, ICANN-accredited registrar. That is progress without creating unnecessary operational risk.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fom0pozb68l5kdeeoyghr.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fom0pozb68l5kdeeoyghr.webp" alt="Infomaniak" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DNS is an even quieter change. I use &lt;a href="https://www.quad9.net/" rel="noopener noreferrer"&gt;Quad9&lt;/a&gt; as my resolver. Quad9 is a Swiss nonprofit, and its documentation says the recommended &lt;code&gt;9.9.9.9&lt;/code&gt; service blocks lookups for malicious domains without logging end-user IP addresses. It also supports encrypted DNS protocols. This is closer to changing an infrastructure setting than migrating an account, but it moves a foundational part of normal browsing away from whichever resolver happened to be the default.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Falb2sxzm6wglp80afe39.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Falb2sxzm6wglp80afe39.png" alt="Quad9" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Replacing Notion meant building the alternative
&lt;/h2&gt;

&lt;p&gt;Notion was different because a knowledge system accumulates structure as well as data. Notes connect to projects, tasks carry context, and the way information is captured affects whether it becomes useful work or remains another page in an archive.&lt;/p&gt;

&lt;p&gt;I replaced Notion with &lt;a href="https://www.brainy-me.com" rel="noopener noreferrer"&gt;Brainy&lt;/a&gt;, my own product.&lt;/p&gt;

&lt;p&gt;Brainy is a practical second brain organized around the PARA method. It captures notes and ideas, connects them to projects and tasks, and focuses on turning stored knowledge into action. The goal was not to reproduce every Notion feature. It was to build a workflow that matches how I move from an idea to a project, from a project to the next task, and from planning to today's 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnfpuajuk0ph1gs30uj7e.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnfpuajuk0ph1gs30uj7e.png" alt="Brainy" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Owning the product gives me control over that workflow, the data model, portability, and the roadmap, but it also gives me the maintenance work. I own the deployments, bugs, database changes, security decisions, and migration logic.&lt;/p&gt;

&lt;p&gt;This is the clearest reason the entire transition cannot happen in two hours. Replacing a service can mean changing a setting or moving years of habits and information. In this case, it also meant building and operating a product.&lt;/p&gt;

&lt;p&gt;For the moment, Brainy is free and you can create your own account here: &lt;a href="https://www.brainy-me.com" rel="noopener noreferrer"&gt;www.brainy-me.com&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Sometimes the correct migration is no migration
&lt;/h2&gt;

&lt;p&gt;I still use Spotify.&lt;/p&gt;

&lt;p&gt;The reason is simple: Spotify is already European and it works for me. Its consumer terms identify Spotify AB in Stockholm, Sweden. Replacing it with another service only to increase the number of changed logos would add work without moving me closer to the actual goal.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frta6coyj8spduyckk30f.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frta6coyj8spduyckk30f.png" alt="Spotify" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A useful migration should leave some rows unchanged. Otherwise the process is driven by ideology rather than requirements.&lt;/p&gt;

&lt;p&gt;Here is the current map:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Workflow&lt;/th&gt;
&lt;th&gt;Current choice&lt;/th&gt;
&lt;th&gt;Decision&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Email&lt;/td&gt;
&lt;td&gt;Proton Mail&lt;/td&gt;
&lt;td&gt;Moved into the Proton ecosystem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VPN&lt;/td&gt;
&lt;td&gt;Proton VPN&lt;/td&gt;
&lt;td&gt;Replaced NordVPN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Passwords&lt;/td&gt;
&lt;td&gt;Proton Pass&lt;/td&gt;
&lt;td&gt;Replaced NordPass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Private AI chats&lt;/td&gt;
&lt;td&gt;Lumo&lt;/td&gt;
&lt;td&gt;Used selectively for private conversations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large file transfers&lt;/td&gt;
&lt;td&gt;SwissTransfer&lt;/td&gt;
&lt;td&gt;Chosen for privacy and transfers up to 50 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New domain registrations&lt;/td&gt;
&lt;td&gt;Infomaniak&lt;/td&gt;
&lt;td&gt;Replaced GoDaddy as the default for new domains&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DNS&lt;/td&gt;
&lt;td&gt;Quad9&lt;/td&gt;
&lt;td&gt;Uses a Swiss nonprofit resolver focused on privacy and security&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search&lt;/td&gt;
&lt;td&gt;Qwant&lt;/td&gt;
&lt;td&gt;In active use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Translation&lt;/td&gt;
&lt;td&gt;DeepL&lt;/td&gt;
&lt;td&gt;In active use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Knowledge and planning&lt;/td&gt;
&lt;td&gt;Brainy&lt;/td&gt;
&lt;td&gt;Replaced Notion with my own product&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Music&lt;/td&gt;
&lt;td&gt;Spotify&lt;/td&gt;
&lt;td&gt;Kept because the European service already fits&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  The process I use before changing a service
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://european-alternatives.eu/" rel="noopener noreferrer"&gt;European Alternatives directory&lt;/a&gt; is useful for discovery, but inclusion in a directory is the beginning of the evaluation, not the conclusion.&lt;/p&gt;

&lt;p&gt;Before changing a service, I work through a small set of questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What job does the current product perform?&lt;/li&gt;
&lt;li&gt;Which features are genuinely non-negotiable, and which ones are habits?&lt;/li&gt;
&lt;li&gt;Where is the service legally based, where is the data processed, and which external platforms does it still depend on?&lt;/li&gt;
&lt;li&gt;Can I export the data in a useful format and move again later?&lt;/li&gt;
&lt;li&gt;Can I run the old and new services in parallel before cancelling anything?&lt;/li&gt;
&lt;li&gt;Is the alternative better, equivalent, or merely acceptable for this specific job?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last question matters. I am willing to accept a small difference when privacy, control, or supporting a European provider makes the trade worthwhile. I am not willing to ignore a product that fails the workflow simply because it is European.&lt;/p&gt;

&lt;p&gt;The safest changes happen at natural decision points. Register the next domain with a different registrar. Test another search engine without removing the old one. Import a copy of a password vault before cancelling the existing subscription. Send the next large file with a service that supports the size you need.&lt;/p&gt;

&lt;p&gt;Moving all of them at once would turn a preference into an outage plan.&lt;/p&gt;
&lt;h2&gt;
  
  
  Change the next decision
&lt;/h2&gt;

&lt;p&gt;I still use services from American companies, and I expect that to continue. Work, collaboration, audience, and network effects do not disappear because a European alternative exists.&lt;/p&gt;

&lt;p&gt;The difference is that I no longer want the American option to win by default. When a subscription renews, a new need appears, or a current product creates friction, I check what exists in Europe before repeating the old decision.&lt;/p&gt;

&lt;p&gt;The outcomes vary. A European product may be good enough, SwissTransfer may be better for large files, or Spotify may already be the European answer. Brainy sits at the other end of the range because I had to build the alternative around the workflow I wanted.&lt;/p&gt;

&lt;p&gt;This process does not need a dramatic finish. The next decision is enough.&lt;/p&gt;



&lt;p&gt;Thanks for reading this post, I hope you found it interesting!&lt;/p&gt;

&lt;p&gt;Feel free to follow me to get notified when new articles are out 🙂&lt;/p&gt;


&lt;div class="ltag__user ltag__user__id__79932"&gt;
    &lt;a href="/kasuken" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=150,height=150,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F79932%2Fbe7bcd76-1f69-4336-9155-6017aebb2c53.png" alt="kasuken image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/kasuken"&gt;Emanuele Bartolesi&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/kasuken"&gt;🏆 Microsoft MVP (12x) &amp;amp; GitHub Star (4x) ⭐
👨‍🎓 LinkedIn Technical Instructor 🎓&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>productivity</category>
    </item>
    <item>
      <title>INP and Partytown: give the main thread back to your users</title>
      <dc:creator>Nicolas Frizzarin</dc:creator>
      <pubDate>Wed, 23 Sep 2026 08:18:37 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/inp-and-partytown-give-the-main-thread-back-to-your-users-o9m</link>
      <guid>https://dev.to/playfulprogramming/inp-and-partytown-give-the-main-thread-back-to-your-users-o9m</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;You click on a button. Nothing happens.&lt;br&gt;
You click again. Still nothing.&lt;br&gt;
Then the menu opens... and closes right away.&lt;/p&gt;

&lt;p&gt;We have all lived this. It's a responsiveness problem. And since March 12, 2024, Google measures it with a Core Web Vital: &lt;strong&gt;INP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Very often, the problem is not your code. It's the code you didn't write: analytics, tag managers, pixels, A/B testing tools...&lt;/p&gt;

&lt;p&gt;This article explains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What INP really measures&lt;/li&gt;
&lt;li&gt;Why third-party scripts hurt it so much&lt;/li&gt;
&lt;li&gt;How Partytown works&lt;/li&gt;
&lt;li&gt;How to use it in &lt;strong&gt;React&lt;/strong&gt; and &lt;strong&gt;Angular&lt;/strong&gt;, with real examples&lt;/li&gt;
&lt;li&gt;When you should &lt;strong&gt;not&lt;/strong&gt; use it&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  INP in a few words
&lt;/h2&gt;

&lt;p&gt;INP means &lt;strong&gt;Interaction to Next Paint&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It measures the time between a user interaction and the next frame painted by the browser. In other words: how long does the user wait before seeing that something happened?&lt;/p&gt;

&lt;p&gt;Only three types of interaction count:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a click with a mouse&lt;/li&gt;
&lt;li&gt;a tap on a touchscreen&lt;/li&gt;
&lt;li&gt;a key press&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scroll, hover and zoom are &lt;strong&gt;not&lt;/strong&gt; measured.&lt;/p&gt;

&lt;p&gt;INP looks at &lt;strong&gt;all&lt;/strong&gt; the interactions during the visit, and reports the worst one. If there are a lot of interactions, the browser ignores the highest one for every 50 interactions. That way, one random hiccup does not ruin your score.&lt;/p&gt;

&lt;p&gt;The thresholds, at the 75th percentile of your real users:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;INP&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;≤ 200 ms&lt;/td&gt;
&lt;td&gt;🟢 Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200 ms to 500 ms&lt;/td&gt;
&lt;td&gt;🟠 Needs improvement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;gt; 500 ms&lt;/td&gt;
&lt;td&gt;🔴 Poor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Just a reminder: INP replaced FID (First Input Delay). FID only measured the waiting time of the &lt;strong&gt;first&lt;/strong&gt; interaction. INP measures &lt;strong&gt;every&lt;/strong&gt; interaction, from start to paint. Much stricter.&lt;/p&gt;
&lt;h2&gt;
  
  
  What's inside an interaction?
&lt;/h2&gt;

&lt;p&gt;An interaction has three phases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; user clicks                                              next frame
     │                                                        │
     ▼                                                        ▼
     ├────────────────┬──────────────────────┬────────────────┤
     │  Input delay   │ Processing duration  │ Presentation   │
     │                │                      │ delay          │
     └────────────────┴──────────────────────┴────────────────┘
     │◄───────────────── interaction latency ───────────────►│
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Input delay&lt;/strong&gt;: the time before your event handlers start. The browser is busy with something else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Processing duration&lt;/strong&gt;: the time to run all your event handlers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Presentation delay&lt;/strong&gt;: the time to compute style, layout and paint the next frame.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep the first one in mind. This is where third-party scripts hurt the most.&lt;/p&gt;

&lt;h2&gt;
  
  
  The main thread: a one lane road
&lt;/h2&gt;

&lt;p&gt;The browser has one main thread. It does everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;runs JavaScript&lt;/li&gt;
&lt;li&gt;handles events&lt;/li&gt;
&lt;li&gt;computes style and layout&lt;/li&gt;
&lt;li&gt;paints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And it does &lt;strong&gt;one thing at a time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A task longer than 50 ms is a &lt;strong&gt;long task&lt;/strong&gt;. During a long task, the browser can't respond to the user.&lt;/p&gt;

&lt;p&gt;Now imagine your page with Google Tag Manager, a marketing pixel and an analytics script. The user clicks at the wrong moment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Main thread, without Partytown

──[ your app ]──[ tag manager 180ms ]──[ pixel 120ms ]──[ click handler ]──[ paint ]──
                      ▲
                user clicks here
                      │◄──────── input delay ────────►│
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your click handler is fast. Your code is clean. And your INP is still bad.&lt;/p&gt;

&lt;p&gt;This is the key point: &lt;strong&gt;you can write perfect code and still fail INP&lt;/strong&gt;, because you share the road with scripts you don't control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Partytown: move the party somewhere else
&lt;/h2&gt;

&lt;p&gt;Partytown is a small library, lazy-loaded, maintained by the QwikDev team. Its goal is simple: move third-party scripts from the main thread into a &lt;strong&gt;web worker&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The philosophy: the main thread is for &lt;strong&gt;your&lt;/strong&gt; code. Everything that is not in the critical path can go elsewhere.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
  subgraph Before["Without Partytown"]
    MT1["Main thread&amp;lt;br/&amp;gt;your app + GTM + pixel + analytics"]
  end
  subgraph After["With Partytown"]
    MT2["Main thread&amp;lt;br/&amp;gt;your app"]
    WW["Web worker&amp;lt;br/&amp;gt;GTM + pixel + analytics"]
    WW -. "DOM calls through a proxy" .-&amp;gt; MT2
  end&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;And the same timeline as before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Main thread, with Partytown

──[ your app ]──[ click handler ]──[ paint ]──
                ▲
          user clicks here: almost no input delay

Web worker

──[ tag manager 180ms ]──[ pixel 120ms ]──   ← nobody waits for this
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The long tasks still exist. They just don't block the user anymore.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Partytown is still in &lt;strong&gt;beta&lt;/strong&gt;. It's not guaranteed to work for every script. Test before you ship.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;Here comes the tricky part.&lt;/p&gt;

&lt;p&gt;A web worker has no DOM. No &lt;code&gt;document&lt;/code&gt;, no real &lt;code&gt;window&lt;/code&gt;. But third-party scripts expect them. They call &lt;code&gt;document.cookie&lt;/code&gt;, &lt;code&gt;getBoundingClientRect()&lt;/code&gt;, &lt;code&gt;addEventListener&lt;/code&gt;...&lt;/p&gt;

&lt;p&gt;On top of that, communication between a worker and the main thread is &lt;strong&gt;asynchronous&lt;/strong&gt;. Third-party scripts are written in a &lt;strong&gt;synchronous&lt;/strong&gt; way. No &lt;code&gt;await&lt;/code&gt;, no callback.&lt;/p&gt;

&lt;p&gt;So Partytown does two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the worker, it creates &lt;strong&gt;JavaScript Proxies&lt;/strong&gt; that look like &lt;code&gt;window&lt;/code&gt; and &lt;code&gt;document&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Each call to these proxies is sent to the main thread through a &lt;strong&gt;synchronous&lt;/strong&gt; channel, and the worker waits for the answer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From the script's point of view, nothing changed. This code works as is inside the worker:&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;rect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBoundingClientRect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// blocking call, like on the main thread&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="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two ways to get this synchronous channel.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1: Service Worker (the fallback)
&lt;/h3&gt;

&lt;p&gt;The worker sends a &lt;strong&gt;synchronous XHR&lt;/strong&gt;. A service worker intercepts it, asks the main thread, and answers.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;sequenceDiagram
  participant W as Web worker (3rd-party script)
  participant SW as Service worker
  participant M as Main thread (DOM)
  W-&amp;gt;&amp;gt;SW: sync XHR "getBoundingClientRect()"
  SW-&amp;gt;&amp;gt;M: postMessage
  M--&amp;gt;&amp;gt;SW: result
  SW--&amp;gt;&amp;gt;W: response { x, y }
  Note over W: for the script, it was a normal blocking call&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;That's why you'll see a lot of &lt;code&gt;proxytown&lt;/code&gt; requests in the network tab. They are not real HTTP requests. They're handled locally. You can hide them with the &lt;code&gt;-url:proxytown&lt;/code&gt; filter in Chrome DevTools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: Atomics (the fast one)
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;Atomics&lt;/code&gt; and &lt;code&gt;SharedArrayBuffer&lt;/code&gt;, the worker writes the request, calls &lt;code&gt;Atomics.wait()&lt;/code&gt;, and reads the result when the main thread answers. No service worker needed.&lt;/p&gt;

&lt;p&gt;It's about &lt;strong&gt;10x faster&lt;/strong&gt; to transfer data between threads.&lt;/p&gt;

&lt;p&gt;But there's a condition: the page must be &lt;strong&gt;cross-origin isolated&lt;/strong&gt;. You need these response headers on your document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;Cross&lt;/span&gt;-&lt;span class="n"&gt;Origin&lt;/span&gt;-&lt;span class="n"&gt;Embedder&lt;/span&gt;-&lt;span class="n"&gt;Policy&lt;/span&gt;: &lt;span class="n"&gt;credentialless&lt;/span&gt;
&lt;span class="n"&gt;Cross&lt;/span&gt;-&lt;span class="n"&gt;Origin&lt;/span&gt;-&lt;span class="n"&gt;Opener&lt;/span&gt;-&lt;span class="n"&gt;Policy&lt;/span&gt;: &lt;span class="n"&gt;same&lt;/span&gt;-&lt;span class="n"&gt;origin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Safari doesn't support &lt;code&gt;credentialless&lt;/code&gt;. So Safari falls back to the service worker.&lt;/li&gt;
&lt;li&gt;You can use &lt;code&gt;require-corp&lt;/code&gt; instead, which works in Safari. But it blocks every cross-origin image, script or video that doesn't have a &lt;code&gt;crossorigin&lt;/code&gt; attribute. Be careful with your CDN.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the headers are not there, no problem: Partytown falls back to the service worker automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two rules to remember
&lt;/h2&gt;

&lt;p&gt;Before the code, two rules. They are the same for every framework.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule 1: &lt;code&gt;type="text/partytown"&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The browser doesn't know this type, so it doesn't run the script. Partytown finds it with a selector and runs it in the worker.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- &amp;lt;script src="https://third-party.com/script.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ &amp;lt;script type="text/partytown" src="https://third-party.com/script.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Partytown is &lt;strong&gt;opt-in&lt;/strong&gt;. Only the scripts with this type move. All the others stay where they are. You choose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule 2: &lt;code&gt;forward&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your own code still calls &lt;code&gt;dataLayer.push(...)&lt;/code&gt; on the main thread. For example, when a user adds a product to the cart.&lt;/p&gt;

&lt;p&gt;But GTM now lives in the worker. So Partytown needs to know which &lt;code&gt;window&lt;/code&gt; functions to patch and send to the worker. That's the role of &lt;code&gt;forward&lt;/code&gt;. It even queues the calls made before Partytown is ready.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
  A["Your code&amp;lt;br/&amp;gt;dataLayer.push(...)"] --&amp;gt;|"patched by forward"| B["Partytown&amp;lt;br/&amp;gt;(main thread side)"]
  B --&amp;gt;|"serialized message"| C["GTM&amp;lt;br/&amp;gt;in the web worker"]
  C --&amp;gt;|"network"| D["Google servers"]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;By default, a forwarded call &lt;strong&gt;only&lt;/strong&gt; runs in the worker. If you need the original function to also run on the main thread, use &lt;code&gt;preserveBehavior&lt;/code&gt;:&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;partytown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;forward&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dataLayer.push&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;preserveBehavior&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;// runs on both sides&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fbq&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;                                           &lt;span class="c1"&gt;// worker only (default)&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;Now let's have some code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular
&lt;/h2&gt;

&lt;p&gt;Our goal: run Google Tag Manager in the worker, and track page views and a click from our Angular app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: install
&lt;/h3&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; @qwik.dev/partytown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: serve the Partytown files
&lt;/h3&gt;

&lt;p&gt;The worker and the service worker are real files. They must be served by your app. In &lt;code&gt;angular.json&lt;/code&gt;, add them to the &lt;code&gt;assets&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"assets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"glob"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node_modules/@qwik.dev/partytown/lib"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/~partytown"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a build, you should see a &lt;code&gt;~partytown/&lt;/code&gt; folder in your output, with &lt;code&gt;partytown.js&lt;/code&gt;, &lt;code&gt;partytown-sw.js&lt;/code&gt;, &lt;code&gt;partytown-atomics.js&lt;/code&gt;...&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: &lt;code&gt;index.html&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Third-party scripts must be there early, before Angular starts. So we put everything in &lt;code&gt;src/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="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;My Shop&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;base&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- 1. Partytown configuration: BEFORE partytown.js --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;partytown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;forward&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;dataLayer.push&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- 2. Partytown itself: no async, no defer --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/~partytown/partytown.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- 3. Google Tag Manager, moved to the worker --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/partytown"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;l&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gtm.start&lt;/span&gt;&lt;span class="dl"&gt;'&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;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gtm.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&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="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="nx"&gt;dl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dataLayer&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;&amp;amp;l=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.googletagmanager.com/gtm.js?id=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;dl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;f&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="nb"&gt;document&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&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;dataLayer&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;GTM-XXXXXXX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;app-root&amp;gt;&amp;lt;/app-root&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 While you set things up, use &lt;code&gt;/~partytown/debug/partytown.js&lt;/code&gt; and add &lt;code&gt;debug: true&lt;/code&gt; to the config. You get logs in the console. Don't ship the debug build to production.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 4: an analytics service
&lt;/h3&gt;

&lt;p&gt;Now, our Angular code must talk to GTM. We only push to &lt;code&gt;dataLayer&lt;/code&gt;. Thanks to &lt;code&gt;forward&lt;/code&gt;, Partytown sends it to the worker.&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;// analytics.service.ts&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;DOCUMENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&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;@angular/core&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;NavigationEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Router&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;@angular/router&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;takeUntilDestroyed&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;@angular/core/rxjs-interop&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;filter&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;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Window&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;dataLayer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&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="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&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;AnalyticsService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DOCUMENT&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;defaultView&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// null on the server (SSR)&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// An SPA has no real page load after the first one.&lt;/span&gt;
    &lt;span class="c1"&gt;// So we send a page view on each navigation.&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;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;filter&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;event&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;NavigationEnd&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;NavigationEnd&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;takeUntilDestroyed&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="nf"&gt;subscribe&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;page_view&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;page_path&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;urlAfterRedirects&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;track&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;string&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="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;unknown&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="k"&gt;void&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="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="nb"&gt;window&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="c1"&gt;// nothing to do on the server&lt;/span&gt;

    &lt;span class="k"&gt;this&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="nx"&gt;dataLayer&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="c1"&gt;// This call is patched by Partytown: it's serialized and sent to the worker.&lt;/span&gt;
    &lt;span class="c1"&gt;// GTM does its heavy work there, not on the main thread.&lt;/span&gt;
    &lt;span class="k"&gt;this&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="nx"&gt;dataLayer&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="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="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;Start it with the application:&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;// app.config.ts&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;ApplicationConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;provideAppInitializer&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;@angular/core&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;provideRouter&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;@angular/router&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;routes&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;./app.routes&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;AnalyticsService&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.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApplicationConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;provideRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;provideAppInitializer&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;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AnalyticsService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// create the service, so it listens to the router&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;h3&gt;
  
  
  Step 5: track a click
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// product-card.ts&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;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;input&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;@angular/core&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;AnalyticsService&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.service&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;CartStore&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;./cart.store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&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="nl"&gt;name&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="nl"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-product-card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;h3&amp;gt;{{ product().name }}&amp;lt;/h3&amp;gt;
    &amp;lt;button (click)="addToCart()"&amp;gt;Add to cart&amp;lt;/button&amp;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;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductCard&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;analytics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AnalyticsService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CartStore&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&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;addToCart&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. What the user wants to see: update the UI first&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;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&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="nf"&gt;product&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Tracking: cheap on the main thread, GTM works in the worker&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;analytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add_to_cart&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;item_id&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="nf"&gt;product&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;value&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="nf"&gt;product&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;price&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;Look at the click handler. Without Partytown, &lt;code&gt;dataLayer.push&lt;/code&gt; wakes up GTM &lt;strong&gt;on the main thread&lt;/strong&gt;, in the same frame as your click. With Partytown, the push is sent to the worker, and GTM runs its tags there. Your handler stays small. Your processing duration stays small.&lt;/p&gt;

&lt;h2&gt;
  
  
  React
&lt;/h2&gt;

&lt;p&gt;Same goal, same app: GTM in the worker, page views and a click.&lt;/p&gt;

&lt;p&gt;There are two ways to do it in React:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Option A: &lt;code&gt;index.html&lt;/code&gt;&lt;/strong&gt;, for a client-side app (Vite, Create React App...). The simplest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Option B: the &lt;code&gt;&amp;lt;Partytown /&amp;gt;&lt;/code&gt; component&lt;/strong&gt;, for apps rendered on the server (React Router framework mode, Remix, Gatsby...).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1: install and copy the files
&lt;/h3&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; @qwik.dev/partytown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Partytown comes with a small CLI to copy its files into your &lt;code&gt;public&lt;/code&gt; folder. Run it before &lt;code&gt;dev&lt;/code&gt; and &lt;code&gt;build&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"partytown"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"partytown copylib public/~partytown"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run partytown &amp;amp;&amp;amp; vite"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run partytown &amp;amp;&amp;amp; vite build"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add &lt;code&gt;public/~partytown&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt;. It's generated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option A: client-side app (Vite)
&lt;/h3&gt;

&lt;p&gt;Everything goes in &lt;code&gt;index.html&lt;/code&gt;, like in Angular:&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="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;My Shop&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;partytown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;forward&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;dataLayer.push&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/~partytown/partytown.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/partytown"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;l&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gtm.start&lt;/span&gt;&lt;span class="dl"&gt;'&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;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gtm.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&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="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="nx"&gt;dl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dataLayer&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;&amp;amp;l=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.googletagmanager.com/gtm.js?id=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;dl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;f&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="nb"&gt;document&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&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;dataLayer&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;GTM-XXXXXXX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"root"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/src/main.tsx"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why not in a component? Because a client-side app renders late. The third-party scripts would wait for React to boot. In &lt;code&gt;index.html&lt;/code&gt;, they're found as soon as Partytown starts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option B: server-rendered app (the &lt;code&gt;&amp;lt;Partytown /&amp;gt;&lt;/code&gt; component)
&lt;/h3&gt;

&lt;p&gt;The package ships a React component: &lt;code&gt;@qwik.dev/partytown/react&lt;/code&gt;. It's a thin wrapper around the Partytown snippet. The config becomes JSX props. And the snippet is &lt;strong&gt;inlined&lt;/strong&gt; in the HTML, so there's no extra request for &lt;code&gt;partytown.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Example with a root layout (React Router framework mode / Remix style):&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;// app/root.tsx&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;Partytown&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;@qwik.dev/partytown/react&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;Links&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Outlet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Scripts&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-router&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;GTM_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GTM-XXXXXXX&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;gtmSnippet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&amp;amp;l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;GTM_ID&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&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;head&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;Meta&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;Links&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;/* 1. Partytown: config as props */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Partytown&lt;/span&gt; &lt;span class="na"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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;dataLayer.push&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&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;/* 2. GTM in the worker */&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;script&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text/partytown"&lt;/span&gt;
          &lt;span class="na"&gt;dangerouslySetInnerHTML&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;__html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gtmSnippet&lt;/span&gt; &lt;span class="p"&gt;}&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;head&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;body&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;Outlet&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;Scripts&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;body&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;html&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;blockquote&gt;
&lt;p&gt;💡 Add &lt;code&gt;debug={true}&lt;/code&gt; on &lt;code&gt;&amp;lt;Partytown /&amp;gt;&lt;/code&gt; while you set things up.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Scripts added later
&lt;/h3&gt;

&lt;p&gt;If a &lt;code&gt;text/partytown&lt;/code&gt; script is added to the page &lt;strong&gt;after&lt;/strong&gt; Partytown started (lazy loading, route change...), recent versions of Partytown (0.14.3 and later) find it automatically. On older versions, you have to tell Partytown:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;script&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;createElement&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&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;script&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;text/partytown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// set the type BEFORE adding it to the DOM&lt;/span&gt;
&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://third-party.com/widget.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&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;script&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;dispatchEvent&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;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ptupdate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// only needed before 0.14.3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Track page views and clicks
&lt;/h3&gt;

&lt;p&gt;A small module, no library needed:&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;// analytics.ts&lt;/span&gt;
&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Window&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;dataLayer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&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;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;track&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;string&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="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;unknown&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="k"&gt;void&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="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&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="c1"&gt;// nothing to do on the server&lt;/span&gt;

  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="c1"&gt;// Patched by Partytown: serialized and sent to the worker&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&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="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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A hook for page views:&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;// usePageView.ts&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;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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useLocation&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-router&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;track&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;usePageView&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useLocation&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="nf"&gt;track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;page_view&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;page_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&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;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&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;Call it once, in your layout:&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;// Layout.tsx&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;Outlet&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-router&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;usePageView&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;./usePageView&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;usePageView&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Outlet&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the click:&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;// ProductCard.tsx&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;track&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useCart&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;./useCart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&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="nl"&gt;name&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="nl"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductCard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Product&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;add&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCart&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;handleAddToCart&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. UI first: this is what the user is waiting for&lt;/span&gt;
    &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Tracking: GTM does the heavy work in the worker&lt;/span&gt;
    &lt;span class="nf"&gt;track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add_to_cart&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;item_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&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;article&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;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;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="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleAddToCart&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Add to cart&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&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;article&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;blockquote&gt;
&lt;p&gt;Using Next.js? Partytown has its own Next.js integration page. Check it, the setup is a bit different.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Check that it works
&lt;/h2&gt;

&lt;p&gt;Don't trust, verify. Three quick checks in Chrome DevTools:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Console&lt;/strong&gt;: with the debug build, Partytown logs what runs in the worker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sources panel&lt;/strong&gt;: look at the threads. GTM should run in the Partytown web worker, not in the main one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance panel&lt;/strong&gt;: record a page load and a few clicks. The long tasks from &lt;code&gt;googletagmanager.com&lt;/code&gt; should be gone from the main thread track.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then check in GTM &lt;strong&gt;Preview mode&lt;/strong&gt; that your &lt;code&gt;page_view&lt;/code&gt; and &lt;code&gt;add_to_cart&lt;/code&gt; events arrive. If nothing arrives, check your &lt;code&gt;forward&lt;/code&gt; config first. It's the most common mistake.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where INP really wins
&lt;/h2&gt;

&lt;p&gt;Let's go back to our three phases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;               Input delay     Processing      Presentation
               ───────────     ──────────      ────────────
Partytown:     big impact      some impact     small impact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Input delay&lt;/strong&gt;: the big win. Third-party long tasks no longer block the main thread, so the click is handled right away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Processing duration&lt;/strong&gt;: the work done by third-party scripts when you push an event (like in &lt;code&gt;addToCart&lt;/code&gt;) happens in the worker, not in your handler's frame.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Presentation delay&lt;/strong&gt;: Partytown batches DOM reads and writes into group updates. Less layout thrashing from third-party code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Trade-offs: no magic here
&lt;/h2&gt;

&lt;p&gt;Partytown is a great tool. It's not a silver bullet. Here is what you need to know before using it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;preventDefault()&lt;/code&gt; has no effect.&lt;/strong&gt; When the worker receives the event, it's already too late. A script that needs to block a link or a form submit won't work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DOM operations are throttled.&lt;/strong&gt; Each call crosses threads. That's fine for analytics, not for scripts that build UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI-heavy scripts are a bad fit.&lt;/strong&gt; A chat widget, a cookie banner with a dialog, anything that injects a lot of DOM nodes: keep them on the main thread.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CORS.&lt;/strong&gt; Some third-party scripts don't send the right CORS headers. You'll need to proxy them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-origin iframes&lt;/strong&gt; created from a Partytown script can't persist cookies or storage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bad scripts stay bad.&lt;/strong&gt; A script that reads the whole &lt;code&gt;document&lt;/code&gt; every X milliseconds with &lt;code&gt;setInterval&lt;/code&gt; will overload Partytown.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A quick rule:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Good fit ✅&lt;/th&gt;
&lt;th&gt;Bad fit ❌&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Google Tag Manager&lt;/td&gt;
&lt;td&gt;Chat widgets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google Analytics 4&lt;/td&gt;
&lt;td&gt;Cookie consent dialogs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Facebook Pixel&lt;/td&gt;
&lt;td&gt;Scripts that call &lt;code&gt;preventDefault()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mixpanel, Segment, Amplitude&lt;/td&gt;
&lt;td&gt;Scripts that inject a lot of DOM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Measure, don't guess
&lt;/h2&gt;

&lt;p&gt;Never move a script "because it should be better". Measure before, measure after.&lt;/p&gt;

&lt;p&gt;INP is a &lt;strong&gt;field&lt;/strong&gt; metric. Your real users on real devices are the source of truth. Use the &lt;code&gt;web-vitals&lt;/code&gt; library with the attribution build to see which phase is slow. It works the same in React and Angular:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;onINP&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;web-vitals/attribution&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;onINP&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attribution&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;INP:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&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;Element:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attribution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interactionTarget&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;Input delay:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attribution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputDelay&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;Processing:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attribution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;processingDuration&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;Presentation:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attribution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;presentationDelay&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// send it to your analytics&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put it in &lt;code&gt;main.ts&lt;/code&gt; (Angular) or &lt;code&gt;main.tsx&lt;/code&gt; (React).&lt;/p&gt;

&lt;p&gt;Here's how to read the result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High &lt;strong&gt;input delay&lt;/strong&gt;? Look at what runs before the click. Third-party scripts are the usual suspects. This is where Partytown shines.&lt;/li&gt;
&lt;li&gt;High &lt;strong&gt;processing duration&lt;/strong&gt;? It's probably your handler. Partytown won't help.&lt;/li&gt;
&lt;li&gt;High &lt;strong&gt;presentation delay&lt;/strong&gt;? Look at your DOM size and your layout.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the lab, INP depends on what you click. Two tips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interact with the page &lt;strong&gt;while it loads&lt;/strong&gt;. That's when the main thread is the busiest.&lt;/li&gt;
&lt;li&gt;Use Total Blocking Time (TBT) as a proxy. It's not INP, but it tells you if the main thread is crowded.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Partytown won't fix your own code
&lt;/h2&gt;

&lt;p&gt;If your click handler takes 400 ms, moving GTM to a worker changes nothing.&lt;/p&gt;

&lt;p&gt;For your own code, the classic tools still apply:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Break long tasks into smaller ones.&lt;/li&gt;
&lt;li&gt;Yield to the main thread (&lt;code&gt;setTimeout&lt;/code&gt;, or &lt;code&gt;scheduler.yield()&lt;/code&gt; where it's supported).&lt;/li&gt;
&lt;li&gt;Show visual feedback &lt;strong&gt;first&lt;/strong&gt;, do the heavy work &lt;strong&gt;after&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Move heavy computation to your own web worker.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Partytown handles the code you don't own. You handle the code you own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;INP measures what users feel: "I clicked, did something happen?"&lt;/p&gt;

&lt;p&gt;Third-party scripts are often the hidden reason for a bad INP. They take the main thread, and your users wait.&lt;/p&gt;

&lt;p&gt;Partytown moves them into a web worker, and keeps them working thanks to a clever synchronous bridge (Atomics or a service worker).&lt;/p&gt;

&lt;p&gt;In Angular and React, the setup is small:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;serve the Partytown files&lt;/li&gt;
&lt;li&gt;add the config and &lt;code&gt;type="text/partytown"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;forward&lt;/code&gt; the functions your code calls&lt;/li&gt;
&lt;li&gt;keep your tracking code as simple as a &lt;code&gt;dataLayer.push&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's not magic. It's in beta, it has trade-offs, and it's not made for UI scripts. But for analytics, tag managers and pixels, it can give the main thread back to the only code that should be there: yours.&lt;/p&gt;

&lt;p&gt;Measure first. Move the right scripts. Measure again. 🎉&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>performance</category>
      <category>webperf</category>
      <category>javascript</category>
    </item>
    <item>
      <title>The Grand Unifying Architecture of Frontend</title>
      <dc:creator>Ryan Carniato</dc:creator>
      <pubDate>Tue, 22 Sep 2026 16:43:06 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/the-grand-unifying-architecture-of-frontend-bhk</link>
      <guid>https://dev.to/playfulprogramming/the-grand-unifying-architecture-of-frontend-bhk</guid>
      <description>&lt;p&gt;I used to joke that the history of frontend development could be retraced by following the argument of who owned the state. And the truth is both sides have gone back and forth on this over the years. We started on the document web where state was on the server. Then we added JavaScript and Applets where we could have stateful things in the client. But developers at the time didn't like either much and tried to wire state across both sides in their server languages and we got ASP.NET and the like.&lt;/p&gt;

&lt;p&gt;I sit here over 30 years later and it is still going on. Going to replace your React app with HTMX? Maybe. Maybe it isn't that simple. Although this seems like an endless spiral, I don't think this is unsolvable. Even though it has been going on so long, I actually think the answer is in front of us.&lt;/p&gt;

&lt;p&gt;The unified model comes down to understanding responsibilities. And they are as old as the web itself. Every time we misalign complexity soars. Every time we ignore part of the puzzle it goes on to bite us. Every solution straddles part of this solution space but we've struggled to cover the whole range.&lt;/p&gt;




&lt;h2&gt;
  
  
  Core Responsibilities
&lt;/h2&gt;

&lt;p&gt;My observation is that all frontend architectures come down to layering of three responsibilities. Some are more present than others but they all exist in some form.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Navigation (client)
&lt;/h4&gt;

&lt;p&gt;The URL is the basis of the web experience and it belongs to the browser. Orchestration is a client responsibility and every solution from SPA to HTML partial agrees with this. JavaScript in the browser is the root of our application.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Content (server)
&lt;/h4&gt;

&lt;p&gt;Conversely, content belongs to the server whether it is markup or JSON. The server is the authoritative source. It hangs off of our navigation.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Affordances (client)
&lt;/h4&gt;

&lt;p&gt;Affordances find us back in the browser. Local UI state, in progress work, optimistic updates. We need to provide our user feedback faster than a round-trip to the server.&lt;/p&gt;




&lt;h2&gt;
  
  
  Following the Shape
&lt;/h2&gt;

&lt;p&gt;All web application architectures follow this client -&amp;gt; server -&amp;gt; client layering. A user navigates or performs an action, content comes back from the server, and the client settles it. That's the Single Page Application(SPA) architecture after load, but it's also HTMX. And also LiveView, if the server -&amp;gt; client protocol is powerful enough.&lt;/p&gt;

&lt;p&gt;It feels like I'm stating the obvious here. But the web generally maps writes to navigation, and the server only pushes reads. Mutations travel through layer 1 with forms, actions, invalidation, and requests for the content. Layer 2, content, never writes and only publishes.&lt;/p&gt;

&lt;p&gt;This asymmetry is what makes it composable. Breaking that leads to two stateful systems fighting against themselves. Which is why affordances like optimism(layer 3) fit cleanly on top if viewed as an overlay. The client never writes server content directly as it doesn't own it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Revisiting the Spectrum
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpzb56rtip9b4vtxaz01t.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpzb56rtip9b4vtxaz01t.png" alt="Unified Quadrants" width="799" height="601"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've tried many times to map the solution space to a 4 quadrant grid but I never found the right axis. But I'm now seeing how all solutions can sit in the same diagram.&lt;/p&gt;

&lt;p&gt;Obviously this is a rough placement, solutions fit spaces not points, and something like React can be used as a frontend in a sync engine like Zero. Here React represents classic SPAs + JSON API. But most frameworks fix both transport and affordance weight so they don't move much.&lt;/p&gt;

&lt;p&gt;But if you put it under our lens of responsibility:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;HTMX&lt;/strong&gt;: Minimal layer 1, HTML carries the majority of the work under layer 2, and layer 3 is almost nothing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LiveView&lt;/strong&gt;: Layer 2 expands its capability, while layer 3 remains nearly non-existent. Same dance with the read window stretched to the length of the session.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DataStar&lt;/strong&gt;: More machinery in layer 2 with the addition of server populated Signals allowing for some closer interaction with the client.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Astro(+ View Transitions)&lt;/strong&gt;: navigation(view transition) -&amp;gt; server(markup) -&amp;gt; client(islands). All 3 parts laid out explicitly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Server Components(Next.js)&lt;/strong&gt;: Like Astro except shared client state is preserved.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SPA + JSON(React)&lt;/strong&gt;: Layer 3 grows until it merges with layer 1. And layer 2 is delegated to JSON APIs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sync Engine(Zero)&lt;/strong&gt;: Layer 2 becomes a persistent read of a replicated store, and layer 3 grows to hold a full local copy of it. Optimism over the whole dataset.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every solution, with a bit of coaxing, is capable of expressing all of these. They differ in transport, window length, and how much affordance code you ship. So it comes down to efficiency and authoring. Which means one framework could cover it all, if each responsibility is adjustable.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Modern Take
&lt;/h2&gt;

&lt;p&gt;Nothing I've laid out is any different than how the base platform has always worked, but can we mirror this in a way that encompasses the full spectrum of the past 30 years?&lt;/p&gt;

&lt;p&gt;I'm going to paint a picture using our 3 responsibilities with Solid 2.0.&lt;/p&gt;




&lt;h3&gt;
  
  
  Navigation: Delegation and progressive enhancement
&lt;/h3&gt;

&lt;p&gt;Solid's router owns nothing in the render tree. There is no &lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt;(or &lt;code&gt;&amp;lt;A&amp;gt;&lt;/code&gt;) component. Instead our JSX compiler registers a claim on every &lt;code&gt;a[href]&lt;/code&gt; and &lt;code&gt;form[action]&lt;/code&gt; element it creates, and server-streamed content gets the same claims swept over any subtree the runtime renders.&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;// lib/users.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renameUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&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="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormData&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;updateUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&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="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formData&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&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="c1"&gt;// routes/users/[id].tsx — plain HTML. The compiler claims the&lt;/span&gt;
&lt;span class="c1"&gt;// anchor and the form. The router intercepts both. Works before &lt;/span&gt;
&lt;span class="c1"&gt;// hydration and after.&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`/users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&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="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;renameUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Rename&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/form&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Note: There are no components in this markup.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A SolidJS router consumes those claims to manage &lt;code&gt;aria-current&lt;/code&gt;, active classes, and interception. This means server-rendered markup with zero client components participates fully in client-side navigation. So it works not only with "No JS" but with JavaScript with no interactive components.&lt;/p&gt;

&lt;p&gt;Forms and buttons delegate to server function actions the same way. You hoist your preloads and invalidation correctly and single-flight mutations mean one round trip settles every piece of UI the mutation invalidated whether that UI is JSON-fed client components or server-owned markup.&lt;/p&gt;




&lt;h3&gt;
  
  
  Content: Reactive reads over the wire
&lt;/h3&gt;

&lt;p&gt;Solid's powerful protocol for layer 2 stands on our &lt;code&gt;"use server"&lt;/code&gt; functions. Solid serializes across the boundary with Seroval, which handles not just plain data but async structures, like Promises and async iterators. The communication between server and client is about the data, including data that hasn't settled yet.&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="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;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&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="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use 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;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;// AsyncIterable&amp;lt;string&amp;gt; — crosses the wire as-is&lt;/span&gt;
    &lt;span class="na"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;watchProgress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&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="c1"&gt;// client: the iterable's latest yield is just a reactive read&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;progress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;r&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&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;r&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&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;progress&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More than that, the reactive graph propagates over the wire. On the server, every expression feeding the client is an open binding for the life of the response. When a promise settles or iterator yields the server re-runs the expression and sends the new value to the client to be replaced or morphed.&lt;/p&gt;

&lt;p&gt;So far I've showcased this in examples around the original response, ie what happens during hydration. Even if the content chunks stream in as they initially are ready, we can continue to update the stream in place as long as the request stays open.&lt;/p&gt;

&lt;p&gt;But the same can be true of server function requests after the fact. We can send back would-be static markup, "Server Components", and have their reactive graph never ship to the client while still doing these streamed pinpoint updates. We will be announcing the preview for our Server Component feature soon (but if you look you can already see &lt;a href="https://github.com/solidjs/solid/tree/next/examples/hackernews" rel="noopener noreferrer"&gt;fully working&lt;/a&gt; &lt;a href="https://github.com/solidjs/solid/tree/next/examples/notes" rel="noopener noreferrer"&gt;examples&lt;/a&gt; &lt;a href="https://github.com/solidjs/solid/tree/next/examples/chat" rel="noopener noreferrer"&gt;in our repo&lt;/a&gt;).&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="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;countdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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="s2"&gt;use 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;ticks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// AsyncIterable&amp;lt;number&amp;gt;, one per second&lt;/span&gt;

  &lt;span class="c1"&gt;// returning a function returns a Server Component&lt;/span&gt;
  &lt;span class="k"&gt;return &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ticks&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Counted&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;&lt;/span&gt;&lt;span class="err"&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;// client — zero component code ships for this&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;countdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Counter&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the communication is one-way with a single authoring model, the transport is pluggable. Today we use Request/Response, so it lives as long as the request is open and you reconnect as needed. Swap in something persistent like SSE or a socket and the same server component becomes a non-terminating live component.&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;// request/response — left side of the grid&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getAuction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use 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;return&lt;/span&gt; &lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// persistent — right side of the grid&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;liveAuction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;live&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use 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;while &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="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;changed&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="c1"&gt;// the consumer doesn't care which one it got&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;auction&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createOptimisticStore&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;liveAuction&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;What makes this safe is what LiveView's failure mode teaches us: &lt;strong&gt;The server's live graph must be a re-derivable projection of durable state.&lt;/strong&gt; It is never the source of truth. Reconnection doesn't depend on sessions, event replay, or anything that dies with a process.&lt;/p&gt;




&lt;h3&gt;
  
  
  Affordances: An async-aware graph that hydrates once
&lt;/h3&gt;

&lt;p&gt;This is an area SolidJS has excelled in. But in 2.0, async became native to the graph. A value arriving from the server content channel and a value computed locally are indistinguishable to the consumer.&lt;/p&gt;

&lt;p&gt;Optimism is a first-class primitive. It isn't just something bundled with the server functions or query caching library. Optimism represents any uncommitted state predicted over async, whatever the async source. It's a general concept. It doesn't just represent what some might consider "lying to the user", it doesn't have to masquerade as an invisible success. It represents any predictive ephemeral affordance. It either settles eventually as truth or it is reverted, but it never outlives its transaction. It prevents getting out of sync.&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;auction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setAuction&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createOptimisticStore&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;liveAuction&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;placeBid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// overlay — reverts on settle&lt;/span&gt;
  &lt;span class="nf"&gt;setAuction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&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;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;highBid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="c1"&gt;// mutation - the write goes up&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nf"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// hold until truth comes back down             &lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nf"&gt;until&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;auction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;highBid&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;amount&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 all works because we &lt;strong&gt;never hydrate more than once.&lt;/strong&gt; After startup, the server should never render a client component as client state has diverged from anything the server could know. This is a critical naive breaking point in HTML Partial and Islands solutions that share client state. Hydrating after state has updated is a recipe for hydration mismatches. While we can safeguard during the initial streaming progression, that isn't a realistic proposition over the life of an application.&lt;/p&gt;

&lt;p&gt;A rule like that only holds if the system enforces it. Every piece of server content is keyed by the call that produced it, the function and its arguments, the same key a query cache would use. Content arriving on any transport only ever writes to that address's store, and mounted UI pulls from the address it's bound to. There is no direct connection from the network to the DOM. So a hover preload can't clobber the page you're looking at, a refetch morphs in place while the client-owned ranges inside it survive, and a stale response doesn't pass a version check. &lt;/p&gt;

&lt;p&gt;Under this unified model, initial load gets the same treatment without paying serialization twice. Client components claim their server-rendered nodes and boot makes zero requests. The only serialized records are values the client requires. Server-only content ships as HTML or as data, never both. The page markup is the payload. For server components, that means always one copy. View-source and search for any piece of content and you'll find it exactly once.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff3pwdopw5mf7zjvk8505.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff3pwdopw5mf7zjvk8505.png" alt="serialized once" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I once positioned this as "Facing Frontend's Existential Crisis", but now it is a solved problem.&lt;/p&gt;

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




&lt;h2&gt;
  
  
  Traversing the Spectrum
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpzb56rtip9b4vtxaz01t.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpzb56rtip9b4vtxaz01t.png" alt="Unified Quadrants" width="799" height="601"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What makes this exciting is with the pieces above you are completely free to move the whole solution space and every point in between.&lt;/p&gt;

&lt;p&gt;If you start in the lower left corner, that would be like a SolidJS app with no client components that just swaps server component chunks on server &lt;code&gt;"use server"&lt;/code&gt; actions bound to &lt;code&gt;&amp;lt;form action&amp;gt;&lt;/code&gt;. Server markup streams and morphs, and navigation is handled without any client components.&lt;/p&gt;

&lt;p&gt;The top left-hand side is when we extend layer 3 until it dominates and you have the SPA and there are no server components or server rendering at all. Just JSON APIs.&lt;/p&gt;

&lt;p&gt;And we can continue to traverse over the top to the right-hand side by the way of persistent &lt;code&gt;"use server"&lt;/code&gt; functions and our granular optimistic layer. Solid's &lt;code&gt;live&lt;/code&gt; and &lt;code&gt;until&lt;/code&gt; operators help close the gap when the client state is streamed in as available. I'd still use a dedicated sync engine, but the base primitives alone do a good impression.&lt;/p&gt;

&lt;p&gt;Finally, the lower right corner, is closer to where we started. Except this time our server components have reactive bindings and their connection is persistent. No client components, just our same "use server" action protocol to write back, while our reads stream fine-grained markup partials over the wire. In this world, even the server components could be optimistic depending on how you write your actions, and &lt;code&gt;until&lt;/code&gt; again gives us a mechanism for completion acknowledgements.&lt;/p&gt;

&lt;p&gt;The incredible part is that this required nothing that users of Solid 2.0 aren't already familiar with. Async Signals and &lt;code&gt;use server&lt;/code&gt;. With that, all these experiences are authorable using basically the same patterns, in the same framework, even in the same application.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fek74ygnjpy4rf4n0gkc3.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fek74ygnjpy4rf4n0gkc3.png" alt="A Möbius strip: one surface that appears to have two sides" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It goes back to where we started. A few pieces that have been sitting in front of us this whole time.&lt;/p&gt;

&lt;p&gt;The browser owns navigation and affordances. The server owns content. Communication is a loop with writes travelling as navigation and content travelling as one-way reads. And the transport and the response window should be configurable without changing the shape of authoring.&lt;/p&gt;

&lt;p&gt;Everything from HTMX to LiveView to islands to the SPA is a coordinate on this grid. The differences between them are efficiency and ergonomics rather than expressiveness.&lt;/p&gt;

&lt;p&gt;What makes this more than an exercise in categorization is that all the hard pieces are built. We serialize and hydrate once. The reactive graph serializes its own updates. Content is stored by address, so stale hydration can't be represented.&lt;/p&gt;

&lt;p&gt;It comes as little surprise to me that Signals can act as that common representation on all sides. Sync and async. Server and client. Stateful and stateless.&lt;/p&gt;

&lt;p&gt;This is stupidly ambitious. RSCs only aimed to provide the left half of the grid and many felt they still fell short. The HTML-over-the-wire camp has aimed to provide the bottom half, but ignoring the client is ignoring an entire responsibility.&lt;/p&gt;

&lt;p&gt;And yet, this is a future that I want to live in. One where choosing a framework doesn't require you to choose a religion. And one, from my perspective, that is already here.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>solidjs</category>
      <category>architecture</category>
    </item>
    <item>
      <title>SpellBook of Skill: The Twin Project Nobody Asked For, But I Built Anyway 🧙🏻‍♂️</title>
      <dc:creator>Domenico Tenace</dc:creator>
      <pubDate>Mon, 21 Sep 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/spellbook-of-skill-the-twin-project-nobody-asked-for-but-i-built-anyway-564c</link>
      <guid>https://dev.to/playfulprogramming/spellbook-of-skill-the-twin-project-nobody-asked-for-but-i-built-anyway-564c</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Hey everyone 👋&lt;/p&gt;

&lt;p&gt;A while back I launched Spellbook of Prompt, my curated collection of prompts organized by use case. It's been out there growing steadily, and at some point while adding new entries I kept thinking: prompts are only half the story. Knowing &lt;em&gt;how&lt;/em&gt; to build something matters just as much as knowing what to ask an AI to do.&lt;/p&gt;

&lt;p&gt;So I built the twin project. Meet &lt;a href="https://github.com/Domenico-Tenace-Open-Labs/spellbook-of-skill" rel="noopener noreferrer"&gt;&lt;strong&gt;Spellbook of Skill&lt;/strong&gt;&lt;/a&gt;, a curated collection of development skills, tutorials, and guides to help you level up your craft from fundamentals to advanced techniques.&lt;/p&gt;

&lt;p&gt;Let's dive in! 🤙&lt;/p&gt;




&lt;h2&gt;
  
  
  What It Actually Is 🤔
&lt;/h2&gt;

&lt;p&gt;Where Spellbook of Prompt gives you the incantation, Spellbook of Skill teaches you the magic behind it. It's structured, practical guides organized by category and difficulty, each one built to save time and actually get you somewhere, not just another wall of theory.&lt;/p&gt;

&lt;p&gt;Every guide follows the same shape: a clear learning objective, a working code example, prerequisites listed upfront, and a troubleshooting section for when things inevitably don't work on the first try. The concepts are language and framework agnostic where possible, so what you learn actually transfers instead of being locked to one stack.&lt;/p&gt;

&lt;p&gt;The whole thing runs on Astro with Starlight, same setup as Spellbook of Prompt, so browsing is fast and the docs feel consistent across both projects. It's a genuine sibling project, same philosophy, different content.&lt;/p&gt;




&lt;h2&gt;
  
  
  Built Entirely with OpenCode + OpenRouter + DeepSeek V4 Pro 🛠️
&lt;/h2&gt;

&lt;p&gt;Here's the part that ties back to everything I've been writing about lately. I built Spellbook of Skill end to end using OpenCode wired through OpenRouter, running on DeepSeek V4 Pro 0813.&lt;/p&gt;

&lt;p&gt;No manual scaffolding, no switching to a pricier model halfway through. The project structure, the Astro and Starlight configuration, the content schema, the initial category setup, all of it came out of that same combo I've been using as my daily driver. If you've read my previous pieces on DeepSeek's cost-to-quality ratio or on wiring OpenRouter into OpenCode, this is that setup doing real, shippable work again.&lt;/p&gt;

&lt;p&gt;It's honestly become my proof of concept for "can a budget model stack actually ship a complete open source project." So far the answer keeps being yes.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's In There Right Now 📚
&lt;/h2&gt;

&lt;p&gt;The structure is organized by category, starting with:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Content Creation&lt;/strong&gt;, structured prompts and guides for writing clear technical articles, the same kind of workflow I use for these posts.&lt;/p&gt;

&lt;p&gt;More categories are marked as coming soon: coding and development workflows, DevOps and deployment, system design and architecture, design and creative development, career and professional growth.&lt;/p&gt;

&lt;p&gt;It's early. I'd rather be upfront about that than oversell it. The foundation, the docs site, the contribution flow, the conventions, is all solid and ready. The content library is what needs to grow now, and that's exactly where the community comes in.&lt;/p&gt;




&lt;h2&gt;
  
  
  This Is Where You Come In 🤝
&lt;/h2&gt;

&lt;p&gt;Spellbook of Skill is MIT licensed and genuinely open to contributions. Whether you want to add a new guide, improve an existing one, or just fix something that's broken, there's a clear path to do it.&lt;/p&gt;

&lt;p&gt;The process is simple: search existing guides first to avoid duplicates, add or update a guide in the right category folder, include a clear title, description, and at least one complete working example, then open a pull request with a short summary. Guides should stay concise and actionable, list prerequisites and learning objectives, include a troubleshooting section, and obviously never contain real secrets or sensitive data.&lt;/p&gt;

&lt;p&gt;Full details are in the CONTRIBUTING.md file if you want the complete picture before jumping in.&lt;/p&gt;

&lt;p&gt;If you've got a skill you wish someone had written down clearly when you were learning it, that's exactly the kind of guide this project needs. Beginner content is just as valuable as advanced content here.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It Out 🚀
&lt;/h2&gt;

&lt;p&gt;The live docs are up at &lt;a href="https://spellbook-of-skill.netlify.app/" rel="noopener noreferrer"&gt;spellbook-of-skill.netlify.app&lt;/a&gt;, or clone it and run it locally:&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/Domenico-Tenace-Open-Labs/spellbook-of-skill.git
&lt;span class="nb"&gt;cd &lt;/span&gt;spellbook-of-skill
pnpm &lt;span class="nb"&gt;install
&lt;/span&gt;pnpm dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open localhost:4321 and start browsing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts 🌟
&lt;/h2&gt;

&lt;p&gt;Spellbook of Prompt and Spellbook of Skill now sit side by side under Domenico Tenace Open Labs, one teaching you what to ask, the other teaching you how to actually do it. Together they cover more ground than either could alone.&lt;/p&gt;

&lt;p&gt;If you're curious about the project, go star it, and if you've got something worth teaching, open a PR. This is exactly the kind of thing that gets better the more people show up.&lt;/p&gt;

&lt;p&gt;The spellbook grows. 🔥&lt;/p&gt;

&lt;p&gt;Happy coding! ✨&lt;/p&gt;




&lt;p&gt;Hi 👋🏻&lt;br&gt;
My name is Domenico, software developer passionate of Open Source, I write article about it for share my knowledge and experience.&lt;br&gt;
Don't forget to visit my social links to discover my links, check out Domenico Tenace Open Labs for my open source projects and subscribe to my newsletter! 🫰🏻&lt;/p&gt;

&lt;p&gt;🌲 Links: &lt;a href="https://links.domenicotenace.dev/" rel="noopener noreferrer"&gt;https://links.domenicotenace.dev/&lt;/a&gt;&lt;br&gt;
🐙 Domenico Tenace Open Labs: &lt;a href="https://github.com/Domenico-Tenace-Open-Labs" rel="noopener noreferrer"&gt;https://github.com/Domenico-Tenace-Open-Labs&lt;/a&gt;&lt;br&gt;
📰 Newsletter: &lt;a href="https://domenicotenace.substack.com/subscribe" rel="noopener noreferrer"&gt;https://domenicotenace.substack.com/subscribe&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you like my content or want to support my work, you can support me with a small donation. I would be grateful 🥹&lt;/p&gt;

&lt;p&gt;☕ Buy me a Coffee: &lt;a href="https://buymeacoffee.com/domenicotenace" rel="noopener noreferrer"&gt;https://buymeacoffee.com/domenicotenace&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>deepseek</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>OpenCode + DeepSeek V4: The Budget Combo That Doesn't Feel Like a Budget Combo 🐋</title>
      <dc:creator>Domenico Tenace</dc:creator>
      <pubDate>Tue, 01 Sep 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/opencode-deepseek-v4-the-budget-combo-that-doesnt-feel-like-a-budget-combo-4kca</link>
      <guid>https://dev.to/playfulprogramming/opencode-deepseek-v4-the-budget-combo-that-doesnt-feel-like-a-budget-combo-4kca</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Hey everyone 👋&lt;/p&gt;

&lt;p&gt;I've spent the last few weeks running most of my daily coding work through OpenCode wired to DeepSeek V4, both Pro and Flash. Not as an experiment, as my actual default setup. And the thing that keeps surprising me is how rarely I miss Claude or GPT for everyday tasks, while paying a fraction of the price.&lt;/p&gt;

&lt;p&gt;I also used DeepSeek exclusively to generate an entire Astro template from scratch, Studio Pulse, which turned out to be a genuinely good test of what these models can actually do unattended.&lt;/p&gt;

&lt;p&gt;Let me walk you through what I found. Let's dive in! 🤙&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Context on DeepSeek V4 🤔
&lt;/h2&gt;

&lt;p&gt;DeepSeek V4 is an open weight, MIT licensed model family that went GA in two waves this year, V4-Flash on July 31 and V4-Pro on August 13. Both are mixture of experts models with a 1M token context window.&lt;/p&gt;

&lt;p&gt;V4-Pro runs 1.6T total parameters with 49B active per token. V4-Flash is leaner, 284B total with 13B active. Both support tool use, function calling, and a thinking mode that effectively replaces the old R1 reasoning line.&lt;/p&gt;

&lt;p&gt;The current API pricing sits at $0.435/$0.87 per million input/output tokens for Pro, and $0.14/$0.28 for Flash. Cache hit input drops even further, down to $0.0036 for Pro and $0.0028 for Flash. For context, Claude Opus output alone costs $25 per million tokens. A dollar buys you roughly 500K off-peak output tokens from V4-Pro, versus 40K from Opus.&lt;/p&gt;

&lt;p&gt;That's not a small gap. That's an order of magnitude.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting It Up with OpenCode 🔧
&lt;/h2&gt;

&lt;p&gt;Since OpenCode is model agnostic, plugging in DeepSeek is just a matter of pointing it at DeepSeek's API endpoint (or routing through OpenRouter, which I also do for some workflows). No special configuration, no compatibility layer, it speaks both OpenAI ChatCompletions and Anthropic API formats natively.&lt;/p&gt;

&lt;p&gt;I keep both models available and switch depending on the task:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DeepSeek V4 Flash&lt;/strong&gt; for quick iterations, small fixes, boilerplate, and anything where speed matters more than depth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DeepSeek V4 Pro&lt;/strong&gt; for anything that needs real reasoning, architecture decisions, or larger refactors.&lt;/p&gt;

&lt;p&gt;Switching between them mid-session in OpenCode takes one command. No restart, no reconfiguration.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Actually Impressed Me 👍
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The cost is almost absurd.&lt;/strong&gt; Running a full day of development work, dozens of file edits, multiple refactoring sessions, test generation, through V4-Pro costs me less than a coffee. Through V4-Flash it's closer to pocket change. I stopped thinking about token budgets entirely, which changes how you work. You stop being conservative with your prompts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coding quality holds up.&lt;/strong&gt; On Artificial Analysis benchmarks, V4-Flash scores 69.1 on the coding index, ranking 29th out of nearly 200 models tracked. That's genuinely competitive, not "good for the price," just good. For everyday tasks, boilerplate, CRUD logic, component generation, API routes, I couldn't tell the difference from more expensive models in blind use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The 1M token context window is a real advantage.&lt;/strong&gt; Both models default to a million tokens of context. For large codebases or long refactoring sessions where you want the model to see everything at once, this matters more than raw intelligence scores.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool use and function calling are solid.&lt;/strong&gt; Agentic workflows in OpenCode, file edits, shell commands, multi-step tasks, worked reliably. I didn't run into the kind of tool-call confusion I've seen with smaller open models in the past.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cache hit pricing rewards good habits.&lt;/strong&gt; If you structure your prompts with static content first and variable content last, DeepSeek's caching drops input costs by roughly 88%. That's a meaningful incentive to write better prompts, and OpenCode's session handling makes this easy to leverage naturally.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where It Falls Short 👎
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Agentic capability trails coding capability.&lt;/strong&gt; DeepSeek themselves and independent reviews are honest about this: the model handles individual coding tasks well, but complex autonomous multi-step processes need tighter task boundaries and more validation than you'd need with Claude Opus or GPT-5.5. Long, loosely defined agent runs are where it's more likely to drift.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No image or video input.&lt;/strong&gt; V4 is text and code only. If your workflow involves reviewing screenshots or mockups, you're switching models anyway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing isn't fully stable.&lt;/strong&gt; DeepSeek has already signaled a broader price increase without publishing new rates yet, and peak/off-peak tiers were introduced in mid-August. The eye-popping pricing I quoted above could shift. Budget for it, but don't assume it's permanent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Less polished on ambiguous instructions.&lt;/strong&gt; When a prompt is vague, DeepSeek tends to make more assumptions than Claude does. Being explicit about what you want pays off more here than with pricier models that are better at inferring intent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reasoning depth on genuinely hard problems.&lt;/strong&gt; For architecture decisions with lots of competing trade-offs, V4-Pro is good but not at the level of the very top tier models. I still occasionally reach for something stronger when the stakes are high enough to justify the cost difference.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Test: Studio Pulse 🚀
&lt;/h2&gt;

&lt;p&gt;To really push this setup, I built &lt;a href="https://github.com/DomeT99/studio-pulse" rel="noopener noreferrer"&gt;Studio Pulse&lt;/a&gt; entirely through DeepSeek, no manual coding, no assist from another model.&lt;/p&gt;

&lt;p&gt;It's a template for a full-service communication agency marketing site: multi-page structure with home, about, blog, and contact, dark and light mode with OS preference detection and persistence, SPA-style page transitions via Astro's ClientRouter, a blog powered by content collections with typed frontmatter, and accessibility handled properly, skip links, semantic landmarks, focus-visible outlines, reduced-motion support.&lt;/p&gt;

&lt;p&gt;Stack is Astro 7 with Bulma 1.0.4, statically generated, dependency-light, pnpm as the package manager. Clean project structure, typed content schema, CSS custom properties for theming.&lt;/p&gt;

&lt;p&gt;What stood out building it: DeepSeek handled the Astro-specific patterns (content collections, the loaders API, client router transitions) correctly on the first or second pass most of the time. The accessibility details weren't an afterthought either, I asked for them once early in the process and the model kept applying them consistently across new pages without being reminded.&lt;/p&gt;

&lt;p&gt;It's live at studio-pulse-demo.netlify.app, MIT licensed, and available as a GitHub template if you want to spin up something similar.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who Should Try This Combo 🎯
&lt;/h2&gt;

&lt;p&gt;If you're cost-conscious and building things where "good enough" coding quality is genuinely enough, which is most day-to-day development, this setup is hard to beat. Side projects, templates, internal tools, prototypes, all of it fits well within DeepSeek's strengths.&lt;/p&gt;

&lt;p&gt;If you're running agent pipelines at scale where token costs actually matter to your bottom line, the math here is compelling enough to at least test against your current model.&lt;/p&gt;

&lt;p&gt;Where I'd still reach for something else: safety-critical code, deeply ambiguous architecture decisions, or long autonomous agent runs with minimal supervision. That's where the extra cost of a stronger model still earns its keep.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts 🌟
&lt;/h2&gt;

&lt;p&gt;I didn't expect to make DeepSeek V4 my daily driver, but that's what's happened. Between OpenCode's flexibility and DeepSeek's pricing, the barrier to just trying things dropped to nearly zero, and that changed how much I experiment.&lt;/p&gt;

&lt;p&gt;Studio Pulse is proof that a fully model-generated project doesn't have to feel like a rough draft. It's not a toy, it's an actual usable template with real attention to detail.&lt;/p&gt;

&lt;p&gt;Is DeepSeek the best model on the market right now? No. Is it the best model per dollar? By a wide margin, yes. And for most of what I build day to day, that's the metric that actually matters.&lt;/p&gt;

&lt;p&gt;Happy coding! ✨&lt;/p&gt;




&lt;p&gt;Hi 👋🏻&lt;br&gt;
My name is Domenico, software developer passionate of Open Source, I write article about it for share my knowledge and experience.&lt;br&gt;
Don't forget to visit my social links to discover my links, check out Domenico Tenace Open Labs for my open source projects and subscribe to my newsletter! 🫰🏻&lt;/p&gt;

&lt;p&gt;🌲 Links: &lt;a href="https://links.domenicotenace.dev/" rel="noopener noreferrer"&gt;https://links.domenicotenace.dev/&lt;/a&gt;&lt;br&gt;
🐙 Domenico Tenace Open Labs: &lt;a href="https://github.com/Domenico-Tenace-Open-Labs" rel="noopener noreferrer"&gt;https://github.com/Domenico-Tenace-Open-Labs&lt;/a&gt;&lt;br&gt;
📰 Newsletter: &lt;a href="https://domenicotenace.substack.com/subscribe" rel="noopener noreferrer"&gt;https://domenicotenace.substack.com/subscribe&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you like my content or want to support my work, you can support me with a small donation. I would be grateful 🥹&lt;/p&gt;

&lt;p&gt;☕ Buy me a Coffee: &lt;a href="https://buymeacoffee.com/domenicotenace" rel="noopener noreferrer"&gt;https://buymeacoffee.com/domenicotenace&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>deepseek</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
