The system nobody wants to touch
Every agency and most large enterprises carry at least one system that everybody depends on and nobody wants to open. It pays claims, or tracks contract administration, or holds the case file a statute says must exist. The people who wrote it retired. The design document describes a version that stopped being true a decade ago. It works, and the price of it working is that the budget goes to keeping it breathing instead of making it better. GAO's June 2019 review of critical federal legacy systems (GAO-19-471) found systems in daily operation ranging from 8 to 51 years old, several still running COBOL or assembly, with vendor support for the underlying components already gone.
The money follows the same shape. GAO has reported for years that roughly four of every five federal IT dollars go to operations and maintenance rather than development, modernization and enhancement. That ratio is the actual problem statement. Nobody preserves a 1980s batch architecture on purpose. The maintenance load crowded out the capacity to change anything, and each year of deferral makes the next year's change harder.
So the program office proposes a rewrite. New stack, new database, clean domain model, cut over on a weekend. It demos well in a briefing, and it is the most reliable way to spend three years and deliver nothing.
Why big-bang rewrites fail
The failure is structural, not managerial. A rewrite promises to reproduce every behavior of a system whose behavior was never written down, on a schedule set before anyone knows what those behaviors are. Meanwhile the old system keeps changing, because statute changes, rates change, and interfaces change. The replacement is chasing a target that moves at the same speed it does.
The public record is unambiguous. The FBI's Virtual Case File was abandoned in 2005 after roughly $170 million; its successor, Sentinel, was restructured in 2010 into small incremental deliveries and deployed in July 2012, documented across DOJ Office of the Inspector General audits. The Air Force cancelled the Expeditionary Combat Support System in 2012 after about $1.1 billion, examined by the Senate Permanent Subcommittee on Investigations in 2014. The pattern is the same in both: total replacement was committed to before anyone understood the system being replaced, and no partial delivery survived when the schedule broke.
An incremental program has a different property that matters more than any technical merit. At every point in its life there is something in production that works, and the money already spent bought something the customer still has.
Where modernization programs lose the schedule
Editorial weighting from public program records and practitioner reading — illustrative, not a measured statistic.
| Dimension | Total rewrite | Incremental replacement |
|---|---|---|
| First production value | At final cutover, usually year two or three | First seam in production inside one quarter |
| Rollback | Restore from backup and lose the window | Flip routing back to the legacy path in minutes |
| Scope discovery | Found late, priced as a change order | Found per seam, priced inside the increment |
| If funding stops | Nothing delivered, sunk cost total | Everything already cut over stays in service |
| Legacy team role | Sidelined, then blamed at cutover | Source of truth for behavior, engaged throughout |
| Typical failure | Cancellation before delivery | A seam takes longer than estimated |
Write down what it does before you change what it does
The first work on any old system is not design. It is capture. Michael Feathers named the technique in Working Effectively with Legacy Code (2004): a characterization test does not assert what the code should do, it records what the code does. You feed real inputs, capture the outputs exactly, and pin them. When a behavior looks wrong, you pin the wrong behavior too, then raise it as a question for the business owner. Correcting silently is how modernization programs get accused of breaking things.

On a batch financial or eligibility system this is mechanical and productive. Take a year of production input files, run them through the current system in a copy of the production environment, and store every output record, reject, generated notice and downstream feed. That corpus becomes the acceptance criteria for everything built afterward, and it is the only honest answer to the question a contracting officer will eventually ask: how will you prove the new one is right?
Two things reliably surface during capture. Dead code, meaning paths no production input has reached in years, which shrinks replacement scope immediately. And rules that appear in no requirement document and no statute, that some transaction still depends on. Finding those in week four costs an afternoon. Finding them the Monday after cutover costs the program.
The strangler fig: route, do not replace
Martin Fowler described the strangler fig application in 2004, taking the name from the fig that grows around a host tree until it can stand on its own. The engineering version: put a routing layer in front of the legacy system, then move one capability at a time behind that layer, leaving everything else untouched. The old system keeps serving what has not moved. Users see one system throughout.
The unit of movement is a seam, meaning a boundary where traffic can be split cleanly. Good seams are usually a whole business capability with a defined input, a defined output, and a data footprint you can describe. Address validation. Rate calculation. Notice generation. Document intake. Poor seams cut through a shared table that thirty programs write to, which produces two systems fighting over the same rows.
Order matters more than most plans admit. Choose the first seam for low blast radius and high learning value, not business importance. Its job is to prove the routing layer, the deployment pipeline, the monitoring, the rollback and the acceptance process end to end. Once that machinery works, every later seam is cheaper.
The facade is the contract
Between the routing layer and the old core sits a facade: an interface that speaks a clean, documented, versioned protocol outward and whatever the legacy system actually speaks inward. Screen scraping a 3270 session. Dropping a fixed-width file on an FTP path at 0200. Calling a stored procedure whose parameters are positional and undocumented. All of that stays behind the facade and stops spreading.
Eric Evans called the same idea an anti-corruption layer in Domain-Driven Design (2003), and the name states the purpose. Without it the legacy data model leaks into every new service, and three years later the replacement carries the same shape as the thing it replaced. The facade is where you translate a packed decimal date, a status code that means four things depending on a sibling field, and a customer identifier that is not unique.
The facade earns its keep again on the way out. Because consumers depend on the facade rather than on the core, the core can be replaced underneath without touching a single caller. That is what makes decommissioning possible, and it is the difference between a modernization that ends and one that runs forever.
Data migration is where the program actually dies
Code can be rewritten twice. Data gets one careful pass, and it carries every compromise made since the system was built. Expect all of these: dates stored as text in three formats, a status field overloaded to carry a second meaning, identifiers reused after a purge in 1997, records whose foreign keys point at rows that were deleted, and free-text fields that operations staff turned into a structured field by convention.
Profile before you plan. Row counts and control totals by domain and by year. Null and distinct distributions on every column. Referential integrity across every declared and undeclared relationship. Duplicate detection under several match rules. That profile sets the real schedule, and it is usually where an estimate written before anyone inspected the data turns out to be off by a factor.
Then write the migration as code, not as a one-time operation. It is versioned, repeatable, and rerunnable from a clean state, with a reconciliation report produced by the run itself: counts in and out, dollar or unit control totals, rows quarantined with the rule that rejected them. Two migrations that produce different results from the same input are a defect, and finding that during rehearsal is the whole reason rehearsal exists. Run it end to end against a full production copy at least three times before anyone talks about a date.
Dual-run verification and how to read a diff
Once a seam is built, do not switch to it. Run both. Every request goes to the legacy path and to the new path, the legacy answer is what gets returned to the user, and the two answers are compared and logged. This is shadow mode, and it converts a risky cutover decision into an accumulating body of evidence.
Reading the differences is where the judgment lives. Categorize every mismatch: the new path is wrong, the legacy path is wrong, both are defensible and a business owner has to choose, or the comparison itself is broken because of timestamps, sequence numbers, or rounding. Only the first category is a defect in your work. The second and third are decisions that belong to the customer, in writing, before cutover. The fourth is noise that must be eliminated or it will drown the signal.
Set the exit criterion numerically and agree it up front. A defensible one: zero unexplained mismatches on financial totals, mismatch rate below a named threshold elsewhere, thirty consecutive days of production-volume shadow traffic including a month-end and a period-close, every remaining difference dispositioned by name. When that ledger is clean, cutover is a formality.
Cutover in increments, with a way back
Cut over by slice, and pick the slice dimension that matches how the business already segments work: one region, one program office, one document type, one customer cohort, one percentage of traffic. Small first, then widen on a published ramp. Each step has a stated rollback trigger, and rollback is a routing change, not a restore.
The write path is the hard case, because once the new system owns a record, going back means reconciling what changed while it owned it. Three workable patterns: keep the legacy system as the system of record and have the new path write through the facade; dual-write with a reconciliation job that alarms on divergence; or move ownership per domain, so a record is owned by exactly one side at a time and transfer is an explicit, logged, reversible event. Choose one deliberately and write it into the plan. Programs that leave this implicit discover during the first incident that nobody can say which copy is authoritative.
Increment shape for a first modernization slice
Sequence so value lands in the first quarter
A modernization program that produces nothing visible for eighteen months will be defunded, and it will deserve to be. Sequencing is a funding strategy as much as an engineering one, and the ordering rule is simple: move first the thing that is both cheap to move and expensive to keep.
Four candidates repeatedly pay early. A reporting or query path, read-only, no write-path risk, and it removes the reason people extract data into spreadsheets. A public-facing intake form, because the usability and accessibility gain is visible to leadership in a week. A batch job carrying a license or hosting cost, because retiring it produces a number the CIO can put in a budget exhibit. An interface another program office is waiting on, because it converts a peer into an advocate.
Sequence away from the general ledger, the statutory calculation engine, and anything touching the fiscal-year close until the machinery is proven. Those are the parts where a mistake is a headline, and where accumulated shadow-run evidence is worth the most. Move them fourth, not first.
Contract it so it can be incremental
An incremental technical plan inside a contract structured for one delivery will lose. FAR 39.103 directs modular contracting for information technology acquisitions, implementing 41 U.S.C. 2308: successive, interoperable increments, each delivering a usable capability, each sized to be awarded and completed within a reasonable period. Firm-fixed-price task orders sized to an increment work well once the characterization corpus exists, because acceptance criteria are objective.
Federal programs have a funding path built for this: the Technology Modernization Fund, created by the Modernizing Government Technology Act in the FY2018 NDAA (Public Law 115-91) and capitalized with an additional $1 billion by the American Rescue Plan Act of 2021. TMF proposals are reviewed in phases and expect incremental delivery with measurable outcomes. On the state side, Medicaid systems funded at enhanced federal match must meet the CMS conditions and standards at 42 CFR 433.112, and modularity is written into them, so a modular plan is the condition of the money.
Modernization can invalidate the authorization you were relying on
Under NIST SP 800-37 Rev. 2, a significant change to a system or its environment triggers reassessment and reauthorization. Adding a cloud-hosted routing layer in front of an on-premises core changes the boundary, the data flows, and the inherited controls. Get the authorizing official into the room during increment one, while the change is small enough to describe on a page.
The obligations you inherit on day one
Old federal systems carry duties that a commercial rewrite plan will not mention. Records are the first. The Federal Records Act (44 U.S.C. Chapter 31) and NARA's electronic records requirements at 36 CFR Part 1236 mean the data being migrated is often a federal record, with retention, disposition and metadata obligations that survive the migration. A transformation that drops an audit column or rewrites a timestamp can destroy record integrity while passing every functional test.
Accessibility is second. Any new interface falls under Section 508 of the Rehabilitation Act (29 U.S.C. 794d), and the Revised 508 Standards incorporate WCAG 2.0 Level AA. Retrofitting accessibility costs multiples of designing to it, and it is a common source of late rejection. Third is the interface inventory: every downstream consumer, every nightly extract, every partner agency feed, with an owner's name against each. Systems this old always have more consumers than the documentation lists, and the ones you did not find are the ones that break.
What a defensible modernization roadmap contains
When our engineers review a modernization plan, before or after award, these are the items whose absence predicts trouble. A roadmap that has all of them can be defended in front of an IG, a CIO council, or a source-selection board.
- A named inventory of seams, with the data each one touches and which side owns those rows during transition
- A characterization corpus drawn from real production traffic, with the volume and time span stated
- A data profile with row counts, control totals, integrity findings and a quarantine rule per known defect class
- Numeric shadow-run exit criteria per increment, agreed with the business owner before build starts
- A rollback procedure per increment, with its trigger, its owner and the time it takes, rehearsed at least once
- An interface register naming every consumer and a human accountable for each
- Records, accessibility and authorization-boundary treatment written into the increment, not deferred to a later phase
- A decommissioning line item per retired module, with the cost it removes from the operations budget
How we start
Precision Federal builds AI, data and software systems for federal, state and commercial customers, and modernization is where several of those disciplines meet at once. Our first six weeks look the same whether the core is COBOL on a mainframe, a Delphi client-server application, or a decade-old Java monolith nobody dares redeploy. We read the code and the job schedule, capture real production behavior into a test corpus, profile the data hard enough to price the migration honestly, and map every interface to a named owner. Out of that comes a seam inventory, a sequenced increment plan with the first slice scoped, and a data-risk register with the ugly findings on page one.
The engineers we assign pair with the people who have kept the system running, because those people hold the behavior no document contains. Our domain specialists cover the surrounding obligations: authorization boundaries, records retention, accessibility, and the acquisition structure that lets an increment be awarded and accepted on its own. We work as a prime or as a subcontractor to an incumbent integrator. On modernization the subcontract role is often faster, because the incumbent already holds the contract and needs engineering depth on the seam.
Bottom line
The rewrite is not attractive because it is better. It is attractive because it lets everyone defer the hard part, which is learning exactly what the old system does and moving its data without losing anything. Do that part first and the modernization becomes a series of ordinary engineering increments, each one small enough to verify and reverse. Skip it and the program becomes a bet that gets called in year three.
Frequently asked questions
A routing layer is placed in front of the existing system, and capabilities are moved behind it one at a time while everything not yet moved continues to be served by the original. The old system shrinks until it can be retired. Users see a single system the entire time, and any increment can be routed back if it misbehaves.
It depends on the number of seams and the state of the data, which is why the first four to six weeks go to characterizing behavior and profiling data before a schedule is committed. What is controllable is when value first lands: a well-chosen first seam should be in production inside one quarter, and any plan where nothing reaches production for a year should be challenged.
Code can be rebuilt as often as needed and tested against captured behavior. Data gets one careful pass and carries thirty years of workarounds: overloaded status fields, reused identifiers, broken references, dates stored as text. Write migration as versioned, rerunnable code with a reconciliation report, and rehearse it against a full production copy several times before setting a cutover date.
Both the legacy path and the new path process every request. The legacy answer is returned to the user, and the two answers are compared and logged. Differences are categorized as new-path defects, legacy defects, business decisions, or comparison artifacts. Cutover happens when a numeric exit criterion agreed in advance is met over a stated period, including a month-end and a period-close.
It fits them better than a single monolithic delivery. FAR 39.103 directs modular contracting for IT acquisitions under 41 U.S.C. 2308, calling for successive interoperable increments that each deliver usable capability. The Technology Modernization Fund is structured for phased delivery, and CMS conditions at 42 CFR 433.112 make modularity a condition of enhanced federal match on Medicaid systems.