In Ruby 3.x and 4.0, why are deprecation warnings hidden by default, and how do you surface them with -W:deprecated or Warning[:deprecated]?
answer
- off by default since 2.7.2
- Warning[:deprecated] is false
- -W:deprecated or -w
- RUBYOPT for the whole suite
- Warning.extend to raise in CI
basics
~20 sSince Ruby 2.7.2, deprecation warnings are off by default so end users are not flooded by library internals. Developers turn them on with ruby -W:deprecated, -w, RUBYOPT or Warning[:deprecated] = true, ideally in tests and CI.
solid answer
~40 sRuby has warning **categories**, and `Warning[:deprecated]` is `false` by default: since 2.7.2 deprecation warnings are hidden so that people running an app or CLI are not flooded by warnings about code they did not write. That makes them a developer's job. Turn them on with `ruby -W:deprecated` (just that category), `-w` (verbose mode, which also enables deprecations), `RUBYOPT="-W:deprecated"` for every Ruby process a command starts, or `Warning[:deprecated] = true` at the top of the test helper. To make them fail CI, `Warning.extend` a module whose `warn(message, category: nil, **kwargs)` raises when `category == :deprecated` and calls `super` otherwise. Your own library can emit them with `warn "...", category: :deprecated`, which prints only when the category is on.
code
bash · 5 linesruby -W:deprecated app.rb # only deprecation warnings
ruby -w app.rb # verbose mode, deprecations included
RUBYOPT="-W:deprecated" bundle exec rake test # every child ruby process too
ruby -e 'p Warning[:deprecated]' # false
ruby -W:deprecated -e 'p Warning[:deprecated]' # truego deeper
Recall that deprecation warnings are hidden by default and that ruby -W:deprecated or -w shows them.
Explain the category model: Warning[:deprecated] defaults to false, -W:deprecated flips one category, -w turns on verbose mode, RUBYOPT applies to child processes.
Make deprecations actionable: enable them in the test helper, extend Warning to raise in CI with an allow-list for third-party noise, and fix them on the current Ruby before upgrading.
Set a fleet rule that CI runs with deprecations failing the build, so upgrades are paid for continuously rather than discovered at the next version jump.
## Warning categories Ruby sorts many of its own warnings into **categories**, and each category has an on/off switch readable with `Warning[category]` and settable with `Warning[category] = flag`. `Warning.categories` (added in 3.4) lists them; Ruby 4.0 has `:deprecated`, `:experimental`, `:performance` and `:strict_unused_block`. The defaults with no flags are: | Category | Default | Typical content | |---|---|---| | `:deprecated` | `false` | APIs and behaviours scheduled for removal | | `:experimental` | `true` | experimental features such as Ractor | | `:performance` | `false` | patterns that defeat optimisations | An unknown name is an error, not `false`: `Warning[:deprecation]` raises `ArgumentError` ("unknown category"), because the category is `:deprecated`. ## Why deprecations are hidden by default Deprecation warnings used to print by default, and Ruby 2.7's keyword-argument transition showed the cost: applications and command-line tools sprayed warnings about code inside their dependencies at people who could do nothing about them. **Since Ruby 2.7.2 they are off by default**, and the 3.0 release notes repeat the rule. The consequence is that nobody sees them unless a developer asks, and many teams discover a removal only when an upgrade turns a silent deprecation into an error. The chilled-string warning, about mutating a string literal that a future Ruby will freeze, is in this category too, so it stays invisible until you opt in. ## Turning them on Pick the switch that matches the scope you need: - `ruby -W:deprecated app.rb` - enables only deprecation warnings. `-W:no-deprecated` turns them off again. - `ruby -w app.rb` - verbose mode: sets `$VERBOSE` to `true` and enables all the default categories, including deprecations, plus many other warnings. - `RUBYOPT="-W:deprecated" bundle exec rake test` - applies to every Ruby process the command starts, including child processes, without editing code. - `Warning[:deprecated] = true` - from Ruby code, typically as the first line of `test_helper.rb` or `spec_helper.rb`, so every test run shows them. `-W0` goes the other way: it silences warnings entirely, including `Kernel#warn` output, so it hides exactly what you are looking for. ## Making deprecations fail the build Printing is not enough in a large suite; people stop reading. Ruby routes every warning through **`Warning.warn`**, and its documentation says to customise it by extending the module, not by redefining the instance method: ```ruby module FailOnDeprecation def warn(message, category: nil, **kwargs) raise message if category == :deprecated super end end Warning[:deprecated] = true Warning.extend(FailOnDeprecation) ``` Calling `super` keeps normal printing for everything else. A common refinement is an allow-list of messages from gems you cannot fix yet, so the rule applies to your own code first. ## Emitting your own deprecations Library code should use the same channel: `warn "Payroll::Report#total is deprecated; use #gross", category: :deprecated, uplevel: 1`. `Kernel#warn` with a category prints only when that category is enabled, so your users get the same opt-in behaviour as Ruby's own warnings, and `uplevel: 1` points the message at the caller's line. ## A workflow for a noisy suite Turning the category on in an old codebase can print hundreds of lines. A sequence that keeps the work manageable: 1. **Collect.** Enable `Warning[:deprecated]` in the test helper and log every distinct message with its file and line. 2. **Sort by owner.** Split messages coming from your own code from those coming from installed gems; the path in each message tells you which. 3. **Fix your own code first.** These are usually mechanical: a renamed method, a literal that is mutated, a removed argument form. 4. **Upgrade or report gems.** Most gem warnings disappear with a newer gem release; for the rest, open an issue upstream. 5. **Flip to failing.** Once the list is short, extend `Warning` to raise on `:deprecated`, with an allow-list for the remaining third-party messages, and shrink that list over time. ## Where this sits in an upgrade Before moving a service to the next Ruby, run its suite on the **current** version with deprecation warnings on and fix what appears. Most removals in the 3.x line were deprecated first, so this is the cheapest early-warning system an upgrade has. Warnings about libraries leaving the default gems are a separate mechanism: they are printed through plain `Kernel#warn`, with no category, so they appear even without `-W:deprecated`.
- Why is overriding Warning.warn preferred to setting $VERBOSE = true in tests?Setting `$VERBOSE = true` in code turns on verbose-only warnings, which is noisy, still only prints, and does not even enable the separate `:deprecated` category that `-w` also switches on. Extending `Warning` with a `warn(message, category: nil, **kwargs)` method lets you act per category, for example raising on `:deprecated` while calling `super` for the rest, so deprecations fail the build and other output stays as it was.
- How should a gem announce that one of its own methods is deprecated?Call `warn "... is deprecated; use ...", category: :deprecated, uplevel: 1`. With a category, `Kernel#warn` prints only when that category is enabled, so the gem's users get the same opt-in behaviour as Ruby's own deprecations, and `uplevel: 1` reports the caller's file and line instead of the gem's.
- Are the "no longer part of the default gems" messages deprecation warnings?No. RubyGems prints them through plain `Kernel#warn` without a category, so they appear with default settings and do not depend on `Warning[:deprecated]`. Only `-W0`, which silences all warnings, hides them.
saying these in an interview costs you the question
- Ruby prints deprecation warnings by default, so a quiet run means no deprecations
- -W0 is the flag that shows every warning
- Warning[:deprecated] = :error makes deprecations raise
- Warning[:deprecation] simply returns false
- Redefining the instance method Warning#warn is the documented way to filter warnings