Most runtimes hide deprecation warnings by default, so turn them on in a test run before you upgrade. Python has warning flags, Node.js has deprecation flags, and Gradle has a warning mode. Collect the output, fix what is yours, and fail CI on new warnings.
Why warnings are quiet by default
Deprecation warnings are aimed at developers, not users. Python's documentation says DeprecationWarning is ignored by default unless the code that triggers it is in the main module, and PendingDeprecationWarning, for features that will be deprecated later, is ignored by default. Node.js treats pending deprecations the same way: they are off unless you ask. Django's release process notes that its deprecation warnings are silent by default.
The result is a common trap. A project runs for years with warnings nobody has seen, and the major upgrade turns each one into an error at once.
Turn them on, per ecosystem
Python accepts a warning action on the command line. The documentation lists actions including default, error, always and ignore.
python -W default -m pytest
python -W error::DeprecationWarning -m pytest
Node.js has flags that surface deprecations at different strengths.
node --pending-deprecation app.js
node --trace-deprecation app.js
node --throw-deprecation app.js
The Node documentation describes pending deprecation as an early warning mechanism, trace as printing stack traces for deprecations, and throw as throwing errors for deprecations. Tracing tells you which line, and often which dependency, is calling the old API.
Gradle's upgrade guide tells you to run your build and review deprecation warnings that apply to it, and mentions a warning mode option that prints deprecations to the console.
gradle help --warning-mode=all
Sort the warnings by who owns them
Not every warning is yours to fix. Group them:
| Source | Action |
|---|---|
| Your own code | Fix now, usually a small change |
| A direct dependency | Upgrade that dependency to a release that no longer triggers it |
| A transitive dependency | Check if a newer parent release exists, or raise it upstream |
| A build plugin | Update the plugin before the tool itself |
Gradle's guide makes the plugin point directly: some plugins break on a new version because they use internal APIs that were removed or changed, so update plugins as part of preparation.
Make it stick in CI
A one time cleanup decays. Add a job that runs the test suite with deprecations enabled and records the count. Start by failing only on new warnings in code you changed, then tighten to fail on any warning from your own packages. Keep an allowlist file for third party warnings you cannot fix yet, with an owner and a review date next to each line.
Review the allowlist when you plan each upgrade. An entry that has sat there for a year is a sign the dependency may need replacing, not patching.
What warnings do not tell you
A clean run shows you what the code exercised. Code paths without tests can still call removed APIs. Pair the warning run with a static check where your platform offers one, and with a staging soak before production.
Quick check
Should I fail the build on every deprecation warning?
Not on day one. Start with new warnings from your own code, then widen as the backlog shrinks.
Do deprecation warnings mean a security problem?
Not by themselves. They mean the API will change or disappear. Check advisories separately.
Safeguard scans dependencies for known vulnerabilities and uses reachability analysis to show which ones your code actually calls. Try the free scan at gold.safeguard.sh/scan.
Sources
- Python warnings module documentation
- Python command line documentation
- Node.js command line options
- Gradle upgrade guide
- Django release process
Last reviewed: 19 October 2026.