Making traps for future mistakes

I love to raise ArgumentErrors, TypeErrors, etc. whenever an API is misused. It prevents a lot of bugs. Recently I thought of another use for exceptions.

Traps for future mistakes

Suppose you are a developer of a gem and a method needs to be deprecated. Gem has version 0.1.3 and the method needs to warn about deprecation until version 0.2 and to be removed in version 0.2. It could be marked with TODO for removal in 0.2. Do you think that TODO will ensure timed removal? I don't.

I use exceptions to trap myself so I cannot do such mistakes.

raise 'remove deprecation' if Algebrick.version >= Gem::Version.new('0.2')

def +(block)
  warn 'a_matcher +-> {} is deprecated, it\'ll be removed in 0.2'
  self - block
end

If I use this I cannot forget about the deprecation removal. The gem will simply error when I bump version to 0.2 telling me what I've forgotten to fix.

Another examples

  • Monkey patch - When writing a monkey patch, a monkey patch should error when patched gem goes outside of version range which was tested to work with the patch.

    range = Gem::Version.new('0.1') ... Gem::Version.new('0.3')
    range.cover? Gem::Version.new(a_patched_gem_version) or
        raise "monkey eats only banana version #{range}"
    
  • Internal API usage - Suppose a code is tied with an internal methods of Rails 3.0 and it's known that an upgrade to 3.2 is planned at some point. An error should be thrown when Rails are updated.

    Gem::Version.new(Rails.version) < Gem::Version.new('3.1') or
        raise 'revisit api usage'
    

Caution

Checks like these should be done in class or module scope to be evaluated only once when loading. They should not be placed inside methods.