· via dev.to (home feed)
Team replaces 180,000-line C++ DNS resolver with 12,000-line Rust version, no major outage
A first-person engineering account describes replacing a 180,000-line C++ DNS resolver with a 12,000-line Rust build using traffic shadowing and staged cutover, at millions of queries per second and without a major outage.

A rewrite that ended in deletion
In 2023, a team at a mid-sized cloud provider retired the C++ DNS resolver it had been running since the early 2010s and replaced it with roughly 12,000 lines of Rust, down from 180,000 lines, while the service kept answering millions of queries per second. According to the first-person account published on dev.to and originally posted on the author's site tamiz.pro, the six-month migration produced no major outage and required no postmortem.
The author describes the legacy system as a maintenance trap: memory-safety bugs appeared regularly, performance problems were hard to isolate, and engineers were reluctant to change anything. The stated goals for the replacement were memory safety, built-in observability, an auditable codebase, and modern asynchronous I/O.
Replacement over refactoring
The team decided early that patching the old resolver would only pile more layers onto existing debt, so it chose a careful rewrite instead. The new resolver is built on trust-dns and implements RFC-compliant recursive resolution, DNS-over-TLS and DNS-over-HTTPS for upstream queries, Prometheus metrics, structured logging through the tracing ecosystem, and graceful shutdown. Tokio supplies the concurrency model, rustls replaces the old TLS stack, and a standard exporter replaces hand-built metrics.
Managed DNS services were rejected because internal services needed low-latency, in-process resolution and the tail latency of external options was unacceptable, the post says. Go was considered briefly but ruled out; the author cites better performance and fewer runtime surprises under load as the reasons for choosing Rust.
Shadow first, then cut over slowly
Before any production traffic depended on the new code, the team mirrored 1% of queries to it and compared answers against the legacy resolver, logging mismatches without acting on the new results. This shadowing phase, which lasted about two weeks, exposed subtle differences in TTL handling and cache behavior.
The cutover then moved through 1%, 5%, 25% and 75% of traffic, with each stage held for at least an hour while the team watched latency percentiles, error rates, cache hit ratios, and CPU and memory use. After roughly a month of staged rollout, the old resolver was switched off. Writing the core took about three months; the author notes that most of the overall effort went into testing, shadowing and gradual rollout rather than the code itself.
Where the two systems disagreed
The post highlights three gotchas. First, DNS resolution looks deterministic, but edge cases in caching, retries and timeouts can differ between implementations, and shadowing caught most of the discrepancies. Second, upstream resolvers such as Cloudflare, Google and AWS occasionally returned slightly different answers, so the team standardized on Cloudflare for consistency. Third, the legacy resolver clamped TTLs in non-standard ways while the Rust version follows the RFCs strictly, which caused a brief period of cache churn early in the rollout.
The roughly 168,000 net lines removed included custom DNS parsers, hand-rolled event loops, a legacy TLS stack and ad-hoc metrics code, all replaced by maintained libraries.
The author closes with five lessons: build observability in from day one; never assume equivalence between old and new systems; let each rollout phase run long enough to surface regressions; keep rollback instant at every step; and do not be afraid to delete dead code once its replacement is proven.
Why it matters
DNS sits inside nearly every request path, which makes a resolver one of the riskiest components to replace; a botched cutover at millions of queries per second becomes a visible, company-wide outage. This account is a useful template precisely because nothing dramatic happened: shadow traffic to prove equivalence, shift percentages slowly, keep rollback cheap at every step, and treat observability as a launch requirement rather than an afterthought. The fifteenfold reduction in code size is also a concrete data point for teams weighing a Rust rewrite against maintaining legacy C++, since most of the deleted code was plumbing that the open-source ecosystem now supplies.
One caveat: this is a single first-person account from an unnamed provider, so the numbers and timeline cannot be independently verified. As a migration playbook, though, it ranks among the more detailed public write-ups of swapping a live, high-volume DNS resolver.
- #dns
- #rust
- #infrastructure
- #migration
- #cloud