Test a runtime upgrade in five stages: rehearse on the old runtime with warnings on, run your tests on the new runtime with the same lockfile, refresh dependencies, canary in production, and rehearse rollback. Move on only when each stage meets a written exit criterion.
Why stages
A runtime change touches your code, your dependencies and your build at once. If you change everything and something fails, you have three suspects. Staging isolates the cause.
The five stages
Stage 1: rehearse on the old runtime. Turn on deprecation warnings and read them. Python can raise deprecations as errors, Java provides tools such as jdeps and jdeprscan, and .NET documents breaking changes by version. This gives a list of work before any switch.
Exit: warnings triaged, with each one fixed or ticketed.
Stage 2: new runtime, same lockfile. Change only the runtime version. Run the full test suite.
Exit: tests pass, or failures are traced to a named runtime change.
Stage 3: refresh dependencies. Update packages that need a newer release to support the runtime. Run the tests again.
Exit: tests pass and the lockfile diff has been reviewed. Check new or changed packages, not just version bumps.
Stage 4: canary. Send a slice of production traffic to the new runtime. Watch error rate, latency, memory and startup time.
Exit: metrics match the baseline for an agreed period.
Stage 5: rollback rehearsal. Practice switching back. Keep the old image tag for the rollback window.
Exit: rollback completes within the time you set, without data changes that block it.
What to compare
| Signal | Why |
|---|---|
| Test results | Behavior changes |
| Build warnings | Future removals |
| Startup and memory | Runtime internals change |
| Dependency findings before and after | The upgrade should not add risk |
| Container image contents | Base image changes bring new packages |
Security specific checks
- Re-scan the application and image after the upgrade and compare findings with the previous scan.
- Check that no flag was added to re-open something the new runtime closed. For example, Java's
add-openswidens access that Java 17 restricted. - Confirm the runtime is on a supported line. Support rules differ by runtime.
- Record the version and date as evidence, since auditors and customers ask for it.
Common failure patterns
Native modules not rebuilt, encoding defaults that changed, build scripts that assumed an old tool path, and CI images that still carry the old runtime next to the new one. Search for the old version string across the repository when you finish.
Try it yourself
The fix available now demo shows a finding with an upgrade ready. Use it to picture the before and after scans that bracket your upgrade.
Click through it at your own pace, or open it full screen.
Safeguard can scan before and after an upgrade and open fix pull requests, which are off by default and need your approval. The free scan is at gold.safeguard.sh/scan.
Quick check
Should I upgrade the runtime and dependencies in one pull request?
Separate pull requests are easier to debug. Change the runtime first, then dependencies.
How long should a canary run?
Long enough to cover your normal traffic cycle, including peak hours and scheduled jobs.
Related: How to plan a runtime upgrade before end of life.
Last reviewed: 19 October 2026.