Skip to content

Instantly share code, notes, and snippets.

@czeise
Created May 25, 2023 15:02
Show Gist options
  • Save czeise/458ae0ea3277b82fb0f730a208b53e0d to your computer and use it in GitHub Desktop.
Save czeise/458ae0ea3277b82fb0f730a208b53e0d to your computer and use it in GitHub Desktop.
Deprecation Subscriber

Setup for Big Rails Upgrades: bootboot + DeprecationSubscriber

For setting up "big" Rails upgrades that could take a while and still allow for feature work to continue while the upgrade is in progress, at Merchants, we put together a process that combines dual booting with Shopify's bootboot gem along with adding a DeprecationSubscriber as described by Ali in this blog post.

Here I'm sharing the DeprecationSubscriber piece of this. The challenges around this were finding the correct placement/initialization process of the subscriber to ensure that it caught as many deprecations as we could, and including useful information in the local exception output and the Sentry errors.

The benefits are being able to have deprecation warning bubble up in a much more noticeable way as we're working through the Rails upgrade. This includes in tests, and usage in their staging and production environments

This was a joint effort between A.J. Hekman and myself, but my contributions were meaningful enough that it seemed appropriate for this group.

Deprecation Subscriber

# config/initializers/deprecation_subscriber.rb
class DeprecationSubscriber < ActiveSupport::Subscriber
  class UnallowedDeprecation < StandardError
    def initialize(message)
      super("Unallowed deprecation found. Please fix it.\n#{message}")
    end
  end

  attach_to :rails

  # Rails 6.1 deprecations that do not need to be resolved before shipping 6.1
  # Deprecations listed here should be removed before shipping Rails 7
  RAILS_6_1_DEPRECATIONS = [
    "Rendering actions with '.' in the name is deprecated"
  ]

  # Known deprecations that we are going to fix.
  # As deprecations are fixed, they should be removed from this list to prevent regressions.
  ALLOWED_DEPRECATIONS = [
    # Autoloading autoloadable constants will cause a NameError when classic mode
    # is removed.
    # *** In MBC, we need to take another look at this. Several more constants are autoloaded
    # *** than active support. We may need to move some of the initializers to a "load once" path,
    # *** for example.
    "Initialization autoloaded the constants"
  ]

  # append RAILS_6_1_DEPRECATIONS to ALLOWED_DEPRECATIONS
  ALLOWED_DEPRECATIONS.concat(RAILS_6_1_DEPRECATIONS)

  def deprecation(event)
    return if ALLOWED_DEPRECATIONS.any? { |allowed| event.payload[:message].include?(allowed) }
    exception = UnallowedDeprecation.new(event.payload[:message])
    exception.set_backtrace(event.payload[:callstack].map(&:to_s))

    if Rails.env.development? || Rails.env.test?
      raise exception
    else
      Rails.logger.warn("Unallowed deprecation found\n#{event.payload[:message]}")
      if defined?(Sentry) && !ENV["DEPENDENCIES_NEXT"]
        Sentry.capture_exception(exception)
      end
    end
  end
end
# config/environments/*.rb

# Send deprecation notices to subscriber.
REMOVE: config.active_support.deprecation = :log
ADD: config.active_support.deprecation = :notify
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment