The Memory Safety Reckoning: Part 3 of 4
In the first two blogs of this series, we discussed why memory safety has become a national security priority and why maintaining large C++ codebases becomes more expensive over time.
If older software creates security risks and costs more to maintain, it might seem like the obvious answer is to throw it away and redevelop the desired capabilities from scratch using modern, more secure development practices. In practice, though, only a handful of organizations can afford to do that. For organizations with millions of lines of existing C or C++ code — the source of many memory-related vulnerabilities — a full software rewrite is usually too expensive, too slow, and too risky in its own right: introducing new bugs, delaying important projects, and requiring years of work before delivering any benefit at all.
In this post, we explain why complete software rewrite is not a practical option for most organizations, and advocate for gradual, well-scoped, incremental migration of existing software towards a memory-safe implementation.
Large Software Systems Are Extremely Complex
Many government agencies, defense contractors, manufacturers, and telecommunications companies have software that has been growing for decades. Some systems contain millions of lines of code that have been updated and improved by hundreds of developers over many years. Developers come and go over that time, taking their knowledge of the system with them when they leave. As a result, it’s common for no single person to fully understand the software stack in its entirety — each developer typically knows their own corner of the system well but lacks understanding of how parts fit together.
These systems are also deeply interconnected, often relying on specialized hardware, older software, databases, operating systems, and third-party libraries built up over that same time. Every one of those connections has been tested and refined over years of real-world use. A complete rewrite doesn’t just mean recreating the system’s own code — it means re-establishing every one of these connections from scratch, all at once, with none of the accumulated testing and tuning that made the original system reliable.
The Code Contains Years of Experience
Beyond its sheer size, software carries something less visible: years of engineering decisions. Developers solve unusual edge cases, implement workarounds for hardware limitations, optimize performance, and respond to issues discovered only after years of deployment. Much of that reasoning was never properly documented — it lives inside the code itself. And as mentioned above, developers eventually leave, taking with them any understanding that was never written down.
A team attempting to reimplement the system from scratch is working from an incomplete picture — the intended functionality as understood today, not the full set of edge cases and quiet decisions accumulated over years. As a result, they are likely to miss many of the same small but important behaviors all over again, since those behaviors were never fully captured as “intent” to begin with. These kinds of regressions are difficult to predict and even harder to discover before deployment. This isn’t just a theoretical concern — in his well-known essay Things You Should Never Do, Part I, software engineer Joel Spolsky made this same argument two decades ago, warning that rewrites routinely discard hard-won fixes and edge-case handling that took years to accumulate.
Certification Can Take Longer Than Rewriting the Code
Many organizations cannot simply deploy new software after it is written. Industries like aerospace, defense, medical devices, energy, and industrial control systems require extensive testing before software can go live. Updates may also require security reviews, documentation, certification activities, and approval from customers or regulators.
In government and defense systems, this often means going through an Authority to Operate (ATO) process — a formal review in which a system must be reassessed and re-approved before it can be deployed, even for a routine update. ATOs are notoriously slow and labor-intensive, often involving extensive manual documentation, security testing, and sign-off from multiple stakeholders, and can take months to complete. As a result, some organizations tend to delay or bunch together small functionality updates and security patches — deploying them in fewer, larger batches — simply to reduce how often they trigger the ATO process. Even so, an incremental update can typically reuse much of the assurance case, documentation, and test evidence from prior approvals, updating only the portions affected by the change. A full rewrite offers no such shortcut: because the entire system is new, the assurance case must be built from scratch as well.
Organizations Cannot Wait Forever
While the idea of rewriting software from scratch may sound appealing, history has shown that many large rewrite projects take much longer than expected. Some never reach the same level of reliability as the original software. During the rewrite, organizations also have to continue maintaining the old system, which increases both cost and complexity.
Yet standing still is not an option either. The government is actively encouraging organizations to reduce their dependence on memory-unsafe programming languages. Both CISA and the NSA have recommended using memory-safe languages whenever possible as part of a broader Secure by Design approach to cybersecurity.
The reality is that many organizations cannot wait five or ten years for a complete software rewrite. Security threats exist today. Legacy software is still running today. Critical systems still need updates today. Waiting years for a perfect drop-in replacement is simply not practical for most organizations.
A Better Approach Is Incremental Modernization
Instead of replacing an entire system all at once, many organizations are modernizing their software a little at a time. This approach allows developers to focus on the highest-risk parts of the code first — the components most exposed to untrusted input or most prone to memory-related vulnerabilities — while keeping the rest of the system running. It also reduces risk because smaller changes are easier to test, validate, and certify than a single massive rewrite, often building on the assurance work already done for the existing system rather than starting over. Incremental modernization helps organizations improve security today, without giving up decades of proven software or waiting years for a replacement that may never fully catch up.
Looking Ahead
In the final blog post of this series, we will look at how automated software migration is changing the modernization process. We will explain why deterministic migration is different from AI-generated code, and why automation, combined with expert oversight, offers a practical way to modernize large C++ codebases.
