The AngularJS question always arrives in the same shape. Someone has been asked for a number, the number is large, and the argument that follows is about whether a rewrite or an incremental migration costs fewer engineering hours in total.
That argument is unwinnable, because nobody knows the total. Migration estimates go wrong on the behaviour nobody wrote down, and that is exactly what you cannot measure before you start. Comparing two numbers derived from the same unknown is not a decision; it is a preference dressed as analysis.
The useful question is different, and it has an answer. Not which costs less, but where does the risk sit, and how early do you find out you were wrong?
What you are actually choosing between
A rewrite concentrates learning at the end. The module three other teams depend on, the validation rule that turned out to be load-bearing, the screen far more complicated than its ticket suggested — most of it surfaces during integration and cutover, when the budget is spent and the remaining options are ship it or abandon it. That is why cancelled rewrites are cancelled late: the information that would have changed the plan arrives after the plan can still be changed.
An incremental migration spreads the same learning across the timeline. The undocumented behaviour surfaces one screen at a time, in production, while the rest of the system keeps working. The bad news arrives early and in pieces small enough to respond to. If the second screen takes far longer than the first, you have learned that while you still have most of your budget.
Every other trade-off follows from this one. The rewrite buys a clean result at the price of a long blind period; incremental buys continuous feedback at the price of running two things at once. Neither is free, and neither is universally right — which is what gets lost when the conversation is framed as a total.
When the rewrite is genuinely the right call
There are real cases where coexistence costs more than it saves and a rewrite is simply correct. Four come up often enough to name.
The app is small enough that the blind period is weeks, not quarters. The whole argument for incremental is that a long blind period is dangerous; shorten it enough and the argument stops applying. If a competent team can rebuild the thing before the current roadmap item ships, the coexistence machinery — a shell, a boundary, two build pipelines — costs more than it protects you from. Estimate honestly rather than optimistically.
The domain model is being deliberately replaced. Migration preserves behaviour, which is worth nothing if the business has already decided the behaviour is wrong. When pricing, permissions or the core workflow are being redesigned, a careful port of the existing logic is work commissioned in order to throw away. You are not migrating an application; you are building a different one that happens to replace it.
No tests, no documentation, and nobody left who worked on it. The distinction here is narrow. If the behaviour still matters and nobody can enumerate it, characterise it before anything moves — harder, but honest. A rewrite wins in the narrower case where the code is unowned and the requirements are recoverable from somewhere other than the code: a spec, a regulator's rules, a handful of users who can state what they need. Then incremental migration is mostly an elaborate way of carrying accumulated mess into a new stack.
A hard external cut-off. A platform requirement, an infrastructure end-of-life or a compliance deadline on a fixed date changes the shape of the problem. Incremental earns its overhead by giving you optionality over time; if the date removes that optionality, you are paying for coexistence without collecting what it pays out.
When incremental wins, and why
The conditions that favour the incremental path are equally specific, and most legacy AngularJS estates meet several at once.
- Large surface area. Risk scales with how much has to be right simultaneously on cutover day. Past a certain number of screens, no amount of planning makes a single switch-over safe, because the failure modes interact.
- Revenue flowing through the application today. A rewrite asks the business to accept a period where the thing generating revenue is frozen and its replacement is unproven. Very few businesses actually agree to that, whatever the plan says.
- A roadmap nobody will pause. The rewrite's hidden requirement is a feature freeze. Without one the old application keeps growing while the new one chases it, and parity recedes. If you cannot get the freeze in writing, you do not have a rewrite plan.
- You need to hire against the new stack now. The AngularJS hiring market has effectively closed. Incremental lets a new engineer be productive in the target framework in their first week, on a real screen, rather than waiting for a cutover.
The mechanics of running two frameworks in one page are a separate subject, covered in Angular and React in one app and, architecturally, on our coexistence page.
The costs nobody puts in the estimate
Three expenses never make it into the comparison, and they decide how the project feels to live through.
Two mental models, for the entire duration. Every engineer carries both frameworks in their head for as long as the migration runs. Reviews get slower; onboarding covers twice the ground. This tax is not recoverable — you can only shorten the period you pay it over, which is the argument for sequencing by feature area rather than scattering work across the product.
Two runtimes in the page while both are loaded. Routing keeps most screens on one framework and the legacy bundle shrinks as screens move, so the curve bends downward — but during the overlap you ship weight you otherwise would not. Measure it per screen from the first week. If the number is not falling, the migration is not progressing, however many tickets closed.
The discipline to actually finish. This is the failure mode specific to incremental, and it is quiet. Once the painful screens have moved and the remaining AngularJS is confined to a few admin pages nobody likes, urgency evaporates and the migration stops — leaving you owning both stacks permanently with the benefits of neither. The middle state is a corridor, not a room. Put the last screen on the roadmap with a date before you move the first one, and treat slipping it as a decision rather than a default.
The third option, which is the one being chosen
AngularJS reached end of life at the end of 2021: support ended, security patches ended, and the framework has been unmaintained since. Yet the most common outcome of the rewrite-versus-incremental debate is neither. The decision gets deferred, and deferral is itself a decision — made every quarter, signed by nobody.
Doing nothing is the worst of the three, and it degrades on three axes at once. Hiring gets harder every year, and the engineers who do know the framework are rarely the ones who want to spend a career on it. Security issues in the framework and its dependencies have no upstream fix, so each becomes a bespoke patch you maintain yourself. And the ecosystem keeps moving — build tools, Node versions, browser APIs — so the cost of merely keeping the thing building rises while buying nothing.
Choosing wrong between rewrite and incremental is recoverable. Not choosing has no floor under it.
A checklist you can actually run
Answer these about your own codebase. They are weighted, not scored: one strong yes in the rewrite column can outweigh several weak ones the other way.
- Can a competent team rebuild the whole thing before your next major release? Yes → lean rewrite.
- Is the business deliberately changing the domain model? Yes → lean rewrite; you are not porting that logic anyway.
- Will the business sign a feature freeze, in writing, for the full estimate plus contingency? No → incremental; the rewrite plan assumes something you do not have.
- Does revenue flow through this application today? Yes → lean incremental.
- Can you state the requirements from a source other than the source code? No → characterise the behaviour first, which pushes you incremental.
- Is there a fixed external date everything must be off the old stack by? Yes → lean rewrite; coexistence buys optionality you have already lost.
- Do you need to hire against the new stack within the next two quarters? Yes → lean incremental.
- Has a rewrite of this system already been attempted and abandoned? Yes → strongly lean incremental. Whatever stopped it is still there.
What to do with the answer
If the checklist points at a rewrite, commit properly: get the freeze in writing, capture the current behaviour at the boundaries before deleting anything, and set a checkpoint early enough that cancelling is still an option with budget behind it. A rewrite entered deliberately is a reasonable engineering decision; one entered because it sounded cleaner is the one abandoned halfway.
If it points at incremental, the first move is not code. It is the analysis: what depends on what, where the global stylesheet reaches, and which screen is small enough to prove the boundary on and boring enough that failing in production is survivable. That is most of what our legacy modernization work is, and it is worth doing even if you then decide to rewrite — the map is the same either way.
And if the honest answer is that you do not know yet, that is a reason to look at the codebase, not to wait another quarter. For a second opinion on which way a specific system points — including us telling you it is a rewrite — start a technical conversation.