Modernizing legacy software with AI: understand, change, verify
A practical workflow for improving an existing system: map the business rules, give coding agents focused context, and replace one part at a time without losing control.
Revised September 4, 2026. This guide consolidates our articles on brownfield development and context engineering.
Replacing expensive legacy software is not mainly a code-generation problem. The difficult work is discovering which behavior the operation depends on, deciding what should change, and moving users and data without breaking that behavior.
AI coding agents can help investigate and implement a change. They still need evidence about the existing system and a clear definition of success. A clean new interface is not enough if the month-end export no longer balances.
Start with the operational constraint
Do not begin with “rewrite the application.” Pick a concrete source of friction: a dispatch screen that cannot reflect the way routes are assigned, approvals handled outside the system, or reports assembled by copying data between tools.
Write down what should improve and what must remain true. For example:
Replace the manual assignment spreadsheet with a dispatch view. Preserve existing order IDs, prevent duplicate assignments, and keep the current export working during the rollout.
This is an illustrative brief, not a client case. Its value is that an operator and an engineer can both tell whether it has been satisfied.
Sometimes the right result is a small integration. Sometimes it is a purpose-built module alongside the old system. A full replacement should follow from the constraints, not from frustration with the current UI.
Investigate the path the change actually touches
Ask the agent to trace one real case through the system before proposing edits. Where does the request enter? Which component validates it? Where is state written? Who reads it afterward?
The investigation should produce:
- Entry points, relevant files, and the revision inspected.
- Business rules visible in code, tests, and operator explanations.
- Consumers such as scheduled jobs, reports, and external integrations.
- Known uncertainties, explicitly separated from confirmed behavior.
- Commands that reproduce the current behavior and exercise its checks.
Repository evidence and operator knowledge complement each other. Code can reveal what happens today; it cannot, by itself, tell you which accidental behavior the business wants to preserve.
Context engineering is a working brief, not a repository dump
Give the implementation agent the smallest useful set of verified facts: the goal, constraints, file references, interface shapes, and acceptance examples. Let it retrieve additional code when needed.
For long-running work, preserve decisions and unresolved questions in a concise handoff. Remove repeated logs and abandoned approaches, but keep the reasons behind important constraints. Anthropic describes retrieval, compaction, and structured notes as context-management techniques; it also notes that aggressive compression can discard important information. There is no universal context-window percentage at which every model stops reasoning well. Read the context-engineering discussion.
A useful handoff for our dispatch example might say:
Goal: assign an order without the spreadsheet.
Preserve: order IDs and the existing export format.
Evidence: relevant source paths, test names, inspected commit.
Unknown: whether reassignment after export is permitted.
Acceptance: two dispatchers cannot assign the same order twice.
Stop: ask the process owner before changing the reassignment rule.
Notice the unknown. A good brief does not disguise missing information as a design decision.
Make a small, observable change
Choose a slice that includes the behavior, not just a layer of code. For dispatch, that could mean selecting one eligible order, assigning it, persisting the result, and showing the operator confirmation.
Capture the current behavior in a regression test where it must remain compatible. Add a separate acceptance test for the intended change. Review those expectations with the person who owns the process; reproducing an old defect in a test does not make it a requirement.
If several agents work in parallel, assign clear file ownership and shared interfaces. Separate files reduce editing conflicts, but they do not prevent semantic disagreements. Reserve an integration step that exercises the whole path.
Treat migration as its own piece of work
A replacement often has a period in which old and new systems coexist. Decide which one is authoritative for each record and how changes flow between them.
Before moving real users or data, answer:
- How will existing identifiers and relationships be preserved?
- How will missing, duplicate, or incompatible records be reported?
- What comparison will show that the migration is complete?
- Can a partially completed run be retried without duplicating work?
- If you roll back, what happens to records created in the new system?
“Restore the backup” is not a complete rollback plan if it discards work completed after cutover. Test recovery with representative data before relying on it.
Verify more than compilation
Run the repository's real checks and the changed workflow. Include permission boundaries, duplicate submissions, dependency failures, and the reports or jobs downstream of the edit.
Then ask an operator to complete the task. Can they identify the current state? Correct a mistake? Handle an exception? Technical correctness and operational usability need different checks.
Measure the outcome against the baseline: total handling time, corrections, failed handoffs, support effort, and operating cost. More generated code is not a business result.
The competitive advantage comes from a system that supports your way of working and can evolve with it. AI can help build that system; it does not remove the responsibility to understand it.