Caution on the pavement

Goh Rhy Yan

Use Active Support in Rails for deprecation messages

When authors of open source libraries change major functionality they often adhere to something called semantic versioning. At its simplest you can think of it as a rule meaning features aren’t removed until a major version is released.

Before any big changes it’s a good idea to send messages to users of the software in a harmless, but visible, way. This is often done via the logs output when the code runs.

You might have seen these when your tests run or in your live application logs.

blah/blah/file.rb:34: warning: constant ::Fixnum is deprecated

Although in our private codebases our changes do not impact hundreds of other unseen developers, it’s still a good idea to communicate with your team when functionality changes.

The Rails framework goes through this enough that it has a standard depreciation approach baked into Active Support, and in our code we can piggyback off the feature they use to deprecate internal features.

Instead of…

…just logging:

class Coffee
  def make
    put_instant_in_cup
    add_hot_water
    puts "We should #make_properly with an Aeropress"
  end

  def make_properly
    grind_on_medium_fine
    add_to_inverted_aeropress
    sleep(37)
    invert_and_apply_pressure
  end
  # ...
end

Use…

…Active Support’s deprecation library.

class Coffee
  def make
    put_instant_in_cup
    add_hot_water
    ActiveSupport::Deprecation.warn(
      "We should #make_properly with an Aeropress")
  end

  def make_properly
    grind_on_medium_fine
    add_to_inverted_aeropress
    sleep(37)
    invert_and_apply_pressure
  end
  # ...
end

When you or one of your co-workers calls Coffee.new.make, you’ll see…

DEPRECATION WARNING: We should #make_properly with an Aeropress
(called from coffee.rb:4)

… in your logs.

Why?

First, instant coffee is horrible and you just shouldn’t.

Second, this is a relatively lightweight way to ‘soft protect’ old ways of doing things in favour of new code.

This is useful in two ways.

One is as a way to discover places in the code that use your existing method. It might be called in places you don’t expect.

Another is to prevent your coworkers from adding new uses of the deprecated method since, when they attempt to, they’ll see the deprecation message.

Another way to use Rails’ deprecation functionality is within scopes. You’d do this when you want to maintain existing logic, but discourage further use.

class TakeawayCoffeeCup < ApplicationRecord
  scope :good_choice, -> {
    ActiveSupport::Deprecation.warn("Be more ecologically minded")
    where(material: "cardboard")
  }
  scope :eco_choice, -> {
    where(brand: "keepcup", reusable: true)
  }
end

Why not?

If you’re working on your own, or in the early, “messy”, stages of an application, this is probably overkill.

In a non-Rails app, importing the whole of Active Support might also be a lot of library for a sliver of its functionality.

The principle of deprecation over time is still a good idea though, if you’re working with other folks.

Last updated on October 20th, 2019