How to Estimate the Cost of a Legacy Migration
A practical guide for estimating the true cost of migrating a legacy frontend — AngularJS, jQuery or an outdated React — to a modern stack, including the real cost drivers and the budgeting mistakes teams make most often.
Legacy migration is one of the hardest software projects to budget. The code “already works”, so stakeholders expect a rewrite to be cheap and fast — while engineers know that the risky, expensive parts are usually invisible from the outside. A credible estimate is less about guessing a number and more about systematically surfacing what actually drives the cost. This guide walks through the framework we use at WebMriya to estimate frontend legacy migrations, including a worked example with real numbers.
Why migration budgets go wrong
Unlike a greenfield build, a migration is constrained by decisions someone else made years ago. Business logic is often buried inside views, behavior is undocumented, and “edge cases” encode real customer requirements that nobody remembers. The cost is dominated not by re-typing UI, but by reverse-engineering intent and preserving behavior. That is why line-of-code counts alone are misleading.
1. Audit the existing codebase
Every reliable estimate starts with an audit. You cannot price what you have not measured. Catalog the surface area and the health of the code before proposing any number:
- Scope: number of screens/routes, distinct components, and reusable vs one-off UI.
- Framework and version: AngularJS 1.x, jQuery, Backbone, or an outdated React/Angular — each has a different migration path.
- Dependencies: how many third-party libraries are abandoned, and which have no modern equivalent.
- Test coverage: existing automated tests are the single biggest de-risking factor for a migration.
- State and data flow: global mutable state, two-way bindings, and direct DOM manipulation are expensive to untangle.
- Backend coupling: how much presentation logic leaks into templates and how tightly the UI is bound to specific API shapes.
2. Choose a migration strategy
The strategy you pick changes the cost curve more than any other decision. There are two broad options, and the right one depends on team size, release cadence and risk tolerance:
- Big-bang rewrite: rebuild the app in the new stack and switch over once. Lower coordination overhead, but high risk and a long period with no shippable value.
- Incremental (strangler-fig): run old and new side by side, migrating route by route or feature by feature. The pattern was named by Martin Fowler; it needs more scaffolding up front, but you ship continuously and de-risk as you go — usually the better choice for products in active use.
| Criterion | Big-bang rewrite | Incremental (strangler-fig) |
|---|---|---|
| Risk | High — all-or-nothing cutover | Low — verify piece by piece |
| Time to first value | Long — nothing ships until the end | Short — ship continuously |
| Rollback | Hard | Easy — old app stays live |
| Best for | Small apps, hard cutoff dates | Products in active use |
3. Identify the real cost drivers
Two apps with the same number of screens can differ 3–4x in migration cost. These are the factors that actually move the estimate:
- Business logic in the view layer — the more logic lives in templates and controllers, the more you must reverse-engineer.
- State management complexity — two-way data binding and shared mutable state need careful re-modeling in a unidirectional world.
- Design-system gaps — if there is no component library, you are effectively building one alongside the migration.
- Hidden and undocumented features — feature flags, role-specific screens and rarely used flows that only surface in testing.
- Test coverage — low coverage means every migrated screen needs manual regression, which dominates QA cost.
- Browser, device and accessibility requirements — strict support matrices multiply verification effort.
- Backend readiness — migrations often reveal that APIs also need reshaping, which expands scope.
4. Build the estimate
With the audit and cost drivers in hand, estimate bottom-up rather than top-down. Break the app into modules, size each one, and roll them up:
- Estimate per module (or per route), not for the app as a whole — small units are far easier to size accurately.
- Classify each module as simple, medium or complex, and calibrate against one module you migrate first as a reference.
- Add explicit buffers for integration, QA and stabilization — typically 20–35% depending on test coverage.
- Account for parallelization: more engineers shortens the calendar but adds coordination overhead, not linear speedup.
A worked example (with numbers)
To make this concrete, here is a representative example from our work (anonymized). A fintech company came to us with an AngularJS 1.x platform: 40 screens, heavy two-way data binding and just 12% test coverage. Using the module-level framework above — and migrating one reference module first — we estimated 23 engineer-weeks. Actual delivery landed at 26 weeks (+13%), an overrun driven almost entirely by undocumented, role-specific flows that only surfaced during QA.
| Phase | Framework estimate | Actual |
|---|---|---|
| Audit + reference module | 3 weeks | 3 weeks |
| Core screens (28) | 9 weeks | 10 weeks |
| Complex / stateful screens (12) | 7 weeks | 8 weeks |
| QA + stabilization | 4 weeks | 5 weeks |
| Total | 23 weeks | 26 weeks (+13%) |
The overrun stayed small precisely because the estimate was validated against a real reference module before committing — the discipline at the core of our Legacy Modernization practice. For the kind of complex, stateful applications this framework applies to, see our engineering case studies.
5. Common budgeting mistakes
- Estimating from screen counts alone, ignoring hidden logic and state complexity.
- Assuming a rewrite is cheaper than incremental — it usually is not, once risk and lost shipping time are priced in.
- Forgetting non-feature work: build tooling, CI, analytics, SEO and accessibility parity.
- No reference module: pricing the whole app before validating estimates against one real migration.
- Treating QA as an afterthought instead of the largest single line item in a low-coverage codebase.
A quick estimation checklist
- Audit scope, framework, dependencies, tests and state.
- Pick a migration strategy (incremental by default for live products).
- Migrate one reference module and measure actual effort.
- Size the remaining modules relative to the reference.
- Add QA, integration and stabilization buffers.
- Re-estimate after the first milestone with real data.
How WebMriya approaches migrations
We start every legacy modernization with a fixed-scope audit that produces a module-level estimate and a de-risked, incremental migration plan — so you get a defensible budget before committing to the full project. If you are planning a move from AngularJS or jQuery to a modern React stack, get in touch and we will help you turn these factors into a concrete number.
Frequently asked questions
How much does a legacy migration cost for a mid-size project?
There is no flat rate — cost scales with hidden logic, state complexity and test coverage, not screen count alone. A mid-size app (30–50 screens) with reasonable coverage usually falls in a predictable range once a reference module is migrated. The audit exists precisely to turn “it depends” into a defensible number.
Can you migrate without downtime?
Yes. With an incremental (strangler-fig) approach the legacy app stays live while routes are migrated one by one behind the same domain, so users experience no downtime and you can roll back instantly.
How long does a typical migration take?
For a mid-size frontend, expect a few months of incremental delivery rather than one big release. The exact timeline depends on the cost drivers above, which is why we measure a reference module before committing to a schedule.