<?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: codewitharyan29</title>
    <description>The latest articles on DEV Community by codewitharyan29 (@codewitharyan29).</description>
    <link>https://dev.to/codewitharyan29</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%2F4065878%2F04861b36-4758-40cc-88c6-b96cee0bd97f.png</url>
      <title>DEV Community: codewitharyan29</title>
      <link>https://dev.to/codewitharyan29</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codewitharyan29"/>
    <language>en</language>
    <item>
      <title>Killed My Own Process 300 Times to Prove ChronoVault Doesn't Lie About Durability</title>
      <dc:creator>codewitharyan29</dc:creator>
      <pubDate>Thu, 03 Sep 2026 15:27:27 +0000</pubDate>
      <link>https://dev.to/codewitharyan29/chronovault-writes-9-13x-slower-than-diskcache-heres-why-i-shipped-it-anyway-3gb1</link>
      <guid>https://dev.to/codewitharyan29/chronovault-writes-9-13x-slower-than-diskcache-heres-why-i-shipped-it-anyway-3gb1</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; I built &lt;strong&gt;ChronoVault v2&lt;/strong&gt; for Hackathon Raptors' Zero Dependency Hackathon (Track D — Data &amp;amp; Storage) — a content-addressable snapshot and recovery engine, zero runtime dependencies, backed by a hand-rolled pack-file format. A comment saying "this is atomic" is worth nothing to a judge, so instead of asserting crash-safety, I hard-killed my own writer process mid-write, over and over, and made the vault prove it survived. This is that story, plus a benchmark number I didn't want to publish and three Windows bugs that only showed up on real hardware.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;279 tests. 20 CLI commands. 0 runtime dependencies. &lt;code&gt;judge_mode.py&lt;/code&gt; VERIFIED on Python 3.11 and 3.14.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Proving crash-safety instead of asserting it
&lt;/h2&gt;

&lt;p&gt;Anyone can write "atomic writes, crash-safe" in a README. It costs nothing and proves nothing. So &lt;code&gt;recover-check&lt;/code&gt; is tested against a real &lt;code&gt;SIGKILL&lt;/code&gt;, not a simulated one: a writer subprocess runs a snapshot, gets hard-killed at a randomized point mid-write, and the vault is reopened cold — no manual repair step allowed.&lt;/p&gt;

&lt;p&gt;The test checks three things, not one:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every snapshot acknowledged &lt;em&gt;before&lt;/em&gt; the kill is intact.&lt;/li&gt;
&lt;li&gt;The in-flight snapshot at the moment of the kill is either cleanly absent or a discarded partial write — never garbage returned from a read.&lt;/li&gt;
&lt;li&gt;The vault is immediately usable afterward, with no repair command required.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is also where the pack-quarantine logic earned its place instead of being a "nice to have." Early in testing, a kill landing mid-pack-write left a truncated pack that the recovery scan initially tried to read as valid — and returned a wrong length from. Quarantine — isolate anything that fails its checksum before it ever reaches the index — closed that hole. The fix wasn't "trust the file less," it was "never let an unverified pack answer a read at all."&lt;/p&gt;

&lt;p&gt;Run it enough times and the interesting failures stop being about the crash itself and start being about the code paths a crash exposes that a clean shutdown never does. That's the actual value of hard-killing your own process 300 times instead of once: the first ten runs prove the happy path works, and the next 290 are what find the pack-quarantine gap.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture at a glance
&lt;/h2&gt;



&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    subgraph L1["CLI"]
        CLI["cli.py — 20 subcommands"]
    end

    subgraph L2["Core Engine"]
        Snap["snapshot.py&amp;lt;br/&amp;gt;atomic rename + fsync"]
        CAS["Content-Addressed Store&amp;lt;br/&amp;gt;hashlib SHA-256, pack files"]
        Idx["path_history.py&amp;lt;br/&amp;gt;rename-aware lineage"]
        Rec["recover.py&amp;lt;br/&amp;gt;recover-check, SIGKILL-tested replay"]
    end

    subgraph L3["Persistence"]
        Pack[("Pack files on disk")]
        Quar["Pack quarantine&amp;lt;br/&amp;gt;corrupt/truncated packs isolated"]
    end

    CLI --&amp;gt; Snap
    CLI --&amp;gt; CAS
    CLI --&amp;gt; Idx
    CLI --&amp;gt; Rec
    Snap --&amp;gt; CAS
    Snap --&amp;gt; Idx
    CAS --&amp;gt; Pack
    Idx --&amp;gt; Pack
    Rec -.-&amp;gt;|verified via SIGKILL injection| Snap
    Rec --&amp;gt; Pack
    Pack --&amp;gt; Quar

    style L3 fill:#2d2d2d,stroke:#f5a623,stroke-width:2px,color:#fff
    style Rec fill:#2d2d2d,stroke:#f5a623,stroke-width:2px,color:#fff&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The shape that matters is where recovery sits: it reads pack files directly, never through the CLI or the snapshot writer, the same way a &lt;code&gt;verify&lt;/code&gt;-style tool deliberately bypasses the normal open path. That's deliberate — if the write path itself is what crashed, recovery can't depend on any code the crash might have left half-updated.&lt;/p&gt;




&lt;h2&gt;
  
  
  The number I didn't want to publish
&lt;/h2&gt;

&lt;p&gt;Every other number in this post makes ChronoVault look good. This one doesn't.&lt;/p&gt;

&lt;p&gt;I benchmarked &lt;code&gt;vault snapshot&lt;/code&gt; against &lt;code&gt;diskcache&lt;/code&gt; — the closest real-world comparison for local content-addressed storage:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;ChronoVault&lt;/th&gt;
&lt;th&gt;diskcache&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Write throughput&lt;/td&gt;
&lt;td&gt;baseline&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;9–13x faster&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;On-disk size, repeated / near-duplicate snapshots&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5–47x smaller&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Why it loses on writes: every &lt;code&gt;vault snapshot&lt;/code&gt; call pays for a full content hash of each file plus a pack-index lookup before a single byte is committed, because deduplication has to happen &lt;em&gt;before&lt;/em&gt; the write, not after. &lt;code&gt;diskcache&lt;/code&gt; skips that entirely. That's the whole trade in one sentence: I pay a hashing-and-lookup tax on every write so a hundred near-identical snapshots don't cost a hundred times the disk.&lt;/p&gt;

&lt;p&gt;If your workload is write-latency-bound, &lt;code&gt;diskcache&lt;/code&gt; is the right tool and I'm not pretending otherwise. ChronoVault wins when the workload is storage-bound — long retention windows, many near-duplicate snapshots, disk-constrained environments. A real loser column next to a real winner column, not a benchmark that only shows its best number.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where the standard library actually made me suffer
&lt;/h2&gt;

&lt;p&gt;Three places, all invisible until I tested on real Windows hardware instead of assuming POSIX behavior generalizes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;multiprocessing&lt;/code&gt;'s spawn context raised a &lt;code&gt;KeyboardInterrupt&lt;/code&gt; that wasn't one.&lt;/strong&gt; Worker processes died mid-operation with nobody near Ctrl+C. Windows' spawn-based multiprocessing ties process teardown to &lt;code&gt;_winapi.WaitForSingleObject&lt;/code&gt;, which can surface as a spurious interrupt with no signal ever sent — a path fork-based Unix multiprocessing never touches. I misdiagnosed this &lt;strong&gt;three separate times&lt;/strong&gt; as my own bug before proving otherwise, using a detached &lt;code&gt;Start-Job&lt;/code&gt; test that ran the worker fully isolated from the parent's console.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;WinError 32&lt;/code&gt; — Windows refusing to delete a file it considers still open.&lt;/strong&gt; POSIX lets you &lt;code&gt;unlink()&lt;/code&gt; an open file; the inode survives until the last handle closes. Windows refuses the delete outright. Handling it meant retrying past the handle-release window instead of importing &lt;code&gt;psutil&lt;/code&gt; to hide it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A join-before-drain ordering bug deadlocked the IPC queue.&lt;/strong&gt; &lt;code&gt;worker.join()&lt;/code&gt; was called before the result queue was fully drained. If the OS-level pipe buffer filled before the parent drained it, the child blocked on a full pipe and the parent blocked on &lt;code&gt;join()&lt;/code&gt; — a deadlock that only appears under load. Fixed by draining before joining.&lt;/p&gt;

&lt;p&gt;None of these are exotic. They're the specific price of touching &lt;code&gt;multiprocessing&lt;/code&gt; and cross-process queues without a battle-tested wrapper library between you and the OS.&lt;/p&gt;




&lt;h2&gt;
  
  
  The edge case that ate an afternoon
&lt;/h2&gt;

&lt;p&gt;Rename-aware file lineage in the path-history index. The question sounds simple — "was this file renamed, or deleted and recreated?" — until content, path, and inode can each change independently, sometimes in the same tick, with zero heuristics borrowed from someone else's diffing library to lean on.&lt;/p&gt;

&lt;p&gt;The ambiguous case that actually happened: a file renamed &lt;em&gt;and&lt;/em&gt; edited in the same snapshot cycle. "Renamed A→B, then edited" and "A deleted, unrelated B created" are both defensible reads of the same delta, and guessing wrong either invents a false history or silently drops a real one.&lt;/p&gt;

&lt;p&gt;I didn't solve it by getting cleverer. I wrote down, explicitly, which cases the lineage tracker resolves correctly, and which ones it honestly doesn't try to disambiguate. A documented limitation beats a confident guess that's wrong on the exact case a judge tests.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;p&gt;The Windows CI matrix went in &lt;em&gt;after&lt;/em&gt; most of the Windows-specific bugs had already been found manually, one laptop test at a time. Every area I later ran through real Windows hardware for the first time produced at least one genuine finding. If Ubuntu + Windows CI had existed from the start, at least two of those bugs would have surfaced in an automated run instead of on my own machine, days closer to the deadline than I'd have liked.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where it landed
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;279 tests&lt;/strong&gt; passing on Python 3.11 and 3.14, &lt;code&gt;judge_mode.py&lt;/code&gt; VERIFIED&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;20 CLI commands&lt;/strong&gt;, zero third-party runtime dependencies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crash-safety proven, not asserted&lt;/strong&gt; — &lt;code&gt;recover-check&lt;/code&gt; verified against real &lt;code&gt;SIGKILL&lt;/code&gt; injection, with pack quarantine closing the gap testing found&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI green&lt;/strong&gt; across Python 3.10–3.14 × Ubuntu + Windows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;STDLIB.md&lt;/code&gt;&lt;/strong&gt; documents 14 real substitutions, each cross-referenced to the file that uses it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;All four bonus categories claimed (+16)&lt;/strong&gt; — Single File, Reproducible Build, Package Killer re-earned via the diskcache numbers above, STDLIB Log
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python judge_mode.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;runs the full verification — test suite, single-file hash check, isolated-mode dependency audit, and the content-addressing proof's 8 invariants — as one command, so a judge doesn't have to trust this post; they can re-derive it.&lt;/p&gt;




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

&lt;p&gt;Anyone with an AI coding agent can produce a CLI that hashes files and calls it content-addressed storage. The hard part was building enough ways to catch myself being wrong — a crash-safety claim verified with a real &lt;code&gt;SIGKILL&lt;/code&gt; instead of a comment asserting atomicity, a benchmark table honest enough to put a 9–13x loss next to a 5–47x win, and a &lt;code&gt;judge_mode.py&lt;/code&gt; that re-derives every claim in this post instead of asking a judge to trust it. The vault is the artifact. The willingness to kill it 300 times and publish what survived is the actual submission.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/codewitharyan29/ChronoVault-v2" rel="noopener noreferrer"&gt;github.com/codewitharyan29/ChronoVault-v2&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Track:&lt;/strong&gt; D — Data &amp;amp; Storage&lt;/p&gt;

&lt;p&gt;Built for Zero Dependency 2026, run by &lt;a href="https://dev.to/raptorsdev"&gt;@partnerships_raptors&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ZeroDependencyHack #Python #OpenSource #DeveloperTools #CLI
&lt;/h2&gt;

&lt;h2&gt;
  
  
  PythonStandardLibrary #BuildInPublic
&lt;/h2&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>hackhathonraptors</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>Porting natsort to Rust taught me that "it compiles" is the easy 5%</title>
      <dc:creator>codewitharyan29</dc:creator>
      <pubDate>Thu, 06 Aug 2026 14:44:40 +0000</pubDate>
      <link>https://dev.to/codewitharyan29/porting-natsort-to-rust-taught-me-that-it-compiles-is-the-easy-5-2ac3</link>
      <guid>https://dev.to/codewitharyan29/porting-natsort-to-rust-taught-me-that-it-compiles-is-the-easy-5-2ac3</guid>
      <description>&lt;p&gt;TL;DR: I ported Python's natsort library to Rust for Port Mortem 2026.&lt;/p&gt;

&lt;p&gt;Writing the sorting algorithm turned out to be the easy part.&lt;/p&gt;

&lt;p&gt;The difficult part was proving the Rust implementation behaved &lt;strong&gt;identically&lt;/strong&gt; to the original Python library across thousands of edge cases without quietly changing behavior.&lt;/p&gt;

&lt;p&gt;That meant treating verification as the real project.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Why I chose natsort&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most demo ports are calculators, parsers, or utilities with a handful of tests.&lt;/p&gt;

&lt;p&gt;I wanted something that would actually stress the verification process.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;natsort&lt;/code&gt; sorts strings the way humans expect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;file2
file10
file20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of lexicographic order.&lt;/p&gt;

&lt;p&gt;That sounds simple until you discover it supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;seven algorithm variants&lt;/li&gt;
&lt;li&gt;signed and floating-point numbers&lt;/li&gt;
&lt;li&gt;scientific notation&lt;/li&gt;
&lt;li&gt;full Unicode numeric characters&lt;/li&gt;
&lt;li&gt;locale-aware behavior&lt;/li&gt;
&lt;li&gt;Roman numerals&lt;/li&gt;
&lt;li&gt;fractions&lt;/li&gt;
&lt;li&gt;a real upstream test suite covering years of edge cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A port that looks correct on five examples proves almost nothing.&lt;/p&gt;

&lt;p&gt;A port that survives the library's own test suite, differential fuzzing, CLI comparisons, property testing, and mutation testing starts becoming evidence.&lt;/p&gt;




&lt;h1&gt;
  
  
  The architecture
&lt;/h1&gt;

&lt;p&gt;The verification pipeline ended up looking like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             Python Test Suite
                    │
                    ▼
          Thin Python Adapter
                    │
                    ▼
             Compiled Rust Binary
                    │
                    ▼
          Rust natsort Implementation
                    │
                    ▼
 Compare:
 • stdout
 • stderr
 • exit codes
 • ordering
 • fuzz results
 • properties
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The adapter intentionally contains almost no logic.&lt;/p&gt;

&lt;p&gt;Its only job is forwarding Python test inputs to the Rust implementation.&lt;/p&gt;

&lt;p&gt;That design choice turned out to matter more than I expected.&lt;/p&gt;




&lt;h1&gt;
  
  
  What broke (and why those bugs mattered)
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. Differential fuzzing found a bug in the original library
&lt;/h2&gt;

&lt;p&gt;Differential fuzzing generates thousands of random inputs and compares both implementations.&lt;/p&gt;

&lt;p&gt;Most mismatches were my mistakes.&lt;/p&gt;

&lt;p&gt;One wasn't.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;natsorted&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1e400&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1e500&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;alg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REAL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1e400&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1e500&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;natsorted&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1e500&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1e400&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;alg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REAL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1e500&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1e400&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both calls succeed.&lt;/p&gt;

&lt;p&gt;Both disagree.&lt;/p&gt;

&lt;p&gt;The reason is surprisingly simple.&lt;/p&gt;

&lt;p&gt;Both values overflow to floating-point infinity.&lt;/p&gt;

&lt;p&gt;Since both become &lt;code&gt;inf&lt;/code&gt;, the comparison reports them equal, and Python's stable sort simply preserves whichever order the inputs arrived in.&lt;/p&gt;

&lt;p&gt;Rust's &lt;code&gt;f64&lt;/code&gt; behaves the same way.&lt;/p&gt;

&lt;p&gt;That means the correct port is &lt;strong&gt;not&lt;/strong&gt; one that "fixes" the behavior.&lt;/p&gt;

&lt;p&gt;The correct port reproduces it.&lt;/p&gt;

&lt;p&gt;I reported the issue upstream:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;natsort#192&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Finding a real bug in a mature library simply by holding another implementation beside it was one of the most satisfying moments of the project.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. My adapter was lying to me
&lt;/h2&gt;

&lt;p&gt;I assumed the adapter was a thin wrapper.&lt;/p&gt;

&lt;p&gt;It wasn't.&lt;/p&gt;

&lt;p&gt;One function calculated the correct Rust-backed answer...&lt;/p&gt;

&lt;p&gt;...and then ignored it.&lt;/p&gt;

&lt;p&gt;Instead it silently fell back to a handwritten Python implementation that didn't understand signed numbers or Unicode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;index_natsorted&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;alg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REAL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;returned&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&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="n"&gt;correct&lt;/span&gt;&lt;span class="p"&gt;:&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;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Differential testing doesn't automatically catch bugs in the adapter.&lt;/p&gt;

&lt;p&gt;You have to verify the adapter itself.&lt;/p&gt;

&lt;p&gt;That lesson probably saved me more time than any optimization.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Windows disagreed with Linux
&lt;/h2&gt;

&lt;p&gt;Everything looked perfect.&lt;/p&gt;

&lt;p&gt;Until I ran it on Windows.&lt;/p&gt;

&lt;p&gt;Five bugs immediately appeared.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;executable needed &lt;code&gt;.exe&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;python3&lt;/code&gt; didn't exist (&lt;code&gt;py&lt;/code&gt; does)&lt;/li&gt;
&lt;li&gt;subprocess output defaulted to CP-1252 instead of UTF-8&lt;/li&gt;
&lt;li&gt;file reads had the same encoding problem&lt;/li&gt;
&lt;li&gt;Unix-only &lt;code&gt;resource&lt;/code&gt; crashed benchmarks entirely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those appeared on Linux.&lt;/p&gt;

&lt;p&gt;None appeared in my development environment.&lt;/p&gt;

&lt;p&gt;Every one appeared on a real Windows machine.&lt;/p&gt;

&lt;p&gt;"Works on my machine" turned out to be the weakest verification strategy of all.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. GitHub Actions lied with a green checkmark
&lt;/h2&gt;

&lt;p&gt;The deployment workflow looked successful.&lt;/p&gt;

&lt;p&gt;The live website returned 404.&lt;/p&gt;

&lt;p&gt;The culprit?&lt;/p&gt;

&lt;p&gt;A multi-line shell block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl ... | sh
build
deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The installation silently failed.&lt;/p&gt;

&lt;p&gt;The following commands still exited successfully.&lt;/p&gt;

&lt;p&gt;GitHub reported the entire step as green.&lt;/p&gt;

&lt;p&gt;The fix wasn't clever.&lt;/p&gt;

&lt;p&gt;It was boring.&lt;/p&gt;

&lt;p&gt;I switched to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo &lt;span class="nb"&gt;install &lt;/span&gt;wasm-pack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and added a deployment check that refuses to publish unless the expected build artifacts actually exist.&lt;/p&gt;

&lt;p&gt;A green checkmark only matters if it's checking the thing you care about.&lt;/p&gt;




&lt;h1&gt;
  
  
  How I proved behavioral equivalence
&lt;/h1&gt;

&lt;p&gt;The implementation wasn't the deliverable.&lt;/p&gt;

&lt;p&gt;Evidence was.&lt;/p&gt;

&lt;p&gt;I ended up with six independent layers of verification.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Upstream tests
&lt;/h3&gt;

&lt;p&gt;Run the original &lt;strong&gt;natsort&lt;/strong&gt; test suite without modifying it.&lt;/p&gt;

&lt;p&gt;Tests excluded only when Rust genuinely cannot reproduce Python-specific behavior, and every exclusion is documented.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Differential fuzzing
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;27,000&lt;/strong&gt; seeded comparisons across all algorithm variants.&lt;/p&gt;

&lt;p&gt;Same inputs.&lt;/p&gt;

&lt;p&gt;Same outputs.&lt;/p&gt;

&lt;p&gt;Reproducible failures.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. CLI differential testing
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;2,800&lt;/strong&gt; CLI invocations comparing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stdout&lt;/li&gt;
&lt;li&gt;stderr&lt;/li&gt;
&lt;li&gt;exit codes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Matching output alone isn't enough.&lt;/p&gt;

&lt;p&gt;Programs communicate failure through exit codes too.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Property testing
&lt;/h3&gt;

&lt;p&gt;Randomized verification of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;idempotence&lt;/li&gt;
&lt;li&gt;antisymmetry&lt;/li&gt;
&lt;li&gt;transitivity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These find classes of bugs that handwritten examples never will.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Mutation testing
&lt;/h3&gt;

&lt;p&gt;I deliberately broke my implementation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reversed comparisons&lt;/li&gt;
&lt;li&gt;removed signs&lt;/li&gt;
&lt;li&gt;altered parsing logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the verification pipeline couldn't detect injected bugs, it wasn't trustworthy.&lt;/p&gt;

&lt;p&gt;Every injected mutation was caught.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Honest benchmarking
&lt;/h3&gt;

&lt;p&gt;Instead of publishing a single "10× faster" headline, I reported:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;mean&lt;/li&gt;
&lt;li&gt;median&lt;/li&gt;
&lt;li&gt;p95&lt;/li&gt;
&lt;li&gt;p99&lt;/li&gt;
&lt;li&gt;peak memory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The real result was &lt;strong&gt;6–8× faster&lt;/strong&gt;, depending on workload.&lt;/p&gt;

&lt;p&gt;Honest numbers build more confidence than inflated ones.&lt;/p&gt;




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

&lt;p&gt;I spent a huge amount of time chasing perfect Unicode parity.&lt;/p&gt;

&lt;p&gt;It was technically rewarding.&lt;/p&gt;

&lt;p&gt;It pushed differential matching from roughly &lt;strong&gt;43%&lt;/strong&gt; to &lt;strong&gt;100%&lt;/strong&gt; across the full Unicode character set.&lt;/p&gt;

&lt;p&gt;I'm proud of that work.&lt;/p&gt;

&lt;p&gt;But I crossed the point of diminishing returns.&lt;/p&gt;

&lt;p&gt;One adapter-level discrepancy survived until the deadline on a single CI runner.&lt;/p&gt;

&lt;p&gt;I excluded that specific test rather than claim support I couldn't fully defend.&lt;/p&gt;

&lt;p&gt;Looking back, I'd stop polishing Unicode earlier and spend those hours investigating the remaining verification discrepancy instead.&lt;/p&gt;

&lt;p&gt;Verification gets exponentially more expensive near the finish line.&lt;/p&gt;

&lt;p&gt;Knowing when to stop is part of engineering.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final numbers
&lt;/h1&gt;

&lt;p&gt;By the end of the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Original upstream tests running against Rust&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;27,000&lt;/strong&gt; differential fuzz comparisons&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2,800&lt;/strong&gt; CLI differential runs&lt;/li&gt;
&lt;li&gt;Property testing&lt;/li&gt;
&lt;li&gt;Mutation testing&lt;/li&gt;
&lt;li&gt;Honest benchmark suite&lt;/li&gt;
&lt;li&gt;6–8× speedup&lt;/li&gt;
&lt;li&gt;One upstream bug reported&lt;/li&gt;
&lt;li&gt;Cross-platform verification on Linux and Windows&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Reproduce everything
&lt;/h1&gt;

&lt;p&gt;Everything needed to verify the project is public.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/jVz_2AP4U4w" rel="noopener noreferrer"&gt;https://youtu.be/jVz_2AP4U4w&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live WASM demo + verification dashboard&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codewitharyan29.github.io/Port-Mortem/" rel="noopener noreferrer"&gt;https://codewitharyan29.github.io/Port-Mortem/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codewitharyan29/Port-Mortem" rel="noopener noreferrer"&gt;https://github.com/codewitharyan29/Port-Mortem&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upstream issue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/SethMMorton/natsort/issues/192" rel="noopener noreferrer"&gt;https://github.com/SethMMorton/natsort/issues/192&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;**Hackathon rapters : &lt;a href="https://www.raptors.dev/project/code-resurrection-2026-port-mortem" rel="noopener noreferrer"&gt;https://www.raptors.dev/project/code-resurrection-2026-port-mortem&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Closing thoughts
&lt;/h1&gt;

&lt;p&gt;Anyone with an AI coding agent can produce something that compiles.&lt;/p&gt;

&lt;p&gt;Compilation is the easy part.&lt;/p&gt;

&lt;p&gt;The difficult part is proving—without hand-waving—that another implementation behaves the same as the original across thousands of edge cases, different operating systems, different execution paths, and even the original project's own bugs.&lt;/p&gt;

&lt;p&gt;By the end of this project, I stopped measuring success by whether the Rust code compiled.&lt;/p&gt;

&lt;p&gt;I measured success by how much evidence I had that it behaved like Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built for Port Mortem / Code Resurrection by Hackathon Raptors
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;#PortMortem2026 #HackathonRaptors #Rust #Python #OpenSource #SystemsProgramming #Testing #Verification #Fuzzing&lt;/em&gt;&lt;br&gt;
**&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>python</category>
      <category>rust</category>
    </item>
  </channel>
</rss>
