<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Rukia Labs — Engineering</title>
        <link>https://rukialabs.com/engineering/</link>
        <description>Rukia Labs Engineering Blog</description>
        <lastBuildDate>Sat, 12 Sep 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <copyright>Copyright © 2026 Rukia Labs</copyright>
        <item>
            <title><![CDATA[The chat panel nobody opened was costing us 400MB]]></title>
            <link>https://rukialabs.com/engineering/the-chat-panel-nobody-opened</link>
            <guid>https://rukialabs.com/engineering/the-chat-panel-nobody-opened</guid>
            <pubDate>Sat, 12 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A lesson editor that got slower the longer you typed, and the two causes I did not expect to find.]]></description>
            <content:encoded><![CDATA[<p>The lesson editor is where an instructor writes a lesson: a rich text editor for
the body, a place for a video link, file attachments, and a preview showing what
a student will see. Instructors spend hours in it. It is, more than anything
else we ship, the thing our customers actually work in.</p>
<p>And for a few months, it had been getting worse.</p>
<div class="theme-admonition theme-admonition-note admonition_IZjC alert alert--secondary"><div class="admonitionHeading_uVvU"><span class="admonitionIcon_HiR3"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_bl22"><p>This first appeared on Medium, in the first person, and it is kept that way
here. The code screenshots in the original are real code blocks in this version
so you can copy them.
<a href="https://medium.com/@gitaumanasseh1/the-chat-panel-nobody-opened-was-costing-us-400mb-0da7d5fd8c85" target="_blank" rel="noopener noreferrer" class="">Read the original</a>.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="what-were-building">What we're building<a href="https://rukialabs.com/engineering/the-chat-panel-nobody-opened#what-were-building" class="hash-link" aria-label="Direct link to What we're building" title="Direct link to What we're building" translate="no">​</a></h2>
<p>I work at <a href="https://rukialabs.com/" target="_blank" rel="noopener noreferrer" class="">Rukia Labs</a>, a learning platform for
organizations that train people. Coding schools, bootcamps, and company L&amp;D
teams. Each customer gets their own branded space: students take courses there,
instructors write and publish the material, and admins run the whole thing.</p>
<p>Under the hood that's four separate React applications sharing one component
library. A student app, an instructor console, an admin console, and a platform
console for us. They live in one repository, so a button looks the same
everywhere, and they're deployed to Cloudflare Pages.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-symptom">The symptom<a href="https://rukialabs.com/engineering/the-chat-panel-nobody-opened#the-symptom" class="hash-link" aria-label="Direct link to The symptom" title="Direct link to The symptom" translate="no">​</a></h2>
<p>Authors would open the editor, start writing, and watch the browser tab climb
past 400MB of memory. Then keystrokes would start arriving late. Then the page
would stop responding altogether. Students got a milder version of the same
thing on the lesson page.</p>
<p>The bug reports were the kind you can't act on. "It hangs." "It's heavy today."
"I lost a paragraph." Nobody could reproduce it on demand, because it wasn't a
crash. It was a slide.</p>
<p>I want to write up what we found, partly because the main cause is something
I've never seen flagged in a code review, and partly because my first diagnosis
was confidently wrong in a way that cost me an afternoon.</p>
<p><img decoding="async" loading="lazy" alt="The lesson editor: course outline on the left, the lesson being written on the right" src="https://rukialabs.com/engineering/assets/images/lesson-editor-8777cfe2847066788d9765358b5dd9f6.png" width="1912" height="990" class="img__Ss2"></p>
<p><strong>A note for readers who don't build web apps for a living:</strong> I've kept the
jargon to a minimum and explained the rest as it comes up. If you know what a
bundle is, skim the indented notes. If you don't, they're the whole story.</p>
<blockquote>
<p><strong>Bundle, chunk, build output.</strong> Browsers can't run a folder of a thousand
source files efficiently, so before we ship, a tool called a <em>bundler</em> stitches
them into a handful of larger files. Each of those files is a <strong>chunk</strong>. The
collection is the <strong>bundle</strong>. Modern bundlers are clever enough to split it up
so a page only downloads the chunks it needs, which is called <strong>code
splitting</strong>. Remember that phrase. It's where this goes wrong.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="starting-in-the-wrong-place">Starting in the wrong place<a href="https://rukialabs.com/engineering/the-chat-panel-nobody-opened#starting-in-the-wrong-place" class="hash-link" aria-label="Direct link to Starting in the wrong place" title="Direct link to Starting in the wrong place" translate="no">​</a></h2>
<p>The first thing I did was look at the built output of the admin app, sorted by
size.</p>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#393A34"><span class="token plain">1220K  MarkdownRenderer-8084fe48.js</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> 772K  MarkdownEditor-49c46f2f.js</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> 764K  emacs-lisp-93221a04.js</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> 612K  cpp-ccf25956.js</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> 608K  wasm-cd10bedf.js</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> 544K  mermaid.core-90442b6d.js</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> 436K  cytoscape.esm-b0e8ddfb.js</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> 260K  wolfram-e51de66a.js</span><br></div></code></pre></div></div>
<p>460 chunks, 18MB total. Somewhere in there, our learning platform was shipping
syntax-highlighting rules for Cobol, Fortran, and Wolfram Language, which I
think we can agree nobody was asking for.</p>
<blockquote>
<p><strong>Syntax highlighting</strong> is the colouring you see in code snippets: keywords one
colour, strings another. To do it, a library needs a <em>grammar</em> for each
language: a big set of rules describing what Python looks like, what Rust looks
like, and so on. There are hundreds of languages, so there are hundreds of
grammars, and each one has real weight.</p>
</blockquote>
<p>So I assumed the big renderer chunk and the pile of language chunks were the
same problem. One syntax highlighter, configured badly, easy fix.</p>
<p>That was wrong.</p>
<p>There were <strong>two</strong> syntax highlighters. Not one misconfigured highlighter. Two
entirely separate ones that didn't know about each other.</p>
<p>Our markdown renderer used Prism, through a package called
<code>react-syntax-highlighter</code>. Our AI chat components used Shiki. They'd been added
about two years apart, by different people solving different problems, and
neither had any reason to notice the other existed. If you only look at the list
of chunk names, two tidy highlighters and one messy one produce output that
looks the same.</p>
<p>The Prism side was the worse of the two because of how that library's default
entry point behaves.</p>
<div class="language-js codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-js codeBlock_TAPP thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">Prism</span><span class="token imports"> </span><span class="token imports keyword module" style="color:#00009f">as</span><span class="token imports"> </span><span class="token imports maybe-class-name">SyntaxHighlighter</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'react-syntax-highlighter'</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>That single import pulls in <code>refractor/all</code>, which registers around 237 grammars
into memory the moment the file is loaded. Not lazily. Not on first use. A
lesson made entirely of prose, with no code in it at all, paid for every one of
them.</p>
<p>The Shiki side was the full bundle: roughly 290 languages and 60 themes, each
emitted as its own chunk. Hence, <code>emacs-lisp</code> at 764K, sitting in a build for a
school that teaches Python and JavaScript.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-part-i-hadnt-seen-before">The part I hadn't seen before<a href="https://rukialabs.com/engineering/the-chat-panel-nobody-opened#the-part-i-hadnt-seen-before" class="hash-link" aria-label="Direct link to The part I hadn't seen before" title="Direct link to The part I hadn't seen before" translate="no">​</a></h2>
<p>Here's the code that actually mattered. I'll show it the way it appeared in the
file, because I don't think most people would look twice at it.</p>
<div class="language-jsx codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-jsx codeBlock_TAPP thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">InstructorChatPanel</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@/pages/course-master/components/InstructorChatPanel"</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">// ... about 600 lines later ...</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 punctuation" style="color:#393A34">{</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">editorFullscreen </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> showChat </span><span class="token operator" style="color:#393A34">&amp;&amp;</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 tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">InstructorChatPanel</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onClose</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 punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript function" style="color:#d73a49">setShowChat</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript boolean" style="color:#36acaa">false</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><span class="token tag punctuation" 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><br></div></code></pre></div></div>
<p><code>showChat</code> starts as <code>false</code>. Most authors never open the chat at all. The render
is conditional and correct, and it had been through code review more than once.</p>
<p>But <code>InstructorChatPanel</code> imports a message component, which imports a library
called <code>streamdown</code>, whose own dependencies include Mermaid (for diagrams),
Shiki (highlighter number two), KaTeX (for maths), and a stack of markdown
plugins. Mermaid in turn brings in a graph-layout library and a separate chunk
per diagram type.</p>
<p><strong>None of that is gated by <code>showChat</code>.</strong></p>
<blockquote>
<p><strong>Static versus dynamic imports.</strong> A normal <code>import X from "..."</code> at the top of
a file is a <em><strong>static</strong></em> import. The bundler treats it as "this file needs that
file, always", and follows the chain outwards: what does that file need, and
what does <em><strong>that</strong></em> one need. The result is a graph, and everything in it
ships and runs when the page loads.</p>
<p>A <em><strong>dynamic</strong></em> import, written <code>import("...")</code>, is a promise. It says "fetch
this later, if I ask". That's the one the bundler is allowed to split off into
a chunk that only downloads on demand.</p>
<p>The conditional <code>{showChat &amp;&amp; &lt;Panel /&gt;}</code> happens far later, at render time,
and only decides whether React puts an element on screen. By then every module
in that graph has already been fetched, parsed and executed.</p>
</blockquote>
<p>Opening the lesson editor was downloading and running roughly 1.9MB of source
for a panel that stayed shut.</p>
<p>The bit that actually stung was finding this in our own student app, in a
different file, written months earlier:</p>
<div class="language-jsx codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-jsx codeBlock_TAPP thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Lazy, and deliberately so. FloatingChatWidget pulls in ChatPage, which pulls</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">// MarkdownRenderer (katex + react-syntax-highlighter -&gt; refractor) and</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">// ai-elements' streamdown - around 1.9MB of source that ended up in the ENTRY</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">// chunk... None of it is needed to paint a dashboard, and most visitors never</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">// open the chat at all.</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 maybe-class-name">FloatingChatWidget</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">lazy</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><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">import</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'@/pages/ChatPage/FloatingChatWidget'</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><br></div></code></pre></div></div>
<p>Someone had already worked this out. Same libraries, same 1.9MB, and they'd left
a clear note explaining exactly why the lazy wrapper was there.</p>
<p>Then we built the lesson routes and reintroduced the identical problem in three
more places.</p>
<p>A comment only reaches the person already reading that file. It has no way of
stopping the next person doing the same thing somewhere else. Hold that thought,
because it's where this ends up.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="why-it-got-worse-as-you-typed">Why it got worse as you typed<a href="https://rukialabs.com/engineering/the-chat-panel-nobody-opened#why-it-got-worse-as-you-typed" class="hash-link" aria-label="Direct link to Why it got worse as you typed" title="Direct link to Why it got worse as you typed" translate="no">​</a></h2>
<p>Bundle weight explains a slow page load. It doesn't explain a page that's fine
for ten minutes and then isn't. That turned out to be somewhere else entirely.</p>
<div class="language-js codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-js codeBlock_TAPP thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> queryClient </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">QueryClient</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 literal-property property" style="color:#36acaa">defaultOptions</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"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">queries</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"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">staleTime</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">30</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000</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 literal-property property" style="color:#36acaa">gcTime</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">24</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// 24 hours</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"></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"></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"></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 keyword" style="color:#00009f">const</span><span class="token plain"> persister </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">createSyncStoragePersister</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 literal-property property" style="color:#36acaa">storage</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token dom variable" style="color:#36acaa">window</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">localStorage</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 literal-property property" style="color:#36acaa">throttleTime</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000</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 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">persistQueryClient</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> queryClient</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> persister</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">maxAge</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">24</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>Every line of this is reasonable on its own.</p>
<blockquote>
<p><strong>What this code does.</strong> We use a library that caches data fetched from our
API, so moving between pages doesn't refetch everything. <code>gcTime</code> is how long
an unused cache entry sticks around: 24 hours here, so coming back to a page
tomorrow paints instantly instead of showing a spinner. <code>persistQueryClient</code>
writes that cache into the browser's <code>localStorage</code>, so even a full page reload
doesn't lose it. <code>throttleTime: 1000</code> limits those writes to at most one a
second, which sounds like the safety rail.</p>
</blockquote>
<p>Put the three together on a page that holds course content, and you get
something else.</p>
<p>Writing to <code>localStorage</code> means converting the entire cache into a single text
string first, using <code>JSON.stringify</code>. That happens on the <em><strong>main thread</strong></em>,
which is the one thread the browser also uses to respond to your keyboard. And
it happens whenever anything in the cache changes, up to once a second.</p>
<p>There was no filter on what got persisted, so "the entire cache" meant every
course outline we'd loaded, and each of those carried its topics with all their
lessons, quizzes, and assignments expanded inline.</p>
<p>Now follow an edit through. The author saves. The save succeeds. The app marks
the course data stale and refetches it. The cache updates. The whole thing gets
converted to a string again while they're partway through the next sentence.
With <code>gcTime</code> at 24 hours, nothing was ever evicted, so every course anyone had
opened that shift was still in there, being stringified along with it.</p>
<p>There's a second failure hiding inside the first. <code>localStorage</code> tops out at
around 5MB. Go past it and the write throws an error, and a cache that can't be
written is a cache that restores nothing. You don't get a red message in the
console. You get a feature that quietly stopped doing the job it was added for,
with no signal that anything changed.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-ordinary-react-mistakes">The ordinary React mistakes<a href="https://rukialabs.com/engineering/the-chat-panel-nobody-opened#the-ordinary-react-mistakes" class="hash-link" aria-label="Direct link to The ordinary React mistakes" title="Direct link to The ordinary React mistakes" translate="no">​</a></h2>
<p>The rest was nothing clever. Just normal patterns at a size where normal stops
working.</p>
<p><strong>Two full serializations of the document, on every render, to work out one
true-or-false question:</strong></p>
<div class="language-js codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-js codeBlock_TAPP thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> isDirty </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  lessonDraft </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token known-class-name class-name">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">stringify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">lessonDraft</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> </span><span class="token known-class-name class-name">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">stringify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">originalDraft</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>This asks, "Has the author changed anything?" by converting the entire lesson to
a string twice and comparing. It sits at the top of the component with no
caching, so it runs on every single render. On a 200 KB lesson at typing speed,
that's megabytes a second of throwaway text.</p>
<p>Comparing the fields one by one answers the same question almost for free,
because an unedited body is literally the same object in memory on both sides.
The computer can check that with a single pointer comparison instead of reading
200,000 characters twice.</p>
<p><strong>Then this one, which I think is the most common version of this mistake:</strong></p>
<div class="language-js codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-js codeBlock_TAPP thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> components </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">buildComponents</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">theme</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 comment" style="color:#999988;font-style:italic">// 26 fresh closures, every render</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Why a "new object every time" is expensive.</strong> React skips re-rendering a
component when its inputs haven't changed, and it decides that by checking
whether the inputs are <em><strong>the same object</strong></em>, not whether they <em><strong>look the
same</strong></em>. Build a fresh object on every render and React can never take the
shortcut.</p>
</blockquote>
<p><code>react-markdown</code> compares this prop by identity. Handing it a new object each
time meant the entire markdown document re-rendered, and every code block got
re-highlighted whenever anything on the page changed. Caching that one object
was a single line and probably the best return of the whole exercise.</p>
<p><strong>An entire second editor, mounted permanently and hidden with CSS:</strong></p>
<div class="language-jsx codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-jsx codeBlock_TAPP thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#393A34"><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">className</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 function" style="color:#d73a49">cn</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript string" style="color:#e3116c">"absolute inset-0"</span><span class="token tag script language-javascript punctuation" style="color:#393A34">,</span><span class="token tag script language-javascript" style="color:#00009f"> isSourceMode </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">"opacity-100"</span><span class="token tag script language-javascript" style="color:#00009f"> </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">"opacity-0"</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 punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">  &lt;Editor /* Monaco */ options=</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 literal-property property" style="color:#36acaa">automaticLayout</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</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 plain-text"> /&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></div></code></pre></div></div>
<p>Our editor has two modes: a rich text view and a raw markdown view. The raw view
uses Monaco, the same editor that powers VS Code. It was never unmounted. It was
just made invisible.</p>
<p>So every markdown editor on the page carried a live Monaco instance, its
document model, its tokenizer, and a resize observer, sitting alongside the rich
text editor; in the default mode most sessions never leave. Our quiz question
form mounts two editors, so it was doing all of that twice.</p>
<p><strong><code>opacity-0</code> doesn't unmount anything.</strong> Neither does <code>display: none</code>, or
pushing something off-screen with a transform. If the hidden thing owns an
editor, a video player, or a map, it is fully alive and fully costing you.</p>
<p><strong>And one that scaled badly:</strong> the quiz review screen renders two markdown blocks
per question, and each one, if you don't tell it which theme to use, sets up its
own watcher on the page to detect dark mode. Forty questions meant eighty
watchers on the same element. Toggling dark mode fired eighty updates, each
re-parsing and re-highlighting a full document inside a single frame.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-number-we-should-have-been-tracking">The number we should have been tracking<a href="https://rukialabs.com/engineering/the-chat-panel-nobody-opened#the-number-we-should-have-been-tracking" class="hash-link" aria-label="Direct link to The number we should have been tracking" title="Direct link to The number we should have been tracking" translate="no">​</a></h2>
<p>Total bundle size caught none of this. It's 18MB before and 18MB after, because
code splitting means most of it is never fetched. The number tells you nothing
about what any given page actually costs.</p>
<p>What you want is the <em>static closure of a route</em>: start at the chunk for one
page, follow only the static imports, and add up the bytes. That's what the
browser really downloads and runs before that page becomes usable. Dynamic
imports are excluded on purpose, because deferring them is the entire point of
them.</p>
<p>It's about twenty lines of Node.</p>
<div class="language-js codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-js codeBlock_TAPP thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> fs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'fs'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> path </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'path'</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">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">dir</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> entryPattern</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">argv</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">slice</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">2</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 keyword" style="color:#00009f">const</span><span class="token plain"> entry </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> fs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">readdirSync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dir</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 method function property-access" style="color:#d73a49">find</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">f</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> f</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">startsWith</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">entryPattern</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> f</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">endsWith</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'.js'</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 keyword" style="color:#00009f">const</span><span class="token plain"> seen </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">Set</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">const</span><span class="token plain"> stack </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">entry</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">let</span><span class="token plain"> total </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</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 keyword control-flow" style="color:#00009f">while</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">stack</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">length</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">const</span><span class="token plain"> file </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> stack</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">pop</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 control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">seen</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">file</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 keyword control-flow" style="color:#00009f">continue</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">  seen</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">file</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 keyword" style="color:#00009f">const</span><span class="token plain"> full </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> path</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">join</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dir</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> file</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">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">fs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">existsSync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">full</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 keyword control-flow" style="color:#00009f">continue</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">  total </span><span class="token operator" style="color:#393A34">+=</span><span class="token plain"> fs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">statSync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">full</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">size</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 keyword" style="color:#00009f">const</span><span class="token plain"> src </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> fs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">readFileSync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">full</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'utf8'</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">// Static edges only: `from"./x.js"` and `import"./x.js"`.</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">// Not `import("./x.js")`, since a dynamic import is exactly what we want to skip.</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"> m </span><span class="token keyword" style="color:#00009f">of</span><span class="token plain"> src</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">matchAll</span><span class="token punctuation" style="color:#393A34">(</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex group punctuation" style="color:#393A34">(?:</span><span class="token regex regex-source language-regex" style="color:#36acaa">from</span><span class="token regex regex-source language-regex alternation keyword" style="color:#00009f">|</span><span class="token regex regex-source language-regex" style="color:#36acaa">import</span><span class="token regex regex-source language-regex group punctuation" style="color:#393A34">)</span><span class="token regex regex-source language-regex" style="color:#36acaa">"</span><span class="token regex regex-source language-regex special-escape escape" style="color:#36acaa">\.</span><span class="token regex regex-source language-regex escape" style="color:#36acaa">\/</span><span class="token regex regex-source language-regex group punctuation" style="color:#393A34">(</span><span class="token regex regex-source language-regex char-class char-class-punctuation punctuation" style="color:#393A34">[</span><span class="token regex regex-source language-regex char-class char-class-negation operator" style="color:#393A34">^</span><span class="token regex regex-source language-regex char-class" style="color:#36acaa">"</span><span class="token regex regex-source language-regex char-class char-class-punctuation punctuation" style="color:#393A34">]</span><span class="token regex regex-source language-regex quantifier number" style="color:#36acaa">+</span><span class="token regex regex-source language-regex special-escape escape" style="color:#36acaa">\.</span><span class="token regex regex-source language-regex" style="color:#36acaa">js</span><span class="token regex regex-source language-regex group punctuation" style="color:#393A34">)</span><span class="token regex regex-source language-regex" style="color:#36acaa">"</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-flags" style="color:#36acaa">g</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">    stack</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">push</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">m</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</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 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 console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">entryPattern</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">seen</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">size</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c"> chunks, </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation punctuation" style="color:#393A34">(</span><span class="token template-string interpolation">total </span><span class="token template-string interpolation operator" style="color:#393A34">/</span><span class="token template-string interpolation"> </span><span class="token template-string interpolation number" style="color:#36acaa">1048576</span><span class="token template-string interpolation punctuation" style="color:#393A34">)</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation method function property-access" style="color:#d73a49">toFixed</span><span class="token template-string interpolation punctuation" style="color:#393A34">(</span><span class="token template-string interpolation number" style="color:#36acaa">2</span><span class="token template-string interpolation punctuation" style="color:#393A34">)</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c"> MB</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>Point it at your build output and a page's chunk name. The pattern-matching in
it assumes Rollup or Vite output, so adjust that line for other bundlers.</p>
<p>The variant I've found more useful doesn't measure anything at all. It just asks
whether a given library is reachable from a given page by filtering the list of
visited files instead of adding up their sizes.</p>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#393A34"><span class="token plain">LessonExperiencePage closure matching /mermaid|cytoscape|refractor/: NONE</span><br></div></code></pre></div></div>
<p>That line is an assertion. Put it in your continuous integration, and "somebody
statically imported the chat panel again" becomes a failing build instead of a
support ticket eight months later. Unlike a size budget, there's no threshold to
argue about. It's true or it's false.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="where-we-ended-up">Where we ended up<a href="https://rukialabs.com/engineering/the-chat-panel-nobody-opened#where-we-ended-up" class="hash-link" aria-label="Direct link to Where we ended up" title="Direct link to Where we ended up" translate="no">​</a></h2>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#393A34"><span class="token plain">markdown renderer chunk      1220K              -&gt;  704K</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">student lesson route         3.4MB / 38 chunks  -&gt;  2.50MB / 24 chunks</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">admin lesson editor route    not measured       -&gt;  2.60MB / 39 chunks</span><br></div></code></pre></div></div>
<p>Mermaid, the graph library, and the 237 Prism grammars are no longer reachable
from any of the three lesson pages, which is the result I actually care about.</p>
<p>The heavy chunks still exist in the build. They're only reachable from the
lazily loaded chat now, because <code>streamdown</code> bundles its own copy of Shiki, and
getting rid of that means replacing the library. That's a behavior change, not a
performance fix, so we left it alone.</p>
<p>I think that distinction is worth holding onto. We weren't trying to make the
build folder smaller. We were trying to stop a page paying for things it doesn't
use. Those are different goals that happen to overlap sometimes, and confusing
them leads you to optimize the wrong number.</p>
<hr>
<p><em>I'm Manasseh, and I am part of the team building <a href="https://rukialabs.com/" target="_blank" rel="noopener noreferrer" class="">Rukia Labs</a>.
If you're running training programs and fighting your tooling instead of
teaching, come say hello.</em></p>]]></content:encoded>
            <category>Performance</category>
            <category>Frontend</category>
        </item>
        <item>
            <title><![CDATA[The browser is not a witness]]></title>
            <link>https://rukialabs.com/engineering/the-browser-is-not-a-witness</link>
            <guid>https://rukialabs.com/engineering/the-browser-is-not-a-witness</guid>
            <pubDate>Thu, 10 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Two services, built eighteen months apart, both believed whatever the browser told them about which organisation it was.]]></description>
            <content:encoded><![CDATA[<p>We found the same hole twice, in two systems written a year and a half apart by
people solving unrelated problems. Neither was a subtle cryptographic mistake.
Both were the same sentence, written twice:</p>
<blockquote>
<p>The browser told us which organisation this request is for, and we believed it.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="what-multi-tenancy-has-to-guarantee">What multi-tenancy has to guarantee<a href="https://rukialabs.com/engineering/the-browser-is-not-a-witness#what-multi-tenancy-has-to-guarantee" class="hash-link" aria-label="Direct link to What multi-tenancy has to guarantee" title="Direct link to What multi-tenancy has to guarantee" translate="no">​</a></h2>
<p>Every organisation on the platform has its own courses, its own people, its own
grades and its own uploaded files. The guarantee is simple to state: a signed-in
user of one organisation must not be able to read or write anything belonging to
another.</p>
<p>It is simple to state and easy to get wrong, because almost every request has to
carry an organisation id <em>somewhere</em>, and an id in a request looks the same
whether the server verified it or merely received it.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-first-one-a-file-storage-worker">The first one: a file-storage worker<a href="https://rukialabs.com/engineering/the-browser-is-not-a-witness#the-first-one-a-file-storage-worker" class="hash-link" aria-label="Direct link to The first one: a file-storage worker" title="Direct link to The first one: a file-storage worker" translate="no">​</a></h2>
<p>Uploads used to go through a small worker that sat in front of object storage.
The browser called it with the file, the target path, and the organisation id.</p>
<p>Two things were true of that worker:</p>
<ul>
<li class="">It read <code>orgId</code> <strong>out of the request body</strong>.</li>
<li class="">It authenticated the caller with a <strong>shared secret that the browser had to
hold</strong> in order to make the call at all.</li>
</ul>
<p>So the credential proving "this request is allowed" was the same string for every
user of every tenant, and it was shipped to every browser. And the field deciding
<em>which tenant's files to touch</em> was supplied by the caller.</p>
<p>Anyone signed in to any organisation — or anyone who opened the network tab and
read the secret — could list, read, write or delete in any other organisation's
bucket. Not through a flaw in the logic. Through the logic working as written.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-second-one-an-ai-service">The second one: an AI service<a href="https://rukialabs.com/engineering/the-browser-is-not-a-witness#the-second-one-an-ai-service" class="hash-link" aria-label="Direct link to The second one: an AI service" title="Direct link to The second one: an AI service" translate="no">​</a></h2>
<p>Eighteen months later we added an assistant. The agent service behind it sits on
a private network and identifies callers with an internal shared secret, which is
correct for a service-to-service boundary.</p>
<p>Five call sites across three applications then fetched it <strong>straight from the
browser</strong>, passing <code>X-Org-Id</code> and <code>X-User-Id</code> as headers.</p>
<p>The agent service verifies nothing. That is not a bug in it — it was built to be
called by another service that had already done the verifying. Pointed at from a
browser, its contract becomes "tell me who you are and I will believe you". Any
signed-in user of any tenant could read another tenant's conversation threads and
spend their rate limit, silently.</p>
<p>Same shape. Different decade of the codebase.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-rule-we-now-hold">The rule we now hold<a href="https://rukialabs.com/engineering/the-browser-is-not-a-witness#the-rule-we-now-hold" class="hash-link" aria-label="Direct link to The rule we now hold" title="Direct link to The rule we now hold" translate="no">​</a></h2>
<blockquote>
<p><strong><code>orgId</code> is a claim the server verifies, never an instruction it follows.</strong></p>
</blockquote>
<p>Concretely, three things changed and all three matter:</p>
<p><strong>The organisation moved into the path, and the path is checked.</strong> Asset calls go
to <code>/orgs/{orgId}/assets/*</code> on our own API. The API resolves the caller from
their access token, then requires a membership row joining that user to that
organisation before it touches a bucket. The id in the URL is a <em>claim being
tested</em>, not a parameter being honoured.</p>
<p><strong>The browser stopped holding credentials.</strong> There is no shared secret in any
bundle. The only thing the browser carries is the signed-in user's own token,
which identifies exactly one person and grants exactly what that person has.</p>
<p><strong>Every call to the private service goes through our API.</strong> The assistant's
requests are proxied. The API resolves the caller, checks their membership, and
forwards the organisation's <em>verified</em> internal id — a value the browser never
sees and therefore cannot choose.</p>
<p>The old worker's path now answers <code>410 Gone</code> rather than quietly disappearing, so
anything still pointed at it fails loudly.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="what-we-would-tell-someone-auditing-their-own-system">What we would tell someone auditing their own system<a href="https://rukialabs.com/engineering/the-browser-is-not-a-witness#what-we-would-tell-someone-auditing-their-own-system" class="hash-link" aria-label="Direct link to What we would tell someone auditing their own system" title="Direct link to What we would tell someone auditing their own system" translate="no">​</a></h2>
<p><strong>Ask where each id came from, not whether it is correct.</strong> "Is this org id
valid?" is the wrong question — it was valid, it just belonged to someone else.
The right question is "could the caller have chosen this value?"</p>
<p><strong>A shared secret in a browser is not a secret.</strong> If the client must hold it to
make the call, every client holds it. It authenticates the <em>application</em>, which
is not a thing you needed to authenticate; it says nothing about the person.</p>
<p><strong>A service that trusts its caller is fine until its caller is a browser.</strong> The
agent service was not wrong. It was correct for the boundary it was designed
for, and we moved it to a different boundary without moving its assumptions.
That is the version of this mistake that is easiest to make and hardest to see
in review, because the diff that introduces it is a URL change.</p>
<p><strong>Rotate, do not just remove.</strong> The storage secret was shipped to browsers for as
long as it existed. Deleting the variable does not un-ship it.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-check-that-would-have-caught-both">The check that would have caught both<a href="https://rukialabs.com/engineering/the-browser-is-not-a-witness#the-check-that-would-have-caught-both" class="hash-link" aria-label="Direct link to The check that would have caught both" title="Direct link to The check that would have caught both" translate="no">​</a></h2>
<p>Not a code review. Both of these passed code review — the code was clear, and
each change was locally reasonable.</p>
<p>What catches it is a boundary question asked at design time: <em>for each trust
boundary in this request path, what is verified, by whom, and against what?</em> Both
holes are visible in a single sentence once somebody asks it. Neither is visible
from inside the file where it lives.</p>]]></content:encoded>
            <category>Security</category>
            <category>Multi-tenancy</category>
            <category>Architecture</category>
        </item>
        <item>
            <title><![CDATA[One documentation site, two front doors]]></title>
            <link>https://rukialabs.com/engineering/one-docs-two-front-doors</link>
            <guid>https://rukialabs.com/engineering/one-docs-two-front-doors</guid>
            <pubDate>Sat, 05 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[These pages are served from two places in two different sets of colours, from a single source.]]></description>
            <content:encoded><![CDATA[<p>This site is served at two addresses:</p>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#393A34"><span class="token plain">docs.rukialabs.com            Rukia Labs' own colours</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">yourorg.rukialabs.com/docs    your organisation's colours</span><br></div></code></pre></div></div>
<p>Same words, same pages, same build inputs. Only the branding differs — and,
less obviously, the path everything is mounted at.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="why-not-just-one">Why not just one<a href="https://rukialabs.com/engineering/one-docs-two-front-doors#why-not-just-one" class="hash-link" aria-label="Direct link to Why not just one" title="Direct link to Why not just one" translate="no">​</a></h2>
<p>An organisation's documentation should look like their product, because inside
their programme it <em>is</em> their product. Sending a learner from a branded console
to a page in somebody else's orange is a seam where there does not need to be
one.</p>
<p>And the general site has to exist too, for everyone who has not signed in to
anything yet.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-colours-are-the-easy-half">The colours are the easy half<a href="https://rukialabs.com/engineering/one-docs-two-front-doors#the-colours-are-the-easy-half" class="hash-link" aria-label="Direct link to The colours are the easy half" title="Direct link to The colours are the easy half" translate="no">​</a></h2>
<p>Branding already arrives at the edge, injected into the HTML before the page
runs — see <em>Branding that arrives before the JavaScript does</em>, earlier on this
blog. The docs read the same value and repaint their palette from it.</p>
<p>The one subtlety is ordering. The injected configuration lands at the very end
of <code>&lt;head&gt;</code>, so anything in the head runs too early to see it. The repaint
happens in the first tag of <code>&lt;body&gt;</code> instead: after the whole head, before
anything is painted. Late enough to have the data, early enough that nobody sees
the default first.</p>
<p>A version of this that ran at hydration would also be correct, eventually, and
would flash on every page load — which is the whole thing we were avoiding.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-path-is-the-hard-half">The path is the hard half<a href="https://rukialabs.com/engineering/one-docs-two-front-doors#the-path-is-the-hard-half" class="hash-link" aria-label="Direct link to The path is the hard half" title="Direct link to The path is the hard half" translate="no">​</a></h2>
<p>A static site generator compiles its base path into everything: every link,
every asset URL, the client router's notion of where the root is. Ours is no
exception.</p>
<p>That makes one build physically unable to answer at both mounts. Served under
<code>/docs</code>, a build made for <code>/</code> emits a link to <code>/guides/intro</code> — and the first
click walks the reader out of the documentation and into the learner
application, which has never heard of that route.</p>
<p>Rewriting the HTML at the edge does not save it either. You can re-base the
<code>href</code> in the markup; you cannot re-base the base path already compiled into the
router bundle. The first click is client-side and never reaches the edge at all.</p>
<p>So it is two builds of one directory, differing in a single variable. Same
content, same theme, same everything — two artifacts, because the mount point is
a build input rather than a runtime one.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-bit-we-nearly-got-wrong">The bit we nearly got wrong<a href="https://rukialabs.com/engineering/one-docs-two-front-doors#the-bit-we-nearly-got-wrong" class="hash-link" aria-label="Direct link to The bit we nearly got wrong" title="Direct link to The bit we nearly got wrong" translate="no">​</a></h2>
<p>Two copies of identical content on different hostnames is a duplicate-content
problem. Left indexable, an organisation's copy competes with the general site
for the same queries, and search engines pick a winner we did not choose.</p>
<p>The per-organisation mounts are <code>noindex</code>. They are for the people already
inside that organisation; the copy meant for everyone else is the one that gets
indexed.</p>
<p>Not a deep insight, but the kind of thing that is free to get right on the day
and expensive to notice six months later.</p>]]></content:encoded>
            <category>Documentation</category>
            <category>Multi-tenancy</category>
            <category>Edge</category>
        </item>
        <item>
            <title><![CDATA[The 404 that moved every deploy]]></title>
            <link>https://rukialabs.com/engineering/the-404-that-moved-every-deploy</link>
            <guid>https://rukialabs.com/engineering/the-404-that-moved-every-deploy</guid>
            <pubDate>Sat, 29 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[One button stopped opening its dialog. The next release fixed it and broke a search bar instead.]]></description>
            <content:encoded><![CDATA[<p>A bulk-enrolment dialog stopped opening. No error, no spinner, nothing — the
button simply did not respond. Everything around it worked. It worked in
development. It worked against a local preview of the production build. It
worked on a colleague's machine.</p>
<p>Then we shipped a fix for something unrelated, and the dialog came back on its
own. The search and filter bar stopped working instead.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="what-it-looked-like">What it looked like<a href="https://rukialabs.com/engineering/the-404-that-moved-every-deploy#what-it-looked-like" class="hash-link" aria-label="Direct link to What it looked like" title="Direct link to What it looked like" translate="no">​</a></h2>
<p>The symptom was always the same shape, and the shape is what made it hard.</p>
<p>One feature, in one part of the app, doing nothing. Not failing — <em>nothing</em>. The
surrounding page was fine. Reloading did not help. And the feature that was
broken changed between deploys, which is the detail that sent us looking in the
wrong place for a week.</p>
<p>A broken feature that moves reads as a race condition, or as state getting
corrupted, or as a caching problem. It does not read as a deployment problem,
because deployments are supposed to fail in all-or-nothing ways.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-network-tab">The network tab<a href="https://rukialabs.com/engineering/the-404-that-moved-every-deploy#the-network-tab" class="hash-link" aria-label="Direct link to The network tab" title="Direct link to The network tab" translate="no">​</a></h2>
<p>One 404. A single JavaScript file:</p>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#393A34"><span class="token plain">GET /assets/BulkEnrollCohortDialog-DhoHOVP-.js   404</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">GET /assets/CohortDetailPage-a7f31c2e.js         200</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">GET /assets/EnrollStudentsDialog-9b04dd1a.js     200</span><br></div></code></pre></div></div>
<p>Every neighbouring file was fine. The file that 404'd was present in the build
output on disk, had been uploaded, and was listed in the deployment.</p>
<p>Look at its name.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="cloudflare-pages-will-not-serve-a-file-whose-name-ends-in-a-hyphen">Cloudflare Pages will not serve a file whose name ends in a hyphen<a href="https://rukialabs.com/engineering/the-404-that-moved-every-deploy#cloudflare-pages-will-not-serve-a-file-whose-name-ends-in-a-hyphen" class="hash-link" aria-label="Direct link to Cloudflare Pages will not serve a file whose name ends in a hyphen" title="Direct link to Cloudflare Pages will not serve a file whose name ends in a hyphen" translate="no">​</a></h2>
<p>That is the whole bug.</p>
<p><code>BulkEnrollCohortDialog-DhoHOVP-.js</code> — note the hyphen immediately before the
extension. Every file named like that answers 404 from the edge. Every file that
does not is served normally.</p>
<p>Vite hashes chunk filenames so that a changed file gets a new name and can be
cached forever. Rollup's default alphabet for those hashes is base64url, which
includes <code>-</code> and <code>_</code> alongside the letters and digits. So roughly one chunk in
thirty ends up with a hash whose last character is one of those two.</p>
<p>Which chunk that happens to is decided by the content hash. Change anything in
the application and the hashes shuffle, the affected files change, and the
broken feature moves. It moves on <em>every deploy</em>, which is exactly what we saw
and exactly why we did not believe it was the build.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-fix">The fix<a href="https://rukialabs.com/engineering/the-404-that-moved-every-deploy#the-fix" class="hash-link" aria-label="Direct link to The fix" title="Direct link to The fix" translate="no">​</a></h2>
<p>One line in each app's Vite config:</p>
<div class="language-js codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-js codeBlock_TAPP thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#393A34"><span class="token literal-property property" style="color:#36acaa">build</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"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">rollupOptions</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"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">output</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"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">hashCharacters</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"hex"</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 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 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>Hex is <code>[0-9a-f]</code>. It cannot produce a hyphen or an underscore, so it cannot
produce a filename the edge refuses.</p>
<p>It is in all five applications now, and the comment beside it names the two
chunks that actually broke, because a bare <code>hashCharacters: "hex"</code> is the kind
of line somebody removes while tidying.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="checking-a-build-for-it">Checking a build for it<a href="https://rukialabs.com/engineering/the-404-that-moved-every-deploy#checking-a-build-for-it" class="hash-link" aria-label="Direct link to Checking a build for it" title="Direct link to Checking a build for it" translate="no">​</a></h2>
<p>Cheap enough to run anywhere:</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">ls</span><span class="token plain"> dist/assets </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">grep</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-E</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'[-_]\.(js|css)$'</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># must print nothing</span><br></div></code></pre></div></div>
<p>If that prints anything, that deployment has a feature which will 404 for every
user, and nothing else in the build will tell you.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="what-we-took-from-it">What we took from it<a href="https://rukialabs.com/engineering/the-404-that-moved-every-deploy#what-we-took-from-it" class="hash-link" aria-label="Direct link to What we took from it" title="Direct link to What we took from it" translate="no">​</a></h2>
<p><strong>A symptom that moves between releases is evidence about the build, not against
it.</strong> We reasoned the opposite way for several days. Application bugs are
usually stable; it is the <em>build</em> that is different every time, so a wandering
symptom points at the layer that changes on every deploy.</p>
<p><strong>"Works in preview" was a false negative.</strong> A local preview server serves
whatever is on disk. The file was on disk. Only the edge refused it, so the one
environment that could reproduce the problem was the one we could not easily
poke at.</p>
<p><strong>Defaults inherit assumptions.</strong> Nobody chose base64url; it is Rollup's default,
and it is a perfectly good default in most places. It only becomes a bug where it
meets a host with a rule about filenames. Two reasonable decisions, made by
people who never spoke, producing something that neither of them is wrong about.</p>
<p>That last one keeps coming up. It is the same shape as
<a class="" href="https://rukialabs.com/engineering/the-chat-panel-nobody-opened">the chat panel nobody opened</a>, where two
syntax highlighters arrived two years apart and neither had any reason to notice
the other.</p>]]></content:encoded>
            <category>Edge</category>
            <category>Frontend</category>
        </item>
        <item>
            <title><![CDATA[Four frontends, one address]]></title>
            <link>https://rukialabs.com/engineering/four-frontends-one-deployment</link>
            <guid>https://rukialabs.com/engineering/four-frontends-one-deployment</guid>
            <pubDate>Sat, 22 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[How the learner, instructor and admin consoles share a domain and a session without being one application.]]></description>
            <content:encoded><![CDATA[<p>An organisation's address serves three separate applications:</p>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#393A34"><span class="token plain">yourorg.rukialabs.com/            learner</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">yourorg.rukialabs.com/instructor  authoring and grading</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">yourorg.rukialabs.com/admin       administration</span><br></div></code></pre></div></div>
<p>They are three builds, from three directories, with three bundles. They share a
domain, a session and a design system, and a person moving between them does not
sign in again.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="why-not-one-application">Why not one application<a href="https://rukialabs.com/engineering/four-frontends-one-deployment#why-not-one-application" class="hash-link" aria-label="Direct link to Why not one application" title="Direct link to Why not one application" translate="no">​</a></h2>
<p>The three audiences barely overlap. A learner never opens the grading queue; an
administrator rarely takes a course. Built as one application, everyone
downloads all three — and the admin console, the heaviest of them, is the one
the fewest people open.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="why-not-three-domains">Why not three domains<a href="https://rukialabs.com/engineering/four-frontends-one-deployment#why-not-three-domains" class="hash-link" aria-label="Direct link to Why not three domains" title="Direct link to Why not three domains" translate="no">​</a></h2>
<p>Because of the session. Three domains means the sign-in state has to be shared
across origins, and every mechanism for doing that is either a third-party
cookie — increasingly not a mechanism at all — or a redirect dance that is
visible to the user and fragile in exactly the situations where it matters.</p>
<p>One origin means one cookie jar. The session problem disappears rather than
being solved.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-part-that-is-genuinely-awkward">The part that is genuinely awkward<a href="https://rukialabs.com/engineering/four-frontends-one-deployment#the-part-that-is-genuinely-awkward" class="hash-link" aria-label="Direct link to The part that is genuinely awkward" title="Direct link to The part that is genuinely awkward" translate="no">​</a></h2>
<p>Serving three applications from one origin means each has to know where it lives.
A build served at <code>/admin</code> has to request its assets from <code>/admin/assets/…</code>,
and its router has to know that <code>/admin</code> is the root and not a route.</p>
<p>That value is compiled in at build time. Which means the same source, built for
a different mount point, is a different artifact — and if the mount point and
the build disagree, the failure is not subtle. You get a blank page and a
console full of 404s for JavaScript files that exist, at a path nobody asked for.</p>
<p>We have hit this from both directions. A build that captured the browser's path
into a post-sign-in redirect — a path that already contained the prefix — and
replayed it through a router that prepends the prefix itself, landing everyone
on <code>/instructor/instructor/</code>. The learner console escaped it because its prefix
is empty, which is exactly why it took a while to see: the bug was invisible in
the application we look at most.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="what-holds-it-together">What holds it together<a href="https://rukialabs.com/engineering/four-frontends-one-deployment#what-holds-it-together" class="hash-link" aria-label="Direct link to What holds it together" title="Direct link to What holds it together" translate="no">​</a></h2>
<p>A router at the edge decides which application serves a path, and rewrites the
asset URLs in the HTML on the way back so the browser only ever sees the public
layout. The applications behind it can be one deployment or several, at their
own roots or under their prefixes, and the choice is configuration rather than
code.</p>
<p>That flexibility earns its keep during migrations, and costs something the rest
of the time: there are now two places a mount point is decided, and they have to
agree. Our defence is that disagreement fails loudly — an unconfigured
application returns an error naming the variable to set, rather than quietly
falling back to a default that serves the wrong environment's code under the
right hostname.</p>
<p>That specific failure — "not configured" and "working" being indistinguishable —
is worth more attention than it usually gets. A fallback that makes a broken
configuration look healthy does not prevent the outage. It just moves it to a
worse moment.</p>]]></content:encoded>
            <category>Architecture</category>
            <category>Multi-tenancy</category>
        </item>
        <item>
            <title><![CDATA[A dash is not a zero]]></title>
            <link>https://rukialabs.com/engineering/a-dash-is-not-a-zero</link>
            <guid>https://rukialabs.com/engineering/a-dash-is-not-a-zero</guid>
            <pubDate>Sat, 08 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[The same submission showed as 300%, 0% and 12000% in three consoles, and none of those numbers was flagged as unreliable.]]></description>
            <content:encoded><![CDATA[<p>A learner scored 120 out of 300 on a project. The admin console showed <strong>300%</strong>.
The instructor console showed <strong>0%</strong>. The student's own course card showed
<strong>12000%</strong>.</p>
<p>Nobody had written a bug. Every one of those numbers came out of code that was
individually reasonable.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="how-one-mark-becomes-three">How one mark becomes three<a href="https://rukialabs.com/engineering/a-dash-is-not-a-zero#how-one-mark-becomes-three" class="hash-link" aria-label="Direct link to How one mark becomes three" title="Direct link to How one mark becomes three" translate="no">​</a></h2>
<p>Three applications each needed to turn a score into something a person reads.
None of them had a shared helper for it, so each re-derived the arithmetic from
whatever fields it happened to have in hand.</p>
<p>The API was not sending the denominator. So each app invented a fallback for it,
and they invented four different ones:</p>
<table><thead><tr><th>Where</th><th>Missing denominator became</th></tr></thead><tbody><tr><td>Instructor console</td><td><code>0</code></td></tr><tr><td>Student course card</td><td><code>?? 1</code></td></tr><tr><td>Student progress row</td><td><code>|| 25</code></td></tr><tr><td>AI review rows, in seven places</td><td>a hard-coded <code>300</code></td></tr></tbody></table>
<p>Divide 120 by each of those and you get the three numbers above, plus a division
by zero that rendered as <code>Infinity%</code> until something else swallowed it.</p>
<p>Every fallback was written by somebody being pragmatic about a value that was
sometimes absent. Each is defensible on its own. Collectively they meant the
platform had four opinions about a learner's grade and no way to notice.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-part-that-actually-mattered">The part that actually mattered<a href="https://rukialabs.com/engineering/a-dash-is-not-a-zero#the-part-that-actually-mattered" class="hash-link" aria-label="Direct link to The part that actually mattered" title="Direct link to The part that actually mattered" translate="no">​</a></h2>
<p>The wrong numbers were not the worst of it. <strong>None of them was marked as
uncertain.</strong> A confident <code>300%</code> and a confident <code>72%</code> render identically: same
font, same colour, same position on the card. A learner reading one has no signal
that the platform is guessing.</p>
<p>That is the failure worth naming. Not "we computed the wrong number" but "we
presented a guess as a fact".</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="one-rule">One rule<a href="https://rukialabs.com/engineering/a-dash-is-not-a-zero#one-rule" class="hash-link" aria-label="Direct link to One rule" title="Direct link to One rule" translate="no">​</a></h2>
<p>There is now exactly one module that turns a mark into something a human reads,
and it is built on a single rule:</p>
<blockquote>
<p><strong>A missing denominator is unknown. It is not zero and it is not one.</strong></p>
</blockquote>
<p>Every function in it can return <code>null</code>, and callers render <code>—</code>.</p>
<div class="language-js codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-js codeBlock_TAPP thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">export</span><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">scorePercent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">score</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> total</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">const</span><span class="token plain"> s </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">toNumber</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">score</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">const</span><span class="token plain"> t </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">toNumber</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">total</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 plain">s </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> t </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> t </span><span class="token operator" style="color:#393A34">&lt;=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</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 known-class-name class-name">Math</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">round</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">s </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> t</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100</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>Note what is <em>not</em> null: a score of zero. Zero is a real result and returns <code>0</code>.
Only a missing or non-positive <strong>total</strong> makes the answer unknown. Conflating
"they scored nothing" with "we do not know what they scored" would reintroduce
the original bug from the other direction.</p>
<p>Being silent about a mark we cannot compute is recoverable — somebody asks, and
we find out why the denominator was missing. Being confidently wrong about a
learner's grade is not.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-dash-is-a-diagnostic">The dash is a diagnostic<a href="https://rukialabs.com/engineering/a-dash-is-not-a-zero#the-dash-is-a-diagnostic" class="hash-link" aria-label="Direct link to The dash is a diagnostic" title="Direct link to The dash is a diagnostic" translate="no">​</a></h2>
<p>An unexpected benefit: because the dash means one specific thing, it points at
one specific cause.</p>
<p>A dash where a number belongs almost always means an assignment was created with
no maximum score. That is a two-minute fix by whoever authored it. Before, the
same condition produced a plausible-looking percentage that nobody ever
questioned, so the underlying data stayed broken indefinitely.</p>
<p>A visible unknown gets fixed. A silent guess does not.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="totals-above-100">Totals above 100<a href="https://rukialabs.com/engineering/a-dash-is-not-a-zero#totals-above-100" class="hash-link" aria-label="Direct link to Totals above 100" title="Direct link to Totals above 100" translate="no">​</a></h2>
<p>The same module owns one more decision that used to be made three different
ways.</p>
<p>Our grading engine adds bonus points on top of the 100 that the weighted
categories divide between them, and only clamps the sum when the course's grade
scale says extra credit may not exceed full marks. Where a scale allows it, a
final mark of 108 is a real and correct result.</p>
<p>Rendering that as <code>108 / 100 (108%)</code> states a ceiling that is not the ceiling and
a percentage that reads as arithmetic gone wrong. So there is one predicate for
"is this over the scale", and callers drop the denominator when it is true.</p>
<p>Three surfaces render that total — both consoles and the learner's own progress
page — and before there was one predicate to agree on, they disagreed about it.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="what-we-would-say-to-anyone-with-the-same-shape-of-problem">What we would say to anyone with the same shape of problem<a href="https://rukialabs.com/engineering/a-dash-is-not-a-zero#what-we-would-say-to-anyone-with-the-same-shape-of-problem" class="hash-link" aria-label="Direct link to What we would say to anyone with the same shape of problem" title="Direct link to What we would say to anyone with the same shape of problem" translate="no">​</a></h2>
<p><strong>A formatting helper is not a formatting concern</strong> when what it formats is
somebody's grade. It decides a number that affects a person, and it belongs in
one tested place for the same reason the grading engine does.</p>
<p><strong>Count your fallbacks.</strong> Four different defaults for one missing value is not
four small pragmatic decisions. It is the absence of a decision, made four times.</p>
<p><strong>Make uncertainty visible in the output.</strong> If your renderer cannot distinguish
"zero" from "unknown", your users cannot either, and neither can you when you go
looking.</p>]]></content:encoded>
            <category>Frontend</category>
            <category>Architecture</category>
        </item>
        <item>
            <title><![CDATA[Branding that arrives before the JavaScript does]]></title>
            <link>https://rukialabs.com/engineering/branding-before-the-bundle</link>
            <guid>https://rukialabs.com/engineering/branding-before-the-bundle</guid>
            <pubDate>Sat, 18 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Why every organisation's colours are correct in the first painted frame, and what it took to get there.]]></description>
            <content:encoded><![CDATA[<p>Every organisation on the platform gets its own address and its own branding.
The obvious way to build that is to fetch the branding when the app starts. We
did, and it was wrong in a way that took a while to name.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-flash">The flash<a href="https://rukialabs.com/engineering/branding-before-the-bundle#the-flash" class="hash-link" aria-label="Direct link to The flash" title="Direct link to The flash" translate="no">​</a></h2>
<p>Fetching branding on mount produces a sequence: the app boots with defaults,
asks the server who this organisation is, and repaints. On a fast connection
that is a flicker. On a slow one it is two seconds of the wrong logo.</p>
<p>It is worst exactly where it matters most — the sign-in page, the first thing a
new learner sees. They arrive at their own organisation's address and are shown
somebody else's brand while the page makes up its mind.</p>
<p>No amount of optimising the fetch fixes this. The request cannot start until the
bundle runs, and the bundle cannot run until it is downloaded. The branding is
always at least one round trip behind the first paint.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="moving-the-work-in-front-of-the-app">Moving the work in front of the app<a href="https://rukialabs.com/engineering/branding-before-the-bundle#moving-the-work-in-front-of-the-app" class="hash-link" aria-label="Direct link to Moving the work in front of the app" title="Direct link to Moving the work in front of the app" translate="no">​</a></h2>
<p>Every request to an organisation's address already passes through our router at
the edge, which is what decides which application serves it. That router knows
the hostname before any application does, and the hostname is precisely what
identifies the organisation.</p>
<p>So it looks the organisation up and writes the result into the HTML on its way
past:</p>
<div class="language-html codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-html codeBlock_TAPP thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token script language-javascript dom variable" style="color:#36acaa">window</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">__TENANT__</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"> </span><span class="token script language-javascript string-property property" style="color:#36acaa">"name"</span><span class="token script language-javascript operator" style="color:#393A34">:</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">"…"</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript string-property property" style="color:#36acaa">"logoUrl"</span><span class="token script language-javascript operator" style="color:#393A34">:</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">"…"</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript string-property property" style="color:#36acaa">"settings"</span><span class="token script language-javascript operator" style="color:#393A34">:</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"> … </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" 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 tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">head</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></div></code></pre></div></div>
<p>By the time the browser parses the document, the branding is already in it. The
application reads a value that is present before it starts rather than fetching
one, and the first painted frame is correct. There is nothing to flash from.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="what-it-cost">What it cost<a href="https://rukialabs.com/engineering/branding-before-the-bundle#what-it-cost" class="hash-link" aria-label="Direct link to What it cost" title="Direct link to What it cost" translate="no">​</a></h2>
<p>Three things, none of them free.</p>
<p><strong>The HTML cannot be shared.</strong> A page carrying one organisation's name must
never be served to another, so these responses are <code>no-store</code>. The HTML shell is
small and the hashed assets behind it still cache normally, so the trade is
mostly fine — but it is a real one, and it is the reason this technique does not
generalise to every page on the internet.</p>
<p><strong>Lookups have to be fast.</strong> A per-request database query in front of every page
load is not viable. The lookup reads a key-value store at the edge, written by
the platform whenever an organisation's settings change, with a short in-process
cache in front of it. Which means writes have to invalidate that cache — a cache
that also caches misses, so a newly-created organisation stays "not found" for
the length of the TTL unless someone purges it. We learned that the direct way.</p>
<p><strong>The fallback has to stay.</strong> Injection can fail: a misconfigured host, a
lookup that times out, local development where there is no edge at all. The
application still fetches its own configuration when the injected value is
absent. The fast path is an optimisation, never a requirement — the moment it
becomes a requirement, every one of those cases becomes an outage.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="where-else-it-goes">Where else it goes<a href="https://rukialabs.com/engineering/branding-before-the-bundle#where-else-it-goes" class="hash-link" aria-label="Direct link to Where else it goes" title="Direct link to Where else it goes" translate="no">​</a></h2>
<p>Once the shell is being rewritten at the edge for one reason, other things want
the same treatment. Shared badge and certificate pages now get their
<code>og:</code> tags injected the same way, because link-preview crawlers do not run
JavaScript either — a credential pasted into Slack used to preview as the
generic application shell.</p>
<p>Same mechanism, same constraint: if it has to be in the HTML, it cannot wait for
the bundle.</p>]]></content:encoded>
            <category>Edge</category>
            <category>Multi-tenancy</category>
            <category>Architecture</category>
        </item>
    </channel>
</rss>