# Skill Migration Guide — Code Migration & Upgrade Assistant > Pure-markdown AI agent skill for guiding code migrations, detecting breaking changes, and planning safe upgrades across 7 major technologies. ## Metadata - **Name:** skill-migration-guide - **Version:** 1.0.0 - **Category:** code-quality, developer-tools - **Tags:** migration, upgrade, breaking-changes, react, nodejs, python, typescript, nextjs, docker, tailwind - **Trigger Phrases:** "migrate", "upgrade", "breaking changes", "migration guide", "upgrade from X to Y", "deprecated API" - **License:** MIT ## Activation Use this skill when: - A user asks about migrating between versions of a framework, language, or tool - A user encounters deprecated APIs or removed features - A user wants to plan a dependency upgrade - A user needs a cost/risk assessment for a migration - A user wants to check backward compatibility before upgrading ## 5-Phase Migration Framework Every migration follows this structured process: ``` ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ ASSESS │───▶│ PLAN │───▶│ EXECUTE │───▶│ VERIFY │───▶│ CLEAN UP │ │ │ │ │ │ │ │ │ │ │ │ • Audit │ │ • Order │ │ • Apply │ │ • Test │ │ • Remove │ │ • Deps │ │ • Risk │ │ • Test │ │ • Bench │ │ legacy │ │ • Scope │ │ • Steps │ │ • Commit │ │ • Smoke │ │ • Update │ │ • Risk │ │ • Fallback│ │ • Log │ │ • Report │ │ docs │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ ``` ### Phase 1: ASSESS Before touching any code, understand the current state: 1. **Dependency Audit** ```bash # Node.js npm ls --depth=0 2>/dev/null || yarn list --depth=0 npx npm-check-updates # Python pip list --outdated pipdeptree # Check for known vulnerabilities npm audit pip audit # or safety check ``` 2. **Current Version Detection** ```bash node --version && npm --version python --version && pip --version npx tsc --version docker --version && docker compose version ``` 3. **Codebase Scan** — Use the Breaking Change Detection patterns below to identify affected code. 4. **Scope Assessment** - How many files/packages are affected? - Are there third-party plugins that may break? - What is the deployment environment? - Are there CI/CD pipelines that need updating? 5. **Output:** Assessment summary with: - Current versions → target versions - List of affected files and patterns - Risk level (LOW / MEDIUM / HIGH / CRITICAL) - Estimated effort ### Phase 2: PLAN Create a migration plan with ordered steps: 1. **Dependency Ordering** - Upgrade peer dependencies first (TypeScript before React, Node before everything) - Group related upgrades together - Separate major from minor/patch upgrades 2. **Risk Assessment** — Use the Migration Cost Estimator below. 3. **Step-by-Step Plan** Each step should include: - What to change - Commands to run - Files to modify - Tests to run - Rollback procedure 4. **Branching Strategy** ```bash git checkout -b migration/upgrade--v ``` 5. **Rollback Plan** — Document how to revert each step. See Rollback Planning below. ### Phase 3: EXECUTE Systematic application of changes: 1. **Update Dependencies** ```bash # Node.js — use lockfile-aware upgrades npm update @ # or for major versions: npm install @ # Python pip install --upgrade == pip freeze > requirements.txt # or use pip-compile ``` 2. **Apply Code Changes** — Follow the relevant Migration Playbook below. 3. **One change at a time. Commit after each logical step.** ```bash git add -A git commit -m "migration(): " ``` 4. **Run Tests After Each Step** ```bash npm test # or pytest, cargo test, etc. npm run lint # or ruff, eslint, etc. npm run typecheck # or npx tsc --noEmit ``` 5. **If tests fail:** Stop. Investigate. Fix or rollback. Never push broken changes forward. ### Phase 4: VERIFY Comprehensive validation: 1. **Full Test Suite** ```bash npm test -- --coverage ``` 2. **Type Checking** ```bash npx tsc --noEmit --strict ``` 3. **Linting** ```bash npm run lint ``` 4. **Build Verification** ```bash npm run build ``` 5. **Smoke Tests** — Manually verify critical user flows. 6. **Performance Check** — Run benchmarks if available. Some upgrades change performance characteristics. 7. **Generate Migration Report** — Use the Migration Report Generator below. ### Phase 5: CLEAN UP Post-migration housekeeping: 1. **Remove deprecated code** that was kept for backward compatibility during migration. 2. **Update documentation** (README, CHANGELOG, comments). 3. **Update CI/CD** if node versions, docker images, or build steps changed. 4. **Pin new versions** in lockfiles and commit. 5. **Create PR or merge** the migration branch. 6. **Update team** on breaking changes that affect other developers. --- ## Migration Playbooks ### 1. React 17 → 18 → 19 #### 17 → 18 (Major Breaking Changes) | Change | Impact | Action | |--------|--------|--------| | Automatic batching | Behavioral | Review useEffect timing expectations | | New root API | `ReactDOM.render` removed | Replace with `createRoot` | | `flushSync` for sync rendering | New API available | Use where immediate DOM update needed | | Suspense on server | New SSR streaming | Update SSR if used | | Strict mode double-invoke | Dev only | Fix side effects in effects/useMemo | | `unmountComponentAtNode` removed | Breaking | Use `root.unmount()` | | `renderToNodeStream` removed | SSR breaking | Use `renderToPipeableStream` | **Migration Steps:** ```jsx // BEFORE (React 17) import ReactDOM from 'react-dom'; ReactDOM.render(, document.getElementById('root')); // AFTER (React 18) import { createRoot } from 'react-dom/client'; const root = createRoot(document.getElementById('root')); root.render(); ``` ```jsx // BEFORE: Unmounting ReactDOM.unmountComponentAtNode(container); // AFTER: root.unmount(); ``` **Automatic Batching — What to check:** ```jsx // In React 17, these caused 2 re-renders: // In React 18, they're batched into 1 re-render (good, but check timing) function handleClick() { setCount(c => c + 1); setFlag(f => !f); // Any code that relied on the DOM updating between these calls // will now see stale state. Use flushSync if needed. } ``` **New Suspense Features (React 18):** ```jsx // SSR Streaming with Suspense boundaries import { Suspense } from 'react'; function App() { return ( }> ); } ``` **`useId` hook (React 18):** ```jsx import { useId } from 'react'; function Checkbox() { const id = useId(); return ; } ``` #### 18 → 19 (Key Changes) | Change | Impact | Action | |--------|--------|--------| | React Server Components | New paradigm | Opt-in, migrate selectively | | Ref as prop cleanup | `ref` cleanup functions | Add cleanup returns to ref callbacks | | Document metadata | ``, `<meta>` in components | Use built-in metadata support | | Asset references | New asset handling | Update import patterns for assets | | `useFormStatus` | New hook for forms | Replace manual form state | | `useOptimistic` | New hook | Replace manual optimistic updates | | `useActionState` | New hook | Replace reducer patterns for form actions | | Removed: `propTypes` warnings | Cleanup | Remove PropTypes if unused | | Context as provider | `<Context>` as provider | Replace `<Context.Provider>` with `<Context>` | **Migration Steps:** ```jsx // BEFORE (React 18) const ThemeContext = createContext('light'); <ThemeContext.Provider value="dark"><App /></ThemeContext.Provider> // AFTER (React 19) const ThemeContext = createContext('light'); <ThemeContext value="dark"><App /></ThemeContext> ``` ```jsx // BEFORE: ref callback <input ref={(node) => { node.focus(); }} /> // AFTER: ref callback with cleanup <input ref={(node) => { node.focus(); return () => { /* cleanup */ }; }} /> ``` **New Hooks (React 19):** ```jsx import { useActionState, useOptimistic, useFormStatus } from 'react'; // useFormStatus — must be in a child component function SubmitButton() { const { pending } = useFormStatus(); return <button disabled={pending}>{pending ? 'Submitting...' : 'Submit'}</button>; } // useOptimistic function LikeButton({ postId, likes }) { const [optimisticLikes, addOptimisticLike] = useOptimistic( likes, (state, newLike) => state + 1 ); // ... } ``` **Breaking Change Detection Patterns (React):** ``` # Search for removed patterns grep -rn "ReactDOM.render" src/ grep -rn "unmountComponentAtNode" src/ grep -rn "renderToNodeStream" src/ grep -rn "Context\.Provider" src/ grep -rn "propTypes" src/ grep -rn "defaultProps.*function" src/ # defaultProps on function components removed in 18.3+ # Search for potential batching issues grep -rn "setTimeout\|requestAnimationFrame" src/ # May need flushSync grep -rn "flushSync" src/ # Verify still needed after migration ``` --- ### 2. Node.js 16 → 18 → 20 #### 16 → 18 | Change | Impact | Action | |--------|--------|--------| | V8 engine upgrade (10.1 → 10.2) | Performance | Benchmark critical paths | | `fetch` global available | New API | Can remove node-fetch dependency | | `Web Streams` API stable | New API | Consider for stream processing | | `test` runner (stable) | New tool | Consider replacing Jest/Mocha for unit tests | | `.env` file support via `--env-file` | New feature | Can remove dotenv dependency | | `fs.glob` | New API | Use for file globbing | | Permission model (experimental) | Security | Evaluate for production | | `vm.compileFunction` cache | Performance | Update if using vm module | **ESM Migration (Critical for Node 16→18):** ```json // package.json — set type to ESM { "type": "module" } ``` ```js // BEFORE (CJS) const express = require('express'); const path = require('path'); module.exports = { handler }; // AFTER (ESM) import express from 'express'; import path from 'path'; import { fileURLToPath } from 'url'; export { handler }; // Common ESM gotchas: // __dirname → import.meta.dirname (Node 20+) or: const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // require() → dynamic import() // BEFORE: const foo = require('./foo'); // AFTER: const foo = await import('./foo'); ``` **Conditional Exports in package.json:** ```json { "exports": { ".": { "import": "./dist/index.mjs", "require": "./dist/index.cjs", "default": "./dist/index.mjs" } } } ``` #### 18 → 20 | Change | Impact | Action | |--------|--------|--------| | Stable `test` runner | Production ready | Migrate from Jest/Vitest if desired | | `import.meta.dirname` / `.filename` | ESM convenience | Remove fileURLToPath workarounds | | `fs.glob` stable | File operations | Simplify glob operations | | Permission model (stable) | Security | Evaluate | | Single executable apps | Distribution | Consider for CLI tools | | `readableStream.from`, `Readable.fromWeb` | Streams | Modernize stream usage | | `WebSocket` global | New API | Can remove ws dependency | | Performance improvements | ~10-15% faster | Benchmark | **Breaking Change Detection Patterns (Node.js):** ```bash # Check for CJS patterns that need ESM migration grep -rn "require(" src/ --include="*.js" grep -rn "module\.exports" src/ --include="*.js" # Check for __dirname/__filename (ESM incompatible) grep -rn "__dirname\|__filename" src/ # Check for JSON imports (need assert type) grep -rn 'import.*\.json' src/ # Check for native module dependencies (may need rebuild) grep -rn "node-gyp\|prebuild\|napi\|node-addon-api" package.json cat package.json | grep -E "bcrypt|canvas|sharp|sqlite3|node-sass|fsevents|better-sqlite3|cpu-features|esbuild|" # Check Node version in CI/CD grep -rn "node.*16\|node.*18" .github/workflows/ .gitlab-ci.yml Dockerfile docker-compose.yml ``` --- ### 3. Python 3.9 → 3.10 → 3.11 → 3.12 #### 3.9 → 3.10 | Change | Impact | Action | |--------|--------|--------| | Structural Pattern Matching (`match/case`) | New feature | Refactor complex if/elif chains | | `ParamSpec` and `Concatenate` | Type hints | Enhanced generic typing | | `int.bit_count()` | Performance | Replace bin(x).count('1') | | `zip(strict=True)` | Debugging | Use for catching length mismatches | | Error messages improved | DX | Update expected error patterns | | `bisect` now key functions | Performance | Simplify sorted lookups | | `|` for union types | Type hints | Replace `Union[X, Y]` with `X \| Y` | ```python # BEFORE: Union types from typing import Union, Optional def process(data: Union[str, int, None]) -> Optional[dict]: ... # AFTER: Python 3.10+ def process(data: str | int | None) -> dict | None: ... # Structural Pattern Matching def handle_command(cmd: str) -> str: match cmd.split(): case ["quit"]: return "Goodbye" case ["load", filename]: return f"Loading {filename}" case ["move", x, y] if x.isdigit() and y.isdigit(): return f"Moving to ({x}, {y})" case _: return "Unknown command" # zip with strict mode keys = ["a", "b", "c"] values = [1, 2, 3, 4] # This will raise ValueError! combined = dict(zip(keys, values, strict=True)) ``` #### 3.10 → 3.11 | Change | Impact | Action | |--------|--------|--------| | **Up to 60% faster** | Performance | Free performance win | | `ExceptionGroup` and `except*` | Error handling | Migrate grouped exception handling | | `Self` type | Type hints | Use for return-type annotations | | `tomllib` | Stdlib | Remove `tomli` dependency | | `TaskGroup` (asyncio) | Async | Use for structured concurrency | | Type parameter syntax | Type hints | New `class MyClass[T]:` syntax | | `contextlib.chdir()` | File ops | Replace os.chdir patterns | ```python # TOML parsing (no more external dependency) import tomllib # Python 3.11+ with open("pyproject.toml", "rb") as f: config = tomllib.load(f) # TaskGroup for structured concurrency import asyncio async def fetch_all(): async with asyncio.TaskGroup() as tg: task1 = tg.create_task(fetch_users()) task2 = tg.create_task(fetch_orders()) # Both tasks are done here # New type parameter syntax from typing import Self class LinkedList: def copy(self) -> Self: ... # Type parameter syntax from typing import TypeVar # BEFORE: T = TypeVar('T', bound=int) # AFTER: class Stack[T]: def push(self, item: T) -> None: ... def pop(self) -> T: ... ``` #### 3.11 → 3.12 | Change | Impact | Action | |--------|--------|--------| | **Up to 25% faster** (on top of 3.11) | Performance | Another free win | | `type` statement (PEP 695) | Type hints | Replace TypeVar usages | | `f-string` improvements | DX | Nested quotes, comments, expressions | | `buffer` protocol | C extension | Review C extension code | | `subprocess` improvements | CLI tools | Review subprocess usage | | `itertools.batched()` | Utilities | Use for batching | | Better error messages | DX | Update error pattern tests | | `perf` profiler built-in | Profiling | Replace external profilers | ```python # New type parameter syntax (PEP 695) # BEFORE: from typing import TypeVar, Generic T = TypeVar('T') class Box(Generic[T]): def __init__(self, item: T): self.item = item def get(self) -> T: return self.item # AFTER (Python 3.12): class Box[T]: def __init__(self, item: T): self.item = item def get(self) -> T: return self.item # Improved f-strings f"{'hello world'!upper}" # Nested quotes! f"{x=}" # Debug expressions f"result: {x if x > 0 else 0}" # More flexible # itertools.batched from itertools import batched for batch in batched(range(10), 3): print(batch) # (0,1,2), (3,4,5), (6,7,8), (9,) ``` **Breaking Change Detection Patterns (Python):** ```bash # Check for removed/deprecated patterns grep -rn "typing.Union\|typing.Optional\|typing.List\|typing.Dict" src/ # Can use built-in types grep -rn "from __future__ import annotations" src/ # May no longer be needed grep -rn "import tomli" src/ # Replace with tomllib (3.11+) grep -rn "distutils" src/ # Removed in 3.12 grep -rn "imp module\|import imp" src/ # Long removed grep -rn "asyncio.get_event_loop()" src/ # Deprecation warnings in 3.10+ # Check Python version pins grep -rn "python-version\|python_version\|_requires_python" setup.py setup.cfg pyproject.toml ``` --- ### 4. TypeScript 4 → 5 #### Major Changes in TS 5.0 | Change | Impact | Action | |--------|--------|--------| | Decorators (Stage 3) | New feature | Migrate from `experimentalDecorators` to standard decorators | | `const` type parameters | Performance | Use for literal inference | | `--verbatimModuleSyntax` | Config | Replace `--isolatedModules` for ESM/CJS | | `enum` improvements | Config | All enums are union enums by default | | JSDoc `@satisfies` | Documentation | Type-check without widening | | Export type `*` | Imports | Use `export type *` for type-only re-exports | | `--moduleResolution bundler` | Config | For bundler-based projects | #### TS 5.0 → 5.4+ | Change | Impact | Action | |--------|--------|--------| | `NoInfer` utility | Type safety | Prevent unwanted type inference | | `Object.groupBy` / `Map.groupBy` | Utilities | Use for grouping operations | | Variable narrowing in closures | DX | Fewer type assertions needed | | `satisfies` operator (5.0) | Type safety | Use for precise type checking | | `using` / `Symbol.dispose` | Resource management | Use for automatic cleanup | ```typescript // BEFORE (TS 4): Decorators "experimentalDecorators": true @testable class MyClass { } // AFTER (TS 5): Standard decorators (Stage 3) function logged(originalMethod: any, context: ClassMethodDecoratorContext) { // ... } class MyClass { @logged myMethod() { } } // const type parameters function createRoute<const T extends string>(paths: T[]) { return paths; // Inferred as literal types, not string[] } const routes = createRoute(["/home", "/about"]); // readonly ["/home", "/about"] // satisfies operator (TS 4.9+) const config = { port: 3000, host: "localhost", } satisfies Record<string, string | number>; // config.port is typed as number, not string | number // NoInfer function createMachine<T>(options: { states: T[]; initialState: NoInfer<T>; }) { /* ... */ } // grouped const grouped = Object.groupBy(items, item => item.category); ``` **Breaking Change Detection Patterns (TypeScript):** ```bash # Check experimental decorators usage grep -rn "experimentalDecorators" tsconfig*.json # Check module resolution settings grep -rn "moduleResolution" tsconfig*.json # Check for outdated type patterns grep -rn ": any\b" src/ --include="*.ts" --include="*.tsx" # Check tsconfig settings that changed defaults grep -rn "strict\|noImplicitAny\|isolatedModules" tsconfig*.json # Check for enum patterns grep -rn "enum " src/ --include="*.ts" ``` **tsconfig.json Migration:** ```jsonc { // Recommended TS 5.x settings "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "bundler", // or "node16" / "nodenext" "verbatimModuleSyntax": true, // replaces isolatedModules "strict": true, "skipLibCheck": true, "noUncheckedIndexedAccess": true, "noPropertyAccessFromIndexSignature": true // Remove: "experimentalDecorators": true (use standard decorators) } } ``` --- ### 5. Next.js 13 → 14 → 15 #### 13 → 14 | Change | Impact | Action | |--------|--------|--------| | App Router stable | Paradigm shift | Migrate from Pages Router | | Server Actions stable | New API | Replace API routes for mutations | | Turbopack (stable for dev) | Performance | Enable in next.config | | Partial Prerendering (experimental) | Performance | Evaluate for mixed pages | | Metadata API | Config | Replace Head component usage | ```typescript // BEFORE (Pages Router): pages/index.tsx export default function Home({ data }) { return <div>{data}</div>; } export async function getServerSideProps() { const data = await fetchData(); return { props: { data } }; } // AFTER (App Router): app/page.tsx export default async function Home() { const data = await fetchData(); // Direct async component return <div>{data}</div>; } // Server Actions // app/actions.ts 'use server'; export async function createPost(formData: FormData) { const title = formData.get('title'); await db.posts.create({ data: { title } }); revalidatePath('/posts'); } // app/components/NewPost.tsx import { createPost } from '../actions'; export function NewPost() { return ( <form action={createPost}> <input name="title" /> <button type="submit">Create</button> </form> ); } // Metadata API // BEFORE: import Head from 'next/head'; <Head><title>My Page // AFTER: // app/page.tsx export const metadata = { title: 'My Page', description: 'Page description', }; ``` #### 14 → 15 | Change | Impact | Action | |--------|--------|--------| | Turbopack default for dev | Performance | Remove webpack overrides if any | | `next/image` enhancements | DX | Review image optimization | | `after()` API | Caching | Use for post-render caching | | `unstable_*` APIs stabilized | API | Update import paths | | Partial Prerendering (beta) | Performance | Evaluate for production | | Instrumentation hook | Monitoring | Add performance monitoring | **App Router Migration Checklist:** ``` □ Rename pages/ to app/ (or create app/ alongside) □ Convert pages to app/layout.tsx hierarchy □ Convert _app.tsx → app/layout.tsx □ Convert _document.tsx → app/layout.tsx □ Move getServerSideProps → Server Components (async/await) □ Move getStaticProps → Server Components + generateStaticParams □ Move API routes → Route Handlers (app/api/*/route.ts) □ Move middleware.ts → app/middleware.ts □ Update Head → metadata export □ Update next/image → @next/image □ Update next/link → remove nesting □ Update CSS imports → CSS Modules or Tailwind □ Move public/ → public/ (same, verify paths) □ Update environment variable access (NEXT_PUBLIC_ prefix for client) □ Remove type router from useRouter (use App Router router) ``` **Breaking Change Detection Patterns (Next.js):** ```bash # Check for Pages Router patterns find src/ -name "*.tsx" -exec grep -l "getServerSideProps\|getStaticProps\|getInitialProps" {} \; grep -rn "from 'next/head'" src/ grep -rn "from 'next/router'" src/ # Use next/navigation instead grep -rn "from 'next/link'" src/ # Check nesting # Check for old API routes find src/ -path "*/api/*" -name "*.ts" ! -name "route.ts" # Check config files cat next.config.js | grep -E "experimental|webpack|turbopack" ``` --- ### 6. Docker & Docker Compose #### Compose V1 → V2 | Change | Impact | Action | |--------|--------|--------| | `docker-compose` → `docker compose` | CLI | Update all scripts and CI | | YAML spec changes | Config | Review compose file format | | `depends_on` behavior | Startup | Services wait by default | | Profiles | Orchestration | Use for optional services | | GPU support | Config | New deploy.resources.reservations.devices | ```yaml # BEFORE (Compose V1): docker-compose.yml version: "3.8" # No longer needed in V2 services: web: depends_on: - db # V1: just ordering, no health check command: sh -c "wait-for db:5432 && node server.js" # AFTER (Compose V2): docker-compose.yml (no version needed) services: web: depends_on: db: condition: service_healthy # Native health check waiting # No need for wait-for scripts! db: healthcheck: test: ["CMD", "pg_isready", "-U", "postgres"] interval: 5s timeout: 5s retries: 5 ``` #### BuildKit (Default in Modern Docker) ```bash # Enable BuildKit (usually default in Docker 23+) DOCKER_BUILDKIT=1 docker build . # BuildKit features: # 1. Parallel build stages # 2. Better caching (cache mounts, remote cache) # 3. Secret mounting (--secret) # 4. SSH agent forwarding (--ssh) # Remote cache example docker buildx build --cache-to type=gha --cache-from type=gha . ``` #### Multi-Platform Builds ```bash # Setup buildx docker buildx create --use --name multiarch # Build for multiple platforms docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest --push . # Or use docker-bake for complex builds cat > docker-bake.hcl << 'EOF' group "default" { targets = ["app"] } target "app" { platforms = ["linux/amd64", "linux/arm64"] context = "." dockerfile = "Dockerfile" cache-from = ["type=gha"] cache-to = ["type=gha,mode=max"] } EOF docker buildx bake ``` **Breaking Change Detection Patterns (Docker):** ```bash # Check for Compose V1 usage grep -rn "docker-compose" Makefile scripts/ .github/workflows/ .gitlab-ci.yml grep -rn 'version:' docker-compose*.yml # Check for deprecated Dockerfile patterns grep -rn "COPY --from=.*:.*" Dockerfile* # Verify stage names grep -rn "HEALTHCHECK" Dockerfile* # Moved to compose in some cases grep -rn "EXPOSE" Dockerfile* # Review (informational only) grep -rn "volumes:" docker-compose*.yml # Check bind mount syntax grep -rn "links:" docker-compose*.yml # Deprecated, use depends_on + network ``` --- ### 7. Tailwind CSS v3 → v4 #### Major Changes | Change | Impact | Action | |--------|--------|--------| | CSS-first configuration | Config paradigm | Migrate `tailwind.config.js` → CSS `@theme` | | No PostCSS plugin needed | Build | Remove `@tailwindcss/postcss` or update setup | | Automatic content detection | Config | Remove `content` array from config | | New color system | Theming | Update custom color references | | `@apply` behavior changes | CSS | Review `@apply` usage | | `@screen` removed | CSS | Replace with `@media` queries | | Container queries | CSS | New `@container` support | | 3D transforms | Utilities | New `rotate-*`, `perspective-*` utilities | | `@variant` | CSS | Replace `variant:` with `@variant` | | Font size utilities | Breaking | Review responsive font sizes | | `darkMode` auto-detection | Config | Updated to `prefers-color-scheme` by default | ```css /* BEFORE (Tailwind v3): tailwind.config.js */ module.exports = { content: ['./src/**/*.{js,jsx,ts,tsx}'], darkMode: 'class', theme: { extend: { colors: { brand: '#ff6b6b', }, fontFamily: { sans: ['Inter', 'sans-serif'], }, }, }, plugins: [], }; /* AFTER (Tailwind v4): app.css */ @import "tailwindcss"; @theme { --color-brand: #ff6b6b; --font-sans: "Inter", sans-serif; --color-primary: oklch(0.7 0.15 250); --color-secondary: oklch(0.8 0.1 180); } ``` ```html
``` **Breaking Change Detection Patterns (Tailwind):** ```bash # Check for v3-specific patterns grep -rn "bg-opacity-\|text-opacity-\|border-opacity-" src/ grep -rn "@screen " src/ # Removed in v4 grep -rn "tailwind.config" . # Config migration needed grep -rn "darkMode:" tailwind.config.js # New default behavior grep -rn "plugins:" tailwind.config.js # Most plugins may need update grep -rn "@tailwind" postcss.config.js tailwind.config.js # PostCSS setup grep -rn "@apply" src/ # Review each usage grep -rn "purge\|content:" tailwind.config.js # Auto-detection in v4 ``` --- ## Dependency Upgrade Strategy ### Semantic Versioning Rules ``` MAJOR.MINOR.PATCH │ │ └── Backward-compatible bug fixes → Always safe │ └─────── Backward-compatible new features → Usually safe └──────────── Breaking changes → Read changelog, plan migration ``` ### Upgrade Order 1. **Dev dependencies first** (linters, test frameworks, type definitions) 2. **Peer dependencies next** (TypeScript before React, React before React-Router) 3. **Runtime dependencies last** (libraries your app imports) 4. **One major version at a time** (Don't jump from v2 to v5 directly) ### Lockfile Management ```bash # Node.js npm ci # Install from lockfile (CI) npm update # Update within semver range npx npm-check-updates # Check what's available npx npm-check-updates -u # Update package.json (not lockfile) # Python pip install --upgrade pip freeze > requirements.txt # Regenerate # Or with pip-tools: pip-compile requirements.in # Generate lockfile pip-sync requirements.txt # Sync environment # Always commit lockfile changes: git add package-lock.json pnpm-lock.yaml yarn.lock requirements.txt git commit -m "chore: update lockfile after dependency upgrade" ``` ### Testing After Upgrades ``` 1. Run linter → npx eslint src/ (or ruff check .) 2. Run type check → npx tsc --noEmit 3. Run unit tests → npm test (or pytest) 4. Run build → npm run build 5. Run e2e tests → npx playwright test 6. Manual smoke test → Start the app, click around ``` --- ## Backward Compatibility Checker ### Common Breaking Change Patterns ``` Pattern | Technologies | Severity ─────────────────────────────────┼─────────────────────┼───────── Function renamed/removed | All | HIGH Default export changed | JS/TS | HIGH Config file format changed | All | MEDIUM CLI flags renamed | CLI tools | MEDIUM API response shape changed | REST/GraphQL | HIGH Environment variable renamed | All | MEDIUM File/directory structure changed | Frameworks | HIGH Peer dependency version bumped | npm/pip | HIGH CSS class names changed | UI libraries | MEDIUM Behavioral change (implicit) | All | CRITICAL Node.js engine requirement | npm packages | MEDIUM Python version requirement | pip packages | MEDIUM Docker base image removed | Docker | HIGH ``` ### Automated Compatibility Scan ```bash # Node.js project scan echo "=== Dependency Version Check ===" npx npm-check-updates 2>/dev/null || echo "npm-check-updates not available" echo "=== Deprecated API Usage ===" # React grep -rn "ReactDOM.render\|findDOMNode\|defaultProps\|String refs" src/ 2>/dev/null # Node.js grep -rn "require(\|module.exports\|__dirname" src/ 2>/dev/null # General grep -rn "deprecated\|@deprecated\|TODO.*remove\|HACK" src/ 2>/dev/null echo "=== Type Safety ===" npx tsc --noEmit 2>&1 | head -20 echo "=== Outdated Dependencies ===" npm outdated 2>/dev/null | head -20 ``` --- ## Migration Cost Estimator ### Effort Matrix ``` Migration Type | Effort | Risk | Downtime ────────────────────────┼───────────┼──────────┼───────── Patch version upgrade | 0.5-1h | LOW | None Minor version upgrade | 1-4h | LOW-MED | None Major version upgrade | 4h-3d | MEDIUM | Planned Multi-major jump | 1-5d | HIGH | Planned Framework migration | 2-10d | HIGH | Planned Language version bump | 1-3d | MEDIUM | Planned Full stack upgrade | 1-4 weeks | CRITICAL | Staged ``` ### Risk Factors | Factor | Low Risk | Medium Risk | High Risk | |--------|----------|-------------|-----------| | Test coverage | >80% | 40-80% | <40% | | Documentation | Good changelog | Minimal | None | | Breaking changes | <5 | 5-20 | >20 | | Dependencies affected | <10 | 10-50 | >50 | | Team familiarity | Used to upgrades | Some experience | First major upgrade | | Rollback capability | Easy (git) | Possible | Hard (DB schema) | ### Cost Estimation Prompt When a user asks for a migration estimate, provide: 1. **Effort range** (hours/days) based on the matrix above 2. **Risk level** based on the risk factors 3. **Recommended approach** (phased vs big bang) 4. **Prerequisites** (tests, backups, staging environment) 5. **Timeline** with milestones --- ## Rollback Planning ### Pre-Migration Checklist ``` □ Full backup of codebase (git tag: pre-migration--v) □ Full database backup (if applicable) □ All tests passing on current version □ Deployment pipeline green □ Staging environment available □ Team notified of planned maintenance window □ Rollback plan documented and tested □ Monitoring/alerting configured ``` ### Rollback Procedures #### Git-Based Rollback (Recommended) ```bash # If migration goes wrong: git tag pre-migration-react-18 # ... do migration ... # Rollback: git checkout main git reset --hard pre-migration-react-18 git push --force # Only if no one else pushed # Better: revert merge git revert -m 1 ``` #### Database Rollback ```bash # Before migration: pg_dump dbname > backup-$(date +%Y%m%d).sql # After failed migration: psql dbname < backup-20240101.sql ``` #### Docker Rollback ```bash # Pin to specific image version docker tag myapp:latest myapp:backup-$(date +%Y%m%d) docker pull myapp:previous-version docker compose up -d ``` #### npm/pip Rollback ```bash # npm npm install @ npm audit fix # pip pip install == pip freeze > requirements.txt ``` ### Technology-Specific Rollback Notes | Technology | Rollback Difficulty | Notes | |------------|-------------------|-------| | React | Easy | Component-level, can mix old/new | | Node.js | Easy | Version manager (nvm/fnm) | | Python | Easy | Version manager (pyenv) | | TypeScript | Easy | Revert config + reinstall | | Next.js | Medium | App Router changes are structural | | Docker | Easy | Pin image tags | | Tailwind CSS | Medium | CSS changes may cascade | --- ## Testing Strategy ### Pre-Migration Testing ``` 1. Ensure ALL tests pass on current version 2. Add integration tests for critical user flows 3. Snapshot current test coverage: npm test -- --coverage 4. Record baseline performance metrics 5. Test the migration on a feature branch first ``` ### During Migration Testing ``` After each migration step: □ Run linter □ Run type checker □ Run unit tests □ Run build □ Run integration tests □ Quick manual smoke test If any step fails: → Investigate → Fix if trivial (< 15 min) → Rollback if non-trivial → Document the issue ``` ### Post-Migration Testing ``` 1. Full test suite (unit + integration + e2e) 2. Visual regression testing (if UI changes) 3. Performance benchmarking (compare to baseline) 4. Accessibility testing (if HTML/ARIA changes) 5. Cross-browser testing (if browser APIs changed) 6. Load testing (if runtime changed, e.g., Node.js version) 7. Security scan (npm audit / pip audit) ``` ### Test Priority Matrix | Test Type | Priority | When | |-----------|----------|------| | Unit tests | P0 | Every step | | Type checking | P0 | Every step | | Build | P0 | Every step | | Integration tests | P1 | After each phase | | E2E tests | P1 | Before merge | | Performance tests | P2 | After full migration | | Visual regression | P2 | If UI framework changed | | Security scan | P1 | Before merge | --- ## Migration Report Generator After completing a migration, generate a structured report: ### Report Template ```markdown # Migration Report: **Date:** YYYY-MM-DD **Status:** ✅ Success / ⚠️ Partial / ❌ Failed ## Summary - **Scope:** files changed, dependencies updated - **Duration:** - **Risk Level:** - **Test Coverage:** ## Changes Made ### Dependencies Updated | Package | From | To | Type | |---------|------|----|------| | react | 17.0.2 | 18.2.0 | major | ### Code Changes | File | Change | Status | |------|--------|--------| | src/index.tsx | Replace ReactDOM.render with createRoot | ✅ | | src/App.tsx | Update useEffect for batching | ✅ | ### Configuration Changes | File | Change | |------|--------| | tsconfig.json | Updated moduleResolution to bundler | | package.json | Added type: "module" | ## Issues Encountered 1. **Issue:** - **Resolution:** - **Time spent:** ## Test Results - **Before migration:** 142/142 passing, 87% coverage - **After migration:** 145/145 passing, 89% coverage - **New tests added:** 3 (for new APIs) ## Performance Impact - **Build time:** 12s → 8s (33% faster) - **Bundle size:** 245KB → 238KB (3% smaller) - **Runtime:** No measurable change ## Rollback Status - **Rollback tested:** Yes/No - **Rollback time:** - **Data loss risk:** None ## Recommendations 1. 2. 3. ``` ### Generating the Report After completing a migration, review all git commits in the migration branch to auto-populate the report: ```bash # Generate list of changed files git diff --stat main...migration/branch # Generate commit summary git log main...migration/branch --oneline # Check test results npm test -- --coverage 2>&1 | tail -20 # Build time comparison time npm run build ``` --- ## Quick Reference ### One-Liner Migration Checks ```bash # "Should I upgrade this dependency?" npm view versions --json | tail -5 npm view dist-tags # "What changed between these versions?" npm view @ --json 2>/dev/null | grep -E "version|deprecated|peerDependencies" # "Is this package still maintained?" npm view time --json 2>/dev/null | tail -3 ``` ### Emergency Commands ```bash # Last known good state git stash && git checkout main # Rollback a specific package npm install @ # Nuclear option (careful!) rm -rf node_modules package-lock.json && npm install # Python equivalent pip install == ``` --- ## Anti-Patterns to Avoid 1. ❌ **Upgrading everything at once** — One thing at a time, verify each step. 2. ❌ **Skipping tests** — "I'll test it later" = "I'll debug it in production." 3. ❌ **Ignoring peer dependency warnings** — They exist for a reason. 4. ❌ **Force-resolving conflicts** — Understand the conflict before forcing a resolution. 5. ❌ **Not reading the changelog** — Changelogs contain migration guides and breaking changes. 6. ❌ **Forgetting lockfiles** — Always commit lockfiles; they ensure reproducible builds. 7. ❌ **No rollback plan** — Always have a way back. Always. 8. ❌ **Upgrading on Friday** — Monday's problem, your weekend's anxiety. 9. ❌ **Ignoring deprecation warnings** — Today's warning is tomorrow's breaking change. 10. ❌ **Not testing in staging** — Production is not a test environment.