The conversation about memory safety is finally mainstream, but moving giant codebases to safer languages is not a blog-post-length problem. At Microsoft we’ve been incrementally replacing high-risk surfaces with Rust and adding new mitigations where a full rewrite isn’t practical.
The most important lesson: set clear risk thresholds. For components that process untrusted input, we draw a line—new work lands in memory-safe languages or it doesn’t ship. For legacy code, we use compiler hardening, Control Flow Guard, and runtime exploit mitigations to buy time while we carve out the worst offenders.
Two principles keep us honest:
- Prioritize edges that handle untrusted data. Rendering pipelines, parsers, and input stacks get first treatment.
- Measure regressions relentlessly. Compile-time guards are great, but we still verify with fuzzing, crash telemetry, and differential testing to make sure we didn’t trade one class of bugs for another.
We are not finished, but we’re measurably safer. The mix of rewrites, hardened toolchains, and better telemetry is how we keep improving without pausing the release train.