Migration Plan: Angular 15 → Angular 20 (Tanuki Shop)
## Overview
This issue tracks the complete migration of the **Tanuki Shop** application from **Angular 15.2** to **Angular 20**. The migration spans 5 major versions (15 → 16 → 17 → 18 → 19 → 20) and requires significant architectural changes, including the shift from NgModules to standalone components, new control flow syntax, signal-based reactivity, and the new build system.
### Current State Analysis
| Area | Current | Target |
|---|---|---|
| Angular | 15.2.x | 20.x |
| Angular Material | 15.2.9 | 20.x |
| TypeScript | 4.9.4 | 5.8+ |
| Zone.js | 0.12.0 | Remove (optional zoneless) |
| RxJS | 7.8.x | 7.8+ |
| Node.js | Unknown | 20.x / 22.x |
| Build system | `@angular-devkit/build-angular:browser` | `@angular/build:application` (esbuild) |
| Test runner | Karma + Jasmine | Vitest / Web Test Runner |
### Project Inventory
- **11 components**: AppComponent, NavbarComponent, SearchResultComponent, LoginComponent, RegisterComponent, BasketComponent, ProductDetailsComponent, ContactComponent, AboutComponent, PaymentComponent, OrderCompletionComponent
- **3 services**: BasketService, ProductService, UserService (all `providedIn: 'root'`)
- **2 models**: Product, BasketItem
- **1 NgModule** (`AppModule`) with 1 routing module (`AppRoutingModule`)
- **13 Angular Material modules** imported individually
- **Patterns used**: `BehaviorSubject`, `Observable`, constructor-based DI, `ngOnInit`, template-driven + reactive forms, `styleUrls` (plural)
---
## Phase 1: Preparation & Angular 16 Update
> **Goal**: Establish a safe baseline and reach Angular 16.
### 1.1 Pre-migration setup
- [ ] Create a dedicated `migration/angular-20` branch
- [ ] Ensure CI pipeline is green on the current codebase
- [ ] Pin Node.js version to 18.x (minimum for Angular 16+)
- [ ] Update `mise.toml` with the correct Node.js version
### 1.2 Update to Angular 16
- [ ] Run `ng update @angular/core@16 @angular/cli@16`
- [ ] Run `ng update @angular/material@16 @angular/cdk@16`
- [ ] Update TypeScript to `~5.1.0`
- [ ] Update `zone.js` to `~0.13.0`
- [ ] Resolve any breaking changes flagged by `ng update`
- [ ] Verify build and test pass
### 1.3 Angular 16 adoption tasks
- [ ] Begin converting components to **standalone** (available as developer preview in 16)
- [ ] Replace `styleUrls` with `styleUrl` (singular) across all 11 components
- [ ] Update `tsconfig.json`: remove `useDefineForClassFields: false` (no longer needed)
---
## Phase 2: Angular 17 - New Control Flow & Build System
> **Goal**: Adopt the new template syntax and esbuild-based builder.
### 2.1 Update to Angular 17
- [ ] Run `ng update @angular/core@17 @angular/cli@17`
- [ ] Run `ng update @angular/material@17 @angular/cdk@17`
- [ ] Update TypeScript to `~5.2.0`
- [ ] Update `zone.js` to `~0.14.0`
### 2.2 Migrate to standalone components
- [ ] Run `ng generate @angular/core:standalone` schematic to automate conversion
- [ ] Convert all 11 components to `standalone: true`
- [ ] Move Material module imports into each component's `imports` array
- [ ] Remove `AppModule` (`app.module.ts`)
- [ ] Remove `AppRoutingModule` - move routes to `app.routes.ts`
- [ ] Update `main.ts` to use `bootstrapApplication()` with `provideRouter()`, `provideAnimationsAsync()`
### 2.3 Migrate to new control flow syntax
- [ ] Run `ng generate @angular/core:control-flow` schematic
- [ ] Replace `*ngIf` → `@if` / `@else` in all templates
- [ ] Replace `*ngFor` → `@for` (with required `track` expression) in all templates
- [ ] Replace `[ngSwitch]` → `@switch` if used
- [ ] Remove `CommonModule` / `NgIf` / `NgFor` imports (no longer needed with built-in control flow)
### 2.4 Switch to the new build system
- [ ] Change `angular.json` builder from `@angular-devkit/build-angular:browser` to `@angular-devkit/build-angular:application`
- [ ] Replace `main` with `browser` entry point in build options
- [ ] Remove deprecated options: `buildOptimizer`, `vendorChunk`, `namedChunks`
- [ ] Update `browserTarget` → `buildTarget` in serve/extract-i18n configs
- [ ] Verify build output and dev server work correctly
---
## Phase 3: Angular 18 - Signals & Zoneless Prep
> **Goal**: Adopt signal-based reactivity and prepare for zoneless operation.
### 3.1 Update to Angular 18
- [ ] Run `ng update @angular/core@18 @angular/cli@18`
- [ ] Run `ng update @angular/material@18 @angular/cdk@18`
- [ ] Update TypeScript to `~5.4.0`
### 3.2 Adopt Angular Signals
- [ ] Refactor `NavbarComponent`: replace `itemCount` property with `signal()`, replace `isLoggedIn$` / `currentUser$` Observables with `toSignal()`
- [ ] Refactor `SearchResultComponent`: convert `products` and `filteredProducts` to `signal()`
- [ ] Refactor `BasketComponent`: convert `items` to use `toSignal()` from basket service
- [ ] Refactor `ProductDetailsComponent`: convert `product` and `quantity` to `signal()`
- [ ] Refactor `LoginComponent`, `RegisterComponent`: convert form fields to `signal()` where appropriate
- [ ] Refactor `PaymentComponent`: convert form fields to `signal()`
- [ ] Refactor `ContactComponent`: convert form fields to `signal()`
- [ ] Consider converting `BasketService` and `ProductService` internal state from `BehaviorSubject` to `signal()` + `computed()`
### 3.3 Adopt new DI patterns
- [ ] Replace constructor-based injection with `inject()` function in all 11 components and 3 services
- [ ] Replace `ActivatedRoute` snapshot usage with `input()` for route params (e.g., `ProductDetailsComponent`)
### 3.4 Prepare for zoneless
- [ ] Add `provideExperimentalZonelessChangeDetection()` to `app.config.ts` (experimental in 18)
- [ ] Test all components for proper change detection without Zone.js
- [ ] Fix any components that rely on implicit Zone.js change detection
---
## Phase 4: Angular 19 - Stabilization
> **Goal**: Adopt stable APIs from Angular 19 and finalize modern patterns.
### 4.1 Update to Angular 19
- [ ] Run `ng update @angular/core@19 @angular/cli@19`
- [ ] Run `ng update @angular/material@19 @angular/cdk@19`
- [ ] Update TypeScript to `~5.6.0`
### 4.2 Finalize standalone & signals
- [ ] `standalone: true` is now the default - remove explicit `standalone: true` from all component decorators
- [ ] Ensure all `input()` / `output()` signal-based APIs are used where applicable
- [ ] Replace any remaining `@Input()` / `@Output()` decorators with signal-based equivalents
- [ ] Adopt `linkedSignal()` for two-way derived state if applicable
- [ ] Adopt `resource()` API for async data loading if applicable
### 4.3 Migrate to stable zoneless
- [ ] Replace `provideExperimentalZonelessChangeDetection()` with `provideZonelessChangeDetection()` (stable in 19)
- [ ] Remove `zone.js` from `package.json` dependencies
- [ ] Remove `zone.js` from `polyfills` in `angular.json`
### 4.4 Update test infrastructure
- [ ] Migrate from Karma to **Web Test Runner** or **Vitest**
- [ ] Remove `karma.conf.js`, `karma` and related devDependencies
- [ ] Update `angular.json` test configuration
- [ ] Ensure all existing tests pass with the new runner
### 4.5 Angular Material updates
- [ ] Migrate to Material 3 (M3) theming if not already done
- [ ] Replace `@angular/material/prebuilt-themes/indigo-pink.css` with M3 theme
- [ ] Update any deprecated Material component APIs
---
## Phase 5: Angular 20 - Final Migration
> **Goal**: Complete the migration to Angular 20.
### 5.1 Update to Angular 20
- [ ] Run `ng update @angular/core@20 @angular/cli@20`
- [ ] Run `ng update @angular/material@20 @angular/cdk@20`
- [ ] Update TypeScript to `~5.8.0`
- [ ] Update Node.js to 20.x or 22.x
### 5.2 Adopt Angular 20 features
- [ ] Adopt stabilized APIs introduced in Angular 20
- [ ] Review and adopt any new template syntax or compiler improvements
- [ ] Ensure `angular.json` uses `@angular/build:application` builder (final form)
- [ ] Review and apply any new performance optimizations (incremental hydration, etc.)
### 5.3 Final cleanup
- [ ] Remove all deprecated API usage flagged by the Angular compiler
- [ ] Remove `experimentalDecorators` from `tsconfig.json` (no longer needed)
- [ ] Remove `downlevelIteration` from `tsconfig.json`
- [ ] Update `moduleResolution` from `"node"` to `"bundler"` in `tsconfig.json`
- [ ] Clean up any remaining legacy imports
- [ ] Update `README.md` with new development instructions
---
## Phase 6: Validation & Rollout
### 6.1 Quality assurance
- [ ] Full regression test of all 9 routes/pages
- [ ] Verify basket flow: add to cart → update quantity → checkout → payment → order completion
- [ ] Verify auth flow: register → login → logout
- [ ] Verify search functionality
- [ ] Verify Angular Material components render correctly with M3 theme
- [ ] Performance audit: compare bundle sizes before/after migration
- [ ] Lighthouse audit for performance, accessibility, best practices
### 6.2 CI/CD updates
- [ ] Update CI pipeline Node.js version
- [ ] Update any Docker images used in CI
- [ ] Verify build and deploy pipeline works end-to-end
### 6.3 Documentation
- [ ] Update `README.md` with new prerequisites (Node.js, npm versions)
- [ ] Document any breaking changes for contributors
---
## Key Risks & Mitigations
| Risk | Impact | Mitigation |
|---|---|---|
| Angular Material breaking changes across 5 versions | High | Migrate Material one version at a time; use `ng update` schematics |
| Template syntax migration errors | Medium | Use official `ng generate` schematics for control flow migration |
| Zone.js removal breaks change detection | Medium | Test incrementally; keep Zone.js as fallback until fully validated |
| Build system migration breaks CI | Medium | Run old and new builders in parallel during transition |
| TypeScript version jumps (4.9 → 5.8) | Low | TypeScript upgrades are generally backward-compatible |
## References
- [Angular Update Guide](https://angular.dev/update-guide)
- [Angular Signals](https://angular.dev/guide/signals)
- [Standalone Migration Guide](https://angular.dev/reference/migrations/standalone)
- [Control Flow Migration](https://angular.dev/reference/migrations/control-flow)
- [Angular Material M3 Theming](https://material.angular.io/guide/theming)
issue