Risk hides in behavior nobody wrote down
Older software often contains important exceptions in scripts, databases, batch jobs and users' habits. Source code is therefore not a complete product description. A team can build a new screen quickly yet lose the rule support staff used to repair an unusual case. Before designing replacement, follow several real journeys from entry to report. Record who changes data, which other systems read it and what happens on failure. Give each rule an owner able to confirm a correct outcome. You do not need to understand the entire history perfectly, but uncertainty around a critical path must be visible. Interview operators as well as designers.
Inventory parts by impact and frequency of change. Some components are technically old but stable and cheap to operate; others obstruct every release. Give priority to an area where improvement can be demonstrated. Measure time to make a safe change, incidents, manual repair and work needed to manage dependencies. If no baseline exists, create one from representative cases. A full rewrite without such measurement may deliver newer technology with no proven benefit. Risk comes not only from age, but from unclear ability to change and recover the system. Record what it would cost to leave a component in place.
Characterization tests capture the present reality
Before replacing a part, write tests that reveal what it currently does. A characterization test need not claim that existing behavior is right; it records it so a change is conscious. Prepare ordinary and exceptional inputs, historical data, invalid values and repeated requests. For a financial process compare not only an API response but resulting amounts, case status and audit evidence. Anonymize test data where needed. For each surprising result decide with the process owner whether to preserve it or correct it. Keep the test case so a later team can see the reason for the choice.
This step may reveal a hidden rule faster than trying to rewrite everything from memory. It must not blindly freeze old defects. Divide results into confirmed behavior, deliberate changes and open questions. During a pilot run old and new versions against the same cases and explain differences before switching. With live data, ensure a comparison run cannot create another payment or customer message. Comparison may occur in an isolated environment or a shadow mode without side effects. Every discrepancy needs an owner of the decision. The test suite should grow as previously unknown exceptions are discovered.
Transitional architecture needs an end date
Microsoft describes the Strangler Fig pattern as gradually routing selected functions to a new implementation. AWS presents branch by abstraction for components difficult to separate at an external boundary. Both can reduce the size of a single intervention but add temporary routing and coordination. Before adding either, define the boundary, source of truth, observation method and return rule. Sometimes wrapping the older system with a stable API and changing its internals later is enough. Elsewhere it may be best to retain a working core and modernize access or operations. The pattern should address a particular risk, not become the goal itself.
A transitional layer may begin by forwarding requests unchanged. That verifies identifiers, latency and error responses before business logic changes. If more rules and data copies accumulate there, however, it can become a permanent third system. For each temporary component record why it exists, who operates it and when it will be removed. After each stage ask whether boundaries and manual checks are decreasing. If they grow, gradual modernization may be moving complexity rather than reducing it. Include the operating cost of these components when comparing the next stage with other options.
Data need meaning, not just matching row counts
Historical records may contain duplicates, empty fields and values interpreted under old rules. A migration should define mapping, ownership of each important field and treatment of problematic data. Keep a link to the original identifier for investigation. Compare totals, open cases, relationships and calculation results rather than only table counts. For ambiguities decide whether to repair the value before migration or mark it for later work. Silently applying a default can create a new business error that later tests will not detect. Preserve evidence of manual corrections so a changed record can be explained after cutover.
During coexistence identify which path may write. Dual writing without reconciliation hides disagreement. If synchronization is needed, specify ordering, retry and behavior during interruption. A backup of original data and rehearsed restoration are separate conditions for switching. PostgreSQL documentation explains different approaches to backup and recovery; the appropriate one depends on the database and acceptable loss and return time. Eventually the older application may not understand data written under the new schema. State that point openly. Simply deploying an old container can be unsafe after it. Give the cutover approver a clear view of this limitation.
Every stage needs evidence and a stop route
Choose a first pilot for measurable value and a manageable boundary rather than only the most visible screen. Set a baseline and success conditions in advance: correct outcome, processing time, exception count, support impact and recoverability. Release a restricted type of case or user group where the product allows. Observe both old and new paths because the fault may be between them. Assign a person authorized to stop the pilot. If a difference cannot be explained, do not expand merely to meet a date. A useful pilot answers a specific question even if its proposed design proves wrong.
Make stopping signals concrete: unexplained amount differences, a growing queue of unknown states, missing history or inability to restore within the agreed time. Stopping should trigger investigation, not automatically cancel the entire program. After repair repeat the tests and decide again. For stages that change schema or protocol, prepare a return procedure for data created after cutover. Rollback is not always a click on the previous version. Rehearsing it separately reveals this before a live incident. The person approving release must know where the return path ends and manual repair begins.
People and operations are part of migration
During transition users may work across old and new screens. They need to know where a case is now processed and whether a change appears in both places. Support needs to trace an identifier across systems and explain status without involving a developer. Provide a short guide for common exceptions and a contact for decisions. The original team may hold unwritten knowledge of historical rules; involve it in verification without making one person indispensable. Every new stage should leave tests and operating instructions that preserve what was learned. Schedule training around the actual user task, not only the release date.
After release compare real work with the previous process. If staff begin copying information manually between systems because the integration leaves cases unfinished, the project has not achieved the intended improvement. Also measure time to make the next change. Modernization should gradually reduce fear of alteration and shorten the path from decision to safe outcome. Track incidents and restoration time, not just the number of moved functions. Operations staff should be able to name each temporary component and owner. Otherwise maintenance may become harder than before the project. Observe the unusual cases that were most costly in the old workflow.
The decision after a pilot must remain open
The right answer after a first stage need not be to continue at the same pace. It may be enough to repair a few critical boundaries, or poor data quality may require a separate project. Record what was verified, which assumptions failed, what the delivery cost and how the estimate for later stages changes. Include parallel operating cost and removal of temporary layers in the comparison. Stopping a poor route early is a good decision, not a team failure. What matters is whether the organization can change and operate the service more safely. Share the decision record with both product and support owners.
EXCOIN can design a specific modernization path, but cannot guarantee scope or duration without knowing the system, data and people. A useful initial delivery is often a dependency map, characterization tests for critical cases, a proposed boundary, migration rules and a return plan. These outputs have value even if the business keeps part of the old system. A verifiable pilot gives a stronger basis for investment than a generic promise of a complete rewrite. The aim is to reduce uncertainty with every stage while keeping a service people depend on available. Document how the next milestone will test the largest remaining risk.