<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: HasanH47</title>
    <description>The latest articles on DEV Community by HasanH47 (@hasanh47).</description>
    <link>https://dev.to/hasanh47</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3911471%2F6a8d8fff-219b-4664-9ec0-8e350c87cc0d.png</url>
      <title>DEV Community: HasanH47</title>
      <link>https://dev.to/hasanh47</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hasanh47"/>
    <language>en</language>
    <item>
      <title>Our 72-hour soak lost 81% of its throughput. We had to prove it wasn't the runtime.</title>
      <dc:creator>HasanH47</dc:creator>
      <pubDate>Mon, 28 Sep 2026 12:42:07 +0000</pubDate>
      <link>https://dev.to/hasanh47/our-72-hour-soak-lost-81-of-its-throughput-we-had-to-prove-it-wasnt-the-runtime-3jml</link>
      <guid>https://dev.to/hasanh47/our-72-hour-soak-lost-81-of-its-throughput-we-had-to-prove-it-wasnt-the-runtime-3jml</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Evidence box&lt;/strong&gt;: every Usai post states the limits of its claims.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; Usai v0.0.15, alpha. The soaks ran on 2026-09-19/22 and 2026-09-23/24, on the builds of those days.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup:&lt;/strong&gt; one VM (16 vCPU Xeon) running a production-shaped deployment: Caddy in front of the published Usai runtime image, PostgreSQL 18, and the examples/invoicing app. Eight closed-loop clients; a status sample once a minute. Other work on the same host is named in the post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Supports:&lt;/strong&gt; over 73.6 M and 124.5 M requests, no 5xx and no 503 from the runtime, with memory, descriptors and ownership counters flat; the 72-hour decay came from the application's unpruned table; the runtime's cost per request stayed flat while throughput fell.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does not support:&lt;/strong&gt; behaviour under saturation, on other hardware, across several hosts, or for other applications; a general claim about PostgreSQL performance; that Usai is ready for every production workload.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source data:&lt;/strong&gt; &lt;a href="https://usai.sakala.dev/docs/measurements/2026-09-18-p5-p6-qualification/" rel="noopener noreferrer"&gt;72-hour soak&lt;/a&gt; · &lt;a href="https://usai.sakala.dev/docs/measurements/2026-09-24-bounded-soak/" rel="noopener noreferrer"&gt;24-hour control run&lt;/a&gt; · &lt;a href="https://usai.sakala.dev/docs/measurements/2026-09-26-idle-decommit/" rel="noopener noreferrer"&gt;idle decommit&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Our three-day soak served 73,627,701 requests without a single 5xx, and still lost 81% of its throughput along the way. A second, 24-hour run with the application's data pruned showed no slowdown at all. The cause was one ever-growing table, not the runtime. This post is how we got from the first observation to the second, and why we didn't stop at the easy answer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;72-hour soak     hour 0–6: 794 requests/second    hour 66–72: 154 requests/second
24-hour control  hour 1: 1,416 requests/second    hour 24: 1,480 requests/second
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;A soak test runs steady load for a long time. It catches what short benchmarks miss: slow leaks, gradual drift, and things that only break on the millionth request.&lt;/p&gt;

&lt;p&gt;Ours ran against a production-shaped deployment on one VM. Caddy sat in front of the published Usai runtime image, which talked to PostgreSQL 18 and ran the &lt;code&gt;examples/invoicing&lt;/code&gt; application from the repository. Eight closed-loop clients repeated one cycle: list invoices, fetch one, create one. About 10% of requests wrote a new invoice. Once a minute, a sampler recorded memory, CPU, open file descriptors and the runtime's own status page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three days, one falling line
&lt;/h2&gt;

&lt;p&gt;By every correctness criterion we wrote down beforehand, the 72-hour soak passed. There was no 5xx and no 503, and memory stayed flat. Throughput was another story:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Window&lt;/th&gt;
&lt;th&gt;Requests/second&lt;/th&gt;
&lt;th&gt;Median latency (p50)&lt;/th&gt;
&lt;th&gt;Slow latency (p99)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0–6 h&lt;/td&gt;
&lt;td&gt;794&lt;/td&gt;
&lt;td&gt;4.6 ms&lt;/td&gt;
&lt;td&gt;84 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12–18 h&lt;/td&gt;
&lt;td&gt;349&lt;/td&gt;
&lt;td&gt;5.1 ms&lt;/td&gt;
&lt;td&gt;289 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24–30 h&lt;/td&gt;
&lt;td&gt;244&lt;/td&gt;
&lt;td&gt;5.8 ms&lt;/td&gt;
&lt;td&gt;450 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;48–54 h&lt;/td&gt;
&lt;td&gt;181&lt;/td&gt;
&lt;td&gt;5.4 ms&lt;/td&gt;
&lt;td&gt;666 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;66–72 h&lt;/td&gt;
&lt;td&gt;154&lt;/td&gt;
&lt;td&gt;5.3 ms&lt;/td&gt;
&lt;td&gt;810 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The median barely moved. The slow tail grew tenfold, and throughput fell to a fifth.&lt;/p&gt;

&lt;p&gt;Meanwhile, everything the runtime owns stayed flat:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU inside the application's world went from 1.56 to 1.59 ms per request.&lt;/li&gt;
&lt;li&gt;CPU for the whole process went from 2.80 to 3.04 ms per request.&lt;/li&gt;
&lt;li&gt;SQL operations went from 2.52 to 2.59 per request.&lt;/li&gt;
&lt;li&gt;Memory charged to the container went from 51.5 to 53.5 MiB, and open descriptors from 29 to 30.&lt;/li&gt;
&lt;li&gt;The runtime created 73,627,744 execution worlds, detected no detached work, quarantined no database connection, and logged no WARN or ERROR line in three days.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The easy explanation, and the other one
&lt;/h2&gt;

&lt;p&gt;The workload wrote about 7.4 million invoices, with their line items, into one table that nobody pruned. More rows mean bigger indexes, more write-ahead log, and more checkpoint and autovacuum work. The application waits longer for PostgreSQL, so a closed-loop client completes fewer requests per second. Flat CPU per request fits that story.&lt;/p&gt;

&lt;p&gt;It also fits a less comfortable one. A runtime leaking something outside the CPU would draw the same curve. Think of a structure that keeps growing and gets walked once per request, a connection pool that slowly degrades, or a table of handles nobody frees. CPU per request stays flat, memory can stay flat if the growth is small, and throughput still falls.&lt;/p&gt;

&lt;p&gt;Both stories predicted exactly what we saw. Picking the convenient one would have been a guess, written up as a finding.&lt;/p&gt;

&lt;h2&gt;
  
  
  The control run
&lt;/h2&gt;

&lt;p&gt;So we ran it again: the same deployment, the same eight clients and the same load, for 24 hours. The only change was that the harness deleted what the load created, the way an operator would. Every minute it removed invoices beyond the newest 20,000, and finished queue rows older than a minute.&lt;/p&gt;

&lt;p&gt;If the data caused the decay, the decay would disappear. If the runtime caused it, the decay would still be there.&lt;/p&gt;

&lt;p&gt;It disappeared. Throughput was 1,416 requests per second in the first hour and 1,480 in the last, roughly twice the rate of the 72-hour run's opening hours. Over 124,471,717 successful requests:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Server errors&lt;/td&gt;
&lt;td&gt;0 × 5xx, 0 × 503 (and 4 × 4xx)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mean latency&lt;/td&gt;
&lt;td&gt;4.39 ms across the whole run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lifecycle&lt;/td&gt;
&lt;td&gt;0 detached work, 0 quarantined connections, 0 late or stale completions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory charged to the container&lt;/td&gt;
&lt;td&gt;53.2 to 55.2 MiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open descriptors&lt;/td&gt;
&lt;td&gt;29 to 31&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The decay was the application's table, not the runtime.&lt;/strong&gt; A day of machine time let us say that as a measurement rather than an opinion.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bad seconds
&lt;/h2&gt;

&lt;p&gt;The control run was not perfect. The load client recorded 344 timeouts, spread over 91 seconds. None came from the server, which answered no 5xx and no 503 in those seconds. All of them fell in two windows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In hour 1, another job on the same host was building a Docker image. That explains 12 timeouts in 28 seconds.&lt;/li&gt;
&lt;li&gt;In hours 16 to 18, other work on the host cut the container's CPU share from about 415% to 65% for a few minutes. A closed-loop client that gets fewer cycles sends fewer requests, and eventually hits its own 10-second timeout.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Outside those two windows, over 22 hours and 114 million requests, not one second produced an error. We report the timeouts anyway. A soak that hides its bad seconds makes readers wonder what else it hides.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two memory numbers, 36 MiB apart
&lt;/h2&gt;

&lt;p&gt;The control run also taught us something about our instrument. Midway through, the runtime's status page said the process used 89 MiB, while &lt;code&gt;docker stats&lt;/code&gt; said 53 MiB. Both were correct.&lt;/p&gt;

&lt;p&gt;The process figure, &lt;code&gt;VmRSS&lt;/code&gt;, counts a shared page once for every place it is mapped. Usai maps one prepared application image into each of its eight pooled world slots, so about 13 MiB of real pages showed up as 36 MiB.&lt;/p&gt;

&lt;p&gt;The container figure, the cgroup charge, is what decides whether a container gets killed. It under-counts the runtime's own binary. Linux charges a file's pages to whichever container read them first, and something else had read that image layer before.&lt;/p&gt;

&lt;p&gt;The honest middle is PSS, which splits shared pages between the processes using them: about 66 MiB here. The soak summary now prints all three numbers. The result doesn't change, because each run used the same instrument from start to end. But a memory figure from one tool should never be compared with a figure from another.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the same campaign caught earlier
&lt;/h2&gt;

&lt;p&gt;A soak that passes is only half the story. The same campaign found and fixed real problems before the long runs.&lt;/p&gt;

&lt;p&gt;The first was about replacing the running application. Doing it 1,000 times in a row killed the process after about 270 replacements, as memory grew about 2 MB each time until it hit the container's 512 MB limit. It wasn't a leak in the runtime, since the count of compiled images stayed at two. The cause was glibc's &lt;code&gt;malloc&lt;/code&gt;, which keeps a separate memory arena per worker thread; each arena held on to a freed, module-sized chunk the others could not reuse. Capping the number of arenas fixed it. The final run did 1,000 replacements under load, with 2,039,500 requests and no errors.&lt;/p&gt;

&lt;p&gt;The second is a trade-off rather than a bug: memory after a burst does not come back. A world slot that has been used keeps its pages, which buys about 16% more throughput on the next burst. A new release path added upstream in the engine reclaims only about 12 MiB and leaves the plateau where it was. The sizing guide says so, but it means your peak concurrency sets your memory, not your current load.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means if you run a backend
&lt;/h2&gt;

&lt;p&gt;Most of this is not about Usai.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A long soak of your application is also a soak of your database. If one table takes millions of inserts, the soak will measure that table. Prune or partition it, or run a control with bounded data, before you blame the runtime.&lt;/li&gt;
&lt;li&gt;Flat CPU per request does not prove there is no leak. It only rules out one kind. Change one variable and run again.&lt;/li&gt;
&lt;li&gt;Know which memory number you are reading. Process RSS, PSS and the container's charge answer different questions, and only the last one decides whether the container gets killed.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create @sakaladev/usai@latest my-app
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything above is on the site with the raw numbers: the &lt;a href="https://usai.sakala.dev/benchmarks/" rel="noopener noreferrer"&gt;benchmark page&lt;/a&gt;, the &lt;a href="https://usai.sakala.dev/docs/measurements/2026-09-18-p5-p6-qualification/" rel="noopener noreferrer"&gt;72-hour soak report&lt;/a&gt; and the &lt;a href="https://usai.sakala.dev/docs/measurements/2026-09-24-bounded-soak/" rel="noopener noreferrer"&gt;24-hour control run&lt;/a&gt;. If you see a hole in the reasoning, open an issue. That is exactly what we want to hear.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Usai is an open-source, workload-native application runtime for TypeScript backends: &lt;a href="https://usai.sakala.dev" rel="noopener noreferrer"&gt;usai.sakala.dev&lt;/a&gt; · &lt;a href="https://github.com/gmedia/usai" rel="noopener noreferrer"&gt;github.com/gmedia/usai&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Drafted with AI assistance and reviewed by the Usai maintainers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>testing</category>
      <category>backend</category>
      <category>performance</category>
    </item>
    <item>
      <title>Why should request state survive after the request is finished?</title>
      <dc:creator>HasanH47</dc:creator>
      <pubDate>Thu, 24 Sep 2026 21:16:27 +0000</pubDate>
      <link>https://dev.to/hasanh47/why-should-request-state-survive-after-the-request-is-finished-1i99</link>
      <guid>https://dev.to/hasanh47/why-should-request-state-survive-after-the-request-is-finished-1i99</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Evidence box&lt;/strong&gt; — every Usai post states the limits of its claims.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; Usai v0.0.15, alpha. Production qualification is in progress; contracts may still change. The sweep was measured on 0.0.8.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Supports:&lt;/strong&gt; a fresh execution world per unit of work (a module-level counter answers 1 on every request); on routes that touch PostgreSQL, throughput level with a single Node process and ahead of it on transactions; a measurable CPU cost for the fresh world, 6–14× Node's on a trivial route.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does not support:&lt;/strong&gt; that Usai is generally faster than Node, Bun, Deno, PHP or Rust; that a fresh world is a security boundary (it is semantic isolation); that Usai is ready for every production workload.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source data:&lt;/strong&gt; &lt;a href="https://usai.sakala.dev/docs/measurements/2026-09-23-sweep/" rel="noopener noreferrer"&gt;the concurrency sweep&lt;/a&gt; · &lt;a href="https://usai.sakala.dev/docs/lifecycle-contracts/" rel="noopener noreferrer"&gt;lifecycle contracts&lt;/a&gt; · &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html" rel="noopener noreferrer"&gt;AWS Lambda best practices&lt;/a&gt; · &lt;a href="https://developers.cloudflare.com/workers/best-practices/workers-best-practices/" rel="noopener noreferrer"&gt;Cloudflare Workers best practices&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A request arrives.
It creates some mutable application state.
The request finishes.

Why is that state still alive?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The usual answer is simple: because the process is still alive. In most backends the process starts once, and every request after that runs inside the same long-lived application world.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;process starts
├─ request A
├─ request B
├─ request C
└─ …and every mutable thing any of them left behind
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That model is fast, familiar and correct for a great many programs. It also means state gets its lifetime from &lt;strong&gt;where it happens to be stored&lt;/strong&gt;, not from &lt;strong&gt;what it is for&lt;/strong&gt;. Usai is an attempt to separate those two things.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug nobody writes on purpose
&lt;/h2&gt;

&lt;p&gt;Here is a handler with a variable at the top of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// module-level state&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/count&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nobody ships a counter like this. People do ship its cousins: a &lt;code&gt;currentUser&lt;/code&gt; set by middleware, a tenant id cached "for convenience", a memoised object that someone later mutates. They behave exactly like &lt;code&gt;counter&lt;/code&gt;: they outlive the request that set them.&lt;/p&gt;

&lt;p&gt;We ask every server in our benchmark suite for exactly this counter, ten times in a row. Here is what comes back:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Server&lt;/th&gt;
&lt;th&gt;Ten requests&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;One Node process&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1 2 3 4 5 6 7 8 9 10&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node cluster, 8 workers&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1 1 1 1 1 1 1 1 2 2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Usai&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1 1 1 1 1 1 1 1 1 1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The first row is the obvious leak. The second row is worse. Eight processes each remember something different, so &lt;strong&gt;the same request gets a different answer depending on which worker picked it up&lt;/strong&gt;. A bug like that passes review, passes tests on a laptop, and then shows up in production as "cannot reproduce".&lt;/p&gt;

&lt;h2&gt;
  
  
  This is not a problem we invented
&lt;/h2&gt;

&lt;p&gt;Platforms that reuse execution environments for speed document the same tension, and they leave the fix to you.&lt;/p&gt;

&lt;p&gt;AWS Lambda asks you to reuse the environment for expensive things like SDK clients and database connections, and in the same breath warns:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To avoid potential data leaks across invocations, don't use the execution environment to store user data, events, or other information with security implications.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cloudflare Workers is blunter:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Workers reuse isolates across requests. A variable set during one request is still present during the next.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Both platforms reach the same conclusion. &lt;strong&gt;Reusing infrastructure is essential for performance, and reusing mutable application state is dangerous.&lt;/strong&gt; Both ask developers to hold that line by discipline.&lt;/p&gt;

&lt;p&gt;Usai asks a different question: what if that line were part of the runtime's structure instead?&lt;/p&gt;

&lt;h2&gt;
  
  
  Persistent infrastructure is not persistent state
&lt;/h2&gt;

&lt;p&gt;The inference we question is small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;the infrastructure is persistent
therefore
the application state is persistent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line is good engineering. Listeners, schedulers, connection pools and compiled code are expensive to build and safe to reuse. The second line does not follow from the first. It is simply what happens when both live in the same process.&lt;/p&gt;

&lt;p&gt;PHP-FPM has kept these apart for a long time. Its worker processes persist, but application state is rebuilt for every request. That is why PHP-FPM answers &lt;code&gt;1 1 1 1 1 1 1 1 1 1&lt;/code&gt; to the same probe, for the same reason Usai does.&lt;/p&gt;

&lt;p&gt;Usai splits a backend into three layers, each with its own lifetime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;persistent runtime              lives for the process
  listeners · scheduler · pools · metrics

immutable application definition   built once per deploy
  compiled code · routes · schemas · config

execution world                 lives for one unit of work
  request state · auth context · globals · leases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every request, task, cron tick and queue message gets a &lt;strong&gt;fresh execution world&lt;/strong&gt;. When the work is done, the world ends. The runtime does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not everything should be short-lived
&lt;/h2&gt;

&lt;p&gt;A common misreading is that Usai throws everything away. It does not. Some things deserve to live long, and you say so:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Work&lt;/th&gt;
&lt;th&gt;Declared with&lt;/th&gt;
&lt;th&gt;The world lives for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HTTP request&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;http.get&lt;/code&gt; / &lt;code&gt;http.post&lt;/code&gt; / …&lt;/td&gt;
&lt;td&gt;one request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Task&lt;/td&gt;
&lt;td&gt;&lt;code&gt;task&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;one invocation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cron&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cron&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;one tick&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Queue message&lt;/td&gt;
&lt;td&gt;&lt;code&gt;queue.consume&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;one message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebSocket&lt;/td&gt;
&lt;td&gt;&lt;code&gt;socket&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the connection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;td&gt;&lt;code&gt;service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;while the revision is active&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A PostgreSQL pool is a &lt;strong&gt;resource&lt;/strong&gt;. It outlives worlds on purpose, and each world borrows a connection through a lease. A WebSocket world lives exactly as long as its connection. A &lt;code&gt;service&lt;/code&gt; is persistent because you declared it persistent. The default is ephemeral, and persistence is always a deliberate choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Death is not cleanup
&lt;/h2&gt;

&lt;p&gt;Throwing a world away does not make everything safe. A query can still be running on a database connection after the world that started it has ended. A timer can still be scheduled. So Usai adds a second rule: &lt;strong&gt;asynchronous work always has an owner.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// nobody owns this&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a long-lived Node process this usually works, until the process restarts at the wrong moment. In Usai the runtime cancels the timer and tells you why:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WARN http work `POST /orders` ended with live asynchronous work (1 timer).

The http lifetime ended when its result was produced. Work that is
still pending cannot remain owned by this world, so it was cancelled.

Use:
  task()    for independent finite work
  cron()    for scheduled work
  service() for intentional long-running work
or await the work before returning.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix says what you mean: &lt;code&gt;await ctx.tasks.dispatch(sendEmail, …)&lt;/code&gt; hands the work to a task that runs in its own world.&lt;/p&gt;

&lt;p&gt;The same rule decides when a database connection can be reused. A connection goes back to the pool only when Usai knows how its last operation ended: it finished normally, it failed with a known SQL error, or its cancellation was confirmed. If the outcome is ambiguous, the connection is quarantined and replaced, never reused.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs
&lt;/h2&gt;

&lt;p&gt;A fresh world is not free, and we would rather tell you than let you find out.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On a route that does nothing, Usai spends &lt;strong&gt;6–14× Node's CPU per request&lt;/strong&gt;. That is the price of entering a fresh world, validating the boundary, and settling ownership.&lt;/li&gt;
&lt;li&gt;On routes that touch PostgreSQL, which is what real applications are mostly made of, the difference mostly disappears. Usai runs &lt;strong&gt;level with a single Node process&lt;/strong&gt;, ahead of it on transactions, and &lt;strong&gt;1.8–7× ahead of Laravel&lt;/strong&gt;. As the sweep puts it: &lt;em&gt;the query is the bill; the runtime is rounding.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Memory grows with &lt;strong&gt;concurrency&lt;/strong&gt;, not with request count. 64 concurrent requests cost about 0.5 GiB.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So Usai is not the choice for a hot, CPU-bound endpoint where every microsecond counts. It is for backends where you want persistent infrastructure without every piece of state quietly inheriting the lifetime of the process. That usually means mostly-idle services, multi-tenant APIs, webhooks, and apps that mix HTTP with tasks, cron, queues and sockets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it, and try to break it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create @sakaladev/usai@latest my-app
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With pnpm it is &lt;code&gt;pnpm dlx @sakaladev/create-usai my-app&lt;/code&gt;, then &lt;code&gt;pnpm install &amp;amp;&amp;amp; pnpm dev&lt;/code&gt;. pnpm 12 installs a new release only once it is a day old, so on a release day npm is the way to get the latest.&lt;/p&gt;

&lt;p&gt;Usai is alpha, at 0.0.15. It has survived a 72-hour soak and a campaign of deliberate failures, and it has not yet met many developers outside the project. That is the next experiment. If you try it, we want to hear where the model confused you, where it got in your way, and where it caught a bug you did not know you had.&lt;/p&gt;

&lt;p&gt;The next posts in this series go deeper: how a world is created cheaply, what 73 million requests over three days taught us, and where Usai loses.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Usai is an open-source, workload-native application runtime for TypeScript backends. Docs, benchmarks and the research behind it: &lt;a href="https://usai.sakala.dev" rel="noopener noreferrer"&gt;usai.sakala.dev&lt;/a&gt;. Source: &lt;a href="https://github.com/gmedia/usai" rel="noopener noreferrer"&gt;github.com/gmedia/usai&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Drafted with AI assistance and reviewed by the Usai maintainers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>backend</category>
      <category>node</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I Tried to Compress an LLM by 545x. Here's What Happened</title>
      <dc:creator>HasanH47</dc:creator>
      <pubDate>Mon, 04 May 2026 07:09:32 +0000</pubDate>
      <link>https://dev.to/hasanh47/i-tried-to-compress-an-llm-by-545x-heres-what-happened-42kb</link>
      <guid>https://dev.to/hasanh47/i-tried-to-compress-an-llm-by-545x-heres-what-happened-42kb</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A solo dev's journey questioning a 40-year-old assumption in deep learning&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Question That Started It All
&lt;/h2&gt;

&lt;p&gt;I was frustrated.&lt;/p&gt;

&lt;p&gt;VS Code was getting heavier on my laptop. Cursor wanted $20/month. The best AI agents were owned by 5 mega-corporations. As a developer in Indonesia, I sometimes felt we were perpetual consumers, never creators.&lt;/p&gt;

&lt;p&gt;So I asked Claude: "Can AI be smaller?"&lt;/p&gt;

&lt;p&gt;That conversation led somewhere unexpected. We started questioning the most fundamental assumption in deep learning since 1986:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do weights have to be stored as matrices of numbers?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think about it. A human brain doesn't store information as numbers. A seed doesn't contain all the leaves of a tree inside it — a seed contains &lt;em&gt;instructions&lt;/em&gt; to grow leaves.&lt;/p&gt;

&lt;p&gt;What if AI weights could be &lt;strong&gt;grown&lt;/strong&gt; from a small seed when needed, instead of stored as massive matrices? A 30B model could fit on a smartphone. No cloud needed. No subscription. No billion-dollar hardware.&lt;/p&gt;

&lt;p&gt;I named the project &lt;strong&gt;WIJI&lt;/strong&gt; — "seed" in Javanese. The Javanese script: ꦮꦶꦗꦶ&lt;/p&gt;

&lt;p&gt;Slogan: &lt;em&gt;"Memaksimalkan yang minimal"&lt;/em&gt; — maximize the minimal.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;I'm a solo developer. No PhD. No GPU cluster. Just a laptop, curiosity, and AI as a research collaborator.&lt;/p&gt;

&lt;p&gt;The plan:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Take TinyLlama 1.1B (small enough to run on CPU)&lt;/li&gt;
&lt;li&gt;Try to reconstruct its weights using a tiny generator network&lt;/li&gt;
&lt;li&gt;Replace original weights with generated ones&lt;/li&gt;
&lt;li&gt;See if the model still works&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If single layer works, scale up. If not, learn why and pivot.&lt;/p&gt;




&lt;h2&gt;
  
  
  Experiment 1: Single Matrix
&lt;/h2&gt;

&lt;p&gt;I started with one weight matrix: &lt;code&gt;o_proj&lt;/code&gt; of layer 0. It has 4.2 million parameters.&lt;/p&gt;

&lt;p&gt;I built a coordinate-based MLP generator: input is &lt;code&gt;(row, col)&lt;/code&gt; coordinates, output is the weight value at that position. The generator has only 164K parameters — &lt;strong&gt;25x smaller&lt;/strong&gt; than the target.&lt;/p&gt;

&lt;p&gt;Training was simple: sample random coordinates, predict their values, minimize MSE.&lt;/p&gt;

&lt;p&gt;After 5000 steps, MSE settled at 0.000067. I reconstructed the full matrix and replaced the original in the model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test prompt&lt;/strong&gt;: "What is the capital of Indonesia?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Original output&lt;/strong&gt;: "Indonesia's capital is Jakarta."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reconstructed output&lt;/strong&gt;: "The capital of Indonesia is Jakarta."&lt;/p&gt;

&lt;p&gt;It worked. Different words, same meaning. The model still functioned with weights compressed 25x.&lt;/p&gt;

&lt;p&gt;I was elated. &lt;strong&gt;Phase 0 looked promising.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Experiment 2: Going Big
&lt;/h2&gt;

&lt;p&gt;If 1 layer works, why not all 22 layers?&lt;/p&gt;

&lt;p&gt;I added a layer embedding to the generator so it could handle multiple layers. Same 164K params, but now needed to represent &lt;strong&gt;22 different weight distributions&lt;/strong&gt; — 92M parameters total.&lt;/p&gt;

&lt;p&gt;That's 545x compression.&lt;/p&gt;

&lt;p&gt;I trained for 3000 steps. MSE settled at 0.000234 — only 3x higher than experiment 1. Should be fine, right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;: &lt;code&gt;"Ingatescripturecordialoisimoisequalifiesearchivedeastern Discogs, and"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Complete gibberish.&lt;/p&gt;

&lt;p&gt;This was my first lesson: &lt;strong&gt;MSE Loss is not a reliable predictor for LLM output quality.&lt;/strong&gt; The loss only got 3x worse, but the output collapsed entirely.&lt;/p&gt;




&lt;h2&gt;
  
  
  Experiment A: Diagnostic
&lt;/h2&gt;

&lt;p&gt;Before scaling more, I needed to understand: was the failure because of multi-component (Q/K/V/O matrices in one layer) or multi-layer (across 22 layers)?&lt;/p&gt;

&lt;p&gt;So I tested: single generator handling all 4 attention components in just &lt;strong&gt;layer 0&lt;/strong&gt;. 4 matrices, but still 1 layer.&lt;/p&gt;

&lt;p&gt;MSE settled at 0.000400 — &lt;strong&gt;6x higher&lt;/strong&gt; than experiment 1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;: "The capital of Indonesia is Jakarta."&lt;/p&gt;

&lt;p&gt;Still functional. So the issue wasn't multi-component. The issue was multi-layer.&lt;/p&gt;

&lt;p&gt;Now I had a hypothesis: &lt;strong&gt;error compounds across layers&lt;/strong&gt;. Each layer's small error becomes the next layer's wrong input, which produces bigger errors, until the model collapses.&lt;/p&gt;




&lt;h2&gt;
  
  
  Experiment B2: Microservices for Layers
&lt;/h2&gt;

&lt;p&gt;If one generator can't handle 22 layers, what if I built 22 separate generators? One per layer, each specializing.&lt;/p&gt;

&lt;p&gt;22 generators × 164K params = 3.6M total. Compression: 25x. Same as experiment 1.&lt;/p&gt;

&lt;p&gt;I trained each generator for 1000 steps. The training logs revealed something important:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;MSE Loss&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0.000063&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;0.000203&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;0.000225&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;0.000244&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;0.000373&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Error increases monotonically from early layers to late layers.&lt;/strong&gt; Same generator capacity, same training budget, but layer 21 was 6x harder to fit than layer 0.&lt;/p&gt;

&lt;p&gt;This makes sense: late layers in transformers capture complex semantic patterns. Early layers capture simple syntax. A small generator can fit the latter but struggles with the former.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;: "WHEREASPark. ."&lt;/p&gt;

&lt;p&gt;Failure. But informative failure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Experiment B3: Adaptive Capacity
&lt;/h2&gt;

&lt;p&gt;If late layers need more capacity, give them more capacity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Layers 0-7: 128 hidden dim (small)&lt;/li&gt;
&lt;li&gt;Layers 8-15: 256 hidden dim (medium)
&lt;/li&gt;
&lt;li&gt;Layers 16-21: 512 hidden dim (large)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total params: 5.9M. Compression: 15x (smaller compression but should help).&lt;/p&gt;

&lt;p&gt;I also tripled training steps to 3000 per generator.&lt;/p&gt;

&lt;p&gt;Result for layer 21: MSE = 0.000366 (vs B2's 0.000373).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Almost identical.&lt;/strong&gt; 10x more capacity, 3x more training, virtually no improvement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;: &lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;/code&gt; (degenerate loop)&lt;/p&gt;

&lt;p&gt;This was the most important finding of the entire project:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;There's a fundamental limit. MSE plateau at ~0.0003-0.0004 is independent of capacity and training time.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This phenomenon has a name in research: &lt;strong&gt;spectral bias&lt;/strong&gt;. Neural networks with ReLU/GELU activations have an inductive bias toward smooth functions. Transformer weights look like noise — high-frequency random distributions.&lt;/p&gt;

&lt;p&gt;Throwing capacity at the problem doesn't help because the architecture itself is wrong for this task.&lt;/p&gt;




&lt;h2&gt;
  
  
  Experiment B4: The Cliff Edge
&lt;/h2&gt;

&lt;p&gt;I had 22 trained generators. Before giving up, I wanted to know: where exactly does the model fail?&lt;/p&gt;

&lt;p&gt;I ran a progressive swap test. Replace layers 0 to N-1 with generated weights. Test inference. Increment N. See what happens.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;N (layers replaced)&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;"The capital of Indonesia is Jakarta."&lt;/td&gt;
&lt;td&gt;✅ Perfect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;"The capital of Ia is 10."&lt;/td&gt;
&lt;td&gt;⚠️ Partial collapse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;"" (empty)&lt;/td&gt;
&lt;td&gt;❌ Collapse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;"" (empty)&lt;/td&gt;
&lt;td&gt;❌ Collapse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;"" (empty)&lt;/td&gt;
&lt;td&gt;❌ Collapse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;"" (empty)&lt;/td&gt;
&lt;td&gt;❌ Collapse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;"Ingunsuretournalty. WHERE2..."&lt;/td&gt;
&lt;td&gt;❌ Gibberish&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Cliff edge between N=1 and N=3.&lt;/strong&gt; Sharp, not gradual. Phase transition.&lt;/p&gt;

&lt;p&gt;But the most counterintuitive finding: &lt;strong&gt;N=22 produces output, while N=5-16 produce empty strings.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you replace some layers but keep others, the corrupt layers produce outputs that are "out of distribution" for the original layers. The mismatch causes probability collapse — the model produces nothing.&lt;/p&gt;

&lt;p&gt;When you replace ALL layers, the corruption is internally consistent. The model still produces gibberish, but it produces something.&lt;/p&gt;

&lt;p&gt;The lesson: &lt;strong&gt;internal consistency matters more than absolute correctness.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Actually Learned
&lt;/h2&gt;

&lt;p&gt;After 5 experiments and many hours of failure, here's what I have:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Validated empirically&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;✅ Weight matrices have significant redundancy (compress 25-56x for single layer)&lt;/li&gt;
&lt;li&gt;✅ MSE Loss is a misleading metric for LLM compression quality&lt;/li&gt;
&lt;li&gt;✅ Cliff edge phenomenon exists at N=2 layers&lt;/li&gt;
&lt;li&gt;✅ Capacity scaling doesn't solve spectral bias&lt;/li&gt;
&lt;li&gt;✅ Internal consistency &amp;gt; absolute correctness in deep networks&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Open questions for next phase&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Can Fourier features (NeRF-style positional encoding) overcome spectral bias?&lt;/li&gt;
&lt;li&gt;Are FFN layers easier to reconstruct than attention layers?&lt;/li&gt;
&lt;li&gt;Can output-aware loss (KL divergence) replace MSE?&lt;/li&gt;
&lt;li&gt;Does cliff edge shift with bigger models?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Honest probability assessment&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;40% that the next phase finds something useful&lt;/li&gt;
&lt;li&gt;25% that we get a working prototype&lt;/li&gt;
&lt;li&gt;5-10% that this leads to a genuine breakthrough&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But 10% × "fundamentally change AI deployment" = high expected value for a solo dev with AI as collaborator.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I'm Sharing This
&lt;/h2&gt;

&lt;p&gt;I could have buried these failures and only shared the success. That's the temptation.&lt;/p&gt;

&lt;p&gt;Instead, I'm publishing everything: code, failures, insights, and methodology. Why?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Negative results are valuable.&lt;/strong&gt; Someone else attempting this will save weeks knowing where the cliff edge is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open source is legacy.&lt;/strong&gt; Even if I stop maintaining this, the experiments stay accessible forever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solo dev + AI is a new research methodology.&lt;/strong&gt; I want to demonstrate what's possible. Other developers in Indonesia, in developing countries, in their bedrooms — they can ask hard questions and explore them. They don't need to wait for FAANG employment to contribute to AI research.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Centralization is the enemy.&lt;/strong&gt; AI is concentrating into the hands of 5 corporations. If we accept that, our future is dystopian. WIJI is a contrarian bet — that intelligence can be made minimal, affordable, and owned by everyone.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Phase 1 plans:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fourier Features experiment&lt;/strong&gt; — likely solves spectral bias based on NeRF research&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FFN layer test&lt;/strong&gt; — different weight distribution, possibly easier to compress&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming inference system&lt;/strong&gt; — pragmatic system that accepts N=1 limit but works&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rust port&lt;/strong&gt; — for proper performance benchmarking&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'll publish results as I go. Failures and successes both.&lt;/p&gt;

&lt;p&gt;If you're interested in this kind of research, the repo is fully open:&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;github.com/sangkan-dev/wiji-experimental&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Critique welcome. Collaboration welcome. Even philosophical disagreement welcome.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Mari kita lebih menggila di dunia yang udah gila ini."&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Let's get crazier in this already-crazy world.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;strong&gt;About the author&lt;/strong&gt;: HasanH47, a DevOps Enginner based in Yogyakarta, Indonesia. Building products at the intersection of local context and frontier technology. Project under &lt;a href="https://github.com/sangkan-dev" rel="noopener noreferrer"&gt;Sangkan&lt;/a&gt; organization.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If this article gave you something to think about, consider following for updates on Phase 1 results. And if you're working on anything related, please reach out.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>ai</category>
      <category>deeplearning</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
