Detailed upgrade paths for major programming language version transitions.
Key Features Gained:
match/case)typing.TypeAlias for explicit type aliaseszip() gets strict parameterbisect and statistics module improvementsBreaking Changes:
distutils deprecated (use setuptools instead)loop parameter removed from most asyncio high-level APIsint has a new bit_count() method (name collision risk)Migration Commands:
# Update pyproject.toml / setup.cfg
python-requires = ">=3.10"
# Run pyupgrade for syntax modernization
pip install pyupgrade
pyupgrade --py310-plus $(fd -e py)
# Check for distutils usage
rg "from distutils" .
rg "import distutils" .
Key Features Gained:
except* syntaxtomllib in standard library (TOML parsing)asyncio.TaskGroup)Self type in typing moduleStrEnum classBreaking Changes:
asyncio.coroutine decorator removedunittest.TestCase.addModuleCleanup behavior changelocale.getdefaultlocale() deprecatedsmtpd module removed (use aiosmtpd)Migration Commands:
pyupgrade --py311-plus $(fd -e py)
# Replace manual TOML parsing
rg "import toml\b" . # replace with: import tomllib
rg "toml\.loads?" . # replace with: tomllib.loads / tomllib.load
# Check for removed modules
rg "import smtpd" .
rg "asyncio\.coroutine" .
Key Features Gained:
class Stack[T]:, def first[T](l: list[T]) -> T:)type statement for type aliases (type Vector = list[float])pathlib.Path.walk() methodasyncio.TaskGroup semantics__buffer__)Breaking Changes:
distutils package removed entirely (was deprecated in 3.10)imp module removed (use importlib)locale.getdefaultlocale() removedunittest method aliases removed (assertEquals etc.)asyncio legacy API removalspkgutil.find_loader() / get_loader() removedsqlite3 default adapters and converters no longer registered by defaultos.popen() and os.spawn*() deprecatedMigration Commands:
pyupgrade --py312-plus $(fd -e py)
# Check for removed modules
rg "import imp\b" . # replace with importlib
rg "from imp " .
rg "import distutils" . # must use setuptools
rg "from distutils" .
# Check for removed unittest aliases
rg "assertEquals|assertNotEquals|assertRegexpMatches" .
# Adopt new type syntax (optional but recommended)
# Old: T = TypeVar('T')
# New: def func[T](x: T) -> T:
Key Features Gained:
--disable-gil build)locals() returns copy with defined semanticsdbm.sqlite3 as default dbm backendargparse deprecations enforced--enable-experimental-jit build)Breaking Changes:
aifc, audioop, cgi, cgitb, chunk, crypt, imghdr, mailcap, msilib, nis, nntplib, ossaudiodev, pipes, sndhdr, spwd, sunau, telnetlib, uu, xdrlib modules removedpathlib.PurePath.is_relative_to() and relative_to() semantics changetyping.io and typing.re namespaces removedlocale.resetlocale() removedMigration Commands:
# Check for removed stdlib modules
rg "import (aifc|audioop|cgi|cgitb|chunk|crypt|imghdr|mailcap|nis|nntplib|ossaudiodev|pipes|sndhdr|spwd|sunau|telnetlib|uu|xdrlib)" .
# For cgi module replacement
rg "import cgi" . # replace with: from urllib.parse import parse_qs
rg "cgi.FieldStorage" . # replace with: manual multipart parsing or framework
# Check for typing namespace changes
rg "typing\.io\." .
rg "typing\.re\." .
# Test free-threaded mode (experimental)
python3.13t script.py # if built with --disable-gil
| From → To | Key Action | Biggest Risk |
|---|---|---|
| 3.9 → 3.10 | Fix distutils usage, adopt pattern matching |
asyncio loop parameter removal |
| 3.10 → 3.11 | Replace toml with tomllib, enjoy speed boost |
smtpd removal |
| 3.11 → 3.12 | Remove distutils/imp, adopt type syntax |
distutils full removal, sqlite3 adapter changes |
| 3.12 → 3.13 | Remove deprecated stdlib modules | Large number of removed stdlib modules |
Key Features Gained:
--experimental-permission)node:test).env file support (--env-file=.env)import.meta.resolve() unflaggedURL.canParse() static methodArrayBuffer.transfer() and resizable optionWebSocket client (experimental)Breaking Changes:
url.parse() may throw on invalid URLs (stricter parsing)fs.read() parameter validation stricterload, resolve) are off-threadhttp.IncomingMessage connected socket timeout default changeMigration Commands:
# Update nvm / fnm
nvm install 20
nvm use 20
# Or update Docker
# FROM node:20-alpine
# Check for url.parse usage (may need URL constructor)
rg "url\.parse\(" .
# Adopt built-in test runner (optional)
# Replace: jest/mocha test files
# With: import { test, describe } from 'node:test';
# Use .env file support
node --env-file=.env app.js
Key Features Gained:
require() for ESM modules (experimental --experimental-require-module)node --watch)glob and globSync in node:fsArray.fromAsync)node:sqlite built-in module (experimental)--run flag for package.json scriptsAbortSignal.any()Breaking Changes:
node:http stricter header validationnode:buffer Blob changesnode:child_process IPC serialization changesnode:dns default resolver changesMigration Commands:
nvm install 22
nvm use 22
# Or update Docker
# FROM node:22-alpine
# Check for incompatible native modules
npm rebuild
# Test ESM/CJS interop if using mixed modules
node --experimental-require-module app.js
# Adopt built-in features
# Replace: glob package → node:fs { glob, globSync }
# Replace: ws package → built-in WebSocket (for client usage)
# Replace: chokidar/nodemon → node --watch
| From → To | Key Action | Biggest Risk |
|---|---|---|
| 18 → 20 | Rebuild native modules, test URL parsing | Stricter URL validation, loader hooks off-thread |
| 20 → 22 | Rebuild native modules, check glibc version | Native module compatibility, header validation |
Key Features Gained:
const type parameters--moduleResolution bundlerextends on multiple config filesenums become union enums--verbatimModuleSyntax (replaces isolatedModules)satisfies operator (introduced in 4.9, now mature)Breaking Changes:
--target ES3 removed--out removed (use --outFile)--noImplicitUseStrict removed--suppressExcessPropertyErrors removed--suppressImplicitAnyIndexErrors removed--prepend in project references removed--moduleResolution node renamed to node10--module value changesMigration Commands:
npm install -D typescript@5
# Check for removed compiler options in tsconfig.json
rg '"target":\s*"ES3"' tsconfig.json
rg '"out":' tsconfig.json
rg '"suppressExcessPropertyErrors"' tsconfig.json
# If using legacy decorators, keep experimentalDecorators flag
# If adopting new decorators, remove experimentalDecorators
# Adopt bundler module resolution
# tsconfig.json: "moduleResolution": "bundler"
| Version | Key Feature |
|---|---|
| 5.1 | Easier implicit return for undefined, unrelated getter/setter types |
| 5.2 | using declarations (explicit resource management), decorator metadata |
| 5.3 | import attribute support, resolution-mode in all module modes |
| 5.4 | NoInfer<T> utility type, Object.groupBy / Map.groupBy types |
| 5.5 | Inferred type predicates, regex syntax checking, isolatedDeclarations |
| 5.6 | Iterator helper methods, --noUncheckedSideEffectImports |
| 5.7 | --rewriteRelativeImportExtensions, --target es2024 |
TypeScript version upgrade approach:
│
├─ Minor version (5.x → 5.y)
│ └─ Generally safe, just update and fix new errors
│ npm install -D typescript@5.y
│ npx tsc --noEmit
│
└─ Major version (4.x → 5.x)
├─ 1. Update tsconfig.json (remove deleted options)
├─ 2. Install typescript@5
├─ 3. Run tsc --noEmit, fix errors
├─ 4. Decide on decorator strategy (legacy vs ECMAScript)
└─ 5. Consider adopting moduleResolution: "bundler"
Key Features Gained:
log/slog structured logging (standard library)slices and maps packages in standard librarymin() and max() built-in functionsclear() built-in for maps and slicesgo.mod toolchain directiveGOTOOLCHAIN environment variable)Breaking Changes:
go.mod now tracks toolchain versionnil pointer dereference in more casesnet/http minor behavior changesMigration Commands:
# Update go.mod
go mod edit -go=1.21
go mod tidy
# Adopt slog for structured logging
rg "log\.Printf|log\.Println" . # candidates for slog migration
# Replace sort.Slice with slices.SortFunc
rg "sort\.Slice\b" . # consider slices.SortFunc
Key Features Gained:
for range over integers (for i := range 10)net/http routing (method + path patterns)math/rand/v2 packagego/version packageslices.ConcatBreaking Changes:
math/rand global functions deterministic without seedMigration Commands:
go mod edit -go=1.22
go mod tidy
# The loop variable change is backward compatible but may fix hidden bugs
# Review goroutine closures in loops that relied on shared variable
# Adopt enhanced routing
# Old: mux.HandleFunc("/users", handler) + manual method check
# New: mux.HandleFunc("GET /users/{id}", handler)
rg "r\.Method ==" . # candidates for enhanced routing
Key Features Gained:
iter.Seq, iter.Seq2) and range over funcunique package (interning/canonicalization)structs package (struct layout control)slices and maps moved from golang.org/x/exp to standard librarylog/slog handlersBreaking Changes:
time.Timer and time.Ticker behavior change (channels drained on Stop/Reset)os/exec LookPath behavior on Windows (security fix)Migration Commands:
go mod edit -go=1.23
go mod tidy
# Replace x/exp/slices and x/exp/maps with standard library versions
rg "golang.org/x/exp/slices" .
rg "golang.org/x/exp/maps" .
# Replace with: "slices" and "maps"
# Check Timer/Ticker usage
rg "\.Stop\(\)" . --glob "*.go" # Review timer stop behavior
rg "\.Reset\(" . --glob "*.go" # Review timer reset behavior
| From → To | Key Action | Biggest Risk |
|---|---|---|
| 1.20 → 1.21 | Update go.mod toolchain, adopt slog | Toolchain directive in go.mod |
| 1.21 → 1.22 | Enjoy loop variable fix, adopt enhanced routing | Loop variable semantics (usually fixes bugs) |
| 1.22 → 1.23 | Replace x/exp packages, adopt iterators | Timer/Ticker behavior change |
-> impl Trait in trait definitionsasync fn directly in trait definitions (no need for async-trait crate)let chains: if let Some(x) = a && let Some(y) = b { ... }gen blocks (experimental): generator-based iterators-> impl Traitunsafe_op_in_unsafe_fn lint: must use unsafe {} blocks inside unsafe fnuse<> syntax#[diagnostic] attribute namespace for custom diagnosticsgen keyword for generatorsmatch and if let| Change | Impact | Fix |
|---|---|---|
unsafe_op_in_unsafe_fn is deny by default |
unsafe fn bodies need explicit unsafe {} blocks |
Wrap unsafe operations in unsafe {} |
| Lifetime capture rules change | -> impl Trait captures all in-scope lifetimes |
Use use<'a> for precise control |
gen is a reserved keyword |
Cannot use gen as identifier |
Rename gen variables/functions |
never type fallback changes |
! type fallback now ! instead of () |
May affect type inference in rare cases |
| Temporary lifetime changes | Temporaries in match scrutinee have shorter lifetime |
Store temporaries in let bindings |
unsafe extern blocks |
extern items implicitly unsafe to reference |
Add safe keyword to safe extern items |
Disallow references to static mut |
&STATIC_MUT is forbidden |
Use addr_of!() / addr_of_mut!() |
# Automatic edition migration
cargo fix --edition
# Update Cargo.toml
# edition = "2024"
# Fix unsafe_op_in_unsafe_fn warnings
cargo clippy --fix -- -W unsafe_op_in_unsafe_fn
# Check for gen keyword conflicts
rg "\bgen\b" src/ --glob "*.rs"
# Remove async-trait crate if adopting native async traits
rg "async.trait" Cargo.toml
rg "#\[async_trait\]" src/
cargo build
cargo test
cargo clippy -- -D warnings
cargo doc --no-deps # check documentation builds
Key Features Gained:
null, false, true as standalone types\Random\Randomizer)SensitiveParameter attributeBreaking Changes:
#[AllowDynamicProperties] or __get/__set)${var} string interpolation deprecated (use {$var})utf8_encode / utf8_decode deprecatedMigration Commands:
# Rector automated fixes
composer require rector/rector --dev
vendor/bin/rector process src --set php82
# Check for dynamic properties
rg "->(\w+)\s*=" src/ --glob "*.php" # review for undeclared properties
# Check deprecated string interpolation
rg '"\$\{' src/ --glob "*.php"
Key Features Gained:
json_validate() function#[\Override] attribute__clone()$class::{$constant})Randomizer::getBytesFromString()mb_str_pad() functionunserialize() error handlingBreaking Changes:
array_sum() and array_product() behavior changesproc_get_status() multiple calls return same resultrange() type checking stricternumber_format() behavior change with negative zeroMigration Commands:
vendor/bin/rector process src --set php83
# Adopt #[Override] attribute on methods
# This catches parent method renames at compile time
# Adopt typed constants
# Old: const STATUS = 'active';
# New: const string STATUS = 'active';
# Use json_validate() instead of json_decode() for validation
rg "json_decode.*json_last_error" src/ --glob "*.php"
Key Features Gained:
public private(set))#[\Deprecated] attributenew without parentheses in chained expressions\Dom\HTMLDocument)ReflectionClass::newLazyProxy())array_find(), array_find_key(), array_any(), array_all()Multibyte functions for trim, ltrim, rtrimrequest_parse_body() for non-POST requestsBreaking Changes:
E_STRICT constant deprecatedsession_set_save_handler() with open/close etc. deprecatedstrtolower() and strtoupper() locale-insensitiveMigration Commands:
vendor/bin/rector process src --set php84
# Adopt property hooks (optional but recommended)
# Old:
# private string $name;
# public function getName(): string { return $this->name; }
# public function setName(string $name): void { $this->name = $name; }
# New:
# public string $name {
# get => $this->name;
# set(string $value) => $this->name = strtolower($value);
# }
# Adopt asymmetric visibility
# public private(set) string $name;
# Check for implicit nullable types
rg "function \w+\([^)]*\w+ \$\w+ = null" src/ --glob "*.php"
| From → To | Key Action | Biggest Risk |
|---|---|---|
| 8.1 → 8.2 | Fix dynamic properties, deprecation warnings | Dynamic properties deprecated |
| 8.2 → 8.3 | Adopt typed constants, #[Override] | array_sum/array_product behavior |
| 8.3 → 8.4 | Adopt property hooks, asymmetric visibility | Implicit nullable deprecation |
Regardless of which language you are upgrading:
[ ] CI matrix includes both old and new versions during transition
[ ] Linter/formatter updated to support new syntax
[ ] IDE / editor language server updated
[ ] Docker base images updated
[ ] Deployment pipeline runtime version updated
[ ] New language features documented in team style guide
[ ] Deprecated API usage eliminated before upgrade
[ ] All tests pass on new version
[ ] Performance benchmarks compared pre/post upgrade
[ ] Third-party dependencies verified compatible