C.E.O. Bee
Legacy Modernization

Legacy software modernization: rewrite or replace step by step? A seven-question test

C.E.O. Bee Engineering22 min read

In legacy software modernization, "rewrite or replace piece by piece?" has no single right answer: incremental migration is needless overhead for a small, simple system and usually the safer path for a large one that keeps changing. This article tests the direction with seven questions: intercepting requests, code access, size, shutdown speed, natural boundaries, proof of today's behavior and a changing business. Then come the first slice, data, cutover and rollback, and a copy-ready slice plan. General information; scope is settled in a written quote.

Legacy software modernization: "rewrite it?" is two separate decisions

If you own or run IT for a system that has worked for years, you have two questions that usually get asked as one. First: does this system need to be touched at all? Second: if it does, should it be rewritten from scratch or replaced piece by piece? The first is an audit question; the commands, the 0-to-2 scoring matrix and the report skeleton in how to take over an unfinished software project answer it, and we do not repeat them here. This article takes the second: once a direction has been chosen, how a legacy system is sliced and how each slice is put into service. Knowing your own answer before you ask for a custom software development quote makes the quotes easier to read.

The two extreme positions are well known, and the unfinished-project article covers them briefly in the context of an audit result; here we take only the point this article rests on. In 2000 Joel Spolsky called rewriting working code from scratch the single worst strategic mistake a software company can make (Spolsky). Martin Fowler writes that the simple-sounding plan of building a new system that does exactly what the old one does, in a newer technology, has gone "down in flames most of the time" (Fowler). Neither says that incremental replacement works for every system, though; Microsoft's documentation explicitly lists the cases where it may not be suitable. That is why the choice of direction can be written as a test: seven questions, each with its basis.

We describe how we run assessment, roadmap and rebuild work on our software consulting and modernization page.

Four paths, one table

First, the options by name. A source name in a cell points to the page the statement rests on; cells marked "inference" or "judgment" go beyond what the source writes and are our own engineering judgment, not a standard.

Path What it does When it fits Main risk Way back
Rewrite (big bang) Builds the new system from scratch and switches over in one step when it is ready The system is small and replacing all of it is simple (Microsoft Learn says incremental migration may not be suitable in this case; that a rewrite then fits is our inference) Defining and agreeing on the existing behavior can be a big job in itself, and the plan turns into one large cutover (Thoughtworks) If the old system is switched off at cutover, a way back has to be built separately (judgment)
Incremental replacement (strangler) Routes requests between the old and new systems through a facade (proxy), moves functionality over slice by slice, and finally retires the old system (Microsoft Learn) Large systems, critical components or complex features are being replaced, and the old system can stay up throughout the transition (Microsoft Learn) The facade can become a single point of failure or a bottleneck; the temporary architecture has a cost (Microsoft Learn) Pointing the routing back at the old system (judgment); for data, rollback to the old database stays possible until the domain tables and synchronization processes are removed from it (Microsoft Learn)
In-place internal improvement Decouples the system with small, reversible changes: expand the interfaces first, migrate the consumers, then contract the old one (Parallel Change) There is no natural seam to slice along yet; you open one first (judgment) If the behavior is not written down and there are no tests, every change is risky (judgment) Each step is small and separate, so a single step can be reverted (judgment)
Wrapping without touching Puts a new layer around the old system without touching it, and freezes the old one (judgment) There is no access to the source code or to an integration point you can change (Microsoft Learn says incremental migration may not be suitable in this case; that wrapping is the option here is our inference) The old system's problems stay in place (judgment); the new layer may inherit the old system's concepts (Microsoft Learn: without an anti-corruption layer, calls can force the new system to adopt legacy conventions) Removing the new layer is enough; the old system is unchanged (judgment)

What each path costs in total is a separate question; the worksheet in off-the-shelf or custom software: total cost of ownership shows how to work out the line items. It was written for comparing off-the-shelf and custom software, so adapting it to a modernization is up to you.

The decision test: seven questions

Answer each question for your own system with "yes", "no" or "I don't know". "I don't know" is also a result: finding the answer becomes the first job of the migration.

# Question If yes If no Basis
1 Can you step in between requests and route them? A facade (proxy, API boundary, queue) can be built; incremental migration is possible The pattern may not be suitable; first open a point where you can intercept Microsoft Learn
2 Do you have access to the source code, or to an integration point you can change (a database, for example)? You can disable migrated functionality in the old system and redirect internal calls Only wrapping from the outside is possible; who owns the code and how it is handed over is a separate matter Microsoft Learn
3 Is the system small, and is replacing all of it simple? Incremental migration may be unnecessary overhead; a rewrite is worth considering The risk of a rewrite grows Microsoft Learn
4 Will the old system be shut down completely and quickly? The investment in an intermediate architecture may not pay off; the cutover and shutdown plan comes first An intermediate architecture can be a reasonable investment Microsoft Learn
5 Can you find a natural boundary: a separate function, a separate data owner, a separate rate of change? Slices can be released one by one First open a boundary through internal improvement Fowler; Parallel Change
6 Do you know today's behavior in writing, and can you prove it with tests? Each slice's acceptance can be measured against the old behavior First pin the behavior down: characterization tests and a recording of real traffic Thoughtworks; Feathers
7 Will the business keep changing during the migration? A big bang means chasing two moving targets; each slice fixes its own target The risk of a rewrite is lower Judgment; Thoughtworks

The "Basis" column shows which page each question comes from; the consequences in the "If yes" and "If no" columns are our inference. The first four questions come from the Microsoft Learn document that lists the situations where incremental migration may not be suitable. The fifth rests on Fowler's observation: well-decoupled systems are rare, and you usually create the decoupling afterwards. Unlike the first four, the sixth question does not come from the Microsoft Learn document; it rests on an observation behind the Thoughtworks team's work and the characterization-testing approach: whether today's behavior is written down. The Thoughtworks team (Cartwright, Horn, Lewis) writes that for plans aiming to reproduce the old behavior exactly, even defining and agreeing on the existing behavior is a big job, and that it leads to one large cutover (Patterns of Legacy Displacement). To record the behavior of untested code as it is, characterization tests are used; for the audit side, see the takeover article again. The seventh question is our judgment; the same source also describes how the gap between the requirements accepted at the start of a long program and the business need widens over time.

Now let us turn the answers into a direction. Read the table from top to bottom; the first row that fits gives the direction, and the last two rows are conditions added to it. This ordering is our engineering judgment, not a standard.

Answer pattern Direction
4 yes A cutover and shutdown plan: write down how and against what you will retire the old system, without investing in an intermediate architecture
3 yes, 4 no Consider a rewrite; the system is small and easy to replace
2 no Wrap from the outside only; first settle code access and the question of rights
1 or 5 no, 2 yes Internal improvement and opening a boundary first; incremental migration after that
1, 2 and 5 yes; 3 and 4 no Incremental migration
6 no (regardless of the other answers) Whatever the direction, pin the behavior down first
7 yes (regardless of the other answers) If a rewrite is chosen, approach it by slicing: pieces that fix their own targets instead of one big cutover locked to a single target

A hypothetical reading: say your answers are 1 yes, 2 yes, 3 no, 4 no, 5 no, 6 no, 7 yes. The row "1 or 5 no, 2 yes" fits: first open a boundary through internal improvement, then migrate incrementally. Because you said no to question six, the first step is to pin the behavior down; because you said yes to question seven, once the boundary exists you plan slices with separate targets instead of one big cutover. In a real system some answers will come out as "I don't know"; finding the answer to that question is the first job.

Choosing the first slice

The Thoughtworks team starts slicing by looking at how the existing solution maps to business capabilities: where one big technical solution serves several needs, can one need be separated and delivered independently, with the pieces depending on each other as little as possible. Microsoft Learn adds that high-return changes can be made before low-return ones. A table of criteria that combines the two ideas is our judgment:

Criterion Why For the first slice For later slices
Clear boundary (separate function, separate data) A part with few dependencies can be released on its own Required Relaxes as the boundary widens
Independently measurable value You measure the result against the old system; value arrives early (Fowler) Required Required
Owns data with one clear writer No dual-write or consistency risk Required If data is shared, the rules in the data section apply
High rate of change Starting where the return is high (Microsoft Learn: high-return replacements before low-return ones; that frequency drives the return is our assumption) Preferred Moves to the end as the rate drops
Low cost of failure but instructive The first slice teaches the method; Fowler describes learning that grows with each slice Preferred Slices with a high cost of failure come later, with what was learned

Slicing has another side: measuring what you are about to change. In the system described in the low-latency live market data engine article, the load was produced by replaying real connection traces instead of generating synthetic traffic; the sources of the problem were found by measurement, and the rewrite that followed, described in the article as "four parts," focused on the fan-out tier the measurement pointed at. That is a newly built system, not a migration of an old one; we do not repeat the article's figures here. The principle we borrow: do not replace the whole system, replace piece by piece the place the measurement points at.

Data: the hardest part

Slicing code is comparatively easy; slicing data is not. Four rules keep the work manageable.

Expand, migrate, contract. Danilo Sato's Parallel Change pattern splits a backward-incompatible change into three phases: first you expand so that the old and new forms are supported together, then you migrate the consumers one by one to the new, and last you remove the old (Sato). The adaptation to a schema is ours: first add the new column or table and leave the old one alone; move every component that reads and writes to the new one; when no consumer is left, remove the old one.

Shared data and a single writer. Microsoft Learn asks you to think ahead about how to manage the data stores that both the old and new systems may use, and to make sure both systems can reach those resources at the same time. If the old and new systems write to the same record, dual writes create a consistency problem; our judgment is to name one writer for each piece of data.

A translation layer. During the transition the two systems call each other. Microsoft Learn recommends an anti-corruption layer to translate these calls: it protects the design of the new system from the concepts of the old one; without it, the new system may be forced to inherit the old one's rules.

Extract, synchronize, verify. In Microsoft Learn's database example, the relevant tables are first loaded into a new domain database with ETL, kept in sync with change data capture (CDC), and the consistency between the two databases is verified before the cutover. Returning to the old database is possible while the old objects and the synchronization processes are still running; once they are deleted, returning requires rebuilding the objects and replaying the changes, which significantly increases effort and risk. For that reason the document says to treat the removal of the old objects as a deliberate last step taken after the new system is verified. We do not recommend a product; the principle is tool-independent.

To check consistency often and cheaply, comparing counts and digests between old and new (record counts, totals) is enough. In the market-data article, during a long endurance test, a cheap consistency check exposed a leak long before it would have turned into a memory alarm; again not a migration, but an example of the same principle.

Cutover and rollback

A cutover is the moment a new slice is opened to real users. The Thoughtworks article names cutover strategies; below are four of them, side by side with our reading note and a rollback column.

Method When it fits How to roll back
Dark launching Running the new back-end behavior with real calls without showing it to users, to measure load and performance impact; calling old and new code together, comparing the results and returning a single response to the user (Fowler) Switch the new path off with a feature flag (Fowler)
Canary release Opening a change gradually to a small subset of users before opening it to everyone (Sato) Route users back to the old version (Sato)
Divert the flow When a shared function that collects data for many systems (such as reporting) exists, separate it from the old system first; gradual migration and parallel running (Thoughtworks) You can go back to the old system as long as parallel running continues and the old application has not been shut down (judgment)
Stop the world cutover Stopping normal business operations and switching to the new system (Thoughtworks) A rehearsal and a rollback plan must be written before the cutover (judgment)

Whichever method you choose, the rollback criterion is written before the cutover: which indicator going wrong, who decides, where you go back to. Entering a cutover without a written criterion creates an argument at the moment of decision. In the context of Divert the Flow, Thoughtworks describes leaving parallel running open for a period and raising an alert if the old and new applications diverge too much as an extra safeguard; that is the way to run old and new side by side and compare them. The market-data article also shows that measuring by replaying real behavior works.

The intermediate architecture built for the transition is temporary. Fowler writes that the code built for it goes away when the modernization is done and that the reduction in risk outweighs the cost; Microsoft Learn notes that the facade should not become a single point of failure or a bottleneck, and that when the migration ends it is generally removed or may be kept as an adapter for old clients. So every slice's plan has a closing line: which condition, once met, takes the intermediate architecture down.

Migration slice plan template

Copy the two tables below and fill in one row per slice in each, under the same slice name. An empty cell is a question; a cell you cannot fill shows that the slice is not ready. You choose the slice names and the order.

The first table says what the slice is and what it rests on.

Slice Boundary (seam) Data it owns Dependencies Proof of old behavior (test, recording, replay)

The second table says how the slice goes into service and how it is closed out.

Slice Cutover method Rollback criterion (indicator, decision-maker, target) Acceptance criterion Intermediate architecture and teardown condition Status

How to use them: fill in the boundary and data columns of the first table first, because both determine the choice of the first slice. If the proof of old behavior is empty, the slice stays "unknown" in the plan and the pinning work comes first. The acceptance criterion states in writing how the slice will be approved against the old behavior; how to write that line in a quote is shown by the "deliverables and acceptance criteria" item of the scope-document template in how to read a software proposal; apply it as one row per slice. After each slice, update the closing line.

The third table, the pre-cutover check, is filled in right before the cutover. The rows are our framework; they contain no numbers or durations.

Pre-cutover check Yes / no / unknown Owner
Has the rollback path been tried?
Is the rollback criterion written down (indicator, decision-maker, target)?
Is the old system closed to writes for this data, and is the single writer clear?
Are the count and digest comparisons between old and new done?
Does the proof of old behavior (test, recording) pass on the new slice?
Are monitoring and alerts ready?
Is the facade built so that it is not a single point of failure?
Is deleting the old objects planned as a separate, later step after the cutover?
Who makes the cutover decision, and who is informed?
Is the closing line (the teardown condition for the intermediate architecture) written down?

When a rewrite is the right call

Rewriting old software is not bad; an unplanned, big-bang rewrite is risky. Microsoft Learn writes that incremental migration may not be suitable when the system is small and replacing it is simple; a rewrite may be reasonable then (our inference): a system with clear behavior, few external dependencies and a clear owner. Spolsky's article makes the same distinction: throwing away a function or refactoring a class is fine; throwing away the whole program is dangerous. The Corporate Website Redesign card in our portfolio describes rebuilding a corporate website, not a software migration; it says "we keep meeting the same site with pricing buried three menus deep, running on a stack too fragile to touch" and adds "We rebuild these on a modern stack." Those sentences only show that we, too, choose to rebuild from scratch for this kind of work; which path fits your system is decided by the test above. For the question of which layer to rebuild in an app written quickly with AI, see the audit in is your vibe-coded app production-ready.

If you want the decision turned into a written assessment and roadmap, see our software consulting and modernization page.

How we do it

We repeat here only what we have written on our site. Every engagement is a paid project that starts with a written quote you approve. A clear scope, timeline and cost are in writing before anything is billed, and nothing is charged until you approve.

Every technical decision is written down with its reasoning, and you talk directly with the engineer writing your code. Platforms move: dependencies get deprecated, APIs change, security advisories land; on the projects we look after, we keep an eye on what yours depends on and raise what needs attention. Ownership of the delivered work transfers on full payment, as set out in our terms, and if you ever want your infrastructure moved to another team, we help you transfer it cleanly with accounts, domains and documentation. The answer to taking over a project another company left behind is on our custom software development page, and the steps are on the Process page. Tell us about your project.

Frequently asked questions

When is rewriting legacy software the right call?

When the system is small, replacing all of it is simple, its behavior is clear and its external dependencies are few. Microsoft Learn writes that in this case incremental migration can be unnecessary overhead. If the system is large and the business keeps changing during the transition, a one-step rewrite means chasing two moving targets.

What is the strangler fig pattern, and does it apply to every system?

It is the approach of moving functionality to the new system slice by slice through a facade and retiring the old system at the end; the name comes from a kind of vine Fowler saw, and it is a pattern used in legacy software modernization. It does not apply to every system. Microsoft Learn writes that it may not be suitable in four cases: when you cannot intercept requests, when you have no access to the source code or to an integration point you can change, when the system is small and replacing it all is simple, and when the old solution has to be shut down completely and quickly.

Can new features be added while the old system runs, and does the system have to go down during the migration?

In the incremental approach the old system stays up and the facade routes requests. Fowler describes the approach as often starting with small additions built on top of, but separate from, the old system, mostly new features. Whether a shutdown is needed depends on your cutover method: with "stop the world," normal business operations stop by definition; with the incremental methods, the old system stays up.

How is data migrated, and is a rollback possible?

In the order expand, migrate, contract: add the new structure, move the consumers, remove the old one last. Name one writer for each piece of data and verify the consistency between old and new before the cutover. A rollback is easy while the old database and the synchronization processes are still running; after they are deleted it is expensive and risky (Microsoft Learn). This is a general description of a method; a plan specific to your database becomes clear in a written quote together with the scope.

Is it possible to leave the old system completely untouched and only put a new layer around it?

If you have no access to the code and the integration points, this is usually the only way; a translation layer is needed to protect the new layer from the old system's concepts. The cost: the old system's problems stay in place. This is our engineering judgment; for access and handover questions, see the takeover article.

Sources

The pages below were opened and read on November 6, 2026. This article is general information. Pages may change over time.