Wrap Your Dependencies

July 30, 2015

These days it’s almost impossible to write an application that doesn’t depend on any third party code. Ruby/Rails and Bundler make it extremely easy to include any gem from anywhere on the Internet in your application. Third party gems are a wonderful thing as they allow your application to provide new functionality without you having to write hardly any new code yourself. Less code that you have to write means that there is less code that you have to maintain and (hopefully) less potential bugs in your application.

All of these benefits of using external gems do come with some downsides however. The fact that your are introducing code that you yourself did not write means that you are also slightly relinquishing control.

Let’s imagine that we are writing an application that has the business requirement to post messages to Slack whenever a specific action occurs, such as a new user signing up. Not wanting to write a Slack client ourselves, we search Github and find the popular slack-notifier gem which seems to fit our needs. We include the gem in our Gemfile, run bundle install and start to implement our notification code.

# app/models/user.rb
class User < ActiveRecord::Base

  after_create :post_notification

  def post_notification
    @notifier ||= Slack::Notifier.new(ENV['WEBHOOK_URL'], channel: '#users')
    @notifier.ping "New user #{name}!"
  end

end

The Only Thing Constant

Everything works great right out of the box. Your boss is happy since she can keep tabs on new user sign ups right inside of Slack and you are happy since you didn’t have to write hardly any custom code to make it happen. Your boss is so ecstatic in fact that she asks you to start adding Slack notifications all throughout your application. She wants to know when users login, create widgets, and especially when they delete their account.

… You can probably see where this is going.

After weeks of work adding the ability to post notifications to Slack all throughout your application, you come into work one day and hear the news. Your application has been so successful that Atlassian has taken notice and decided to buy your company! It’s wonderful news… except that you soon realize that Atlassian has their own chat solution, and soon word comes down that everyone must now switch to Hipchat!

Crap. You now have to undo weeks of work, finding and replacing every remnant of Slack client code with a Hipchat equivalent!

There’s A Better Way

Instead of directly instantiating the Slack client in your User model, you could instead use your own ‘notification wrapper’.

# lib/notifier.rb
class Notifier

  def initialize(url, channel)
    @client = Slack::Notifier.new(url, channel: channel)
  end

  def notify(message)
    @client.ping(message)
  end

end

# app/models/user.rb
class User < ActiveRecord::Base

  after_create :post_notification

  def post_notification
    @notifier ||= Notifier.new(ENV['WEBHOOK_URL'], '#users')
    @notifier.notify "New user #{name}!"
  end

end

This approach has multiple advantages over the previous one:

  1. You can easily switch out the implementation of Notifier whenever you need to, like when you are required to switch to Hipchat. Instead of grepping the codebase for every instance of the slack-notifier gem, you just make a change in a single location, the Notifier class.
  2. YOU now control the notification API. If you don’t like the method names or order of parameters that the original author chose, you can define your own.
  3. Want to prevent notifications being sent in development or test environments? Implement a Null Object notifier and swap that with your Notifier class when not running in production.

Part of writing production quality code means being able to plan for change. Try wrapping code that you don’t control yourself in your next project, you may find it a worthwhile exercise. Thanks for reading, let me know what you think in the comments below.

Did you find this content helpful?


Let me send you more stuff like this! Unsubscribe at any time. No spam ever. Period.


Subscribe to MarkPhelps.me

* indicates required

Discussion, links, and tweets

Mark Phelps

I'm a Software Engineer at an awesome company in Durham, NC. I mostly write about Ruby, startups and maybe some Java from time to time.