Google migrated 174,000 lines of Fortran to Rust in 59 minutes: the 11-minute baseline is the part to copy

2026-11-04来源 dev.toEnglish

Google's Antigravity team published a walkthrough on November 3 of an agent migrating the core of FVCOM, a coastal ocean model written in Fortran over decades, into memory-safe Rust. The run took 59 minutes for 174,000 lines, surfaced six silent bugs that had been sitting in the solver since the 1990s, halved memory use, and finished 1.88x faster than the compiled original.

The number everyone will quote is 59 minutes. Having read the post a few times, the part that actually made it possible is not the model. It is the ordering. The agent spent its first 11 minutes building a baseline and wrote no Rust until that baseline existed. That decision is what turned a flashy rewrite into a migration you can check, and it is the one part you can copy today with any agent, terminal-only ones included.

What the 59 minutes actually contained

The post breaks the run into three stages, and the arithmetic is honest: 11 plus 3 plus 45 equals 59.

Stage Time What happened
0 11 min Clone FVCOM, read the legacy build flags, compile the original with gfortran -O3, record golden outputs, runtime and memory across 100 random seeds
1 3 min Scan all 264 files, trace cross-file dependencies and every global variable through the 2D and 3D solver loops, write a six-part blueprint artifact
2 45 min Port with #![forbid(unsafe_code)], generate 42 tests, self-heal failures, check 100-seed parity against the Fortran binary, then run an LLVM profile-guided optimization pass

Google also packaged the whole pipeline as an open-source skill in the google/skills repository, so inside Antigravity the entire thing runs as /migrate-to-rust <repo-url>. The skill bundles the prompt guardrails, baseline discovery, the bug audit, the self-healing test loop, the multi-seed parity harness, and the PGO pass. The packaging is a statement in itself: Google treats this as reusable machinery, not a one-off demo.

The 11 minutes that make the other 48 trustworthy

The baseline stage is the least glamorous and the most load-bearing. Before any Rust existed, the agent compiled the original Fortran with the flags the lab actually used, built a benchmark harness, and recorded golden outputs for 100 random seeds along with runtime and peak memory. From that point on, "did the port preserve behavior" stopped being an opinion and became a diff.

That matters because numerical divergence is silent. A sign error in a boundary condition, a reordered floating-point summation, a loop reading an uninitialized cell: none of it crashes, all of it corrupts results. When the port finished, the agent ran its A/B parity harness against the same 100 seeds and matched every run to 12 decimal places (3.91e-13 in the post). Without the baseline, that sentence could not exist. The strongest claim available would be "the tests pass", and the tests were written by the same agent that did the port.

What the compile step caught that tests never would

The bug list is worth reading precisely because the bugs are boring. A mesh builder wrote past a 50-column limit on complex coastal grids. A triangle edge check multiplied three node IDs in 32-bit precision and overflowed on grids with more than 1,300 nodes. Vertical turbulence routines read out-of-bounds index 0 elements on shallow grids and pulled uninitialized values from dry land cells. A seawater density routine took the square root of a salinity value that numerical undershoot had nudged slightly negative. And a boundary workaround dating back decades, a comment reading H1(0) = H1(1) ! STRANGE WAY TO PREVENT DIVISION BY ZERO, quietly tied coastal boundary math to whichever triangle happened to be numbered 1.

Rust's compiler refused several of these outright once the arrays were mapped into strict types. The audit stage flagged the rest, and the single-layer edge case came from the test loop: on the first run 36 of 37 unit tests passed, the failing one hit a bounds check when a basin had a single vertical water layer, and the agent opened the vertical solver, added a guard, and re-ran until all 42 tests passed with 96.8% branch coverage.

The speedup came last, on purpose

The optimization ordering is the second half of the lesson. Idiomatic safe Rust alone took the runtime from 883 ms to 682 ms (1.29x) and peak memory from 11.84 MB to 6.39 MB. Replacing 24 heap allocations per time step in the 2D Runge-Kutta loop, eight temporary arrays allocated and freed over and over, with pre-allocated Struct-of-Arrays workspaces took it to 548 ms (1.61x). A profile-guided optimization pass on a live simulation trace took it to 469 ms (1.88x) at 5.48 MB, 54% below the original.

Every pass was measured against the pinned baseline rather than guessed at. Speed came after correctness was already proven, which is the opposite of how agent-driven rewrites usually go: port first, discover the behavior change in production later.

A baseline harness you can write this afternoon

You do not need Antigravity for the ordering. Any agent with terminal access respects a script and a rules file. Two small scripts give you the same shape.

First, pin the old behavior before anything changes:

# scripts/baseline.sh: record golden outputs before any agent touches src/ set -euo pipefail mkdir -p baseline/outputs for seed in $(seq 1 100); do ./build/bin/model --seed "$seed" --days 30 >"baseline/outputs/$seed.out" done ( cd baseline/outputs && sha256sum *.out >../MANIFEST.sha256 ) git add baseline && git commit -m "baseline: golden outputs for 100 seeds" 

Then hold every later build to it:

# scripts/check-parity.sh: same seeds, new binary, compare hashes set -euo pipefail fail=0 for seed in $(seq 1 100); do new=$( ./target/release/model --seed "$seed" --days 30 | sha256sum | cut -d' ' -f1 ) old=$( grep " $seed.out$" baseline/MANIFEST.sha256 | cut -d' ' -f1 ) [ "$new" = "$old" ] || { echo "seed $seed diverged"; fail=1; } done exit $fail 

Exact-hash comparison is right for parsers, build tools, anything with byte-stable output. For floating-point code it is too strict, so compare outputs against a tolerance the way the parity harness does. If your program has no seed input, pin what you can: freeze clocks, disable network, replay a recorded request stream, and hash whatever the system emits.

The last piece is telling the agent the rules before it starts, in AGENTS.md, CLAUDE.md, or whatever your harness reads:

## Migration rules - Do not edit anything under src/ until scripts/baseline.sh has passed once. - After every batch of edits, run scripts/check-parity.sh and report which seeds diverged. - If any seed diverges, stop editing and show the diff before touching more files. - Never loosen a test or a tolerance to make parity pass. 

If you maintain rules files for systems languages already, the same file is where write restrictions and forbid-unsafe policies belong; the rules I keep for Go and Rust follow that layout.

The limits, stated plainly

These are Google's numbers, on Google's chosen target, published on Google's product blog for Antigravity and Gemini 4 Argon. No independent reproduction exists yet. FVCOM is also close to a best case: a deterministic numeric solver with a clean command-line interface, a stable input format, and an unambiguous notion of correct output, same math, same seeds, same decimals. Most business code has none of that, and building the golden corpus is the real work. A 45-minute port on top of a two-week baseline project is still a win, but it is a different pitch than the headline.

Note also what the workflow does not give you: judgment. Forty-two generated tests passing tells you the port matches the original, original bugs included, modulo the six it caught. Somebody still has to decide whether the original behavior was right, and that decision does not automate. If your target is Rust and you want stronger evidence than sampled seeds, the formal-verification route via GitHub's PDD Kit is the complementary path.

The pocket version of the whole post: baseline first, map second, port third, speed last. If your agent's migration plan starts with rewriting the module and ends with running the tests, the plan is in the wrong order no matter which model wrote it.

原文:https://www.a8q9.cn/en/article/google-migrated-174-000-lines-of-fortran-to-rust-in-59-minutes-the-11-minute-bas-f9af62.html

相关文章