<?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: Devansh Kant Kashyap</title>
    <description>The latest articles on DEV Community by Devansh Kant Kashyap (@devanshkant).</description>
    <link>https://dev.to/devanshkant</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%2F4067449%2Fd26d513e-d4b4-4ace-8a21-40716fbe1af4.png</url>
      <title>DEV Community: Devansh Kant Kashyap</title>
      <link>https://dev.to/devanshkant</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/devanshkant"/>
    <language>en</language>
    <item>
      <title>We Wrote a JSON Parser from Scratch in 72 Hours to Prove We Still Could</title>
      <dc:creator>Devansh Kant Kashyap</dc:creator>
      <pubDate>Wed, 09 Sep 2026 14:44:39 +0000</pubDate>
      <link>https://dev.to/devanshkant/we-wrote-a-json-parser-from-scratch-in-72-hours-to-prove-we-still-could-29on</link>
      <guid>https://dev.to/devanshkant/we-wrote-a-json-parser-from-scratch-in-72-hours-to-prove-we-still-could-29on</guid>
      <description>&lt;p&gt;Somewhere around hour thirty of ZeroDepsHack 2026, we were staring at a&lt;br&gt;
&lt;code&gt;parseUnicodeEscape&lt;/code&gt; method, arguing about whether a lone high surrogate&lt;br&gt;
followed by a regular character should throw at parse time or survive until&lt;br&gt;
serialization. The answer, per RFC 8259, was unambiguous — reject it — but the&lt;br&gt;
fact that we were even having the conversation said something about how much&lt;br&gt;
JSON parsing we'd been outsourcing to Jackson for years without thinking.&lt;/p&gt;

&lt;p&gt;That was the question the hackathon was built around. Not "are dependencies&lt;br&gt;
bad?" — they obviously aren't. But the supply chain keeps catching fire:&lt;br&gt;
left-pad breaking half the JS ecosystem over 11 lines in 2016, the chalk&lt;br&gt;
maintainer getting phished in 2025, npm's Shai-Hulud worm self-replicating&lt;br&gt;
through hundreds of packages, AI coding tools hallucinating package names that&lt;br&gt;
attackers pre-register. Jackson and Gson aren't the problem. The question is&lt;br&gt;
whether we still understand what's inside the box we'd normally just &lt;code&gt;import&lt;/code&gt;.&lt;br&gt;
We decided to find out by building &lt;strong&gt;JValue&lt;/strong&gt; — a zero-third-party-dependency&lt;br&gt;
JSON toolkit for Java 25 — during the Aug 28–31 build window, for Track B:&lt;br&gt;
Parsers &amp;amp; Data Formats.&lt;/p&gt;
&lt;h2&gt;
  
  
  What We Reimplemented
&lt;/h2&gt;

&lt;p&gt;JValue shipped as a hand-written recursive-descent JSON parser, serializer,&lt;br&gt;
RFC 6901 JSON Pointer implementation, file convenience API, and CLI — 14&lt;br&gt;
production source files, roughly 3000 lines of Java, all compiled with &lt;code&gt;javac&lt;/code&gt;&lt;br&gt;
and depending on nothing beyond &lt;code&gt;java.base&lt;/code&gt;. No Maven. No Gradle. No runtime&lt;br&gt;
dependency jar anywhere. We verified this at submission with &lt;code&gt;jdeps&lt;/code&gt;, which&lt;br&gt;
confirmed every production class depended only on &lt;code&gt;java.base&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The public API surface we ended up with fit in a few lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;JsonValue&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonString&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;compact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stringify&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;pretty&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stringifyPretty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pointer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"/users/0/name"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;asString&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writePrettyFile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"out.json"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="nc"&gt;JsonValue&lt;/span&gt; &lt;span class="n"&gt;back&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"out.json"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The value model was a sealed interface hierarchy — &lt;code&gt;JsonObject&lt;/code&gt;, &lt;code&gt;JsonArray&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;JsonString&lt;/code&gt;, &lt;code&gt;JsonNumber&lt;/code&gt;, &lt;code&gt;JsonBoolean&lt;/code&gt;, &lt;code&gt;JsonNull&lt;/code&gt; — which meant the&lt;br&gt;
compiler enforced exhaustive handling in switch expressions. That's something&lt;br&gt;
Jackson's &lt;code&gt;JsonNode&lt;/code&gt; still doesn't give you: if you forget a case, &lt;code&gt;javac&lt;/code&gt;&lt;br&gt;
tells you at compile time rather than letting it slip through to production.&lt;/p&gt;

&lt;p&gt;We also built a CLI with 15 commands — &lt;code&gt;validate&lt;/code&gt;, &lt;code&gt;pretty&lt;/code&gt;, &lt;code&gt;compact&lt;/code&gt;, &lt;code&gt;get&lt;/code&gt;&lt;br&gt;
(JSON Pointer lookup), &lt;code&gt;inspect&lt;/code&gt;, &lt;code&gt;roundtrip&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;array&lt;/code&gt;, &lt;code&gt;numinfo&lt;/code&gt;,&lt;br&gt;
and more — all routed through a single &lt;code&gt;switch&lt;/code&gt; expression on &lt;code&gt;args[0]&lt;/code&gt; in&lt;br&gt;
&lt;code&gt;JValueCli.java&lt;/code&gt;. No picocli. No Commons CLI. Just &lt;code&gt;String[] args&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the Standard Library Made Painful
&lt;/h2&gt;

&lt;p&gt;Java's standard library is enormous and capable, but it was specifically missing&lt;br&gt;
two things that mattered for this project: a JSON implementation and a test&lt;br&gt;
framework.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No JSON anything.&lt;/strong&gt; Python ships &lt;code&gt;json&lt;/code&gt;. Go ships &lt;code&gt;encoding/json&lt;/code&gt;. Ruby ships&lt;br&gt;
&lt;code&gt;json&lt;/code&gt;. Java ships... &lt;code&gt;javax.json&lt;/code&gt; in Jakarta EE, which isn't in &lt;code&gt;java.base&lt;/code&gt;&lt;br&gt;
and requires a runtime provider dependency. The entire reason Jackson exists is&lt;br&gt;
this gap. Building JValue meant writing a complete RFC 8259 parser, a&lt;br&gt;
serializer, string escape handling, Unicode processing, and number grammar&lt;br&gt;
enforcement from nothing but &lt;code&gt;java.lang.String&lt;/code&gt;, &lt;code&gt;java.lang.Character&lt;/code&gt;, and&lt;br&gt;
&lt;code&gt;java.util.LinkedHashMap&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No test framework.&lt;/strong&gt; The JDK doesn't ship JUnit — it's a third-party&lt;br&gt;
dependency, which this hackathon didn't allow. We wrote a test harness from&lt;br&gt;
scratch: &lt;code&gt;TestRunner.runTest(String, Runnable)&lt;/code&gt; wrapped each test, caught&lt;br&gt;
&lt;code&gt;AssertionError&lt;/code&gt; for failures and &lt;code&gt;Exception&lt;/code&gt; for errors, accumulated counts,&lt;br&gt;
and exited nonzero on failure. We wrote &lt;code&gt;assertEquals&lt;/code&gt;, &lt;code&gt;assertTrue&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;assertThrows&lt;/code&gt;, and the rest by hand. It worked. It was not fun. No&lt;br&gt;
parameterized test sugar, no IDE integration, no annotation discovery. You&lt;br&gt;
called your test methods from &lt;code&gt;main()&lt;/code&gt; and you liked it.&lt;/p&gt;

&lt;p&gt;One specific annoyance: fetching external test corpora. We wanted to run&lt;br&gt;
JSONTestSuite's 318 conformance files, but &lt;code&gt;git submodule&lt;/code&gt; and &lt;code&gt;curl&lt;/code&gt; were&lt;br&gt;
external tools. We ended up writing &lt;code&gt;FetchCorpus.java&lt;/code&gt; — a small JDK-only&lt;br&gt;
utility that used &lt;code&gt;java.net.http.HttpClient&lt;/code&gt; to download the ZIP archive from&lt;br&gt;
GitHub, &lt;code&gt;java.util.zip.ZipInputStream&lt;/code&gt; to extract it, and &lt;code&gt;java.nio.file.Files&lt;/code&gt;&lt;br&gt;
to write the files out. It included zip-slip protection. It exited cleanly if&lt;br&gt;
offline so the build didn't break. It was the kind of thing you'd never write if&lt;br&gt;
&lt;code&gt;curl | tar xz&lt;/code&gt; were on the table, but it worked, and it was zero-deps.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Package We Made Look Unnecessary
&lt;/h2&gt;

&lt;p&gt;Jackson (&lt;code&gt;com.fasterxml.jackson&lt;/code&gt;). Specifically, the tree-model workflow:&lt;br&gt;
&lt;code&gt;ObjectMapper.readTree()&lt;/code&gt; → traverse &lt;code&gt;JsonNode&lt;/code&gt; → generate output. JValue&lt;br&gt;
replaced that vertical slice completely. Parse JSON text into a typed tree,&lt;br&gt;
navigate it, serialize it back — all without Jackson, Gson, or any third-party&lt;br&gt;
jar on the classpath.&lt;/p&gt;

&lt;p&gt;We weren't claiming Jackson is unnecessary in general. Jackson's streaming API,&lt;br&gt;
its POJO binding, its annotation system, its format modules — none of that was&lt;br&gt;
in JValue, and we didn't pretend it was. What we demonstrated was that the &lt;em&gt;core&lt;br&gt;
tree-parse-serialize workflow&lt;/em&gt; — the reason most projects add Jackson in the&lt;br&gt;
first place — was buildable from the JDK in a weekend if you were willing to&lt;br&gt;
write it by hand.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;STDLIB.md&lt;/code&gt; tracked 12 specific substitutions with honest tradeoff&lt;br&gt;
documentation for each: Jackson's &lt;code&gt;ObjectMapper&lt;/code&gt; → our recursive-descent parser.&lt;br&gt;
Jackson's &lt;code&gt;JsonGenerator&lt;/code&gt; → our hand-written serializer. Guava's &lt;code&gt;ImmutableMap&lt;/code&gt;&lt;br&gt;
→ &lt;code&gt;Collections.unmodifiableMap()&lt;/code&gt; wrapping &lt;code&gt;LinkedHashMap&lt;/code&gt;. JUnit 5 → our test&lt;br&gt;
harness. AssertJ → hand-written assertions. ICU4J → &lt;code&gt;java.lang.Character&lt;/code&gt;&lt;br&gt;
surrogate methods. picocli → &lt;code&gt;String[] args&lt;/code&gt; switch expressions. Apache Commons&lt;br&gt;
IO → &lt;code&gt;java.nio.file.Files&lt;/code&gt;. Each entry documented what we lost, not just what&lt;br&gt;
we gained.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Edge Case That Ate Real Time
&lt;/h2&gt;

&lt;p&gt;Unicode surrogate pairs.&lt;/p&gt;

&lt;p&gt;The JSON spec allows any Unicode code point in a string, but characters outside&lt;br&gt;
the Basic Multilingual Plane (above U+FFFF — emoji, musical symbols, historic&lt;br&gt;
scripts) must be encoded as a pair of &lt;code&gt;\uXXXX&lt;/code&gt; escapes: a high surrogate&lt;br&gt;
(U+D800–U+DBFF) followed immediately by a low surrogate (U+DC00–U+DFFF). The&lt;br&gt;
parser had to match them, reject lone surrogates in either direction, and&lt;br&gt;
assemble the actual code point via &lt;code&gt;Character.toCodePoint()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's the core of &lt;code&gt;parseUnicodeEscape&lt;/code&gt; from &lt;code&gt;JsonParser.java&lt;/code&gt; as it shipped:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="nf"&gt;parseUnicodeEscape&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;codeUnit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parseHex4&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Character&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isHighSurrogate&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;codeUnit&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isAtEnd&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;peek&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sc"&gt;'\\'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"High surrogate U+"&lt;/span&gt;
                &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%04X"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;codeUnit&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" must be followed by a low surrogate"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;advance&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// consume '\'&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isAtEnd&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;peek&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sc"&gt;'u'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"High surrogate U+"&lt;/span&gt;
                &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%04X"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;codeUnit&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" must be followed by \\uXXXX low surrogate"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;advance&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// consume 'u'&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;lowUnit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parseHex4&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="nc"&gt;Character&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isLowSurrogate&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;lowUnit&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Expected low surrogate but found U+"&lt;/span&gt;
                &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%04X"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lowUnit&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;codePoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Character&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toCodePoint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;codeUnit&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;lowUnit&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Character&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toChars&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;codePoint&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Character&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isLowSurrogate&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;codeUnit&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unexpected low surrogate U+"&lt;/span&gt;
            &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%04X"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;codeUnit&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" without preceding high surrogate"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;[]{(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;codeUnit&lt;/span&gt;&lt;span class="o"&gt;};&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The same strictness showed up on the serialization side — the serializer&lt;br&gt;
validated that Java strings didn't contain lone surrogates before emitting them,&lt;br&gt;
and rejected them with &lt;code&gt;IllegalArgumentException&lt;/code&gt;. Both directions had to agree,&lt;br&gt;
or round-trip tests broke.&lt;/p&gt;

&lt;p&gt;We also burned real time on the number grammar. RFC 8259's number production&lt;br&gt;
looks trivial — &lt;code&gt;[ minus ] int [ frac ] [ exp ]&lt;/code&gt; — but the details compounded:&lt;br&gt;
leading zeros were illegal (except bare &lt;code&gt;0&lt;/code&gt;), a trailing decimal point was&lt;br&gt;
invalid, &lt;code&gt;+1&lt;/code&gt; was invalid, hex notation was invalid, &lt;code&gt;NaN&lt;/code&gt; and &lt;code&gt;Infinity&lt;/code&gt; were&lt;br&gt;
invalid. We preserved the raw lexeme through parsing so that &lt;code&gt;-0&lt;/code&gt;, &lt;code&gt;1.0&lt;/code&gt;, and&lt;br&gt;
&lt;code&gt;1e0&lt;/code&gt; all round-tripped exactly as written — a property Jackson's &lt;code&gt;JsonNode&lt;/code&gt;&lt;br&gt;
doesn't guarantee.&lt;/p&gt;
&lt;h2&gt;
  
  
  What We Shipped
&lt;/h2&gt;

&lt;p&gt;By the Aug 31 code freeze:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;186 hand-written tests&lt;/strong&gt;, all passing. Zero failures, zero errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;305 JSONTestSuite conformance cases&lt;/strong&gt; passing, 0 failed, 13 skipped (the
skipped cases were byte-level encoding tests that didn't apply to our
&lt;code&gt;String&lt;/code&gt;-based parser).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;15 CLI commands&lt;/strong&gt; — routed through a single &lt;code&gt;switch&lt;/code&gt; expression on &lt;code&gt;args[0]&lt;/code&gt;
in &lt;code&gt;JValueCli.java&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero third-party production dependencies&lt;/strong&gt;, confirmed by &lt;code&gt;jdeps&lt;/code&gt; — every
production class depended only on &lt;code&gt;java.base&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The CLI was the part that made the project feel like a real tool rather than just&lt;br&gt;
a library:&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;./jv.sh validate data.json
&lt;span class="go"&gt;  ✔  Valid JSON
  Root type : object
  Keys      : 3

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./jv.sh get data.json /users/0/name
&lt;span class="go"&gt;  Pointer : /users/0/name
  Type    : string
  Value   :
"Ada Lovelace"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  What We Learned
&lt;/h2&gt;

&lt;p&gt;Building a JSON parser from scratch turned out to be deeply educational and&lt;br&gt;
moderately miserable. We learned more about RFC 8259 in that weekend than we had&lt;br&gt;
in a decade of using Jackson. We also discovered that the spec was full of small&lt;br&gt;
traps — surrogate pairs, the exact set of legal whitespace characters, whether a&lt;br&gt;
leading BOM counts as valid input (we rejected it), whether trailing commas are&lt;br&gt;
allowed (they are not) — that libraries handle invisibly.&lt;/p&gt;

&lt;p&gt;If we're honest, the actual hard part of this weekend wasn't writing Unicode-handling code — most of that came together faster than either of us expected once the architecture was in place. The hard part was deciding how much to trust it. It's easy to generate a recursive-descent parser quickly now; it's much harder to know, with a submission deadline closing in, whether the thing you generated actually does what it claims. So most of our real effort in the last stretch went into verification rather than writing: running the parser against files we hadn't tested before and reading the output ourselves instead of trusting a green checkmark, deliberately breaking things to see if the errors pointed where they should, checking that a file written on one path and read back on another actually agreed. None of that shows up as a diff in the repo. If we had to hand one piece of advice to the next team building this way, it wouldn't be about surrogate pairs — it's that when an AI can generate the hard part in an afternoon, the bottleneck quietly moves to verifying it, and that's the part you can't delegate.&lt;/p&gt;

&lt;p&gt;We disclosed every tradeoff. Our &lt;code&gt;STDLIB.md&lt;/code&gt; didn't just list substitutions; it&lt;br&gt;
documented what we lost — no POJO binding, no streaming, no configurable&lt;br&gt;
pretty-printing. Our &lt;code&gt;README.md&lt;/code&gt; had a Limitations section. Overclaiming costs&lt;br&gt;
more than it gains when judges are senior engineers who will read the source.&lt;/p&gt;

&lt;p&gt;If we had the weekend back, there are things we'd explore — &lt;code&gt;Reader&lt;/code&gt;/&lt;br&gt;
&lt;code&gt;InputStream&lt;/code&gt; parsing, streaming serialization, maybe JSON Patch. But the point&lt;br&gt;
of the project was never to replace Jackson wholesale. It was to answer a&lt;br&gt;
narrower question: for the core tree-parse-serialize workflow that most projects&lt;br&gt;
actually use Jackson for, do we still know how to build that ourselves? By&lt;br&gt;
submission, we had 186 passing tests, 305 conformance cases, and a &lt;code&gt;jdeps&lt;/code&gt;&lt;br&gt;
report that said &lt;code&gt;java.base&lt;/code&gt; and nothing else. The answer was yes.&lt;/p&gt;



&lt;p&gt;&lt;em&gt;Built for &lt;a href="https://zerodepshack.com/" rel="noopener noreferrer"&gt;ZeroDepsHack 2026&lt;/a&gt; — Track B: Parsers &amp;amp; Data Formats. Organized by &lt;a href="https://raptors.dev" rel="noopener noreferrer"&gt;Hackathon Raptors&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://www.raptors.dev/" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;raptors.dev&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>java</category>
      <category>hackathon</category>
      <category>json</category>
      <category>hackathonraptors</category>
    </item>
    <item>
      <title>We ported JSBI to C++ — and the benchmark taught us more than the port did</title>
      <dc:creator>Devansh Kant Kashyap</dc:creator>
      <pubDate>Tue, 11 Aug 2026 13:40:46 +0000</pubDate>
      <link>https://dev.to/devanshkant/we-ported-jsbi-to-c-and-the-benchmark-taught-us-more-than-the-port-did-14c</link>
      <guid>https://dev.to/devanshkant/we-ported-jsbi-to-c-and-the-benchmark-taught-us-more-than-the-port-did-14c</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; We ported &lt;a href="https://github.com/GoogleChromeLabs/jsbi" rel="noopener noreferrer"&gt;GoogleChromeLabs/jsbi&lt;/a&gt; — a pure-JS arbitrary-precision integer library — to modern C++17, bridged back to Node via N-API, for Port Mortem 2026 (Track H). The port itself wasn't the hard part. Proving it behaved identically to the original — and being honest about the one place we &lt;em&gt;couldn't&lt;/em&gt; prove that — was.&lt;/p&gt;

&lt;p&gt;This is the story of a compile error that was secretly a correctness bug, a &lt;code&gt;shared_ptr&lt;/code&gt; that lied about owning memory, and a benchmark that changed its mind three times in a row, and what we did when it wouldn't sit still.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why JSBI → C++, of all pairs
&lt;/h2&gt;

&lt;p&gt;JSBI isn't really a JavaScript library. It's V8's internal &lt;code&gt;MutableBigInt&lt;/code&gt; — written in C++ — manually &lt;em&gt;downgraded&lt;/em&gt; to 30-bit digits so its arithmetic could survive inside JavaScript's &lt;code&gt;2^53&lt;/code&gt; safe-integer ceiling without silently losing precision. Every multiply in the original source carries a 15-bit split trick that exists for exactly one reason: JS numbers can't safely hold the product of two 32-bit values.&lt;/p&gt;

&lt;p&gt;So porting it back to C++ isn't a random language swap for Track H credit. It's undoing a constraint. In C++, a &lt;code&gt;uint64_t&lt;/code&gt; accumulator holds that same product natively — no split needed. We weren't rewriting an algorithm. We were giving one back its native width.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Original: V8 C++ MutableBigInt → downgraded to 30-bit digits → shipped as JS
This port: JS jsbi → C++17, native 64-bit accumulators → the loop closes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The verification pipeline
&lt;/h2&gt;

&lt;p&gt;Two of us, 72 hours. The port had to run the &lt;em&gt;original&lt;/em&gt; test suite unmodified — not a translated copy, the literal upstream files, pinned via git submodule at the kickoff commit hash, so nobody has to take our word for what "unmodified" means.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tests/original-upstream/*.mjs   (pinned submodule, byte-identical to upstream)
              │  import JSBI from '../dist/jsbi.mjs'
              ▼
tests/dist/jsbi.mjs             (thin JS bridge — API shape only, zero math logic)
              │  require(native addon)
              ▼
src/addon.cpp                   (N-API boundary — ownership + exception mapping)
              │
              ▼
src/jsbi.cpp / jsbi.hpp         (the actual math — zero Node/V8 headers, by design)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last line matters more than it looks: the core engine has &lt;em&gt;zero&lt;/em&gt; includes from Node or V8. You can &lt;code&gt;grep -L napi.h src/*.cpp&lt;/code&gt; and confirm it yourself. If this project ever needed to compile standalone — WASM, embedded, whatever — the math layer doesn't care that N-API exists.&lt;/p&gt;

&lt;p&gt;On top of the pinned test suite, we ran a three-way differential fuzzer every session: native V8 &lt;code&gt;BigInt&lt;/code&gt;, the real upstream &lt;code&gt;jsbi&lt;/code&gt; npm package, and our port, on identical inputs, for 60+ seconds at a time. Three-way, not two — because a two-way comparison against native &lt;code&gt;BigInt&lt;/code&gt; alone can't tell you whether a divergence is in your math or in your bridge wrapper. Three-way can.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;209,401 iterations, zero divergences, on the final clean run.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What broke — four bugs, in increasing order of "wait, that's not a style nitpick"
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The compile error that was hiding a correctness question
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&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="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;digits&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="o"&gt;|=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was in our division algorithm. It looks like a harmless one-liner. It's actually ill-formed C++ — the ternary operator requires both branches to share a common type, and &lt;code&gt;void&lt;/code&gt; is only compatible with another &lt;code&gt;void&lt;/code&gt;, or with a &lt;code&gt;throw&lt;/code&gt;-expression. &lt;code&gt;r.digits[0] |= 1&lt;/code&gt; has type &lt;code&gt;uint32_t&amp;amp;&lt;/code&gt;. Neither branch qualifies. GCC and Clang under &lt;code&gt;-std=c++17&lt;/code&gt; reject this outright. It happened to &lt;em&gt;build&lt;/em&gt; on our first Windows/MSVC pass, which is a genuinely dangerous kind of luck — it meant our first "it compiles" signal was compiler-specific, not portable. We didn't find this by reading docs. We found it by trying to build on a second toolchain and watching it fail.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. A &lt;code&gt;shared_ptr&lt;/code&gt; that owned nothing
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;shared_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;jsbi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;JSBI_CPP&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;shared_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;jsbi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;JSBI_CPP&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the aliasing constructor, called with an &lt;em&gt;empty&lt;/em&gt; control block. It type-checks. It has &lt;code&gt;-&amp;gt;&lt;/code&gt; and &lt;code&gt;.get()&lt;/code&gt;. Its &lt;code&gt;use_count()&lt;/code&gt; is zero. It contributes nothing to keeping the object alive — it's a raw pointer wearing a &lt;code&gt;shared_ptr&lt;/code&gt; costume, sitting in a codebase whose entire pitch for the Zero-Unsafe bonus was "we don't do that." The fix was smaller than the bug: return an honest, documented, non-owning raw pointer instead, and let the &lt;em&gt;real&lt;/em&gt; &lt;code&gt;shared_ptr&lt;/code&gt; — the one captured in the N-API finalizer closure — do the actual owning. Faking safety is worse than admitting you're borrowing.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The spec quirk nobody wrote a test for, until the fuzzer got wide enough
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_negative&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;invalid_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cannot shift by negative amount"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reasonable-looking guard. Spec-wrong. &lt;code&gt;5n &amp;lt;&amp;lt; -1n&lt;/code&gt; doesn't throw in real BigInt — it redirects to &lt;code&gt;5n &amp;gt;&amp;gt; 1n&lt;/code&gt;. Negative shift counts aren't an error, they're a direction flip. This bug was &lt;em&gt;invisible&lt;/em&gt; to our own fuzz harness for a while, because our shift-amount generator only ever produced values &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;64&lt;/code&gt;. The bug wasn't in the math. It was in the range of inputs we were brave enough to generate. Widening the generator to &lt;code&gt;-64..64&lt;/code&gt; is what actually caught it — not code review, not the original test suite, just deciding to stop being polite to our own implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The sign character nobody thought to negate
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;BigInt("-0x1")&lt;/code&gt; throws — non-decimal radixes don't accept a sign, and our code correctly rejected the &lt;code&gt;-&lt;/code&gt; case. &lt;code&gt;BigInt("+0x1")&lt;/code&gt; should &lt;em&gt;also&lt;/em&gt; throw, for the same reason. Ours didn't. We were only tracking &lt;code&gt;is_negative&lt;/code&gt;, and &lt;code&gt;+&lt;/code&gt; doesn't set that flag — so a &lt;code&gt;+&lt;/code&gt; before a hex prefix sailed straight through unguarded. Caught during a manual code-review pass, not fuzzing, because our fuzz string generator never happened to emit a leading &lt;code&gt;+&lt;/code&gt; before a &lt;code&gt;0x&lt;/code&gt;. A reminder that fuzzing finds what your generator can imagine, and code review finds what it can't.&lt;/p&gt;

&lt;h2&gt;
  
  
  The benchmark that wouldn't agree with itself
&lt;/h2&gt;

&lt;p&gt;Here's the part I actually want other teams to read.&lt;/p&gt;

&lt;p&gt;We built an honest benchmark harness — real &lt;code&gt;hrtime.bigint()&lt;/code&gt; measurements, forced GC between blocks, identical operand pairs fed to both implementations, three operand sizes so we couldn't hide behind a single flattering number. First run, large operands:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Original JS&lt;/th&gt;
&lt;th&gt;Our Port&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;add&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3.78ms p99&lt;/td&gt;
&lt;td&gt;1.60ms p99&lt;/td&gt;
&lt;td&gt;Port wins, clearly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;divide&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1.60ms p99&lt;/td&gt;
&lt;td&gt;3.63ms p99&lt;/td&gt;
&lt;td&gt;Port loses, clearly&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Clean story. Port wins at addition, loses at division — makes sense, our division is a bit-serial restoring-division algorithm, upstream's is presumably limb-serial. Write it up, move on.&lt;/p&gt;

&lt;p&gt;Except we ran it again.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Run 1&lt;/th&gt;
&lt;th&gt;Run 2&lt;/th&gt;
&lt;th&gt;Run 3&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;add&lt;/code&gt; (port vs original)&lt;/td&gt;
&lt;td&gt;1.75× faster&lt;/td&gt;
&lt;td&gt;1.09× faster&lt;/td&gt;
&lt;td&gt;0.95× (slightly &lt;em&gt;slower&lt;/em&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;divide&lt;/code&gt; (port vs original)&lt;/td&gt;
&lt;td&gt;0.69×&lt;/td&gt;
&lt;td&gt;1.08×&lt;/td&gt;
&lt;td&gt;0.86×&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three runs. Same machine. Same code. No consistent winner on &lt;em&gt;any&lt;/em&gt; operation. The "our division algorithm is measurably worse" story I was ready to write didn't survive a second data point.&lt;/p&gt;

&lt;p&gt;We had two options. Report the run that told the best story. Or report what actually happened.&lt;/p&gt;

&lt;p&gt;We reported the range. All three runs, in the README, with the honest conclusion: measurement noise on a general-purpose Windows machine dominates whatever true performance difference exists, we don't have a reliable claim to make, and here's exactly why (shared long-lived process across every block, non-isolated host, cumulative RSS climbing past 1.2GB by the last measurement). We even named the fix we didn't have time to do — isolated process per block, Linux CI runner, median-of-ten instead of a point estimate — as explicit future work instead of a silent gap.&lt;/p&gt;

&lt;p&gt;That's not the exciting version. It's the true one. And per this hackathon's own scoring language — &lt;em&gt;hiding a regression scores worse than disclosing it&lt;/em&gt; — inconclusive-and-honest beats confident-and-wrong every time a judge actually checks your numbers instead of just reading your headline.&lt;/p&gt;

&lt;h2&gt;
  
  
  The decision I'd take back
&lt;/h2&gt;

&lt;p&gt;We spent real hours getting the N-API bridge to a place where the &lt;em&gt;literal, unmodified, pinned&lt;/em&gt; upstream test files could &lt;code&gt;require()&lt;/code&gt; our compiled binary directly — zero-touch, not a translated copy. It's the more defensible architecture, and I'd make the same call again on the merits. But it cost us a full evening fighting Windows toolchain issues that had nothing to do with C++: &lt;code&gt;node-gyp&lt;/code&gt; not recognizing a newer Visual Studio release, a Build Tools install silently missing the Windows SDK component, the kind of error that eats a clock without teaching you anything about your actual port.&lt;/p&gt;

&lt;p&gt;If I ran this again, I'd stand up a Linux/WSL2 build path in the &lt;em&gt;first&lt;/em&gt; hour, not discover I needed one during a compile failure. The bridge architecture was worth it. The hours lost to a toolchain neither of us had properly checked ahead of time were not — that's just tax, and I'd pay it earlier and smaller if I could.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final scorecard
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;246/246&lt;/strong&gt; assertions passing against the pinned, unmodified upstream test suite (submodule-hashed at kickoff, not a copy)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;209,401&lt;/strong&gt; three-way differential fuzz iterations, zero divergences&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;33+ entries&lt;/strong&gt; in our decision log — including, deliberately, the ones marked &lt;em&gt;Superseded&lt;/em&gt; and &lt;em&gt;Known Limitation&lt;/em&gt;, because a log that only records what worked isn't a log, it's a highlight reel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero&lt;/strong&gt; raw &lt;code&gt;new&lt;/code&gt;/&lt;code&gt;delete&lt;/code&gt;, zero fake ownership (after we caught the one that was faking it), zero Node/V8 dependency in the math core&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One benchmark result we can't confidently claim&lt;/strong&gt; — and said so, in writing, instead of picking the run that looked good&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reproduce it yourself
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Repository: &lt;a href="https://github.com/codewisp-ai/Coderesurrection-2026" rel="noopener noreferrer"&gt;https://github.com/codewisp-ai/Coderesurrection-2026&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Demo (original test suite passing live against the port): &lt;a href="https://youtu.be/UKKCHzXfF5Y" rel="noopener noreferrer"&gt;https://youtu.be/UKKCHzXfF5Y&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Full decision log: &lt;code&gt;DECISIONS.md&lt;/code&gt; in the repo — read the &lt;em&gt;Superseded&lt;/em&gt; entries first, they're more honest than the accepted ones&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing thought
&lt;/h2&gt;

&lt;p&gt;Anyone with an AI coding agent can produce C++ that compiles. Ours didn't, the first time — and the compiler catching that was luckier than it should have been, since a more permissive toolchain let it through initially.&lt;/p&gt;

&lt;p&gt;What we actually spent 72 hours on wasn't writing arithmetic. It was building enough ways to catch ourselves being wrong — a pinned test suite we couldn't quietly edit, a three-way fuzzer that could tell our math bugs from our bridge bugs, a benchmark methodology honest enough to report its own noise instead of its best headline.&lt;/p&gt;

&lt;p&gt;The port is the artifact. The willingness to publish the run that didn't flatter us is the actual submission.&lt;/p&gt;

&lt;h1&gt;
  
  
  PortMortem2026 #HackathonRaptors #Cpp #JavaScript #SystemsProgramming #Testing #NAPI
&lt;/h1&gt;

</description>
      <category>cpp</category>
      <category>javascript</category>
      <category>systemsprogramming</category>
      <category>hackathonraptors</category>
    </item>
  </channel>
</rss>
