---
title: What a Legacy App Modernization Looks Like
url: https://varstatt.com/jurij/p/what-a-legacy-app-modernization-looks-like
author: Jurij Tokarski
date: 2026-04-10
description: Legacy application modernization services in 6 weeks: feature-by-feature migration, live app throughout, no big-bang rewrite, no frozen branch.
section: Blog (https://varstatt.com/jurij/archive)
tags: retainer-shapes (https://varstatt.com/jurij/c/retainer-shapes)
---

Most legacy application modernization projects fail because they try to do everything at once. The team freezes new features, spends three months on a parallel build, and does a big-bang cutover that either works or doesn't. Usually it partially doesn't.

The shape I use for legacy software modernization is different: six weeks, feature-by-feature, live app running throughout. No frozen branch, no three-month dark period — targeted legacy code modernization that ships piece by piece.

Most application modernization services price the work as a multi-month consulting engagement billed open-ended against a multi-phase SOW. This is the [weekly retainer](/) — $997/week, cancel anytime — applied to a modernization shape. Same scope an enterprise modernization vendor would pitch as a six-month roadmap, compressed to roughly six weeks because the surgical approach removes the parts that take the most time: the freeze, the parallel build, the cutover risk.

## Big-Bang Rewrites Are Where Companies Die

Joel Spolsky wrote ["Things You Should Never Do, Part I"](https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/) in 2000. The thesis: throwing out working code to rewrite it from scratch is "the single worst strategic mistake any software company can make." Netscape did it. They lost three years and the browser war while Microsoft kept shipping. The essay is 25 years old. The lesson hasn't dated.

A legacy codebase is a body of decisions, accidents, and battle-scars that earned its right to exist by serving real users. Many of them encode constraints — a billing edge case, a regulator's email, a migration from three years ago — that nobody on the current team remembers. The "stupid" code is often the only code that handles the real world correctly. A clean-slate rewrite throws all of that away and rediscovers it the hard way, in production.

The real failure mode of a rewrite isn't bad code. The new code is usually fine. The failure is **the freeze**. Three months of stopped feature shipping. Customers feel it. Sales feel it. Competitors feel it. By the time the new version launches, the original problem — slow development, scared engineers — has been replaced by a new version of itself in TypeScript. And the business momentum is gone.

## The Surgery Doctrine: Coexistence Over Replacement

Feature-by-feature surgery while the patient stays awake. The old app keeps running, taking traffic, generating revenue. The new app goes in alongside it, one feature at a time. Both apps share auth and the same backend, so a user signed into one is signed into the other. Each new-app surface has a [one-click fallback to the old behavior](/principles/delivery/feature-flags), live for as long as the transition takes.

[Migrate in business order](/principles/delivery/wip-one), not developer order. New features ship first — they're the carrot that gets users to try the new app. Mirror work comes second. The everyday surfaces come last, when the new app is stable enough to handle them.

When parity finally lands, the cutover itself is anticlimactic — a single flag flip per user, executed lazily on next login. Stability, not features, gates the final cutover. Parity is reached weeks before the cutover completes; the gap is spent on hardening.

## When the Big-Bang Argument Wins (Spoiler: Almost Never)

There are situations where a clean-slate rewrite is the right call. The platform is genuinely abandoned. The runtime is end-of-life (PHP 5, Rails 2, Angular 1) with no incremental upgrade path. The data model is so wrong no schema migration can save it. These cases exist. They are rare.

In every other case the answer is incremental. The codebase is fixable. The team thinks it isn't because they've been told "it works isn't enough" without being told what to do about it. This is the what-to-do-about-it.

## How This Differs From Most Application Modernization Companies

Most application modernization consulting firms sell a discovery phase, then a roadmap phase, then an implementation phase, then a "stabilization" phase. Each one has its own statement of work and its own invoice. The total is six to nine months and a number that needs board approval.

This isn't a fixed-scope engagement at all. It's the [Varstatt retainer](/) — $997/week, cancel anytime through Stripe — applied to a modernization shape. Six weeks is what the work usually takes. Some migrations finish in four. Some need eight because the codebase surfaces a sub-system that's bigger than it looked at week one. Either way, no re-scoping, no new SOW, no upsell — the weekly rate is the same and the cancel button stays one click away.

The trade-off is real: this only works for codebases a single senior engineer can hold in their head. Million-line enterprise systems with five teams touching them need a different shape — one of the bigger application modernization companies is a better fit there. For typical SaaS, internal tools, and product codebases under ~100k lines, the small-shop approach ships faster and costs less than the firm equivalent.

## Signs the Codebase Needs This

The usual signal isn't a crash. It's friction that accumulates: developers avoiding files they're not sure about, [onboarding a new hire taking weeks of oral history](/principles/diligence/documentation), deploys requiring a ritual. The app works, but it's becoming harder to change.

If the original developer has left and you've **inherited a codebase** nobody fully understands, that's the clearest sign. The other case I see more often now: [vibe-coded apps from six months ago are already legacy code](/jurij/p/it-works-isnt-enough-for-commercial) — low confidence to change, no tests, no conventions.

## The Six-Week Shape

$997/week, six weeks. Week one is assessment: I read the codebase, map dependencies, agree a migration order with you. Weeks two through five are migration — one to two features per week, each shipping independently on the [default stack](/principles/delivery/default-stack) (React, TypeScript, proven infra patterns). Week six is handoff: documentation, test coverage, onboarding notes.

The output is a codebase built for the next developer. Clear patterns, consistent conventions, zero tribal knowledge. Before-and-after benchmarks on what matters: build times, deploy confidence, test coverage, onboarding time.

If you're sitting on a codebase that works but costs you more time than it should, the [tech strategy](/discovery/tech-strategy) and [build cost plan](/discovery/build-cost-plan) tools help frame the migration before we talk.

## Other Shapes the Retainer Takes

Same retainer, different shapes — pick the one that matches the work in front of you:

- **[What a Code Audit Looks Like](/jurij/p/what-a-code-audit-looks-like)** — diagnose the codebase first; modernize what the audit surfaces
- **[What a Fractional CTO Engagement Looks Like](/jurij/p/what-a-fractional-cto-engagement-looks-like)** — when the modernization is one chapter of a longer build arc
- **[What a Software Maintenance Retainer Looks Like](/jurij/p/what-a-software-maintenance-retainer-looks-like)** — once the modernization lands and the codebase needs steady-state care
- **[All retainer shapes →](/jurij/c/retainer-shapes)**

## How to Start

The path is the same for every shape:

1. **[Submit a project brief](/brief)** — 2–3 minutes. Within 24 hours, you get an honest read on whether this engagement fits.
2. **15-minute discovery call** — confirm scope and timing, no sales pitch.
3. **Subscribe to the weekly retainer** — work begins the next business day. Cancel anytime through Stripe, no paperwork.

If you have questions before any of that, the [project brief form](/brief) has a free-text field — write whatever you need to.
