The Upgrade Playbook: v15 to v22 Without a Rewrite In short: Everything so far has been what changed . This is the plan — a step-by-step order that keeps your app shippable at every point. The single most important rule: you cannot skip major versions. runs the changes for each version in turn. Jumping from 15 to 22 in one command skips seven sets of them. One at a time, every time. Phase 0 — before you touch anything Make your tests trustworthy. This upgrade is a long series of mechanical changes, and the only thing standing between you and a slow leak of broken behaviour is tests you believe. If coverage on important paths is thin, fix that first. It is not a detour. Turn on strict checking , if it is not already on: Do this on your current version. will produce a lot of errors on a large project, most of them real, and you want them fixed before the tools start rewriting the same files. Record where you are. App size, build time, and a performance score on two or three key pages. You will want the comparison — and you will want it for the conversation about whether this was worth doing. Check your libraries. This is the most common thing that blocks an upgrade completely: For each Angular-dependent package, find out which version supports your target. A library with no v22-compatible release is a decision — wait, replace, or take it over — and it is much better made now than four versions in. Phase 1 — one version at a time Between each: run tests, build, click through the app, commit. If something breaks, you know exactly which version did it. What to expect at each step: →16 — hydration becomes available. →17 — the build engine changes. This is the step most likely to surface custom build problems. Budget for it. →18 — mostly smooth. →19 — becomes pointless and is removed. Largest automatic change so far, all mechanical. →20 — signals become final. Warnings appear for and friends. →21 — Karma to Vitest. Zoneless becomes the default for new apps (yours is untouched). →22 — becomes the default. The tool writes onto every existing component. Read that change. Deployable at every step. That is what makes this survivable — if priorities shift halfway, you are on v19 and fine, not stranded. Phase 2 — structure Now the real changes, in the order Chapter 1 set out. Then the manual work: unpick into explicit per-component imports rather than replacing it with a shared list. Done when: returns only the cases you chose to keep. Phase 3 — templates Then the part that matters: Every one is a decision. Lists that reorder, filter, or delete from the middle need a stable id. Lists that are genuinely positional can stay. Done when: no / / outside the cases the tool correctly skipped, and every has been looked at. Phase 4 — signals The longest phase, and the one with the most judgement. Read each tool's skipped list — those are the design questions from Chapter 4. Then, feature by feature: holding screen state → pipe used just to show a value → Data fetching → + -that-sets-it → Keep as RxJS: waiting, cancelling, retrying, live feeds, anything about time Done when: anything that changes after the first draw and drives a template is a signal, an input, or an pipe. This is the prerequisite for Phase 5, so be honest about it. Phase 5 — change detection Only now. Clear the strategies in batches — leaf components first, containers second. Then: Ship one release with still loaded , so a missed case shows up late rather than not at all. Then remove it. Done when: no anywhere, and is undefined in the browser. Phase 6 — the optional wins Not required, but this is where the user-visible payoff is: on heavy content below the fold. Largest return per line in the whole framework. — one line, deletes route subscriptions everywhere. Incremental hydration on server-rendered pages, with on static areas. Signal Forms for new forms. for new complex controls. Estimating this honestly Do not take a phase-by-phase estimate from anyone who has not seen your codebase — including this course. The variables that actually drive the number are yours: How many components , and how much shared base-class inheritance How trustworthy your tests are — this dominates everything else Whether any dependency lacks a compatible release — a hard blocker, not a delay Whether you use custom webpack configuration — no direct equivalent exists How much state is currently in plain properties versus already reactive What is reliably true about the shape , regardless of size: Phase Character --- --- 0 — preparation Short, and skipping it costs more than it saves 1 — version bumps Mostly mechanical; v17's build change is the risky one 2 — standalone Largely automated, plus manual work 3 — templates Automated, plus a manual review 4 — signals Dominates. Genuine design work, not find-and-replace 5 — change detection Bounded, if Phase 4 was done properly Phase 4 is the one to plan around. It resists estimation because it involves real decisions — what stays RxJS, what b…