<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://exodra.org/blog</id>
    <title>Exodra Blog</title>
    <updated>2026-07-23T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://exodra.org/blog"/>
    <subtitle>Exodra Blog</subtitle>
    <icon>https://exodra.org/img/favicon.svg</icon>
    <entry>
        <title type="html"><![CDATA[Fewer Allocations Made It Slower — A V8 Hidden-Class Lesson]]></title>
        <id>https://exodra.org/blog/fewer-allocations-made-it-slower</id>
        <link href="https://exodra.org/blog/fewer-allocations-made-it-slower"/>
        <updated>2026-07-23T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[I refactored Exodra's DOM build walk to allocate less per node. It came out 11% slower. Here is exactly why — with the measurements that caught it and the one-line change that fixed it.]]></summary>
        <content type="html"><![CDATA[<p>I refactored Exodra's DOM build walk to allocate <strong>less</strong> per node. It came out <strong>11% slower</strong>. Here is exactly why — with the measurements that caught it and the one-line change that fixed it.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-change-i-expected-to-be-free-or-a-win">The change I expected to be free (or a win)<a href="https://exodra.org/blog/fewer-allocations-made-it-slower#the-change-i-expected-to-be-free-or-a-win" class="hash-link" aria-label="Direct link to The change I expected to be free (or a win)" title="Direct link to The change I expected to be free (or a win)" translate="no">​</a></h2>
<p>The DOM renderer builds a subtree with an iterative depth-first walk. Each node the walker touches used to allocate three short-lived objects:</p>
<ul>
<li class="">the traversal <strong>frame</strong> (<code>{schema, parent, index, …}</code>),</li>
<li class="">a <strong>context</strong> object (<code>{element, scope}</code>) threaded to that node's children,</li>
<li class="">and a <strong><code>{context, children}</code> wrapper</strong> returned by the visitor.</li>
</ul>
<p>I collapsed that. The visitor stopped <em>returning</em> a wrapper and started <em>writing</em> onto the frame directly, and I merged the context into the frame too — so <code>element</code> and <code>scope</code> became fields on the walk node itself:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// before: visitor returns a fresh wrapper + context object per node</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">context</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> element</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> scope </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> children </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// after: visitor writes onto the node it was handed</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">node</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">element</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> element</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">node</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">scope</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> scope</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">node</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">children</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> children</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>Three allocations per node became <strong>one</strong>. On a ~1200-node tree that is ~2400 fewer short-lived objects per build. Obvious win, right?</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-measurement-said-no">The measurement said no<a href="https://exodra.org/blog/fewer-allocations-made-it-slower#the-measurement-said-no" class="hash-link" aria-label="Direct link to The measurement said no" title="Direct link to The measurement said no" translate="no">​</a></h2>
<p>I ran a controlled A/B: the old build path and the new one, same tree, <strong>same machine state</strong>, interleaved sample-by-sample with a forced GC before each sample, reporting the full spread (not a single median).</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">             min     p25     median  p75     max     [ms/mount]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">OLD    :   0.820   1.060   1.100   1.160   2.180</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">NEW    :   0.900   1.160   1.220   1.280   2.440</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Δ median (NEW vs OLD): +10.9%  → NEW slower</span><br></div></code></pre></div></div>
<p>Fewer allocations, <strong>11% slower</strong>. And note the shape of it: the slowdown is <strong>uniform</strong> — <code>min</code> moved +9.8%, <code>median</code> +10.9%, <code>max</code> +11.9%. That detail is the whole diagnosis.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="reading-the-shape">Reading the shape<a href="https://exodra.org/blog/fewer-allocations-made-it-slower#reading-the-shape" class="hash-link" aria-label="Direct link to Reading the shape" title="Direct link to Reading the shape" translate="no">​</a></h2>
<p>A regression that lives only in the <strong>tail</strong> (p95/max) usually means <strong>GC</strong> — occasional pauses inside the timed region. But this shifted the <strong><code>min</code> too</strong>. The cleanest, GC-free sample got slower. That can't be GC; it's <strong>more CPU on every single node</strong>. So the extra work had to be per-node, systematic, and small.</p>
<p>Which is strange, because I <em>removed</em> work (two allocations). Where did per-node CPU appear?</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-cause-v8-sizes-properties-at-birth">The cause: V8 sizes properties at birth<a href="https://exodra.org/blog/fewer-allocations-made-it-slower#the-cause-v8-sizes-properties-at-birth" class="hash-link" aria-label="Direct link to The cause: V8 sizes properties at birth" title="Direct link to The cause: V8 sizes properties at birth" translate="no">​</a></h2>
<p>V8 decides how many <strong>in-object property slots</strong> an object gets <strong>when it is created</strong>, from the shape of the literal. An object born as <code>{schema, parent, children}</code> gets room for exactly those three, stored inline and accessed by a fixed offset.</p>
<p>The new visitor then <strong>added</strong> <code>element</code> and <code>scope</code> <em>after</em> creation. Each addition:</p>
<ol>
<li class="">drives a <strong>hidden-class (map) transition</strong>, and</li>
<li class="">once the object outgrows its in-object capacity, spills the overflow into a <strong>separate out-of-line property store</strong> — an extra allocation, and every later read of <code>element</code>/<code>scope</code> now goes through an indirection.</li>
</ol>
<p>So I didn't remove an allocation for free. I traded a <strong>cheap</strong> one (a young-generation object the scavenger reclaims almost for nothing) for a <strong>slower object representation</strong> on all 1200 nodes. The old code, by contrast, built every object as a <strong>complete literal</strong> — all fields present at creation, everything in-object, no post-hoc growth. More objects, but each one cheap and flat.</p>
<p>The lesson in one line: <strong>growing an object field-by-field is not the same cost as allocating it whole — even when "whole" means allocating more objects.</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-proof-and-the-fix">The proof (and the fix)<a href="https://exodra.org/blog/fewer-allocations-made-it-slower#the-proof-and-the-fix" class="hash-link" aria-label="Direct link to The proof (and the fix)" title="Direct link to The proof (and the fix)" translate="no">​</a></h2>
<p>If the diagnosis is right, then giving the node <strong>all its slots at birth</strong> — so the visitor only writes into <em>existing</em> slots — should erase the gap without giving back the allocation win. It did:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">             min     p25     median  p75     max     [ms/mount]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">OLD          :   0.840   1.000   1.060   1.100   1.340</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">NEW (pre-sized): 0.780   1.020   1.080   1.120   1.280</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Δ median: +1.9%  → tie (within noise)</span><br></div></code></pre></div></div>
<p>Back to parity — and the pre-sized version actually beats the old code on <code>min</code> and <code>max</code>, while still allocating <strong>one</strong> object per node instead of three. Hypothesis confirmed, not asserted.</p>
<p>The shippable form keeps core generic: <code>walkSchema</code> doesn't know DOM field names, so the <strong>caller supplies the node factory</strong> and is responsible for pre-declaring its own context slots:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// in the DOM renderer — node born with every slot the visitor will fill</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">createBuildNode</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">schema</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> parent</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> schema</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> parent</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">children</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword nil" style="color:#00009f">undefined</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">element</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword nil" style="color:#00009f">undefined</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">scope</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword nil" style="color:#00009f">undefined</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">walkSchema</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">root</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> visit</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> createBuildNode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-im-taking-away">What I'm taking away<a href="https://exodra.org/blog/fewer-allocations-made-it-slower#what-im-taking-away" class="hash-link" aria-label="Direct link to What I'm taking away" title="Direct link to What I'm taking away" translate="no">​</a></h2>
<ul>
<li class=""><strong>"Fewer allocations" is not automatically faster.</strong> Object <em>representation</em> (in-object vs. spilled backing store) and hidden-class transitions can dominate the allocation you saved — especially when the saved object was a cheap young-gen one.</li>
<li class=""><strong>Build objects complete.</strong> One literal with every field beats creating a small object and growing it. Give V8 the final shape up front.</li>
<li class=""><strong>The shape of a regression names its cause.</strong> Tail-only → suspect GC. <code>min</code> moved too → per-operation CPU. Look at the whole distribution, never a lone median.</li>
<li class=""><strong>Only a controlled A/B tells the truth.</strong> The same change looked like anything from +8% to +18% against a third framework across runs — pure cross-run noise. Old-vs-new, one machine, interleaved, GC'd, was the only measurement that didn't lie.</li>
</ul>
<p>The refactor shipped: the cleanup stayed, the allocation count dropped, and the build is no slower than before — because the node is now born whole.</p>]]></content>
        <author>
            <name>Andrei Baikov</name>
            <uri>https://github.com/abaikov</uri>
        </author>
        <category label="Performance" term="Performance"/>
        <category label="Benchmarks" term="Benchmarks"/>
        <category label="Design Decisions" term="Design Decisions"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Three Attributes Architecture - Maximum Performance Without Runtime Checks]]></title>
        <id>https://exodra.org/blog/three-attributes-architecture</id>
        <link href="https://exodra.org/blog/three-attributes-architecture"/>
        <updated>2026-06-11T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Every reactive framework faces the same dilemma: how to handle different types of properties efficiently. Most frameworks choose developer convenience over performance. We chose performance.]]></summary>
        <content type="html"><![CDATA[<p>Every reactive framework faces the same dilemma: how to handle different types of properties efficiently. Most frameworks choose developer convenience over performance. We chose performance.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-problem-runtime-type-checking-kills-performance">The Problem: Runtime Type Checking Kills Performance<a href="https://exodra.org/blog/three-attributes-architecture#the-problem-runtime-type-checking-kills-performance" class="hash-link" aria-label="Direct link to The Problem: Runtime Type Checking Kills Performance" title="Direct link to The Problem: Runtime Type Checking Kills Performance" translate="no">​</a></h2>
<p>Traditional frameworks mix all props together:</p>
<div class="language-jsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-jsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// React/Solid approach - looks simple, but...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">static</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">handler</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">items</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">list</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></div></code></pre></div></div>
<p>Behind the scenes, the framework must check EVERY prop:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// What frameworks do internally</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword control-flow" style="color:#00009f">for</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> prop </span><span class="token keyword" style="color:#00009f">of</span><span class="token plain"> props</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">isEvent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prop</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">handleEvent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prop</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">else</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">isReactive</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prop</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">setupReactivity</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prop</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">else</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">isList</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prop</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">setupListTracking</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prop</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">else</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">setStaticProp</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prop</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-cost-adds-up">The Cost Adds Up<a href="https://exodra.org/blog/three-attributes-architecture#the-cost-adds-up" class="hash-link" aria-label="Direct link to The Cost Adds Up" title="Direct link to The Cost Adds Up" translate="no">​</a></h2>
<p>Consider a typical app with 10,000 elements and ~5 props each — roughly 50,000 prop reads. In a runtime-dispatch model, each read asks the same question ("is this reactive? an event handler? a plain attribute?") and does it <em>on every render cycle</em>. Any single check is trivially cheap; the point is that it's work you keep paying. Exodra pays it once, at compile time, and never again at runtime.</p>
<p><strong>Death by a thousand cuts.</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="our-solution-three-attributes-architecture">Our Solution: Three Attributes Architecture<a href="https://exodra.org/blog/three-attributes-architecture#our-solution-three-attributes-architecture" class="hash-link" aria-label="Direct link to Our Solution: Three Attributes Architecture" title="Direct link to Our Solution: Three Attributes Architecture" translate="no">​</a></h2>
<p>We explicitly separate attributes at compile time:</p>
<div class="language-jsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-jsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Exodra approach - explicit and fast</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">  </span><span class="token tag attr-name" style="color:#00a4db">static</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript literal-property property" style="color:#36acaa">id</span><span class="token tag script language-javascript operator" style="color:#393A34">:</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript string" style="color:#e3116c">'container'</span><span class="token tag script language-javascript punctuation" style="color:#393A34">,</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript keyword" style="color:#00009f">class</span><span class="token tag script language-javascript operator" style="color:#393A34">:</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript string" style="color:#e3116c">'box'</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"></span><br></div><div class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">  </span><span class="token tag attr-name" style="color:#00a4db">bindable</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript literal-property property" style="color:#36acaa">onClick</span><span class="token tag script language-javascript operator" style="color:#393A34">:</span><span class="token tag script language-javascript" style="color:#00009f"> handler </span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"></span><br></div><div class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">  </span><span class="token tag attr-name" style="color:#00a4db">bindableList</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript literal-property property" style="color:#36acaa">items</span><span class="token tag script language-javascript operator" style="color:#393A34">:</span><span class="token tag script language-javascript" style="color:#00009f"> list </span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"></span><br></div><div class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f"></span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="zero-runtime-checks">Zero Runtime Checks<a href="https://exodra.org/blog/three-attributes-architecture#zero-runtime-checks" class="hash-link" aria-label="Direct link to Zero Runtime Checks" title="Direct link to Zero Runtime Checks" translate="no">​</a></h2>
<p>With Three Attributes, the runtime knows exactly what to do:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Static attributes - set once, never check again</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">attributes</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">static</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token known-class-name class-name">Object</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">assign</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">element</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> attributes</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">static</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Bindable attributes - set up reactivity</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">attributes</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">bindable</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">setupBindings</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">element</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> attributes</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">bindable</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Lists - optimize for array operations</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">attributes</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">bindableList</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">setupListBinding</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">element</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> attributes</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">bindableList</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>No type checking. No guessing. No overhead.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="performance-impact">Performance Impact<a href="https://exodra.org/blog/three-attributes-architecture#performance-impact" class="hash-link" aria-label="Direct link to Performance Impact" title="Direct link to Performance Impact" translate="no">​</a></h2>
<p>We don't ship a "runtime-type-checking" variant of Exodra to A/B against, so we won't quote a made-up "with checks vs without" delta. What we <em>can</em> show is how the architecture holds up against other reactive frameworks.</p>
<p>In the project's benchmark suite (<code>npm run bench</code>), rendering a large tree — the
case that actually does work at a measurable scale — Exodra lands in the <strong>top
tier</strong>: as fast as the fastest (neck‑and‑neck with Solid, well ahead of React and
Svelte) <strong>without</strong> a memory premium. Full numbers, including retained heap and
honest caveats, live on the dedicated <a class="" href="https://exodra.org/docs/benchmarks">Benchmarks</a> page — with
the fair print that Solid's memory footprint is actually a touch smaller than
ours. We're not claiming a permanent "fastest" crown from our own harness.</p>
<p>Fine-grained updates (a single signal or list op) complete in <strong>sub-millisecond</strong>
time for both Exodra and Solid — below the timer's resolution — so we treat those
as a tie, not a headline multiplier.</p>
<p>Caveats worth stating plainly: this is Exodra's <strong>own</strong> harness (headless Chromium
via Playwright), not an independent benchmark, and numbers move with hardware and
framework versions. Run <code>npm run bench</code> and see for yourself. The point isn't "we
win every micro-benchmark" — it's that pre-separating props at compile time
removes a class of per-update work, and that shows up on real render workloads.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-not-just-use-a-compiler">Why Not Just Use a Compiler?<a href="https://exodra.org/blog/three-attributes-architecture#why-not-just-use-a-compiler" class="hash-link" aria-label="Direct link to Why Not Just Use a Compiler?" title="Direct link to Why Not Just Use a Compiler?" translate="no">​</a></h2>
<p>"Why not let a compiler figure this out?"</p>
<p>We do! Our Babel plugin transforms JSX into Three Attributes. But here's the key: <strong>the runtime still knows the separation</strong>.</p>
<p>Other frameworks compile to a mixed format and still need runtime checks. We compile to a pre-separated format that needs zero runtime decisions.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="developer-experience">Developer Experience<a href="https://exodra.org/blog/three-attributes-architecture#developer-experience" class="hash-link" aria-label="Direct link to Developer Experience" title="Direct link to Developer Experience" translate="no">​</a></h2>
<p>Yes, it's more verbose. But it's also:</p>
<ol>
<li class=""><strong>Explicit</strong> - You know exactly what's reactive and what's not</li>
<li class=""><strong>Predictable</strong> - No surprising re-renders from static props</li>
<li class=""><strong>Debuggable</strong> - Clear separation in DevTools</li>
<li class=""><strong>Fast</strong> - Measurably, significantly faster</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-philosophy">The Philosophy<a href="https://exodra.org/blog/three-attributes-architecture#the-philosophy" class="hash-link" aria-label="Direct link to The Philosophy" title="Direct link to The Philosophy" translate="no">​</a></h2>
<p>We believe developers should control performance, not hope for it. By making the cost visible, we make it avoidable.</p>
<p>Every <code>bindable</code> prop is a conscious choice. Every <code>static</code> prop is a performance win. Every <code>bindableList</code> is optimized for its specific use case.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="real-world-impact">Real-World Impact<a href="https://exodra.org/blog/three-attributes-architecture#real-world-impact" class="hash-link" aria-label="Direct link to Real-World Impact" title="Direct link to Real-World Impact" translate="no">​</a></h2>
<p>We won't quote before/after figures from a "production app" you can't re-run — that's how you end up trusting invented numbers. What you <em>can</em> run is the benchmark suite: <code>npm run bench</code> renders a large tree, and Exodra lands in the top tier in our harness — on par with Solid, ahead of Svelte and React (hardware/version dependent). The architectural claim doesn't hinge on the exact figure: pre-separating props at compile time removes a category of per-update work, and that shows up on real render workloads.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://exodra.org/blog/three-attributes-architecture#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>The Three Attributes Architecture isn't about following trends or making things "simple." It's about giving developers the tools to build genuinely fast applications.</p>
<p>We didn't want to drag type checking everywhere. So we didn't. We separated attributes at the architecture level, eliminated runtime overhead, and gave you maximum performance.</p>
<p><strong>The cost of clarity is verbosity. The reward is speed.</strong></p>]]></content>
        <author>
            <name>Andrei Baikov</name>
            <uri>https://github.com/abaikov</uri>
        </author>
        <category label="Architecture" term="Architecture"/>
        <category label="Performance" term="Performance"/>
        <category label="Design Decisions" term="Design Decisions"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Why the WeakMap Core Is a Better Design Than the Legacy One]]></title>
        <id>https://exodra.org/blog/weakmap-core-design</id>
        <link href="https://exodra.org/blog/weakmap-core-design"/>
        <updated>2026-06-11T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Exodra moved from a legacy per-node bookkeeping model to a WeakMap-based core. This post is about why that's a better design — less work per node, GC-driven cleanup, and cache-friendly lookups. It is deliberately light on "X% faster" numbers, because the honest, reproducible timings come from npm run bench, not prose.]]></summary>
        <content type="html"><![CDATA[<p>Exodra moved from a legacy per-node bookkeeping model to a WeakMap-based core. This post is about <em>why that's a better design</em> — less work per node, GC-driven cleanup, and cache-friendly lookups. It is deliberately light on "X% faster" numbers, because the honest, reproducible timings come from <code>npm run bench</code>, not prose.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-problem-with-legacy-core">The Problem with Legacy Core<a href="https://exodra.org/blog/weakmap-core-design#the-problem-with-legacy-core" class="hash-link" aria-label="Direct link to The Problem with Legacy Core" title="Direct link to The Problem with Legacy Core" translate="no">​</a></h2>
<p>The legacy core relied on manual cleanup and reference counting:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Legacy approach - manual disposal required</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> nodeData </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">dispose</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">node</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">delete</span><span class="token plain"> nodeData</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">node</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">id</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  node</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">children</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">forEach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dispose</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>This approach had several issues:</p>
<ul>
<li class=""><strong>Memory leaks</strong> when developers forgot to call dispose</li>
<li class=""><strong>Performance overhead</strong> from manual tracking</li>
<li class=""><strong>Complex lifecycle management</strong> in dynamic UIs</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="enter-weakmap-core">Enter WeakMap Core<a href="https://exodra.org/blog/weakmap-core-design#enter-weakmap-core" class="hash-link" aria-label="Direct link to Enter WeakMap Core" title="Direct link to Enter WeakMap Core" translate="no">​</a></h2>
<p>WeakMap provides automatic memory management:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// WeakMap approach - automatic cleanup</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> nodeCache </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">WeakMap</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// When node is GC'd, WeakMap entry is automatically removed</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-this-is-a-design-win-not-a-benchmark-claim">Why This Is a Design Win (Not a Benchmark Claim)<a href="https://exodra.org/blog/weakmap-core-design#why-this-is-a-design-win-not-a-benchmark-claim" class="hash-link" aria-label="Direct link to Why This Is a Design Win (Not a Benchmark Claim)" title="Direct link to Why This Is a Design Win (Not a Benchmark Claim)" translate="no">​</a></h2>
<p>We're going to be honest here: Exodra does <strong>not</strong> ship a maintained "legacy vs WeakMap" microbenchmark, so this post won't quote a measured "X% faster" delta — doing so would be inventing numbers. The case for the WeakMap core is <strong>architectural</strong>, and it stands on its own:</p>
<ul>
<li class=""><strong>Node creation</strong> avoids allocating a per-node bookkeeping object and wiring up manual parent/child tracking — there's simply less work per node.</li>
<li class=""><strong>Memory after unmount</strong> is reclaimed by the garbage collector: when a subtree's nodes become unreachable, their WeakMap entries go with them. There is no retained-metadata table to grow or forget to clear.</li>
<li class=""><strong>Cache lookups</strong> (clone-caching static subtrees) are a direct WeakMap <code>get</code> keyed by the schema node — no scanning, no auxiliary index.</li>
</ul>
<p>If you want real, reproducible numbers, run the cross-framework suite with <code>npm run bench</code>: on a large-tree initial render Exodra lands around ~1 ms median — top-tier in our harness (neck-and-neck with Solid, ahead of Svelte and React). Those are Exodra's own benchmarks and are hardware/version dependent — reproduce them yourself rather than trusting a marketing figure.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="real-world-benefits">Real-World Benefits<a href="https://exodra.org/blog/weakmap-core-design#real-world-benefits" class="hash-link" aria-label="Direct link to Real-World Benefits" title="Direct link to Real-World Benefits" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-caching--reusability">1. Caching &amp; Reusability<a href="https://exodra.org/blog/weakmap-core-design#1-caching--reusability" class="hash-link" aria-label="Direct link to 1. Caching &amp; Reusability" title="Direct link to 1. Caching &amp; Reusability" translate="no">​</a></h3>
<p>WeakMap enables powerful caching patterns:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> compiledTemplates </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">WeakMap</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getTemplate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">node</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">compiledTemplates</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">has</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">node</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    compiledTemplates</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">node</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">compile</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">node</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> compiledTemplates</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">node</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-zero-memory-management">2. Zero Memory Management<a href="https://exodra.org/blog/weakmap-core-design#2-zero-memory-management" class="hash-link" aria-label="Direct link to 2. Zero Memory Management" title="Direct link to 2. Zero Memory Management" translate="no">​</a></h3>
<p>No dispose, no cleanup, no leaks:</p>
<ul>
<li class="">Components unmount → references drop → memory freed</li>
<li class="">WeakMap automatically cleans up entries</li>
<li class="">No manual intervention required</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-steady-performance-at-scale">3. Steady Performance at Scale<a href="https://exodra.org/blog/weakmap-core-design#3-steady-performance-at-scale" class="hash-link" aria-label="Direct link to 3. Steady Performance at Scale" title="Direct link to 3. Steady Performance at Scale" translate="no">​</a></h3>
<p>Because there's no manually-managed metadata table accumulating over an app's lifetime, per-update cost doesn't drift upward as the app runs — freed nodes are collected, and there's no growing structure to walk. This is a property of the design, not a number we're quoting from a synthetic 50k-update loop.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-its-a-better-model">Why It's a Better Model<a href="https://exodra.org/blog/weakmap-core-design#why-its-a-better-model" class="hash-link" aria-label="Direct link to Why It's a Better Model" title="Direct link to Why It's a Better Model" translate="no">​</a></h2>
<p>The WeakMap core wins on properties, not just speed:</p>
<ol>
<li class=""><strong>Separation of concerns</strong>: memory management is handled by the runtime/GC</li>
<li class=""><strong>Hard to leak</strong>: you can't forget to clean up what you never manually manage</li>
<li class=""><strong>Cache-friendly</strong>: a natural fit for memoization and clone-caching</li>
<li class=""><strong>Steady at scale</strong>: no manually-grown metadata table to drift with app size</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://exodra.org/blog/weakmap-core-design#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>The WeakMap core is an architectural improvement in how Exodra manages memory: it removes per-node bookkeeping and the whole "did I remember to dispose?" class of bugs. We're framing it as a design win rather than a benchmark headline on purpose — for numbers, <code>npm run bench</code> is the honest source.</p>
<p><strong>Bottom line</strong>: the WeakMap core does less work per node, leans on the GC for cleanup instead of manual disposal, and makes clone-cache lookups a direct keyed <code>get</code>. The win is that it's hard to misuse and easy to reason about — and for actual, reproducible timings, <code>npm run bench</code> is the source of truth, not this post.</p>]]></content>
        <author>
            <name>Andrei Baikov</name>
            <uri>https://github.com/abaikov</uri>
        </author>
        <category label="Performance" term="Performance"/>
        <category label="Architecture" term="Architecture"/>
        <category label="Benchmarks" term="Benchmarks"/>
    </entry>
</feed>