Every sustainment portfolio has one: the system that runs the mission, that nobody on the current team wrote, and that no one is willing to touch. The documentation describes a design from two decades ago. The people who knew why a particular branch exists retired. The database has columns whose meaning lives in an operator's head. And the modernization line item has been in the budget for four years, funded, restated, and quietly rolled to the right, because every credible plan starts with "first we understand the current system" and nobody has been able to price that sentence.
This is written for the program director who owns that line item on a sustainment or modernization contract at a prime. The question is not whether the system should be modernized. It is how to get from a system nobody fully understands to a new one that works, without a big-bang cutover, without a year of discovery that produces a document instead of a capability, and without a period where the program has two systems and no way to reconcile them.
Why modernization programs stall in the same place
The stall is almost never technology. It is knowledge. The current system is the only complete statement of what the business rules are, and it states them in a form nobody can read at the speed the schedule requires.
Three specific things go wrong, and they compound.
Discovery produces a document, not a capability. A discovery phase is funded, analysts interview users, and eighteen months later the program holds a requirements package that describes what people say the system does. The gap between what users describe and what the code executes is where every modernization program dies, because the exceptions are the system. The rule everyone can state is the easy ten percent. The four hundred conditional branches accumulated over twenty years of policy changes are the other ninety, and they are only in the code and the data.
The new system is built against the wrong data. Development proceeds against a sanitized extract or synthetic records, and integration meets the real data for the first time in test. Real operational data contains defaults that mean "unknown", codes retired in the nineties that still appear, records that violate the constraints the schema claims to enforce, and free-text fields carrying structured meaning. Each of those is a design input, and discovering them late converts them into rework.
Cutover is planned as an event. A date is chosen, both systems are run in parallel for a period nobody funds properly, differences are found, and the decision to switch becomes a judgment call under pressure with users watching. The programs that succeed do not have a cutover date; they have a sequence of narrow migrations, each of which is small enough to reverse.
What actually reduces schedule risk on a legacy modernization
Editorial weighting, illustrative rather than measured. The last row is deliberately low: the document is a byproduct of the work, not a prerequisite for it.
Extracting the rules: what the work actually looks like
The core technical activity of a modernization is recovering the executable specification from an artifact that was never written to be read. Done well, it produces a rule inventory that both the government and the new implementation can be tested against. Our engineers work it in five passes, and each pass produces something usable rather than a milestone document.
Pass one: the call graph and the dead ends. Parse the source into a graph of programs, procedures, copybooks or modules, and entry points, then walk it from the actual entry points the system exposes. On a system of this age, a large fraction of the code is unreachable — retired paths, abandoned batch jobs, alternate implementations kept "just in case". Establishing what is live shrinks the problem before anyone reads a line, and it is the cheapest step in the whole program.
Pass two: the data flow to the decisions. For each live entry point, trace which fields feed which conditionals. The interesting output is not the code; it is the list of fields that actually change an outcome, and the list of fields that are read, carried and never used. The second list is usually longer, and it tells the data migration team what they do not have to preserve perfectly.
Pass three: rule candidates, drafted by a model and verified by execution. This is where language models earn their place. A model reading a procedure with its copybooks and its variable names produces a plain-English candidate rule far faster than a person can, and it produces it consistently in a schema the team can review. What a model cannot do is be believed. Every candidate rule is a hypothesis, and the pass is worthless without the next one.
Pass four: verification against production behavior. Take the candidate rule, implement it as an executable predicate, and run it against a large sample of real historical transactions alongside the outcome the legacy system actually produced. A rule that reproduces the historical outcome on a hundred thousand records is a rule. A rule that disagrees on two hundred of them has found either a bug in the extraction or a genuine exception nobody documented, and both are valuable. This is the step that converts an AI-assisted reading exercise into evidence.
Pass five: adjudication with the government. The disagreements become a list of questions with real examples attached: here are two hundred cases where the system did something the stated policy does not explain. Every modernization we have engineered found a set of these, and they are the highest-value conversations of the whole program, because the answer decides whether the new system reproduces a behavior or corrects it. Get it in writing either way, and keep the decision log as a deliverable.
Where AI helps on this work, and where it does not
Being precise about this is worth doing in front of a program office, because the honest version is more persuasive than the enthusiastic one.
| Task | What a model contributes | What must not be delegated to it |
|---|---|---|
| Reading legacy code | Fast plain-English candidate summaries in a consistent schema, across far more code than a team can read | Believing the summary. Every rule is verified by execution against historical records |
| Document intelligence | Extracting structure from scanned manuals, memoranda and change requests that were never digitized as data | Treating an extracted value as authoritative without a confidence threshold and a review queue |
| Data profiling | Proposing meanings for cryptic codes by correlating usage patterns across tables | Deciding what a code means. That is adjudicated with the government and written down |
| Anomaly detection | Flagging records and transactions that differ from the historical pattern during parallel running | Deciding a difference is acceptable. Each one is triaged by an engineer against the rule inventory |
| Test generation | Proposing edge cases from the branch structure and the observed data distribution | Acting as the oracle. The legacy system's historical output is the oracle |
| Writing the new system | Drafting implementations, migrations and tests under review | Owning the design. Architecture, data model and interfaces are engineered decisions |
The pattern across the table is the same one that governs every use of a model in a system of record: the model proposes, and something deterministic disposes. The value is the speed of proposal across a volume of material no team would otherwise read. The discipline is that nothing reaches the new system on a model's authority.
The data layer comes first
The sequencing decision that most changes the outcome is building the data layer before any new user-facing capability. It feels backwards to a stakeholder who wants to see screens, and it is the difference between a program that delivers and one that demonstrates.
The layer we build has four parts. A change feed off the legacy store, so the new side can be kept current without asking the old system to change: log-based capture where the platform supports it, a timestamp-and-key sweep where it does not, always with a reconciliation job that proves the two sides match rather than assuming it. A canonical model for the entities the mission actually cares about, defined by what the rules need rather than by mirroring the legacy schema, because copying the old schema imports the old constraints and guarantees the new system inherits the problem. A translation layer that maps legacy codes to canonical values, with every mapping carrying the provenance of its decision and every unmapped value landing in a queue rather than defaulting silently. And a write path, planned from the start even if it is used late, because a read-only new system can never take over anything.
Once that layer exists, several things become possible that were not: the new capability can be built and tested against real data from its first sprint; the old and new logic can be run side by side on the same inputs; reporting and analytics can be moved off the legacy system early, which is often the first visible win and the one that buys the program its next increment; and a user population can be moved without a data migration event, because the data is already flowing.
Strangling the old system, one population at a time
The pattern is not new and it is not exotic: put a routing layer in front of the system, move one narrow slice of function behind it to the new implementation, verify, and repeat. What makes it work on a defense sustainment program is the specific discipline applied at each step.
Shadow first, always. Before any traffic is served by the new path, run it in shadow: real inputs go to both implementations, the legacy answer is the one returned, and the difference is recorded. Run it long enough to cover the cycle that matters, which on many systems is a month or a quarter rather than a week, because the end-of-period processing is where the exceptions live. The shadow log is the single best artifact for a program review, and it is what converts "we believe it works" into a number.
Choose the first slice by reversibility, not by value. The temptation is to start with the function that would impress the customer. Start instead with the one where switching back costs an afternoon. The first cutover's purpose is to prove the mechanism: the routing, the monitoring, the rollback, the support path, the user communication. Once that is proven, the valuable slices go faster and with less argument.
Move a user population, not a feature, when the workflow is the unit. Where the mission is organized by site, unit or role rather than by transaction type, the reversible slice is a group of users who do their whole job on the new system while everyone else stays. That surfaces the workflow problems a feature-by-feature migration hides until the end.
Keep the difference report running after the switch. When the new path becomes authoritative, do not turn off the comparison. Let the legacy path keep computing in shadow for a stated period. It costs almost nothing and it is the only way to detect that a rare quarterly path was never exercised during the trial.
Write down the retirement criteria. A legacy system that is never decommissioned means the program pays for two systems permanently, which is how modernization savings evaporate. State, at the start, what conditions permit shutting off each legacy component, then treat those conditions as requirements.
How a first increment should be judged
Editorial weighting, illustrative rather than measured. The last row is deliberately low: a demo proves the team can build, not that the system can be replaced.
The security and accreditation path is part of the design
A modernization that stands up new infrastructure inherits an accreditation problem, and the programs that handle it well decide the destination in the first weeks rather than the last. Three questions settle most of it. Does the new capability live inside the existing authorization boundary, extend it, or need its own? Which controls are inherited from the hosting environment and which the program must implement and evidence itself? And what does the change process look like once the system is authorized, because a modernization that can only deploy at an annual review has traded one rigid system for another.
The engineering consequence is that control evidence is generated by the pipeline rather than assembled by hand at the end. Access control, audit logging, encryption in transit and at rest, configuration baselines, vulnerability scanning and dependency provenance are all things a build system can produce continuously as artifacts. The same is true of accessibility: Section 508 conformance is cheap when the component library is built to it and tested in the pipeline, and expensive when it is a remediation project against a finished interface. Both belong in the definition of done for every increment, not in a phase.
How we work inside a prime's program
Precision Federal builds AI, data platforms, software, cloud and full-stack systems and delivers them into production inside federal agencies. On a modernization or sustainment program we come in as the specialist subcontractor on the extraction, data layer and new-build scope while the prime holds the contract, the customer relationship, the operations and the overall systems engineering.
The first four weeks. We take a source snapshot and a production data extract under whatever handling rules apply, run the reachability and data-flow passes, profile the data, and produce three things: a live-code map with the dead paths identified, a first rule inventory for the highest-value function with its verification results against historical records, and a written increment plan naming the first reversible slice with its acceptance criteria. That is a working artifact, not a study, and it is enough for the program to decide whether to proceed with evidence rather than optimism.
Weeks five through sixteen. The data layer, the shadow comparison, and the first slice in production behind the routing layer. Everything is built in the prime's environment, in the prime's repositories, under the prime's pipeline and the program's security controls. We write to the program's CDRL formats and standards, not ours.
What the prime keeps. All of it. Source code, the extraction tooling, the rule inventory and its verification results, the data pipelines, the infrastructure definitions, the documentation, and the customer relationship. We assign the intellectual property in the delivered work by a present written assignment, name and carve out any pre-existing tooling we bring and license it back perpetually so the sustainment team is never blocked, and deliver an exit package as a condition of final payment: repository, build pipeline, infrastructure as code, environment configuration, credential rotation and a runbook, with a handover rehearsal where the receiving team deploys while our engineers watch. We are named on the bid or not as the prime prefers; on a scored proposal, a named specialist with a defined technical scope and committed key personnel usually reads better to an evaluator than an unnamed pool.
How it is priced. The extraction and increment plan works well as a firm-fixed-price first milestone with written acceptance criteria, because its output is defined. The build increments work as either fixed-price milestones with measured acceptance or a committed team at a fixed monthly rate across a task order period, which suits option years where the sequence is set by adjudication outcomes nobody can schedule in advance. We are comfortable starting small so a new teaming relationship proves itself before either side commits to a large workshare.
Five ways this goes wrong
The program buys a study. A discovery contract whose deliverable is a document produces a document. Make the first deliverable a verified rule inventory and a working data path, and the understanding comes with it.
The new schema copies the old one. Mirroring the legacy data model is the fastest way to migrate and the surest way to inherit every constraint that made the old system hard to change.
Nobody adjudicates the exceptions. The two hundred cases where behavior and policy disagree sit in a spreadsheet while development guesses. Every guess becomes a defect found by a user.
Parallel running is scheduled but not staffed. Shadow comparison generates differences, and differences need engineers to triage. Fund that work or the report is ignored and the switch becomes a judgment call again.
The old system is never turned off. Without written retirement criteria treated as requirements, the program ends with two systems, two sustainment lines and no savings to show.
Bottom line
A legacy modernization is a knowledge recovery problem wearing a software project's clothes. The rules that matter are in the code and the historical data, not in the interviews, and they are recovered by reading at machine speed and verifying by execution against real production outcomes. Build the data layer first, run the old and new logic side by side on real inputs, migrate one reversible slice at a time, and adjudicate every disagreement in writing with the government. Programs that do this deliver capability in months and retire components on a schedule. Programs that start with a comprehensive as-is document deliver the document. The first step is one email with a one-page brief describing the system, its platform, the data and the increment you want first, and we return a scoped, priced statement of work.
Frequently asked questions
Recover the rules from the two artifacts that cannot be wrong: the source code and the historical transaction data. Map what code is actually reachable, trace which fields change outcomes, draft candidate rules from the code, then verify each one by running it as an executable predicate against a large sample of real historical records and comparing with what the legacy system actually did. Rules that reproduce history are confirmed. Disagreements become adjudication questions with concrete examples attached.
It can draft them, quickly and consistently, across far more code than a team could read. It cannot be believed on its own. Treat every model-produced rule as a hypothesis and verify it by execution against historical outcomes. That pairing is what makes the approach defensible in front of a program office: the speed comes from the model, the evidence comes from the data. Nothing reaches the new system on a model's authority.
Put a routing layer in front of the legacy system and move narrow slices behind it one at a time. Run each slice in shadow first, with real inputs going to both implementations while the legacy answer is still the one returned, long enough to cover the end-of-period processing where exceptions live. Choose the first slice for reversibility rather than visibility, so the initial migration proves the routing, monitoring, rollback and support path cheaply.
Because everything else depends on it. A change feed off the legacy store plus a canonical model and a translation layer makes it possible to build against real data from the first sprint, run old and new logic side by side, move reporting off the legacy system early as a visible win, and migrate users without a separate migration event. Building features first means integrating with real data late, which is where modernization schedules are lost.
Split it. The extraction and increment planning has a defined output, so it works as a firm-fixed-price milestone with written acceptance criteria. The build increments work either as fixed-price milestones with measured acceptance criteria on named datasets, or as a committed team at a fixed monthly rate where the sequence depends on adjudication outcomes that cannot be scheduled. Starting with a small fixed-price first milestone lets a new teaming relationship prove itself before a large workshare is committed.
