Skip to content

PATTERN Cited by 1 source

Pilot-component language migration

Pattern

When considering switching a significant codebase to a new systems language (a "one-way door" decision), do not start with the hardest or most critical component. Instead:

  1. Pick the smallest / most isolated / most self-contained component whose perf or safety properties are representative of the goal.
  2. Require it to already have an existing implementation in the current language, to act as the baseline.
  3. Require any library / client dependencies to already exist in the candidate language.
  4. Have engineers new to the candidate language do the rewrite — this measures the realistic productivity curve, not best-case expert perf.
  5. Treat the result as the go / no-go signal for broader rewrite.

Compared to patterns/achievable-target-first-migration, this focuses on cross-language migrations specifically — the risk is productivity and one-way-door commitment, not breadth.

Case study: Aurora DSQL's Adjudicator pilot

The systems/aurora-dsql team faced the Crossbar's tail-at-scale problem and had to consider Rust. Rather than start with the Crossbar itself, they chose the Adjudicator — the component that sits in front of the journal and arbitrates conflicts:

Selection criterion Adjudicator match
Small / self-contained Yes — much simpler than the Crossbar.
Existing baseline Yes — Kotlin implementation, years of tuning.
Dependencies already in candidate lang Yes — Rust client for the journal already existed.
Realistic productivity signal Yes — assigned to two JVM engineers new to Rust.

Result:

  • Kotlin baseline: 2,000 → 3,000 TPS after years of incremental optimization.
  • First-cut Rust port by Java developers, no perf tuning: 30,000 TPS. ~10×.

This single data point flipped the internal question from "should we use Rust?" to "where else could Rust help us?" — and licensed the data-plane rewrite and (later) the control-plane rewrite.

Why this is safer than a full rewrite

  • Blast radius is small. If the pilot fails, you lose weeks, not years; the bad-case outcome is "we learned something," not "we're mid-rewrite and can't go back."
  • Productivity data is real. The only way to know how fast engineers are in language X after ramp is to measure. A pilot produces that number under realistic conditions (new devs, not gurus).
  • Perf data is real. No one trusts microbenchmarks for architectural decisions. A real component under real load is signal; a Shootout number is noise.
  • Builds internal confidence. Colleagues watching the pilot succeed are far more open to "let's do the rest" than to "let's do a one-year rewrite on a promise."

When not to use this pattern

  • If no component is small enough to serve as a pilot, you have a decomposition problem, not a language problem — fix that first.
  • If the candidate language's ecosystem is immature for your domain, a pilot will mislead. The DSQL post makes this point for their earlier control-plane-in-Kotlin decision: when they re-evaluated, internal Rust library support had caught up (AWS Authentication Runtime's Rust client was outperforming Java); earlier, it had not.

Seen in

Last updated · 200 distilled / 1,178 read