<?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: Rishi Bharadwaj</title>
    <description>The latest articles on DEV Community by Rishi Bharadwaj (@rishi_bharadwaj_bf8a76182).</description>
    <link>https://dev.to/rishi_bharadwaj_bf8a76182</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%2F4109122%2F4ca71ced-067c-41c6-ba4a-8c5876e4ea4f.jpg</url>
      <title>DEV Community: Rishi Bharadwaj</title>
      <link>https://dev.to/rishi_bharadwaj_bf8a76182</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rishi_bharadwaj_bf8a76182"/>
    <language>en</language>
    <item>
      <title>I Replaced My Entire Dependency Tree With the Standard Library (And Built a Quant Database)</title>
      <dc:creator>Rishi Bharadwaj</dc:creator>
      <pubDate>Sat, 05 Sep 2026 13:15:40 +0000</pubDate>
      <link>https://dev.to/rishi_bharadwaj_bf8a76182/i-replaced-my-entire-dependency-tree-with-the-standard-library-and-built-a-quant-database-27m3</link>
      <guid>https://dev.to/rishi_bharadwaj_bf8a76182/i-replaced-my-entire-dependency-tree-with-the-standard-library-and-built-a-quant-database-27m3</guid>
      <description>&lt;p&gt;Every dependency you install is a black box you don't control. You import it, &lt;code&gt;cargo build&lt;/code&gt; swallows it, and somewhere in that tree of transitive crates is code you will never read, written by people you will never meet, that your production system now trusts completely.&lt;/p&gt;

&lt;p&gt;I've been deep in quantitative finance and optimization math lately, and I kept noticing how fast we reach for a 40-crate dependency tree to do what is, underneath it all, just reading bytes off a socket and writing bytes to a disk. So I decided to find out what happens if you don't.&lt;/p&gt;

&lt;p&gt;I threw away the dependency tree. All of it. And I built a time-series database anyway — in 72 hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Does
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;ZeroTick&lt;/strong&gt; is a high-frequency time-series database for market order flow, built entirely on the Rust standard library. It:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ingests raw TCP order flow at up to &lt;strong&gt;250,000 ticks per second&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Compresses every tick to disk in real time using hand-rolled Gorilla-style bit packing&lt;/li&gt;
&lt;li&gt;Guarantees durability with append-only writes, self-describing frame headers, and automatic crash recovery&lt;/li&gt;
&lt;li&gt;Drives a lock-free, TrueColor ANSI terminal dashboard&lt;/li&gt;
&lt;li&gt;Serves live SVG charts straight to a browser, over the &lt;em&gt;same&lt;/em&gt; TCP port, with no web server behind it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the part I'm proudest of: &lt;code&gt;cargo build&lt;/code&gt; doesn't touch the network once. Open &lt;code&gt;Cargo.toml&lt;/code&gt; and the dependencies table is empty. Not "minimal." Empty.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="c"&gt;# Left intentionally blank&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I didn't just take my word for it either — I ran &lt;code&gt;cargo build&lt;/code&gt; against a completely fresh &lt;code&gt;Cargo.lock&lt;/code&gt; and let Cargo tell me the truth about what it resolved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[package]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"zerotick"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One package. Itself. No tokio, no serde, no axum, no ratatui. If it's in the binary, I wrote it or the standard library did.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Pure Rust (and Ditching the Python + Rust Split)
&lt;/h2&gt;

&lt;p&gt;The industry-standard quant stack is Python for analytics, glued to Rust or C++ through PyO3 for the parts that actually need to be fast. In practice, that architecture is a latency tax you pay on every single tick.&lt;/p&gt;

&lt;p&gt;Marshalling data across the Python/C FFI boundary burns microseconds you don't get back. Add Python's GIL and unpredictable GC pauses on top, and you've built a "low-latency" system with a garbage collector standing in the critical path. Going pure Rust end-to-end removed that tax completely — no FFI boundary to cross, no GIL to contend with, no background thread doing something to your heap that you didn't ask for. Every microsecond in the hot path is one I put there on purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;Instead of an async runtime with a scheduler making decisions on your behalf, ZeroTick is an append-only, thread-per-connection engine built directly on POSIX primitives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ingestion&lt;/strong&gt; binds straight to &lt;code&gt;std::net::TcpListener&lt;/code&gt;. Each connection gets its own OS thread; ingest batches land on an &lt;code&gt;mpsc&lt;/code&gt; channel and get picked up by a dedicated storage-writer thread, so a slow or malicious client can never block anyone else's writes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Storage&lt;/strong&gt; is a WORM (write-once-read-many) log. When you build storage without crates, you have to know exactly where the Rust standard library ends and the Linux kernel begins. To let queries run concurrently with active ingestion, I skipped the usual approach of a single shared, stateful file cursor and built reads around the OS's native support for absolute-offset access instead — so reads and writes never have to contend for the same lock, no matter how much data is flowing in.&lt;/p&gt;

&lt;p&gt;Every batch of ticks gets Gorilla-compressed — delta-of-delta encoding for timestamps, XOR-delta encoding for prices — and appended behind a compact, self-describing header that carries enough information to reconstruct frame boundaries. That header makes crash recovery possible: on startup, the engine walks the frame boundaries and truncates anything that doesn't parse as a complete frame. No external WAL library, just a for-loop that trusts nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Queries&lt;/strong&gt; run through a per-symbol frame index and a single-pass math engine. If you calculate variance the naive way over a continuous data stream—summing the squares and subtracting the square of the sum—you will eventually hit floating-point catastrophic cancellation. As the total sums grow massive, the tiny price differences between financial ticks get rounded off into oblivion. Because my background is in optimization math, I knew the engine needed Welford's online algorithm. It incrementally updates the running mean and sum of squared differences tick-by-tick. This completely avoids precision loss and guarantees the calculation runs in strictly bounded O(1) space—a fixed, tiny amount of state—whether you're querying ten ticks or ten billion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The multiplexer&lt;/strong&gt; serves both its binary tick protocol &lt;em&gt;and&lt;/em&gt; HTTP on the same port. The connection handler peeks at the first few bytes off the socket. If it sees &lt;code&gt;GET&lt;/code&gt;, it parses just enough of an HTTP request line to pull a ticker symbol out of the URL path, runs the same query engine a raw TCP client would use, and hand-writes an SVG line chart directly into the HTTP response body as a formatted string.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Dependency Architecture&lt;/strong&gt; — no CVE supply-chain exposure, because there's no supply chain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-Speed Ingestion&lt;/strong&gt; — sustained 250,000 ticks/sec over raw POSIX sockets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bit-Level Compression&lt;/strong&gt; — delta-of-delta timestamps, XOR-delta prices, shrinking 16-byte ticks down to a handful of bits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crash-Safe Storage&lt;/strong&gt; — self-describing frames plus automatic torn-write truncation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single-Pass Quant Analytics&lt;/strong&gt; — mean, variance, z-score, Bollinger bands, and max drawdown, all computed in one O(1)-memory sweep via Welford's algorithm.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native HTTP/SVG Multiplexer&lt;/strong&gt; — live charts in a browser with no web framework.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Overhead TUI&lt;/strong&gt; — TrueColor terminal dashboard powered by raw &lt;code&gt;stdout&lt;/code&gt; writes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Inspiration
&lt;/h2&gt;

&lt;p&gt;Modern enterprise software is bloated. We routinely ship web wrappers that burn gigabytes of RAM to render a line graph that a dumb terminal could've drawn in 1985. ZeroTick's interface is a deliberate throwback — the high-contrast amber-and-charcoal palette of vintage Bloomberg terminals, density over decoration, every pixel carrying information instead of padding.&lt;/p&gt;

&lt;h2&gt;
  
  
  War Stories: Everything That Went Wrong
&lt;/h2&gt;

&lt;p&gt;Here's the thing nobody tells you about skipping dependencies: every bug you'd normally get to blame on a library is now, definitionally, your bug.&lt;/p&gt;

&lt;h3&gt;
  
  
  The 50GB Disk Exhaustion Loop
&lt;/h3&gt;

&lt;p&gt;I wrote a chaos harness to simulate a power cut mid-flush by injecting garbage bytes into the middle of a data file. At 3 AM, it worked a little too well — it devoured my entire 50GB disk partition and locked up WSL solid.&lt;/p&gt;

&lt;p&gt;Because I calculate every disk offset by hand, a single bit-shift misalignment in the XOR-delta price decoder made the recovery loop misread a frame boundary. Instead of surgically truncating the corrupted bytes, it decided the corruption was actually the &lt;em&gt;start&lt;/em&gt; of a new (garbage) frame, and kept appending "recovered" nonsense forever. Fixing it meant sitting down with graph paper and mapping the exact binary layout of every buffer by hand before writing the self-describing frame headers that exist in the codebase today.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Query That Never Got Faster
&lt;/h3&gt;

&lt;p&gt;I'd built a clean binary search over frame headers by timestamp — O(\log n) to find your starting point in a symbol's history. Very pleased with myself. Except the index that binary search ran over was rebuilt from scratch, by scanning every single frame header on disk from byte zero, on &lt;em&gt;every single query&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;A query for "the last 60 seconds of AAPL" on a symbol with a year of history was paying to re-read a year of frame headers before it ever got to binary-search anything. The fix was to stop throwing the index away after every use. Queries went from "linear in the entire history of the symbol" to "logarithmic in the part you actually asked for."&lt;/p&gt;

&lt;h3&gt;
  
  
  The Random Walk That Wouldn't Walk
&lt;/h3&gt;

&lt;p&gt;My synthetic ingest client generates ticks with a small pseudo-random step. Early on, I clamped the price at a hard floor to keep it from going negative. Except a symmetric random walk with a hard floor and no restoring force doesn't bounce off that floor — it gets &lt;em&gt;stuck&lt;/em&gt; to it. Watch the dashboard long enough and every ticker eventually decays to a few cents and just sits there.&lt;/p&gt;

&lt;p&gt;The real fix was a small Ornstein-Uhlenbeck-style mean-reversion term pulling the price back toward its starting equilibrium. Until it wasn't. I bolted a "chaotic" stress-test mode onto the benchmark, reusing what I thought was the same random-step formula — except I grabbed the version &lt;em&gt;without&lt;/em&gt; the mean-reversion term. Turns out the step formula itself had a tiny, permanent bias baked in: centering a &lt;code&gt;0..199&lt;/code&gt; random draw by subtracting exactly &lt;code&gt;100.0&lt;/code&gt; instead of &lt;code&gt;99.5&lt;/code&gt; leaves you drifting down by half a unit every single tick, forever.&lt;/p&gt;

&lt;p&gt;When you hand-roll the math, you also hand-roll every way it can quietly misbehave.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replacing the Ecosystem
&lt;/h2&gt;

&lt;p&gt;Ripping out dependencies one at a time was the biggest time sink of the whole project, and also the most educational:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;tokio&lt;/strong&gt; → thread-per-connection I/O directly on &lt;code&gt;std::net&lt;/code&gt;, with a hard cap on concurrent connections and a read timeout per socket.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;axum + serde&lt;/strong&gt; → a hand-rolled HTTP line parser that reads just enough of a raw request to tell a browser's request apart from a binary tick frame, and writes Welford's variance output directly into an SVG string. No routing tables, no async middleware, no struct derives, no JSON — just a strict allow-list on what goes into that string.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;lz4&lt;/strong&gt; → hand-rolled Gorilla-style bit-packing: delta-of-delta encoding for timestamps and XOR-delta encoding for prices. Reimplementing a well-known algorithm from scratch doesn't really teach you the algorithm — it teaches you &lt;em&gt;why&lt;/em&gt; the original authors made the choices they made, one edge case at a time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;ZeroTick proves something I half-suspected going in and now believe completely: the Rust standard library is fundamentally complete for systems work. Not "good enough with caveats" — complete. TCP sockets, positional file I/O, atomics, threads, and enough control over memory layout to hand-roll a compression format — it's all sitting there in &lt;code&gt;std&lt;/code&gt;, waiting for someone to actually read it instead of &lt;code&gt;cargo add&lt;/code&gt;-ing past it.&lt;/p&gt;

&lt;p&gt;You don't need a thousand dependencies to build something fast. You need to be willing to own every byte yourself — bugs, 3 AM disk-exhaustion incidents, and all.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>database</category>
      <category>performance</category>
      <category>systems</category>
    </item>
  </channel>
</rss>
