Modernization Strategy
| Field | Value |
|---|---|
| Type | Skill Resource |
| Source | ~/.copilot/skills/architecture/references/discovery/modernization-strategy.md |
| Description | Not specified |
Source Content
Modernization Strategy
How to decide what to do with a legacy system — after discovery, not before. This is phase 7 of the playbook; it only makes sense once the inventory, data model, business-rule catalog, and dependency map exist. Your job is not to invent a strategy — it is to match one (or a sequence of them) to this system, this organization, and this budget.
These strategies are platform-agnostic. The same catalog fits a mainframe COBOL system or an IBM i RPG shop. It fits an end-of-life Java app, an ASP.NET Web Forms site, a VB6 desktop app, or an Oracle Forms system just as well. The platform decoders (ibm-i-as400.md, mainframe-cobol.md, cics-online.md, modern-legacy-platforms.md) tell you how to read each; this file tells you what to do with what you read.
Contents
- The decision flowchart
- Strategy catalog
- Comparing the strategies
- Strategies compose
- Scoring each subsystem
- Data-first migration
- Latency and SLA classification
- The first 30 days
- De-risking the migration
- Anti-patterns
Jack, deployed to a labor department to modernize the unemployment-insurance system, recommended the five-year big-bang rebuild. Three years and two procurement cycles later nothing had shipped, the mainframe still processed every claim, and “modernization” had become a dirty word in the building.
Jill, facing the same kind of mainframe at a motor-vehicle agency, shipped a new frontend in six months — drivers renewing registrations online while the legacy system hummed underneath. Then she migrated the backend one slice at a time, behind the API contract she already had. Same destination; only one of them arrived.
The difference was not talent — it was strategy, and the strategies are a finite, well-known set. Every deployed team eventually lands in that meeting. Leadership is around the table, a system nobody fully understands is on the screen, and someone asks what to do with it. Use this file in three passes: skim the catalog for a shared vocabulary, walk the flowchart for a first answer, then run the discovery in The first 30 days first.
The decision flowchart
flowchart TD Q1{"fas:fa-magnifying-glass System still needed?"} -->|"no"| RETIRE["fas:fa-recycle Retire"] Q1 -->|"yes"| Q2{"fas:fa-magnifying-glass Business logic must change?"}
subgraph Incremental["fas:fa-screwdriver-wrench Modernize incrementally"] Q2 -->|"no — pain at experience layer"| FACADE["fas:fa-desktop New Frontend, Same Backend"] Q2 -->|"yes"| Q3{"fas:fa-magnifying-glass Mature COTS or SaaS market?"} Q3 -->|"no"| Q4{"fas:fa-magnifying-glass Decomposable into slices?"} Q4 -->|"yes"| STRANGLER["fas:fa-circle-check Strangler Fig"] end
subgraph LastResort["fas:fa-triangle-exclamation Replace outright"] Q3 -->|"yes"| REPLACE["fas:fa-box Replace with COTS"] Q4 -->|"no"| Q5{"fas:fa-magnifying-glass Dying hardware, logic fine?"} Q5 -->|"yes"| REHOST["fas:fa-cloud Rehost or Replatform"] Q5 -->|"no"| REBUILD["fas:fa-circle-xmark Rebuild — last resort"] end
style Incremental fill:#eff6ff,stroke:#2563eb,color:#172554 style LastResort fill:#fef2f2,stroke:#dc2626,color:#450a0a style Q1 fill:#fef9c3,stroke:#ca8a04,color:#422006 style Q2 fill:#fef9c3,stroke:#ca8a04,color:#422006 style Q3 fill:#fef9c3,stroke:#ca8a04,color:#422006 style Q4 fill:#fef9c3,stroke:#ca8a04,color:#422006 style Q5 fill:#fef9c3,stroke:#ca8a04,color:#422006 style STRANGLER fill:#dcfce7,stroke:#16a34a,color:#14532d style REBUILD fill:#fee2e2,stroke:#dc2626,color:#450a0aWhat to notice: Rebuild is the last exit, not the first. It sits in the same zone as Replace and Rehost — the paths you reach only after incremental options are ruled out. The first two questions can only be answered with real discovery: usage data and user pain, not the org chart’s opinion.
Strategy catalog
Each entry says what the strategy is, when it earns its place, and — just as important — what stays legacy afterward. No strategy makes the legacy problem vanish; each one moves it somewhere specific.
Rehost (lift and shift)
Move the system as-is onto modern infrastructure — cloud VMs, or an emulation layer for mainframes. Nothing about the application changes: same code, same data model, same screens. It is the fastest move on the board and the least disruptive, because it changes nothing users hate — and nothing they rely on.
- Use it when: the hardware or hosting is the emergency — end-of-life iron, a closing data center, an expiring facility contract — and the application itself is stable.
- Avoid it when: the pain is in the software. Rehosting a system users hate gives you the same hated system with a new invoice.
- Key risks: “we’ll modernize it once it’s in the cloud” becomes a promise nobody funds; cloud licensing and egress costs surprise the budget.
- What stays legacy: everything except the floor it stands on — code, data model, operations, and the talent problem.
Replatform
Rehost plus minimal modernization of the runtime: containerize the application, move the database to a managed service, swap a hand-patched OS for a managed one. Business logic stays untouched — that is the line you do not cross. Think of it as buying operational relief, not a new system.
- Use it when: rehosting alone would carry forward operational pain you can shed cheaply — an unsupported OS, manual failover, backup scripts nobody has tested.
- Avoid it when: “minimal” starts growing. The first “while we’re in there” change to business logic puts you in a rebuild without a rebuild’s budget.
- Key risks: scope creep into logic changes; subtle runtime differences — locale, timezone, character encoding. Test the batch jobs.
- What stays legacy: the business logic and the data model, which is usually where the real modernization debt lives.
Refactor (restructure in place)
Restructure the existing code without changing its behavior or language family — break up a monolithic program, extract shared procedures, add characterization tests. Refactor keeps the accumulated business logic while making it safe to change. It pairs well after a rehost has bought time.
- Use it when: the rules are trusted but the code is unmaintainable, and you need to make it changeable before adding anything new.
- Avoid it when: the platform or language itself is the liability — refactoring EOL code on dying hardware polishes something you still have to leave.
- Key risks: refactoring without characterization tests silently changes behavior; effort disappears into cleanup with no user-visible result.
- What stays legacy: the language and platform — you improved the code’s shape, not its address.
New frontend, same backend (encapsulate)
Build a modern user experience on top of the legacy system, with a facade API between them. The facade is the trick: shape it like the data model you want, not the one you have, and hide the translation behind it. Users get relief now, and the facade becomes a stable contract — so the backend can be swapped later without touching the frontend.
flowchart LR subgraph Today["fas:fa-screwdriver-wrench Current path"] FE["fas:fa-desktop New frontend"] --> FACADE{{"fas:fa-gear Facade API — target data model"}} FACADE --> XLATE["fas:fa-recycle Translation layer"] XLATE --> LEGACY[("fas:fa-database Legacy system and database")] end subgraph Later["fas:fa-cloud Future swap"] NEW["fas:fa-gear Replacement backend"] end FACADE -.->|"same contract"| NEW style Today fill:#eff6ff,stroke:#2563eb,color:#172554 style Later fill:#f5f3ff,stroke:#7c3aed,color:#3b0764 style FE fill:#e8f1fb,stroke:#2f6096,color:#122c42 style FACADE fill:#eaf4ee,stroke:#2f7a52,color:#173926 style XLATE fill:#f6f0e6,stroke:#8a6a2f,color:#3d2f10 style LEGACY fill:#fefce8,stroke:#ca8a04,color:#422006 style NEW fill:#dcfce7,stroke:#16a34a,color:#14532dWhat to notice: the facade speaks the data model you want, so the frontend never learns the legacy one. That is what makes the dashed swap cheap later — the contract, not the screens, is the long-term investment.
Be honest with leadership about the tradeoff: the legacy backend is still there. Its ops burden, license costs, and talent scarcity all remain. This strategy buys user relief and a future exit path — not a smaller legacy footprint.
- Use it when: user pain is acute, the business logic is sound, and you need a visible win while the longer migration gets scoped and funded.
- Avoid it when: the backend itself is the emergency — unpatchable security holes, failing data integrity, a vendor about to switch it off.
- Key risks: the facade quietly adopts legacy shapes (“just this one field”) and stops being an exit path; leadership sees the new screens and forgets the legacy costs are still running.
- What stays legacy: the entire backend — the system, database, batch jobs, licenses, and the two people who know how it works.
Strangler fig
Migrate one capability at a time. A routing layer intercepts traffic: migrated capabilities go to new services, everything else falls through to the legacy system. Each release shrinks the legacy core until it is hollow and can be retired — the strategy is named for the fig that grows around a tree until the tree is gone.
flowchart LR ROUTER{{"fas:fa-gear Routing layer"}} subgraph Migrated["fas:fa-screwdriver-wrench Migrated capabilities"] A["fas:fa-gear New service A"] B["fas:fa-gear New service B"] end subgraph Remaining["fas:fa-triangle-exclamation Legacy core"] LEGACY[("fas:fa-database Legacy core — shrinking")] end ROUTER -->|"migrated: capability A"| A ROUTER -->|"migrated: capability B"| B ROUTER -->|"everything else"| LEGACY LEGACY -.->|"retired when empty"| GONE["fas:fa-circle-check Decommissioned"] style Migrated fill:#eff6ff,stroke:#2563eb,color:#172554 style Remaining fill:#fef2f2,stroke:#dc2626,color:#450a0a style ROUTER fill:#eaf4ee,stroke:#2f7a52,color:#173926 style A fill:#e8f1fb,stroke:#2f6096,color:#122c42 style B fill:#e8f1fb,stroke:#2f6096,color:#122c42 style LEGACY fill:#fefce8,stroke:#ca8a04,color:#422006 style GONE fill:#dcfce7,stroke:#16a34a,color:#14532dWhat to notice: the legacy core only earns its dashed “decommissioned” edge if every slice retires legacy code. A router with no retirement plan is just a second system to operate.
Name the discipline problem out loud: strangler migrations that never finish leave the organization running two systems forever — funding both, staffing both, reconciling both. Every slice must retire legacy code, and the last slice needs a date.
Route every path to the legacy side through an anti-corruption layer. It is an adapter that translates legacy quirks — packed dates, coded flags, EBCDIC text, an undocumented status state machine — so they never reach the new model. Without it, the new system inherits the old data shape and slowly becomes a repaint of the legacy. Glossary: strangler fig, anti-corruption layer.
- Use it when: the system decomposes into capabilities with findable seams, and the organization can sustain a multi-year effort on incremental funding.
- Avoid it when: there are no seams — one giant transaction touching every table — or the system is small enough to replace outright in months.
- Key risks: the two-systems-forever trap; the routing layer growing into a complex system of its own; data ownership split across old and new mid-migration.
- What stays legacy: a shrinking core — or, if discipline slips, a permanent one.
Rebuild (new custom system)
A greenfield replacement: build a new custom system, migrate the data, retire the old one. It is the strategy every organization wants to have chosen and the one that fails most publicly — the multi-year big-bang rewrite is the most famous failure mode in public-sector IT. Treat it as the flowchart does: the option you reach only when nothing else fits.
- Use it when: the business logic must fundamentally change (a new law or program design makes porting the old rules pointless) and no COTS market exists. And only if the system is replaceable in under ~18 months, or the rebuild ships incrementally — real users on real slices, never one cutover.
- Avoid it when: the old logic is sound and the pain sits at the experience or infrastructure layer. That is a facade or a replatform wearing a rebuild’s price tag.
- Key risks: big-bang cutover; requirements archaeology — often the code is the only spec, bugs included; years of spend with nothing in production.
- What stays legacy: nothing, if it ships. Everything, if it does not — plus a burned budget and a leadership team that no longer believes the word “modernization.”
Replace (COTS / SaaS)
Buy instead of build. For commodity functions — payroll, grants management, generic case management — a mature product market usually exists, and a product with thousands of users beats a bespoke system with one. The rule is configuration over customization: the moment you customize COTS beyond configuration, you own a bespoke system at vendor prices — the worst of both worlds.
- Use it when: the function is genuinely commodity and the organization is willing to adapt its process to the product rather than the reverse.
- Avoid it when: the function is the differentiated core — or when the pitch depends on believing the organization’s process is more unique than it is. It almost never is.
- Key risks: vendor lock-in; upgrade paralysis — every customization pins you to an old version and turns each upgrade into a re-implementation; recurring costs that outlive the sponsor who approved them.
- What stays legacy: the data migration is yours either way. And if you paid the vendor to replicate the old process, you bought the old system at new prices.
Retain / retire
Two legitimate recommendations, not cop-outs:
- Retire: decommission a system nobody actually uses. Usage logs, not org charts, tell you which those are.
- Retain: deliberately leave a system alone because changing it costs more than running it, documented with the trigger conditions that would reopen the decision.
You have explicit permission to recommend leaving one alone. Retiring two dead systems and retaining a stable one delivers more value than starting a migration the organization cannot sustain.
- Use it when: usage is near zero (retire), or the system is stable, low-risk, and cheap enough that every alternative costs more than the status quo (retain).
- Avoid it when: “retain” is deferring a real emergency — end-of-life dependencies with no security patches are not “stable.”
- Key risks: retiring a system with one invisible but critical consumer (find the undocumented feeds first); retain decisions with no review date, which quietly become policy forever.
- What stays legacy: for retain, all of it — on purpose, in writing, with a revisit date.
Comparing the strategies
| Strategy | User-facing improvement | Time to first win | Cost | Risk | What stays legacy | Exit path later |
|---|---|---|---|---|---|---|
| Rehost (lift and shift) | None | Months | Low | Low | Everything but the hardware | Replatform or strangler fig |
| Replatform | None | Months | Low to medium | Low | Business logic and data model | Strangler fig |
| Refactor | None (yet) | Months | Medium | Medium | Language and platform | Rebuild or strangler fig |
| New frontend, same backend | High | Months | Medium | Medium | The entire backend | Strangler fig behind the facade |
| Strangler fig | Incremental | Months per slice | High, spread over years | Medium | A shrinking core, until retired | Full retirement |
| Rebuild | High — eventually | Years | High | High | Nothing, if it ships | - |
| Replace (COTS / SaaS) | Medium — the vendor’s UX | Quarters | Medium, recurring | Medium | Your data until migrated; your process debt | Hard — lock-in works against you |
| Retain / retire | None | Immediate for retire | Minimal | Low | Everything (retain) or nothing (retire) | Revisit on trigger events |
Strategies compose
These are moves, not endpoints, and most successful modernizations sequence them. The facade from new frontend, same backend is exactly the contract a strangler fig needs — “frontend now, strangle later” is one plan, not two. Rehost is often step zero, buying time off dying hardware while the real strategy gets funded. And replace-versus-rebuild is rarely all-or-nothing: buy COTS for the commodity functions, build custom only for the truly differentiated core.
A worked sequence for a common case — a legacy system with angry users and a stable rulebook:
- Rehost off the end-of-life hardware. No user-visible change; removes the deadline pressure.
- New frontend, same backend with a facade API in the target data model. Users get relief within months.
- Strangler fig behind the facade, one capability at a time, each slice retiring legacy code.
- Retire the legacy core when the last slice moves. The frontend never notices.
This is Jill’s path from the top of this page: the facade bought her the visible win and the contract, and the strangler finished the job behind it.
Scoring each subsystem
The flowchart gives a first-pass answer for a whole system; this rubric turns it into a defensible, per-subsystem recommendation. Score each subsystem separately, 1–5, on the factors below. The scores do not compute a verdict — they make the tradeoffs visible. Draw subsystem boundaries from the dependency map (see dependency-mapping.md): a subsystem is a cluster of programs and files that share a responsibility.
The scoring factors
- Business value — how much the organization depends on this capability. High means core mission; low means peripheral or reporting-only.
- Rule complexity and volatility — how intricate the logic is and how often it changes. High-volatility custom rules resist COTS; stable rules invite it.
- Data gravity — how hard the data is to move, by volume and coupling. High gravity anchors a subsystem in place and shapes migration order. Glossary: data gravity.
- Coupling — how entangled it is with other subsystems (shared files, data areas, call chains). High coupling raises the cost of any independent move.
- Latency / SLA class — real-time interactive vs batch-tolerant. This drives target architecture; see Latency and SLA classification.
- COTS availability and fit — whether a commercial product covers the capability, and how completely. High fit for a commodity function points at replace.
- Compliance / ATO sensitivity — how much the subsystem touches regulated data or controls a new system must re-certify. High sensitivity slows any move and favors incremental change. Hand the control story to the
ato-readinessskill. - Change risk — the blast radius if this subsystem breaks in production. High risk favors strangler fig and golden-master proof over a big-bang cutover.
The scoring table template
Score every subsystem in one table, then read the pattern of each row — not a single total — to pick the move.
| Subsystem | Biz value | Rule cmplx/volat | Data gravity | Coupling | Latency/SLA | COTS fit | Compliance | Change risk | Candidate move |
|---|---|---|---|---|---|---|---|---|---|
| Payment engine | 5 | 4 | 5 | 4 | 3 (batch) | 2 | 5 | 5 | Rebuild (strangler) |
| GL posting | 3 | 2 | 3 | 2 | 3 (batch) | 5 | 3 | 3 | Replace (COTS) |
| Legacy report writer | 1 | 1 | 2 | 1 | 1 (batch) | 3 | 1 | 1 | Retire |
What to notice: the “Candidate move” column is a proposal, not an output. The row beside it is the justification. The payment engine earns rebuild because high value, high volatility, and high change risk rule out COTS and rehost — so a reviewer challenges the recommendation by challenging a specific score.
Rule of thumb: score patterns to a move
These map the shape of a row to a likely disposition. They are starting points an engineer confirms against the artifacts, never a formula.
| Score pattern | Likely move | Why |
|---|---|---|
| Low business value, low everything | Retire | Deleting it is the cheapest safe move |
| High COTS fit, low rule volatility, commodity function | Replace | The market solved it better than a rebuild would |
| High business value, high rule volatility, low COTS fit | Rebuild | Custom rules worth preserving, no product fits |
| Sound rules, unmaintainable code, right behavior | Refactor | Keep the logic, make it safe to change |
| Acute user pain, sound backend logic | New frontend, same backend | Relief now without touching the rules |
| Any pattern under a hard data-center exit date | Rehost first | Buy time now; refactor or rebuild after |
| High coupling, high change risk, high value | Strangler fig | Cut over incrementally, prove each slice |
What to notice: every rule of thumb names the factor pattern that triggers it, so the recommendation traces back to the numbers rather than being asserted.
Data-first migration
Migrate and model the data before you touch the UI. The extracted data model (from data-model-extraction.md) is the foundation every other layer sits on — get it wrong and every service and screen built above inherits the error.
flowchart LR DATA[1. Model & migrate<br/>the data]:::a --> API[2. Build the API<br/>on the model]:::b API --> UI[3. Build the UI<br/>on the API]:::c UI -.->|"leads to rework<br/>if built first"| DATA classDef a fill:#e5efe6,stroke:#5a8a5f,color:#1e2e1f classDef b fill:#e8eef7,stroke:#5b7aa8,color:#1a2a3a classDef c fill:#f3ecda,stroke:#b08a3a,color:#3a2e14What to notice: the dashed arrow is the anti-pattern — a UI built before the data model is settled forces rework all the way back down the stack. So the data layer leads and the UI comes last. Decode the packed fields and resolve the coded values first, land a clean schema, and validate it against live rows. Then build a stable API over it — one that legacy and new can both bind to during a strangler cutover.
Latency and SLA classification
Sort every function into a latency class early, because the class — not the business domain — drives the target architecture. A real-time lookup and a nightly settlement batch demand opposite designs, and mixing them into one service inherits the stricter cost for the looser need. This feeds phase 7 of the onboarding doc; see onboard-template.md.
| Class | Waits for a user | Latency budget | Target shape | How to detect it |
|---|---|---|---|---|
| Real-time | Yes | Sub-second | Request/response service | Launched by a TRANSID, screen, or HTTP request; user idle until it returns |
| Batch-tolerant | No | Minutes to hours, hard deadline | Scheduled job / queue / pipeline | Launched by SBMJOB/JCL/cron/scheduler; reads and writes datasets or files |
What to notice: the “How to detect it” column ties each class to a concrete signal in the code. A TRANSID, screen, or HTTP handler means real-time. An SBMJOB, a JCL step, or a cron entry means batch. You classify from evidence in the source, not from a guess about how the function feels.
The first 30 days
The flowchart asks questions only discovery can answer, so map the territory before you recommend anything. A recommendation built from the org chart’s description of the system will not survive contact with the system.
- Actual users and their pain: sit with the people who live in the screens, not just the CIO’s office. Their pain decides whether this is a frontend problem or a logic problem.
- Data flows and integrations: find the undocumented nightly batch jobs and feeds. Every legacy system has them, and they fail silently in the middle of a migration.
- Contracts and license expiry dates: an expiring mainframe license or hosting contract may set your deadline for you.
- Procurement runway and funding vehicle: a strategy the organization cannot buy is not a strategy. Know what vehicle would fund each option before you propose it.
- Who really operates the system: often one contractor, or one employee near retirement. Their departure date matters more than any roadmap date.
- What has already been tried and failed: the organization remembers its failed attempts even when the documentation does not. A recommendation that ignores the last failure loses the room in the first meeting.
De-risking the migration
These are the hard lessons of legacy delivery, framed as practice. Every row exists because some project learned it the expensive way.
| Practice | The failure it averts |
|---|---|
| Ship something small and real within the first few months | Multi-year efforts with nothing live; leadership loses faith before the first release |
| Never cut over big-bang | The worst day in IT: old system off, new system broken, no way back |
| Budget data migration as a first-class workstream with its own owner | Discovering late that decades of records do not map to the new schema — it is always the hard part |
| Parallel-run old and new before cutover | Silent calculation differences found by users instead of by the team |
| Prefer modular contracts over one mega-award | A single vendor holding the whole program, with nothing usable if it fails |
| Measure success in user outcomes, not milestones | A project “90% complete” for years while wait times never improve |
Anti-patterns
Name these when you see them — they are easier to stop in a slide deck than in year three.
The big-bang rewrite
Years of build, one cutover weekend, no way back. Detect it early: if the plan’s first user-visible release is more than a year out, you are looking at one.
COTS customized beyond recognition
The product was bought to avoid bespoke code, then customized into bespoke code. Detect it: count the customizations that would break on a vendor upgrade — if that number grows every quarter, you own a custom system at vendor prices.
Pretty screens, no contract
A frontend rewrite with no facade API — new screens scraping the legacy screens or reading its tables directly. It feels like progress and forecloses the exit: the new frontend is now coupled to the data model you were trying to escape.
The strangler that never finishes
A strangler migration with no retirement plan runs two systems forever. Detect it: every slice should name the legacy code it retires — if slices only add and never remove, the fig has stopped strangling.
A technology project, not a service redesign
Scoping the work as “replace the system” instead of “improve the service” replicates the old process in new code — old wait times, new invoice. Detect it: if the success metrics name platforms and milestones but not a single user outcome, the scope is wrong.