<?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: Keshav Varshney</title>
    <description>The latest articles on DEV Community by Keshav Varshney (@ikeshavvarshney).</description>
    <link>https://dev.to/ikeshavvarshney</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%2F3840062%2F87c1b3dd-df21-473e-9f20-8b66c32ccc70.jpg</url>
      <title>DEV Community: Keshav Varshney</title>
      <link>https://dev.to/ikeshavvarshney</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ikeshavvarshney"/>
    <language>en</language>
    <item>
      <title>My laptop was wrong about every optimisation that mattered</title>
      <dc:creator>Keshav Varshney</dc:creator>
      <pubDate>Sat, 07 Nov 2026 14:57:46 +0000</pubDate>
      <link>https://dev.to/ikeshavvarshney/my-laptop-was-wrong-about-every-optimisation-that-mattered-51od</link>
      <guid>https://dev.to/ikeshavvarshney/my-laptop-was-wrong-about-every-optimisation-that-mattered-51od</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; For Speed Demon 2026, a 72 hour performance hackathon run by Hackathon Raptors, I rewrote four deliberately slow Python programs in C++ with zero dependencies: a log aggregator, a search engine, a hostile CSV join and a shortest path solver. They ended between 375x and 2,100x faster than the references, byte for byte identical, and third overall on the live board when it closed. I developed them on a 2-core 2016 laptop and scored them on a 12-thread machine I could never log in to. This is the story of the disagreements between those two machines: a 12% win the laptop could not see, a 20% win the box punished, a memory number that was really a kernel accounting rule, a profile that never applied to the code it was profiling, and a hash that passed every dataset and was still wrong.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/ikeshavvarshney/speed-is-life" rel="noopener noreferrer"&gt;https://github.com/ikeshavvarshney/speed-is-life&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The game
&lt;/h2&gt;

&lt;p&gt;Each track ships a spec, a dataset and a slow, correct Python reference. You rewrite it in anything, the organisers build it offline on their box, run it, and hash the one output file. A single wrong byte scores zero. Otherwise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;K = (T_ref/T)^0.50 * (M_ref/M)^0.20 * (S_ref/S)^0.15 * (D_ref/D)^0.15
points = 50 * log10(K)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;T is wall time, exec to exit. M is the cgroup's peak memory. S is scored&lt;br&gt;
source lines after their formatter. D is one plus your dependency count. So&lt;br&gt;
speed matters most, but memory, code size and dependencies all count, and a&lt;br&gt;
change is only good if K goes up. Early on I made a change that was 6% faster and added 82 lines (run-83a72f8bdff1). K went down. That set the rule for the rest of the event: judge by K, never by T alone.&lt;/p&gt;

&lt;p&gt;The box was an i5-13500: six P-cores, 12 threads, turbo off at 2.5 GHz, 24 MB of L3. My laptop is an i5-6300U: two cores, four threads, 3 MB of L3, running Docker inside WSL. Every push to the competition branch ran each changed track once on the box and posted T and M to a public board. That was the only window I had onto the machine that counted.&lt;/p&gt;
&lt;h2&gt;
  
  
  Where the two machines disagreed
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;th&gt;Laptop said&lt;/th&gt;
&lt;th&gt;Box said&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Four hash table lookups in one &lt;code&gt;vpgatherqq&lt;/code&gt; (Track B)&lt;/td&gt;
&lt;td&gt;Almost nothing&lt;/td&gt;
&lt;td&gt;12% of the whole run, 0.0878 s to 0.0769 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prefetching the input 512 bytes ahead (Track B)&lt;/td&gt;
&lt;td&gt;Parsing 20% faster&lt;/td&gt;
&lt;td&gt;No gain. At 1024 bytes, 0.8 MB more memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raw &lt;code&gt;clone&lt;/code&gt; threads with spin locks (Track A)&lt;/td&gt;
&lt;td&gt;20% faster with 4 workers&lt;/td&gt;
&lt;td&gt;0.0414 s against 0.0162 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Porting from Rust to C++ (all tracks)&lt;/td&gt;
&lt;td&gt;Level&lt;/td&gt;
&lt;td&gt;30% slower on Track A until three build fixes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLMUL quote masks in the CSV parser (Track C)&lt;/td&gt;
&lt;td&gt;16% less CPU&lt;/td&gt;
&lt;td&gt;0.0504 s to 0.0494 s, nothing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Turning transparent huge pages off (Track B)&lt;/td&gt;
&lt;td&gt;Invisible&lt;/td&gt;
&lt;td&gt;57 to 65 MB down to 50 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;None of these is exotic. The gather result is probably the Downfall microcode mitigation, which makes gathers slow on my Skylake and is not needed on the 13500. The prefetch result is a better hardware prefetcher on the box. The spin locks were fine with four workers on four CPUs and terrible with twelve threads sharing six cores, where a spinning thread steals execution slots from the sibling doing real work. The CLMUL result told me something more useful than a speedup would have: a real CPU saving that did not move the board meant parsing was not the bottleneck at all.&lt;/p&gt;

&lt;p&gt;The port is the one I would have got wrong without the board. All four tracks&lt;br&gt;
started in Rust and moved to C++ for 21 to 30% fewer scored lines. On the&lt;br&gt;
laptop the C++ was level. On the box it was slower until I fixed three things&lt;br&gt;
that a 2-core machine does not punish: writable globals sharing cache lines&lt;br&gt;
with hot read-only data, &lt;code&gt;u64&lt;/code&gt; being the same type as &lt;code&gt;size_t&lt;/code&gt; (so the compiler&lt;br&gt;
had to assume a store through one could change sizes and masks held in the&lt;br&gt;
other, and reloaded them), and &lt;code&gt;-mtune=alderlake&lt;/code&gt; to schedule for the box's&lt;br&gt;
P-cores. After that, K was level with Rust in fewer lines.&lt;/p&gt;
&lt;h2&gt;
  
  
  Making the board report more than two numbers
&lt;/h2&gt;

&lt;p&gt;The board publishes T and M and nothing else. On Track B I wanted the real&lt;br&gt;
split of 154 ms across setup, parsing, list building, answering and exit,&lt;br&gt;
measured on the box, before redesigning anything.&lt;/p&gt;

&lt;p&gt;So I built diagnostic versions. They produce the correct output, pass every&lt;br&gt;
gate, and then encode a counter in the two numbers the board does publish:&lt;br&gt;
touch &lt;code&gt;128 MB + code * 256 KB&lt;/code&gt; of memory, release it, and sleep until&lt;br&gt;
&lt;code&gt;T = 3 s + code * 16 ms&lt;/code&gt;. Read T and M off the board, subtract, and you have a&lt;br&gt;
phase time measured on the scoring machine. Those runs score about 20 points,&lt;br&gt;
so they can never become a best run, and the real code went straight back&lt;br&gt;
after each one. They are marked as diagnostics in the commit history.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;On the box&lt;/th&gt;
&lt;th&gt;Before the redesign&lt;/th&gt;
&lt;th&gt;After it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Parse&lt;/td&gt;
&lt;td&gt;88 ms&lt;/td&gt;
&lt;td&gt;54.75 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build lists&lt;/td&gt;
&lt;td&gt;21 ms&lt;/td&gt;
&lt;td&gt;6.75 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Answer queries&lt;/td&gt;
&lt;td&gt;37.5 ms&lt;/td&gt;
&lt;td&gt;14.5 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exec and exit&lt;/td&gt;
&lt;td&gt;7.2 ms&lt;/td&gt;
&lt;td&gt;1.7 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every row surprised me. List building and answering were far larger than the&lt;br&gt;
laptop suggested. Exit was 7 ms, nearly all of it the kernel unmapping a&lt;br&gt;
250 MB input page by page on one thread with the clock running, which is&lt;br&gt;
waste you can remove by clearing the mapping on all threads before returning.&lt;br&gt;
Track B went from K 28.70 and sixth place to K 41.67 and first, and Track C&lt;br&gt;
used the same trick to find that its locked bitset write was only 16% of the&lt;br&gt;
time, not the majority I had guessed.&lt;/p&gt;

&lt;p&gt;I would ask the organisers before doing this again. It never improved a&lt;br&gt;
score directly, but it does put deliberately shaped numbers on a public board,&lt;br&gt;
and that deserves their say-so rather than mine.&lt;/p&gt;
&lt;h2&gt;
  
  
  Using another track as a control group
&lt;/h2&gt;

&lt;p&gt;Board runs are noisy. Identical Track B code ran 0.0656, 0.0715 and 0.0659 s&lt;br&gt;
on three pushes. Identical Track D code scored K from 20.15 to 21.33. On some&lt;br&gt;
pushes every track ran slow together, because the box was busy.&lt;/p&gt;

&lt;p&gt;That last observation is also the fix. Every push reruns every changed track,&lt;br&gt;
so when I changed only Track A, Track B was an unchanged program running on&lt;br&gt;
the same box minutes apart. Dividing A's time by B's in the same push cancels&lt;br&gt;
the box's mood. On Track C, pinning workers to CPUs looked like a 4% win in&lt;br&gt;
plain T. The C/B ratio said about 1%, inside the noise, and it cost 8 lines,&lt;br&gt;
so it was rejected. Close calls got three runs each, compared by median.&lt;/p&gt;
&lt;h2&gt;
  
  
  Memory is a kernel accounting question
&lt;/h2&gt;

&lt;p&gt;M is &lt;code&gt;memory.peak&lt;/code&gt; of the cgroup, and two kernel rules decided more of it than&lt;br&gt;
my data structures did.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Huge pages.&lt;/strong&gt; The box has transparent huge pages set to &lt;code&gt;always&lt;/code&gt;. A first&lt;br&gt;
touch anywhere in a 2 MB aligned range of a large mapping, including a&lt;br&gt;
thread's 8 MB stack, charges 2 MB. Threads racing on the same fault are each&lt;br&gt;
charged until the race resolves. That explained memory wandering between 57&lt;br&gt;
and 71 MB on unchanged code. One line turned it off for the process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;prctl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PR_SET_THP_DISABLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Track B then turns huge pages back on for exactly one hot table, faults it on&lt;br&gt;
one thread so nothing can race, and turns them off again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-CPU charge batches.&lt;/strong&gt; The kernel charges memory to a cgroup in batches&lt;br&gt;
of 64 pages per CPU, so the first charge on each CPU reserves up to 256 KB,&lt;br&gt;
and &lt;code&gt;memory.peak&lt;/code&gt; counts the reserve. Over 12 CPUs that is about 3 MB nobody&lt;br&gt;
uses. On Track C, which peaks under 10 MB, that is a third of the score's&lt;br&gt;
memory term. The fix was to make workers never charge memory at all: the main&lt;br&gt;
thread faults the tables, the pages workers will write, their stacks and the&lt;br&gt;
input's page tables before they need them. Track C went from 10.4 MB to 7.9 MB.&lt;/p&gt;

&lt;p&gt;The same idea failed on Track A, and why it failed is the useful part. A&lt;br&gt;
pool of pre-faulted regions big enough for the busiest moment cost more than&lt;br&gt;
the reserve it saved: M went to 20.1 and 23.8 MB.&lt;/p&gt;

&lt;p&gt;Two more rules from the contract mattered. The output file lives on tmpfs, so&lt;br&gt;
Track A's 10.1 MB of output is 10.1 MB of its 17 MB peak. And the clock stops&lt;br&gt;
when the process is reaped, so exit teardown is scored work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The profile that never applied
&lt;/h2&gt;

&lt;p&gt;Track A is built by clang 17 with profile-guided optimisation. My early&lt;br&gt;
profiles were trained during the Docker build, and the run times varied by up&lt;br&gt;
to 15% between builds of the same source. Final scoring builds once, so that&lt;br&gt;
is a gamble. I trained a profile once on the laptop, committed it as data, and&lt;br&gt;
the build became deterministic: the same binary, sha256 &lt;code&gt;595f6323...&lt;/code&gt;, every&lt;br&gt;
time. Seven runs on the board, all steady.&lt;/p&gt;

&lt;p&gt;Then I compared two profiles side by side, and found out why it was so&lt;br&gt;
steady. Clang names a &lt;code&gt;static&lt;/code&gt; function in a profile by the source path it was&lt;br&gt;
compiled from. The training compiled &lt;code&gt;/w/main.cpp&lt;/code&gt;. The Dockerfile compiles&lt;br&gt;
&lt;code&gt;main.cpp&lt;/code&gt;. So the profile matched &lt;code&gt;main&lt;/code&gt; and the standard library's sort, and&lt;br&gt;
every hot function, from the parser to the percentile selection, was built&lt;br&gt;
with no profile at all.&lt;/p&gt;

&lt;p&gt;The obvious fix made it worse. Profiles trained under the right path ran&lt;br&gt;
0.0169 s (trained with 12 threads) and 0.0193 s (trained with 4) against the&lt;br&gt;
broken profile's 0.0156 s. A build with no profile at all ran 0.0158 s. So&lt;br&gt;
the final Track A ships the profile that does not apply, on purpose, with a&lt;br&gt;
comment in the README saying so. What it buys is not speed. It is a binary&lt;br&gt;
that is the same every time it is built, which is exactly what you want when&lt;br&gt;
someone else builds it once and scores it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tests that pass and prove little
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The hash.&lt;/strong&gt; Track B looks up tokens by a hash that stands in for their&lt;br&gt;
bytes, so a collision involving a query term silently changes an answer. My&lt;br&gt;
first version used two AES rounds, which Track A's notes suggested. Every&lt;br&gt;
dataset matched the reference. Then I tested the hash as its own component on&lt;br&gt;
real tokens, and &lt;code&gt;wodeand&lt;/code&gt; and &lt;code&gt;woweandvi&lt;/code&gt; from the seed 2 corpus came out&lt;br&gt;
equal. Two AES rounds leave half the state colliding for inputs that differ&lt;br&gt;
only along one of AES's diagonals: 28% of a targeted set collided. Three&lt;br&gt;
rounds gave no collisions in 25 million targeted tokens and cost nothing&lt;br&gt;
measurable. Tracks A and C had the same weakness on part of their input, and&lt;br&gt;
got the third round too.&lt;/p&gt;

&lt;p&gt;Byte-identical output on many seeds says very little about a collision that&lt;br&gt;
needs two particular strings to exist. Final scoring runs on a hidden seed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The answer that was too good.&lt;/strong&gt; One change made Track B's answers 5x faster.&lt;br&gt;
A limit had overflowed in list decoding, so every list decoded as empty. The&lt;br&gt;
hash check caught it, but the timing is the part worth remembering: a result&lt;br&gt;
that good is a reason to look for the bug first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The check that checked the wrong binary.&lt;/strong&gt; My fuzz script builds an&lt;br&gt;
instrumented copy of the solution. When a code change broke the&lt;br&gt;
instrumentation patch, the script carried on testing the previous binary and&lt;br&gt;
reported PASS. Nothing wrong was pushed, but it now deletes old binaries first&lt;br&gt;
and stops on any patch failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The crash 1 run in 150.&lt;/strong&gt; Track C maps its tables at a fixed address. With&lt;br&gt;
12 threads forced, about one run in 150 crashed. The kernel log showed the&lt;br&gt;
thread pointer register inside my mapping: current kernels randomise the heap&lt;br&gt;
start by up to 1 GB, so &lt;code&gt;0x40000000&lt;/code&gt; could land on libc's thread block. It&lt;br&gt;
moved to &lt;code&gt;0x60000000&lt;/code&gt; with &lt;code&gt;MAP_FIXED_NOREPLACE&lt;/code&gt;. I only found it because the&lt;br&gt;
test plan said to repeat runs many times. An earlier Track A bug also failed&lt;br&gt;
on only some runs, which is why the plan said so.&lt;/p&gt;

&lt;h2&gt;
  
  
  A wrong program as a measuring instrument
&lt;/h2&gt;

&lt;p&gt;On Track C, every record sets one bit in a per-country bitset of names with a&lt;br&gt;
locked instruction. I wanted to know what that lock cost before spending hours&lt;br&gt;
removing it. So I pushed a version without the lock. It loses updates and gets&lt;br&gt;
the wrong hash, which I expected, and its T was 0.0172 s against 0.0186 s.&lt;/p&gt;

&lt;p&gt;That gave me a ceiling: 8% was the most any lock-free design could win. Three&lt;br&gt;
correct designs (buffered writes with one lock per 64, finer partitions, a&lt;br&gt;
lock per country) all came out level or slower, because each still had to&lt;br&gt;
fetch the same cache lines. Without the ceiling I would not have known when to&lt;br&gt;
stop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Removing work that was already free
&lt;/h2&gt;

&lt;p&gt;Track D is shortest paths on a 1.7 million node road-like graph, with&lt;br&gt;
bidirectional Dijkstra and a bucket queue. Skipping the edge back to the&lt;br&gt;
parent node removes a third of all relaxations. It changed nothing, because&lt;br&gt;
those lookups were already cache hits. A cycle counter around each operation,&lt;br&gt;
after three wrong guesses, showed the real cost: a random lookup in the&lt;br&gt;
distance table at about 105 cycles. Prefetching the next node's neighbours'&lt;br&gt;
labels as each node is popped was worth 11% on one core.&lt;/p&gt;

&lt;p&gt;The layout came from counting the data, not from a textbook: 98.8% of nodes&lt;br&gt;
have out-degree 4 or less, and only 3,478 of 5 million weights need more than&lt;br&gt;
11 bits. So every node gets four 4-byte slots at a computed address, and its&lt;br&gt;
edges can be prefetched from its id alone with no offset lookup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I lost
&lt;/h2&gt;

&lt;p&gt;Track D finished 33rd of 113. The leader ran 0.0614 s, and I ran 0.194 s.&lt;/p&gt;

&lt;p&gt;The honest reasons: I never measured Track D's phase split on the box, so my&lt;br&gt;
picture of it came from a laptop with an eighth of the cache, on the one track&lt;br&gt;
that is mostly cache misses. Renumbering nodes for locality made queries 25 to&lt;br&gt;
30% faster but cost 0.65 to 0.85 s of serial work, and I did not find a&lt;br&gt;
parallel ordering in time. I did not try contraction hierarchies, partly&lt;br&gt;
because the site warned that heavy indexes rarely repay themselves, and the&lt;br&gt;
leaders' times say that warning did not hold for everyone. This was the track&lt;br&gt;
where I applied my own lesson least.&lt;/p&gt;

&lt;h2&gt;
  
  
  By the numbers
&lt;/h2&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;Track A, logs&lt;/th&gt;
&lt;th&gt;Track B, search&lt;/th&gt;
&lt;th&gt;Track C, CSV join&lt;/th&gt;
&lt;th&gt;Track D, paths&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reference time&lt;/td&gt;
&lt;td&gt;32.05 s&lt;/td&gt;
&lt;td&gt;56.35 s&lt;/td&gt;
&lt;td&gt;14.44 s&lt;/td&gt;
&lt;td&gt;72.69 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Final time&lt;/td&gt;
&lt;td&gt;0.0152 s&lt;/td&gt;
&lt;td&gt;0.0640 s&lt;/td&gt;
&lt;td&gt;0.0186 s&lt;/td&gt;
&lt;td&gt;0.194 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speedup&lt;/td&gt;
&lt;td&gt;2,109x&lt;/td&gt;
&lt;td&gt;880x&lt;/td&gt;
&lt;td&gt;776x&lt;/td&gt;
&lt;td&gt;375x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;228 to 17.6 MB&lt;/td&gt;
&lt;td&gt;2,200 to 46.7 MB&lt;/td&gt;
&lt;td&gt;367 to 7.9 MB&lt;/td&gt;
&lt;td&gt;893 to 93.5 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rank at close&lt;/td&gt;
&lt;td&gt;6 of 134&lt;/td&gt;
&lt;td&gt;4 of 104&lt;/td&gt;
&lt;td&gt;4 of 111&lt;/td&gt;
&lt;td&gt;33 of 113&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&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;Build window&lt;/td&gt;
&lt;td&gt;72 hours, 340 commits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Board pushes&lt;/td&gt;
&lt;td&gt;162&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dependencies&lt;/td&gt;
&lt;td&gt;0 on every track&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overall at close&lt;/td&gt;
&lt;td&gt;3rd of 109 on machine score (judges' 35% not yet included)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rejected experiments logged&lt;/td&gt;
&lt;td&gt;More than 60, each with its run ID&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every step and every rejected experiment is in each track's &lt;code&gt;PERF.md&lt;/code&gt;, with&lt;br&gt;
the board run ID that measured it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the time actually went on
&lt;/h2&gt;

&lt;p&gt;The algorithms took the first day. The rest went on finding out which&lt;br&gt;
measurement was about which machine. The laptop could tell me whether code was&lt;br&gt;
correct and roughly whether it did less work. It could not tell me what a&lt;br&gt;
gather cost, what a spin lock cost, what the kernel would charge for memory or&lt;br&gt;
what a compiler flag was worth on a P-core. Each time I trusted it on one of&lt;br&gt;
those, the board corrected me.&lt;/p&gt;

&lt;p&gt;The question that sorted it out was the same every time: which machine is this&lt;br&gt;
number about, and could it have come out the other way? A profile that cannot&lt;br&gt;
apply cannot make a build slower. A dataset that passes cannot show a&lt;br&gt;
collision between two strings it does not contain. A laptop with four threads&lt;br&gt;
cannot show you what twelve threads spinning will do. When the answer was no,&lt;br&gt;
the number was not evidence, however good it looked.&lt;/p&gt;

&lt;p&gt;Built for &lt;a href="https://speeddemon.dev" rel="noopener noreferrer"&gt;Speed Demon 2026&lt;/a&gt;, the 72 hour performance engineering championship run by &lt;a href="https://raptors.dev" rel="noopener noreferrer"&gt;Hackathon Raptors&lt;/a&gt;. All four solutions, their performance logs and the tools used to check them are in the repo.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/ikeshavvarshney/speed-is-life" rel="noopener noreferrer"&gt;https://github.com/ikeshavvarshney/speed-is-life&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>linux</category>
      <category>performance</category>
      <category>showdev</category>
    </item>
    <item>
      <title>I built a hackathon judging platform, and the test data had no winner in it</title>
      <dc:creator>Keshav Varshney</dc:creator>
      <pubDate>Wed, 07 Oct 2026 18:13:58 +0000</pubDate>
      <link>https://dev.to/ikeshavvarshney/i-built-a-hackathon-judging-platform-and-the-test-data-had-no-winner-in-it-57jh</link>
      <guid>https://dev.to/ikeshavvarshney/i-built-a-hackathon-judging-platform-and-the-test-data-had-no-winner-in-it-57jh</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; I built podium, a self-hostable hackathon submission and judging&lt;br&gt;
platform, in 72 hours: Express, Prisma and Postgres behind a Next.js client,&lt;br&gt;
one &lt;code&gt;docker compose up&lt;/code&gt;, 438 tests. The part of the brief that looked like a&lt;br&gt;
formality was "a documented way to even out judges who score harshly or&lt;br&gt;
generously". It turned out to be the whole project. This is the story of a&lt;br&gt;
metric that reached a perfect score by construction and proved nothing, a&lt;br&gt;
judge with two ballots who could move a project 23 places, a test data set&lt;br&gt;
with no signal in it, and a feature I shipped on day one and deleted on day&lt;br&gt;
three.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/ikeshavvarshney/podium" rel="noopener noreferrer"&gt;https://github.com/ikeshavvarshney/podium&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Demo video: &lt;a href="https://www.youtube.com/watch?v=3qqdSy8PHTQ" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=3qqdSy8PHTQ&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Screens and walkthrough: &lt;a href="https://podium-demo.pages.dev/" rel="noopener noreferrer"&gt;https://podium-demo.pages.dev/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The problem that looked easy
&lt;/h2&gt;

&lt;p&gt;Judges do not share a scale. One gives 4 out of 5 because nothing is ever a&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Another gives 5 because the demo worked. Average their raw scores and you
are not ranking projects, you are ranking which judges each project happened
to draw.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The textbook fix is one line. For each judge, subtract their mean and divide&lt;br&gt;
by their standard deviation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;z(ballot) = (total - mean_j) / sd_j
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A project's score is the mean of the z-scores it received. I wrote that on&lt;br&gt;
the first day, the tests passed, and the results screen showed raw rank next&lt;br&gt;
to normalized rank with little arrows between them. It looked finished.&lt;/p&gt;
&lt;h2&gt;
  
  
  The metric that scored 100% and proved nothing
&lt;/h2&gt;

&lt;p&gt;The organizers ship a shared data set, &lt;code&gt;fixtures.json&lt;/code&gt;: 40 projects, 30&lt;br&gt;
judges, 126 score rows. After my importer refused one duplicate submission&lt;br&gt;
and its four ballots, 122 ballots remained.&lt;/p&gt;

&lt;p&gt;I wanted a number for the README, so I split the variance in those ballots&lt;br&gt;
by which judge cast them:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Variance explained by the judge&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Raw weighted total&lt;/td&gt;
&lt;td&gt;27.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-judge z-score&lt;/td&gt;
&lt;td&gt;0.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;27% down to zero. That is a fantastic table, and it is worthless.&lt;br&gt;
Standardizing sets every judge's mean to zero by definition. The judge&lt;br&gt;
effect did not go to zero because the method is good. It went to zero&lt;br&gt;
because that is what subtraction does. Any method that subtracts the judge&lt;br&gt;
mean scores 0.0% here, including a bad one.&lt;/p&gt;

&lt;p&gt;The number I had was measuring my arithmetic, not my ranking. The real&lt;br&gt;
question is whether the normalized order is closer to the truth, and for&lt;br&gt;
that you need a truth.&lt;/p&gt;
&lt;h2&gt;
  
  
  The data set with no winner in it
&lt;/h2&gt;

&lt;p&gt;So I looked at the other column: how much of the variance is explained by&lt;br&gt;
which project a ballot was for.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Explained by judge&lt;/th&gt;
&lt;th&gt;Explained by project&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Raw weighted total&lt;/td&gt;
&lt;td&gt;27.0%&lt;/td&gt;
&lt;td&gt;30.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-judge z-score, shrunk&lt;/td&gt;
&lt;td&gt;3.5%&lt;/td&gt;
&lt;td&gt;33.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-judge z-score, unshrunk&lt;/td&gt;
&lt;td&gt;0.0%&lt;/td&gt;
&lt;td&gt;33.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;About a third looks like signal until you ask what chance alone gives. With&lt;br&gt;
40 groups over 122 ballots, pure noise explains 32.2%. The fixture sits&lt;br&gt;
right on it.&lt;/p&gt;

&lt;p&gt;I checked a second way. Split each project's ballots at random into two&lt;br&gt;
halves, rank the projects by each half, and correlate the two rankings. If&lt;br&gt;
the scores carry any information about the projects, the halves agree. Over&lt;br&gt;
400 random splits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;raw totals        -0.005
normalized         0.023
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zero. The fixture's scores carry judge effects and almost no project effect.&lt;br&gt;
Which is fair: it is test data for checking that a portal works, not a&lt;br&gt;
recorded hackathon. But it meant the benchmark I had planned to prove my&lt;br&gt;
method on could not tell a good method from a random one. No method can&lt;br&gt;
recover an order that is not there.&lt;/p&gt;

&lt;p&gt;This was the benchmark that disappointed me, and I am glad it did before I&lt;br&gt;
published a table built on it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Building a truth to measure against
&lt;/h2&gt;

&lt;p&gt;The fix was a seeded simulation where every project has a known latent&lt;br&gt;
quality. 40 projects, 30 judges, 3 reviews each, 500 simulated events. Judges&lt;br&gt;
differ in severity (mean 45 to 85) and in spread (4 to 16 points). One judge&lt;br&gt;
in ten gives every project the same score. Then measure Spearman correlation&lt;br&gt;
between each method's ranking and the true order:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Mean Spearman with the true order&lt;/th&gt;
&lt;th&gt;Beats raw in&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Raw mean&lt;/td&gt;
&lt;td&gt;0.749&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rank average&lt;/td&gt;
&lt;td&gt;0.838&lt;/td&gt;
&lt;td&gt;86% of events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-judge z-score, unshrunk&lt;/td&gt;
&lt;td&gt;0.865&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-judge z-score, shrunk&lt;/td&gt;
&lt;td&gt;0.889&lt;/td&gt;
&lt;td&gt;100% of events&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That is evidence. It runs with &lt;code&gt;npm run proof&lt;/code&gt;, it is seeded so it&lt;br&gt;
reproduces exactly, and a unit test asserts the numbers so the document&lt;br&gt;
cannot drift from the code.&lt;/p&gt;

&lt;p&gt;The last two rows are where the maths fought back.&lt;/p&gt;
&lt;h2&gt;
  
  
  The judge with two ballots
&lt;/h2&gt;

&lt;p&gt;Look at what plain z-scoring does to a judge who only scored a few projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One ballot.&lt;/strong&gt; Their mean is that ballot. &lt;code&gt;z = 0&lt;/code&gt;, whatever they gave. A
5 and a 1 are the same vote.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two ballots.&lt;/strong&gt; Their mean is the midpoint and their standard deviation
is half the gap. The z-scores are always exactly &lt;code&gt;+1&lt;/code&gt; and &lt;code&gt;-1&lt;/code&gt;. A judge
who gave 80 and 79 has put the two projects two standard deviations
apart.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every ballot identical.&lt;/strong&gt; Standard deviation zero. Division by zero.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fixture has all three. 9 of its 30 judges cast fewer than three ballots,&lt;br&gt;
and &lt;code&gt;jdg_07&lt;/code&gt; gave three projects exactly 75.&lt;/p&gt;

&lt;p&gt;On the fixture, the unshrunk method moved one project, Dry Harbour, from&lt;br&gt;
31st to 8th. Two of its ballots came from judges with fewer than three&lt;br&gt;
ballots. It had not been scored well. It had been scored by people whose&lt;br&gt;
scale was manufactured from one or two data points.&lt;/p&gt;

&lt;p&gt;The fix is the standard empirical-Bayes one: do not trust a thin judge's own&lt;br&gt;
statistics. Pull them toward the panel's.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mean_j* = (n_j * mean_j + 1 * m) / (n_j + 1)        m = mean of all ballots
sd_j*   = sqrt((n_j * var_j + 3 * v) / (n_j + 3))   v = pooled within-judge variance

z = (total - mean_j*) / sd_j*      and 0 for a flat judge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A judge with twenty ballots is read almost entirely on their own scale. A&lt;br&gt;
judge with one is read mostly on the panel's. Dry Harbour now moves from&lt;br&gt;
31st to 17th instead of 8th, and in the simulation agreement with the true&lt;br&gt;
order goes from 0.865 to 0.889.&lt;/p&gt;

&lt;p&gt;Three things I would say about this before a reviewer does:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The priors were tuned on the simulation.&lt;/strong&gt; One ballot for the mean,
three for the variance (variances are noisier). I chose them where the
benchmark was best, so the 0.889 is an in-sample number.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The flat judge gets zero, not a penalty.&lt;/strong&gt; Someone who scores
everything 75 has told you nothing about order. Counting them as zero is
the honest reading. They are also named on the results screen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shrinkage keeps 3.5% of the judge effect on purpose.&lt;/strong&gt; With two
ballots, a judge's mean is as much their projects as their severity.
Removing all of it is the bug, not the goal.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Pairwise mode, and a modest win
&lt;/h2&gt;

&lt;p&gt;The second judging mode asks judges to order small groups instead of scoring&lt;br&gt;
a rubric. Borda count is the obvious tally. I also fitted a Bradley-Terry&lt;br&gt;
model with the MM algorithm, which weighs a win by the strength of the&lt;br&gt;
project beaten, so a project that drew an easy group is not rewarded for the&lt;br&gt;
draw.&lt;/p&gt;

&lt;p&gt;On 300 simulated events: Borda 0.827, Bradley-Terry 0.854, better in 82% of&lt;br&gt;
events. That is a real gain and a small one. I publish the Bradley-Terry&lt;br&gt;
ranking and show Borda beside it, because a ranking an organizer cannot&lt;br&gt;
sanity-check against a simpler one is a ranking they will not trust.&lt;/p&gt;
&lt;h2&gt;
  
  
  The debugging story: a function's name was the documentation
&lt;/h2&gt;

&lt;p&gt;The OpenAPI document is generated from the live Express routes. To know who&lt;br&gt;
may call a route, the generator walks the middleware stack and reads each&lt;br&gt;
function's name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mapped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ROLE_BY_MIDDLEWARE&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;requireAuth&lt;/code&gt; means signed in, &lt;code&gt;requireEventAdmin&lt;/code&gt; means event admin. It&lt;br&gt;
worked for two days.&lt;/p&gt;

&lt;p&gt;On the last day I replaced the single admin check with area-scoped access:&lt;br&gt;
an admin can be limited to Announcements, or Judging, or any of ten areas.&lt;br&gt;
&lt;code&gt;requireEventAdmin&lt;/code&gt; became a factory:&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;function&lt;/span&gt; &lt;span class="nf"&gt;requirePermission&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;areas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EventArea&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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;assertPermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;eventContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;areas&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every route kept working. Every permission test passed. And 69 operations&lt;br&gt;
in the published API document silently lost their access label, because an&lt;br&gt;
arrow function returned from a factory has the name &lt;code&gt;""&lt;/code&gt;. The API still&lt;br&gt;
refused the requests. The documentation now said anyone could make them.&lt;/p&gt;

&lt;p&gt;One test caught it: the one that fetches &lt;code&gt;/api/openapi.json&lt;/code&gt; and checks that&lt;br&gt;
admin routes are labelled. The fix is to name the inner function and attach&lt;br&gt;
the areas to it, which the generator now publishes as &lt;code&gt;x-required-area&lt;/code&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requirePermission&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requirePermission&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;areas&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lesson I took is narrower than "test your docs". Reading &lt;code&gt;fn.name&lt;/code&gt; is&lt;br&gt;
reflection, and a refactor that changes nothing about behaviour can change&lt;br&gt;
everything about what reflection sees. If documentation is derived from&lt;br&gt;
code, the derivation needs its own assertion.&lt;/p&gt;

&lt;p&gt;A second one from the same hour, shorter. My end-to-end check for "a limited&lt;br&gt;
admin is refused on settings" reported that the limit did not work. The API&lt;br&gt;
returned 200. The middleware was correct. The account I had picked as the&lt;br&gt;
limited admin was the instance operator, who&lt;br&gt;
bypasses area checks by design. The product was right and the test was&lt;br&gt;
wrong, and a 200 where I expected 403 looks identical either way.&lt;/p&gt;
&lt;h2&gt;
  
  
  The design I abandoned
&lt;/h2&gt;

&lt;p&gt;On day one I built &lt;strong&gt;judge track scope&lt;/strong&gt;: an organizer could restrict a&lt;br&gt;
judge to certain tracks, and mark a track as restricted. It had a column, a&lt;br&gt;
dialog, a rule inside the assignment engine and its own tests.&lt;/p&gt;

&lt;p&gt;On day three I dropped it. Migration 0026 is two lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="nv"&gt;"event_memberships"&lt;/span&gt; &lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="nv"&gt;"track_scope"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="nv"&gt;"tracks"&lt;/span&gt; &lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="nv"&gt;"restricted"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason is that the platform already had an answer to "which projects&lt;br&gt;
does this judge see": the assignment. A judge reads exactly the projects an&lt;br&gt;
organizer assigned and nothing else, and that boundary is enforced in the&lt;br&gt;
API and tested with &lt;code&gt;curl&lt;/code&gt;. Track scope was a second answer to the same&lt;br&gt;
question. Two sources of truth meant every path had to consult both, the&lt;br&gt;
auto-balancer had to explain why it left a project under-reviewed, and the&lt;br&gt;
organizer had a setting that could quietly contradict what they had just&lt;br&gt;
done by hand.&lt;/p&gt;

&lt;p&gt;The same clean-up hit "reviews per project". It started as a hard cap that&lt;br&gt;
blocked manual assignment past the number. It is now called "Judges per&lt;br&gt;
project" and is only the target the auto-balancer aims for. An organizer who&lt;br&gt;
wants a sixth opinion on a close call can simply assign one.&lt;/p&gt;

&lt;p&gt;Both removals made the judging model smaller: one function decides whether a&lt;br&gt;
judge may review a project, and it checks one thing, that it is not their&lt;br&gt;
own team's.&lt;/p&gt;
&lt;h2&gt;
  
  
  The schema I would redo
&lt;/h2&gt;

&lt;p&gt;Two decisions from the last day, both made because they were fast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Admin areas are a &lt;code&gt;TEXT[]&lt;/code&gt; with a magic string.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="nv"&gt;"event_memberships"&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="nv"&gt;"permissions"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[]::&lt;/span&gt;&lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="nv"&gt;"event_memberships"&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="nv"&gt;"permissions"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'ALL'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nv"&gt;"role"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ADMIN'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The column sits on every membership row, including participants and judges,&lt;br&gt;
where it means nothing. Postgres cannot check that an entry is a real area,&lt;br&gt;
so validity lives in one Zod transform. And &lt;code&gt;'ALL'&lt;/code&gt; is a sentinel sharing a&lt;br&gt;
namespace with real values. The rest of the schema leans on the database for&lt;br&gt;
correctness: one ballot per judge per project is a unique index, the audit&lt;br&gt;
log is append-only by trigger. This column is the exception. It should be an&lt;br&gt;
enum array with a check that it is empty unless the role is ADMIN, or a&lt;br&gt;
small join table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flagging a project reuses the submission status.&lt;/strong&gt; An organizer can remove&lt;br&gt;
a project from public view with a reason. I stored that as&lt;br&gt;
&lt;code&gt;status = DISQUALIFIED&lt;/code&gt; plus three flag columns, which was attractive&lt;br&gt;
because every query that already filtered on &lt;code&gt;SUBMITTED&lt;/code&gt; excluded flagged&lt;br&gt;
projects for free: gallery, judge queues, ballots, results. The cost shows&lt;br&gt;
up on restore, which has to put the status back and can only assume it was&lt;br&gt;
&lt;code&gt;SUBMITTED&lt;/code&gt;. Visibility and lifecycle are two different facts and I stored&lt;br&gt;
them in one field.&lt;/p&gt;

&lt;p&gt;The decision I would keep is the one everything else hangs on: a role is not&lt;br&gt;
a property of an account, it is a row joining an account to an event. The&lt;br&gt;
same person is a judge in one event and a participant in another, and no&lt;br&gt;
request body ever gets to say who the caller is.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I did not build
&lt;/h2&gt;

&lt;p&gt;Stated here instead of left for someone to find.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Community voting counts accounts, not people.&lt;/strong&gt; Registration needs no
email verification, because the platform has to run with no mail server.
The threat model marks Sybil accounts as reduced, not stopped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collusion checks flag, they do not prove.&lt;/strong&gt; Lockstep judge pairs,
outlier ballots, flat judges and shared organizations are surfaced before
publishing. Two judges who vary their scores just enough stay under every
threshold.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The acceptance checker verifies two of my four tiers.&lt;/strong&gt; It has no probes
for T3 or T4, so those claims rest on my own tests and a manual
walkthrough.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A failed seed does not fail the boot.&lt;/strong&gt; The entrypoint runs the seed
with &lt;code&gt;|| echo "seed skipped"&lt;/code&gt;. If the fixture import throws, the API still
starts and reports healthy, minus the event. I found this after
submitting, while reading my own entrypoint to answer a different
question.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uploads live in Postgres.&lt;/strong&gt; Fine for a few hundred images and it keeps
the install to one database. Not fine for a large archive.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  By the numbers
&lt;/h2&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;Build window&lt;/td&gt;
&lt;td&gt;72 hours, 140 commits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server&lt;/td&gt;
&lt;td&gt;about 13,400 lines of TypeScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client&lt;/td&gt;
&lt;td&gt;about 19,500 lines, Next.js 15 and React 19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tests&lt;/td&gt;
&lt;td&gt;438 across 38 files, about 5,400 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Migrations&lt;/td&gt;
&lt;td&gt;26, applied from an empty database on boot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API&lt;/td&gt;
&lt;td&gt;159 operations on 121 paths, OpenAPI 3.1 generated from the routes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Acceptance checker&lt;/td&gt;
&lt;td&gt;7 of 7 probes passing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clean install&lt;/td&gt;
&lt;td&gt;cloned from GitHub, healthy in 83 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raw mean against the true order&lt;/td&gt;
&lt;td&gt;0.749&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shrunk z-score against the true order&lt;/td&gt;
&lt;td&gt;0.889&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What the time actually went on
&lt;/h2&gt;

&lt;p&gt;The z-score took an afternoon. What took the rest was finding out that the&lt;br&gt;
first three ways I tried to show it worked could not have shown it failing.&lt;br&gt;
A variance table that reads zero for any method that subtracts a mean. A&lt;br&gt;
data set where no ranking is recoverable. A two-ballot judge who looks like&lt;br&gt;
a confident expert.&lt;/p&gt;

&lt;p&gt;The question that sorted them out was the same each time: could this number&lt;br&gt;
have come out badly? If the answer is no, it is not evidence, however good&lt;br&gt;
it looks in a README.&lt;/p&gt;

&lt;p&gt;The fixture had no winner in it. The useful thing the platform can do with&lt;br&gt;
data like that is say so.&lt;/p&gt;




&lt;p&gt;Built for &lt;a href="https://dogfoodhack.com" rel="noopener noreferrer"&gt;DOGFOOD 2026&lt;/a&gt;, the 72 hour hackathon run&lt;br&gt;
by Hackathon Raptors, where the brief is to build the platform a hackathon&lt;br&gt;
is judged on. The normalization proof, the judging methodology, the threat&lt;br&gt;
model and the full API document are in the repo.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/ikeshavvarshney/podium" rel="noopener noreferrer"&gt;https://github.com/ikeshavvarshney/podium&lt;/a&gt;&lt;br&gt;
Demo video: &lt;a href="https://www.youtube.com/watch?v=3qqdSy8PHTQ" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=3qqdSy8PHTQ&lt;/a&gt;&lt;br&gt;
Screens and walkthrough: &lt;a href="https://podium-demo.pages.dev/" rel="noopener noreferrer"&gt;https://podium-demo.pages.dev/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>postgres</category>
    </item>
    <item>
      <title>My Go tests never executed a line of the JavaScript they were testing</title>
      <dc:creator>Keshav Varshney</dc:creator>
      <pubDate>Thu, 03 Sep 2026 14:40:42 +0000</pubDate>
      <link>https://dev.to/ikeshavvarshney/my-go-tests-never-executed-a-line-of-the-javascript-they-were-testing-4m0m</link>
      <guid>https://dev.to/ikeshavvarshney/my-go-tests-never-executed-a-line-of-the-javascript-they-were-testing-4m0m</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; I built a Real User Monitoring tool as one Go binary with an empty&lt;br&gt;
dependency manifest, zero entries in &lt;code&gt;go.mod&lt;/code&gt;. 245 test functions, &lt;code&gt;go vet&lt;/code&gt;&lt;br&gt;
clean, CI green on every push, a reproducible build verified on every commit.&lt;br&gt;
Six bugs survived all of it. This is the story of a build that stamped a git&lt;br&gt;
SHA into a binary I had called deterministic, an &lt;code&gt;int64&lt;/code&gt; that saturated into&lt;br&gt;
six-year chart buckets, a demo page that was genuinely slow and reported&lt;br&gt;
green, and a fix that was correct, deployed, and invisible for an hour.&lt;/p&gt;

&lt;p&gt;None of them were findable from Go. That is the actual subject of this post.&lt;/p&gt;


&lt;h2&gt;
  
  
  The premise is a joke that turned out to be the product
&lt;/h2&gt;

&lt;p&gt;RUM tells you how slow your site feels to real visitors. Every mainstream way&lt;br&gt;
of collecting it ships 30 to 60KB of third-party JavaScript from someone&lt;br&gt;
else's CDN, executing before your own code, on every page view. The tool that&lt;br&gt;
measures page weight is itself page weight.&lt;/p&gt;

&lt;p&gt;So the target was a beacon small enough that the size is the argument. It&lt;br&gt;
landed at &lt;strong&gt;942 bytes&lt;/strong&gt;, against 7,226 for Google's &lt;code&gt;web-vitals&lt;/code&gt;. Everything&lt;br&gt;
else followed from refusing to make that number worse: no framework, because&lt;br&gt;
a framework is a bundle; no chart library, because a chart library is 60KB to&lt;br&gt;
draw six lines; no database driver, because the thing being stored is five&lt;br&gt;
floats per page view.&lt;/p&gt;

&lt;p&gt;Writing that was the easy half. The question that decides whether any of it&lt;br&gt;
is real is: how do you know it works?&lt;/p&gt;


&lt;h2&gt;
  
  
  The gap: no Go test can execute JavaScript
&lt;/h2&gt;

&lt;p&gt;Testing a browser beacon from a Go suite means testing everything except the&lt;br&gt;
beacon. I could prove &lt;code&gt;/b.js&lt;/code&gt; was served with the right MIME type and ETag,&lt;br&gt;
that it stayed under its 1024-byte budget, that the ingest endpoint parsed a&lt;br&gt;
payload I hand-wrote and the API read it back. All of that passed, and none&lt;br&gt;
of it proves &lt;code&gt;PerformanceObserver&lt;/code&gt; fires, that &lt;code&gt;visibilitychange&lt;/code&gt; triggers&lt;br&gt;
the flush, or that CLS session-window arithmetic produces a number a browser&lt;br&gt;
would agree with.&lt;/p&gt;

&lt;p&gt;Go tests embed the file, serve it, assert on its bytes, and never execute a&lt;br&gt;
line of it. To a Go test, JavaScript is a byte slice.&lt;/p&gt;

&lt;p&gt;So I drove real Chrome over the DevTools Protocol. Node 22 ships a global&lt;br&gt;
&lt;code&gt;WebSocket&lt;/code&gt;, so the harness needed nothing installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;webSocketDebuggerUrl&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CDP&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/json/version`&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;webSocketDebuggerUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tests/*.mjs (external harness)     drives real Chrome over CDP
        │  Page.navigate, Runtime.evaluate, Input.dispatchMouseEvent
        ▼
vitals binary                      serves dashboard, demo pages, beacon.min.js
        │  beacon executes for real, PerformanceObserver fires for real
        ▼
/api/collect  →  /api/series, /api/events (EventSource)
        │
        ▼
45 scripted assertions against what the browser actually measured
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The harness lives outside the repository, because a browser automation&lt;br&gt;
dependency inside a zero-dependency project would be absurd.&lt;/p&gt;

&lt;p&gt;Two details from building it are worth passing on. Chrome 152 removed&lt;br&gt;
&lt;code&gt;Emulation.setPageVisibilityState&lt;/code&gt;, the usual way to fake a tab switch, so I&lt;br&gt;
replaced it with what a visitor actually does: open a second tab and activate&lt;br&gt;
it. And I was clicking with &lt;code&gt;element.click()&lt;/code&gt;, which made INP appear only&lt;br&gt;
intermittently, because synthetic clicks do not reliably produce Event Timing&lt;br&gt;
entries. Switching to &lt;code&gt;Input.dispatchMouseEvent&lt;/code&gt; made it deterministic and&lt;br&gt;
produced the single best piece of evidence in the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;metric inp collected by the browser  p75=600.00 ms (poor, n=1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;600.00ms against a handler deliberately blocking for exactly 600ms.&lt;/p&gt;




&lt;h2&gt;
  
  
  Six bugs, in increasing order of "that would have shipped"
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The reproducible build that wasn't
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;make repro&lt;/code&gt; builds twice and compares hashes. CI does the same on a clean&lt;br&gt;
runner and runs &lt;code&gt;cmp&lt;/code&gt;. Both passed from the day I wrote them. The README&lt;br&gt;
said the binary contains no build timestamp, git SHA, or injected version,&lt;br&gt;
which is what makes the output byte-identical. False. I caught it only&lt;br&gt;
because a hash changed between two commits that touched nothing but&lt;br&gt;
Markdown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;go version &lt;span class="nt"&gt;-m&lt;/span&gt; vitals
&lt;span class="go"&gt;build   vcs.revision=6acd604c28b79a3db52850287e061b98c0018d7e
build   vcs.time=2026-08-30T10:56:20Z
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since Go 1.18 the toolchain stamps the git revision, a timestamp, and a&lt;br&gt;
module pseudo-version into any binary built inside a repository. On by&lt;br&gt;
default. The fix is one flag, &lt;code&gt;-buildvcs=false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The fix is not the interesting part. My test checked a weaker property than&lt;br&gt;
my claim. "Two builds at the same commit produce identical bytes" and "the&lt;br&gt;
binary contains no commit metadata" are different sentences. Only the first&lt;br&gt;
was under test. The second was what the README asserted, and a passing test&lt;br&gt;
sat next to it the whole time looking like evidence.&lt;/p&gt;

&lt;p&gt;I proved the fix the way I should have proved the claim: build, make an&lt;br&gt;
empty commit so the SHA changes, build again, compare. Most&lt;br&gt;
reproducible-build advice stops at &lt;code&gt;-trimpath&lt;/code&gt; and &lt;code&gt;-ldflags=-buildid=&lt;/code&gt;. The&lt;br&gt;
VCS stamp is newer, quieter, and it undermines you without saying anything.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. The int64 that saturated
&lt;/h3&gt;

&lt;p&gt;I loaded the dashboard and read the caption under the chart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 of 48 buckets have samples, 3202560 min per bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Six years per bucket. On a chart labelled "last 24 hours".&lt;/p&gt;

&lt;p&gt;The dashboard calls &lt;code&gt;/api/series?from=24h&lt;/code&gt; and sends no &lt;code&gt;to&lt;/code&gt;, because "now"&lt;br&gt;
is the obvious default. An open range end was normalised to the year 9999.&lt;br&gt;
Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;From&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Buckets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;time.Duration&lt;/code&gt; is an &lt;code&gt;int64&lt;/code&gt; of nanoseconds, topping out near 292 years.&lt;br&gt;
Subtracting 1970 from 9999 does not error, does not panic, does not warn. It&lt;br&gt;
saturates. Divide by 48 and you get six-year buckets, rendered without&lt;br&gt;
complaint.&lt;/p&gt;

&lt;p&gt;No test caught it because I wrote the tests alongside the API, thinking&lt;br&gt;
about the API, so every one passed both &lt;code&gt;from&lt;/code&gt; and &lt;code&gt;to&lt;/code&gt; like a well-behaved&lt;br&gt;
caller. The dashboard, the only real client this API has, passes one. The&lt;br&gt;
bug lived exactly in the gap between how I documented the endpoint and how&lt;br&gt;
the only thing calling it actually calls it.&lt;/p&gt;

&lt;p&gt;The regression test does not assert a bucket width. It asserts that no&lt;br&gt;
combination of parameters can produce a span longer than a century, because&lt;br&gt;
a span that long means an open end leaked through and the arithmetic&lt;br&gt;
saturated.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. The demo that demonstrated nothing
&lt;/h3&gt;

&lt;p&gt;The project ships four deliberately broken demo pages so the dashboard shows&lt;br&gt;
something other than a wall of green. One is "heavy image": a hero of 2,600&lt;br&gt;
inline SVG shapes, meant to tank Largest Contentful Paint. It is genuinely&lt;br&gt;
expensive to render.&lt;/p&gt;

&lt;p&gt;In Chrome it measured &lt;strong&gt;220ms&lt;/strong&gt;. Good band. Green.&lt;/p&gt;

&lt;p&gt;An inline &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; element is not an LCP candidate. The spec is specific&lt;br&gt;
about what counts: &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;image&amp;gt;&lt;/code&gt; inside an SVG, video poster frames,&lt;br&gt;
background images, and block-level text. A root inline &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; is not on&lt;br&gt;
that list. The page burned real paint time while LCP quietly reported on a&lt;br&gt;
paragraph above it.&lt;/p&gt;

&lt;p&gt;This one stings because it is precisely the failure a performance dashboard&lt;br&gt;
exists to prevent: the page was slow, the metric said fast, both were true,&lt;br&gt;
and the gap was a spec detail I had not read carefully enough.&lt;/p&gt;

&lt;p&gt;Fixed with an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; and a data URI so it is actually a candidate, plus a&lt;br&gt;
deliberate main-thread block so it is slow on any hardware rather than only&lt;br&gt;
on a bad laptop. It reports 2852ms now. My first attempt used a 4200ms block&lt;br&gt;
to force the poor band, which broke differently: the page was still blocked&lt;br&gt;
when the tab hid, so the beacon flushed before any paint entry existed and&lt;br&gt;
sent a record containing only TTFB. Worse demo, worse data. The page now&lt;br&gt;
explains the trap in its own copy, because the trap is more interesting than&lt;br&gt;
the demo.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. I measured my own tooling and called it a result
&lt;/h3&gt;

&lt;p&gt;The headline claim is a size comparison. That claim is the entire pitch, so&lt;br&gt;
it had better be clean. My first numbers were not: I measured our beacon&lt;br&gt;
with Go's gzip and theirs with Python's. Same algorithm, different&lt;br&gt;
implementations, a few percent apart. Nobody would ever have caught it. It&lt;br&gt;
would have been a property of my toolchain, presented as a property of my&lt;br&gt;
code, in the one number the project rests on.&lt;/p&gt;

&lt;p&gt;So I wrote a tool that runs every file through one compressor:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Raw&lt;/th&gt;
&lt;th&gt;Gzipped&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;vitals beacon&lt;/td&gt;
&lt;td&gt;942 B&lt;/td&gt;
&lt;td&gt;571 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vitals full beacon&lt;/td&gt;
&lt;td&gt;2,656 B&lt;/td&gt;
&lt;td&gt;1,415 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;web-vitals.iife.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;7,226 B&lt;/td&gt;
&lt;td&gt;2,601 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;web-vitals.attribution.iife.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;12,505 B&lt;/td&gt;
&lt;td&gt;4,172 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;7.7x smaller raw, 4.6x gzipped. And still not a fair fight, in both&lt;br&gt;
directions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unfair to us:&lt;/strong&gt; &lt;code&gt;web-vitals&lt;/code&gt; only measures. It hands each metric to a&lt;br&gt;
callback and leaves transport entirely to you, so a real deployment adds&lt;br&gt;
reporting code on top of those bytes. Our 942 already include JSON&lt;br&gt;
serialisation, &lt;code&gt;sendBeacon&lt;/code&gt;, a &lt;code&gt;fetch&lt;/code&gt; fallback, and flush-on-hide.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unfair to them:&lt;/strong&gt; &lt;code&gt;web-vitals&lt;/code&gt; does more per metric: back-forward cache&lt;br&gt;
restoration, prerendering and &lt;code&gt;activationStart&lt;/code&gt;, soft navigations,&lt;br&gt;
attribution to the element that caused the bad LCP, real INP grouped by&lt;br&gt;
&lt;code&gt;interactionId&lt;/code&gt; rather than the longest single event, and years of Safari&lt;br&gt;
workarounds.&lt;/p&gt;

&lt;p&gt;At 942 bytes I did none of that. So I wrote a second beacon that does five&lt;br&gt;
of those six, at 2,656 bytes, and kept it as a separate file rather than&lt;br&gt;
folding it into the first. Two reasons, one technical and one not. The&lt;br&gt;
technical one: most sites want the small one, and paying 1.7KB for prerender&lt;br&gt;
correction on a site that never prerenders is exactly the trade this project&lt;br&gt;
exists to argue against. The other: the sub-1KB number is the headline&lt;br&gt;
claim, and quietly redefining "the beacon" to mean the bigger file is the&lt;br&gt;
move I would criticise in someone else's README.&lt;/p&gt;

&lt;p&gt;Neither beacon does the Safari and Firefox work. That is years of&lt;br&gt;
accumulated browser bug knowledge, not something you reproduce by reading a&lt;br&gt;
spec. A comparison is more convincing with its caveats than without, because&lt;br&gt;
a reader who spots an unstated one stops trusting the rest of your numbers&lt;br&gt;
too.&lt;/p&gt;
&lt;h3&gt;
  
  
  5. The cache that outlived the bug
&lt;/h3&gt;

&lt;p&gt;I broke &lt;code&gt;dash.js&lt;/code&gt; with a one-character mistake, a literal newline inside a&lt;br&gt;
string literal, which is a syntax error. The whole script failed to parse,&lt;br&gt;
so the dashboard rendered nothing. Every Go test passed. Of course they did.&lt;br&gt;
&lt;code&gt;node --check&lt;/code&gt; is now in the pre-finish checklist, alongside &lt;code&gt;make check&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The instructive part came next. I fixed it, rebuilt, restarted, and the page&lt;br&gt;
was still blank:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Cache-Control: public, max-age=3600
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The asset names carry no content hash. A browser that had already fetched&lt;br&gt;
the broken script would not ask again for an hour. Server fixed, disk&lt;br&gt;
fixed, user still broken, and no amount of restarting changes that, because&lt;br&gt;
nothing on the server participates in the decision.&lt;/p&gt;

&lt;p&gt;The fix is a policy split rather than a blanket. The dashboard's own scripts&lt;br&gt;
are now &lt;code&gt;no-cache&lt;/code&gt;, which still caches them but forces a revalidation that&lt;br&gt;
answers &lt;code&gt;304&lt;/code&gt; with an empty body. The beacon keeps the long &lt;code&gt;max-age&lt;/code&gt;,&lt;br&gt;
because it is fetched by every page view of an instrumented site, where a&lt;br&gt;
conditional request per view is a real cost. Same file server, two&lt;br&gt;
policies, chosen by who is asking and how often.&lt;/p&gt;

&lt;p&gt;A long cache lifetime is a promise you cannot revoke. It is only safe when&lt;br&gt;
the URL changes with the content. If you are not hashing names, you are&lt;br&gt;
betting you will never ship a bug.&lt;/p&gt;
&lt;h3&gt;
  
  
  6. The half-open window that dropped the present
&lt;/h3&gt;

&lt;p&gt;An end-to-end test started failing about half the time. Same code, same&lt;br&gt;
machine, same command.&lt;/p&gt;

&lt;p&gt;The dashboard's default window ends at "now". The store's range is&lt;br&gt;
half-open, &lt;code&gt;[from, to)&lt;/code&gt;. A measurement recorded in the same clock tick as&lt;br&gt;
the request that reads it lands exactly on the exclusive bound and falls&lt;br&gt;
outside its own window. On Windows, where the wall clock is coarse enough&lt;br&gt;
that two calls microseconds apart return the same value, that is a coin&lt;br&gt;
flip.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsZero&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;            &lt;span class="c"&gt;// excludes anything stamped at this instant&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every test passing an explicit &lt;code&gt;to&lt;/code&gt; was fine. Only the default was wrong,&lt;br&gt;
and the default is what the only real client sends: the same gap as bug 2,&lt;br&gt;
in a different corner of the same function. The failure mode was "I just&lt;br&gt;
recorded a measurement and it is not there", intermittently, which is the&lt;br&gt;
worst kind of bug report to receive from yourself.&lt;/p&gt;

&lt;p&gt;The fix is one millisecond, and the reasoning matters more than the line.&lt;br&gt;
Records are stored at millisecond resolution, so ending the default window&lt;br&gt;
one millisecond after now includes the present instant and nothing that has&lt;br&gt;
not happened yet. An explicit &lt;code&gt;to&lt;/code&gt; is left exactly where the caller put it.&lt;/p&gt;

&lt;p&gt;A flaky test is a race you have already reproduced. I nearly re-ran it and&lt;br&gt;
moved on.&lt;/p&gt;


&lt;h2&gt;
  
  
  The bug the whole suite was structurally blind to
&lt;/h2&gt;

&lt;p&gt;This one arrived last and it is the one I would most want a reviewer to&lt;br&gt;
see. The dashboard has a button that copies a report as a prompt for an AI&lt;br&gt;
agent. Clicking it threw:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Cannot&lt;/span&gt; &lt;span class="nx"&gt;read&lt;/span&gt; &lt;span class="nx"&gt;properties&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nf"&gt;undefined &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reading&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;good&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Go struct tagged one field &lt;code&gt;json:"Distribution"&lt;/code&gt; with a capital D. The&lt;br&gt;
dashboard, the API documentation, and the contract test all said&lt;br&gt;
&lt;code&gt;distribution&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is why nothing caught it: &lt;code&gt;encoding/json&lt;/code&gt; matches field names&lt;br&gt;
case-insensitively when decoding. The contract test unmarshals the response&lt;br&gt;
into a struct tagged &lt;code&gt;json:"distribution"&lt;/code&gt;, and it bound happily to&lt;br&gt;
&lt;code&gt;"Distribution"&lt;/code&gt;. It passed against either spelling. Every Go test in the&lt;br&gt;
repository was structurally blind to this, and only a browser, which is not&lt;br&gt;
case-insensitive about anything, could ever see it.&lt;/p&gt;

&lt;p&gt;The regression test therefore does not use a struct. It decodes into&lt;br&gt;
&lt;code&gt;map[string]any&lt;/code&gt; and walks every key in the document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="sc"&gt;'A'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="sc"&gt;'Z'&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"key %s%s starts upper-case; a JavaScript client "&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="s"&gt;"reading the documented lower-case name gets undefined"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&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;Decoding into a map is the entire point. It is the only way to assert the&lt;br&gt;
key a non-Go client actually receives, rather than the key Go is willing to&lt;br&gt;
accept.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I did not build
&lt;/h2&gt;

&lt;p&gt;Stated here rather than left for someone to notice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The binary segment storage format was planned and cut.&lt;/strong&gt; The design called&lt;br&gt;
for compacting sealed day logs into a hand-written binary format with varint&lt;br&gt;
delta timestamps and a string dictionary. It stayed JSONL. That decision now&lt;br&gt;
has a number attached rather than a shrug: replaying 100,000 records takes&lt;br&gt;
&lt;strong&gt;593ms&lt;/strong&gt;, of which roughly 500ms is &lt;code&gt;encoding/json&lt;/code&gt; on the read path. A&lt;br&gt;
million records is about six seconds and 1.6GB to open. That 500ms is&lt;br&gt;
exactly what the segment format would have removed, and it is the honest&lt;br&gt;
size of what cutting it gave up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is no authentication.&lt;/strong&gt; The dashboard and API are open to anyone who&lt;br&gt;
can reach the port. Collection is rate limited per client address, but&lt;br&gt;
forwarded headers are ignored because they are trivially spoofable, which&lt;br&gt;
means behind a reverse proxy every visitor shares one bucket and one&lt;br&gt;
derived session id.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;reportAllChanges&lt;/code&gt; is not implemented.&lt;/strong&gt; &lt;code&gt;web-vitals&lt;/code&gt; can emit every LCP&lt;br&gt;
candidate as it changes. This sends one record per page view on hide, which&lt;br&gt;
is the whole reason it fits in 942 bytes.&lt;/p&gt;




&lt;h2&gt;
  
  
  The decision I would take back
&lt;/h2&gt;

&lt;p&gt;The beacon exists as two files: &lt;code&gt;beacon.src.js&lt;/code&gt;, readable and commented, and&lt;br&gt;
&lt;code&gt;beacon.min.js&lt;/code&gt;, minified by hand. There is no minifier in the project,&lt;br&gt;
because a minifier is a build dependency and the project has no build step.&lt;/p&gt;

&lt;p&gt;The cost is that a human keeps two files in sync, and humans drift. I&lt;br&gt;
mitigated it rather than solved it. Tests assert that every metric key,&lt;br&gt;
every observed entry type, and the collection endpoint appear in both&lt;br&gt;
files, and that the minified file is actually minified rather than a copy&lt;br&gt;
of the readable one under the wrong name. That catches the catastrophic&lt;br&gt;
mistake. It does not catch a subtle logic change applied to one file and&lt;br&gt;
not the other.&lt;/p&gt;

&lt;p&gt;A real minifier would make the drift structurally impossible, and I would&lt;br&gt;
take the build step. The zero-dependency rule was about &lt;em&gt;runtime&lt;/em&gt;&lt;br&gt;
dependencies, and I let it bleed into build tooling where it bought nothing&lt;br&gt;
but risk.&lt;/p&gt;




&lt;h2&gt;
  
  
  By the numbers
&lt;/h2&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;Beacon&lt;/td&gt;
&lt;td&gt;942 B raw, 571 B gzipped, 1024 B budget enforced by the build&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full beacon&lt;/td&gt;
&lt;td&gt;2,656 B raw, 1,415 B gzipped, separate 2816 B budget&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vs &lt;code&gt;web-vitals&lt;/code&gt; 4.2.4&lt;/td&gt;
&lt;td&gt;7.7x smaller raw, 4.6x gzipped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dependencies&lt;/td&gt;
&lt;td&gt;0. &lt;code&gt;go list -m all&lt;/code&gt; prints one line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;~13,750 lines, of which ~8,100 are tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tests&lt;/td&gt;
&lt;td&gt;245 functions, 10 benchmarks, 1 fuzz target, 20+ table-driven&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;~3,400 lines, no framework, no bundler, no build step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Packages replaced&lt;/td&gt;
&lt;td&gt;41, each with a note on where the original is better&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser checks&lt;/td&gt;
&lt;td&gt;45 scripted, in real Chrome 152&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commits&lt;/td&gt;
&lt;td&gt;50, with the tree building and &lt;code&gt;make check&lt;/code&gt; passing at every one&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two of the later features came from measuring rather than reasoning.&lt;br&gt;
Benchmarking the store turned up a genuine quadratic path: an out-of-order&lt;br&gt;
insert rebuilt both secondary indexes, &lt;strong&gt;0.7 to 1.0 ms against 66 µs&lt;/strong&gt; for&lt;br&gt;
shifting them instead, and out-of-order arrival was not the rare event I had&lt;br&gt;
assumed. The collector stamps the wall clock and takes the store lock as a&lt;br&gt;
separate step, so concurrent page views land reversed constantly. And "you&lt;br&gt;
lose at most two seconds on a crash" was still only a sentence, so I wrote a&lt;br&gt;
test that builds the binary, runs it as a child process, feeds it 500&lt;br&gt;
measurements, and kills it. A clean &lt;code&gt;Close&lt;/code&gt; had never tested the thing the&lt;br&gt;
sentence claimed.&lt;/p&gt;

&lt;p&gt;Said out loud rather than buried: percentiles are read from histogram&lt;br&gt;
buckets and carry up to 4.9% relative error on millisecond metrics and&lt;br&gt;
0.0025 absolute on CLS, while band counts are exact. The small beacon&lt;br&gt;
approximates INP as the longest event over 16ms, so it is pessimistic in&lt;br&gt;
the tail, which is why the full beacon exists. Firefox and Safari are&lt;br&gt;
untested, and so are the full beacon's own bfcache, soft-navigation, and&lt;br&gt;
prerender paths: reviewed against the specs, never watched running in a&lt;br&gt;
browser. That is the largest honest gap in the project, and it is why the&lt;br&gt;
small beacon remains the default.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the time actually went on
&lt;/h2&gt;

&lt;p&gt;Anyone can write a &lt;code&gt;PerformanceObserver&lt;/code&gt; that compiles and a Go server that&lt;br&gt;
serves. The beacon is 942 bytes; it took an afternoon. What the rest of it&lt;br&gt;
went on was building enough ways to catch myself being wrong: a dependency&lt;br&gt;
checker that fails the build on a &lt;code&gt;require&lt;/code&gt; block, a CDN reference, or a web&lt;br&gt;
font; a size tool that measures every file with one compressor; a CI job&lt;br&gt;
that builds twice and runs &lt;code&gt;cmp&lt;/code&gt;; a fuzz target on the only place the&lt;br&gt;
program reads untrusted input; a test that kills a real process; forty-five&lt;br&gt;
scripted checks in a real browser, because my own suite could not execute&lt;br&gt;
the file it was shipping.&lt;/p&gt;

&lt;p&gt;Every one of those exists because a claim I had written down in English&lt;br&gt;
turned out not to be a claim anything was checking. That translation step,&lt;br&gt;
from the sentence in the README to the assertion in the test, is where all&lt;br&gt;
six bugs lived.&lt;/p&gt;

&lt;p&gt;Write the claim in plain English, then ask what would have to be true. It&lt;br&gt;
is the cheapest tool in the list and it found more than any of the others.&lt;/p&gt;

&lt;p&gt;The tool measures page weight and contributes 942 bytes of it. That was the&lt;br&gt;
whole idea, and it survived contact with a real browser. Eventually.&lt;/p&gt;

&lt;p&gt;Built for the &lt;a href="https://zerodepshack.com/" rel="noopener noreferrer"&gt;Zero Dependency Hackathon 2026&lt;/a&gt;, the&lt;br&gt;
72 hour dependency-free build hackathon run by &lt;a href="https://raptors.dev" rel="noopener noreferrer"&gt;Hackathon Raptors&lt;/a&gt;, Track D (Data &amp;amp; Storage). The full project,&lt;br&gt;
&lt;code&gt;STDLIB.md&lt;/code&gt;, the browser test harness, and the size comparison tool are all in&lt;br&gt;
the repo.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/ikeshavvarshney/vitals" rel="noopener noreferrer"&gt;https://github.com/ikeshavvarshney/vitals&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>webperf</category>
      <category>testing</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
