<?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: Carlo Straccialini</title>
    <description>The latest articles on DEV Community by Carlo Straccialini (@straccia17).</description>
    <link>https://dev.to/straccia17</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%2Fuser%2Fprofile_image%2F317094%2F84971f25-e400-420b-bd74-95c7a5daea70.png</url>
      <title>DEV Community: Carlo Straccialini</title>
      <link>https://dev.to/straccia17</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/straccia17"/>
    <language>en</language>
    <item>
      <title>From Angular.js to Fine-Grained Reactivity: Compiling Templates at Build-Time</title>
      <dc:creator>Carlo Straccialini</dc:creator>
      <pubDate>Thu, 25 Jun 2026 10:12:21 +0000</pubDate>
      <link>https://dev.to/straccia17/from-angularjs-to-fine-grained-reactivity-compiling-templates-at-build-time-k41</link>
      <guid>https://dev.to/straccia17/from-angularjs-to-fine-grained-reactivity-compiling-templates-at-build-time-k41</guid>
      <description>&lt;p&gt;We are in 2026, but in some corners of the enterprise world, it still feels like 2014. One of those places is the frontend stack of my current company.&lt;/p&gt;

&lt;p&gt;Over a decade ago, when we launched as a new digital retail bank in the Italian market, we had to choose our technology foundation. We adopted a third-party product—a widget container—that brought Angular.js along as its primary frontend framework.&lt;/p&gt;

&lt;p&gt;Fast forward ten years: hundreds of banking features have been shipped, and our architecture has ballooned into a massive fleet of &lt;strong&gt;about 450 Angular.js widgets&lt;/strong&gt;. Today, our codebase is colossal, making a traditional "rip-and-replace" migration look like an impossible mountain to climb.&lt;/p&gt;

&lt;p&gt;Yet, staying still is no longer an option:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Legacy Trap:&lt;/strong&gt; Critical external dependencies—including Angular.js itself—are no longer maintained, posing security and compliance risks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Architecture Friction:&lt;/strong&gt; Integrating modern architectural patterns or modern design systems is extremely hard without adopting fragile workarounds (like heavy Web Component wrappers).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Talent Crunch:&lt;/strong&gt; Hiring junior or mid-level developers has become an uphill battle. No one wants to build a career on a tech stack from 2012; the market speaks React, Vue, and modern Angular.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Our Path
&lt;/h2&gt;

&lt;p&gt;During the pandemic, our internal team took the first big step toward freedom: we completely ripped out the legacy third-party widget container and replaced it with a custom, in-house solution. With that constraint gone, the Angular.js lock-in was officially broken.&lt;/p&gt;

&lt;p&gt;We faced two clear paths for the migration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Traditional Route:&lt;/strong&gt; Pick a modern framework, hire a massive IT consulting company, and spend millions of euros over a risky, 2-year rewrite—only to potentially find ourselves in the exact same legacy trap a decade from now.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Engineer's Route:&lt;/strong&gt; Develop a custom solution internally, just as we successfully did for our widget container.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Driven by engineering pragmatism, &lt;strong&gt;we chose the second path.&lt;/strong&gt; Inspired by the build-time philosophy of Svelte and the fine-grained reactivity of Solid.js, we decided to build a custom compiler. Our goal? Transform legacy Angular.js HTML templates into highly optimized, modern JS modules based on fine-grained reactivity—&lt;strong&gt;without changing a single line of our existing template or controller files.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Concepts
&lt;/h2&gt;

&lt;p&gt;To understand how we achieved this, let's look at how Angular.js works under the hood.&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="c"&gt;&amp;lt;!-- Template --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Hello {{ name }}!&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Controller&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;SimpleController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$scope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;$scope&lt;/span&gt;&lt;span class="p"&gt;.&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;Mario&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At runtime, the framework parses the template, reads the &lt;code&gt;$scope&lt;/code&gt; object, and replaces the dynamic variables to output vanilla HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Hello Mario!&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This mechanism is driven by the Angular.js &lt;strong&gt;Digest Cycle&lt;/strong&gt;. During this cycle, the framework evaluates all dynamic expressions (interpolations between &lt;code&gt;{{&lt;/code&gt; and &lt;code&gt;}}&lt;/code&gt;) and recomputes their values to check for changes.&lt;/p&gt;

&lt;p&gt;Doing this at scale is an incredibly expensive operation. Because the browser doesn't natively know &lt;em&gt;which&lt;/em&gt; specific part of the DOM needs an update, a single property change on a &lt;code&gt;$scope&lt;/code&gt; can trigger a dirty-checking loop across the entire view.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shifting to Build-Time: What Can We Do?
&lt;/h2&gt;

&lt;p&gt;Our golden constraint was strict: &lt;strong&gt;do not touch the template or controller source code.&lt;/strong&gt; Starting from the exact same template:&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="c"&gt;&amp;lt;!-- Template --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Hello {{ name }}!&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We built a custom template compiler (written in Go) that processes this HTML at build-time and outputs highly efficient, raw JavaScript:&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="c1"&gt;// Compiled JavaScript Output&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;template&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;p_0&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="s2"&gt;p&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;text_1&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;createTextNode&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;p_0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text_1&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="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p_0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="p"&gt;)&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="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="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;text_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="p"&gt;.&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;!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This compiled function returns an object with two lifecycle methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;mount&lt;/code&gt;: Used by our lightweight runtime to inject the element into the DOM container.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;update&lt;/code&gt;: The real heart of our framework. When a property changes, this function targets and mutates &lt;strong&gt;one specific TextNode in the DOM&lt;/strong&gt;, surgically updating the UI.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The browser no longer needs to scan the entire page. Instead, the runtime immediately knows that when &lt;code&gt;name&lt;/code&gt; changes, this exact &lt;code&gt;update&lt;/code&gt; function must be invoked.&lt;/p&gt;

&lt;p&gt;But how does the runtime intercept these changes without a digest cycle? This is where the modern &lt;strong&gt;JavaScript Proxy API&lt;/strong&gt; comes into play—and that is exactly what we will explore in the next part of this series.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Thanks for reading! I’m a Frontend Architect passionate about compilers, reactivity, and performance. Let's connect on &lt;a href="https://www.linkedin.com/in/carlostraccialini/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; to stay updated with the next parts of this journey.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>architecture</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
