All posts by Will Bendix

Implementing StrongParams in Complex Rails Applications with the ParamsPatrol Gem

Staying up-to-date with the latest revisions of the libraries and existing frameworks in your Rails deployment can be challenging. Here at Indiegogo, we’ve been using Rails 3.2 for quite some time, and have been looking to upgrade to version 4. Upgrading our version of Rails would give us numerous improvements, but several API changes (including security upgrades) were necessary to make the switch. And with these additions came the potential to introduce breaking changes to our web app. How could we make the changes that we wanted, while minimizing the risk of delivering a broken experience?

When doing any kind of development work, whether releasing a new feature or upgrading a library version, finding ways to mitigate risk is always beneficial. With a large complex change such as this, a great strategy is to break a problem down into smaller portions that can be released independently. This gives several advantages; work can be paralyzed across multiple teams and can be paused more easily if something more pressing occurs.  Additionally, bugs can be more easily correlated to the code that introduced them.

A large difference between Rails 3 and 4 was the way that parameters are whitelisted when making database calls (also known as mass assignment protection). In controllers, it is very common to see a line like this:

But what if we received a request that tried to inject their own parameters? Such as

We need a way to ensure not just any field can be written into a database field, but only the ones we specify. Rails 3 would handle this by stating what fields were allowed in the Campaign model, and it was up to us to whitelist what was OK, and what wasn’t.

In Rails 4, more of this protection is handled at the controller level, delegating less responsibility to the model. Doing this work in isolation would be a great step before our final version upgrade. Luckily, the Rails development team made this easy for us. The mass assignment protection was wrapped in its own separate gem that we could use, appropriately named Strong Parameters. All we needed to do was whitelist the parameters passed in to the controller.

Our first problem arose when we realized we’d need to make these changes to every one of our controllers at once. At the time we had over 30, and some were not so simple. We’d need a way to break this problem down even further. Upon further inspection we found how the gem was loading itself into all the controllers:

Therefore, if we could remove this line we could then just

on each controller on a case by case basis. Then we could change one at a time and slowly roll our changes out.  We then forked the gem and commented out the line. Our teams were able to slowly protect our controllers over the course of several weeks.

As we started pushing our changes we ran into another issue. Some of our controllers were very large and were taking requests from various areas across the site. It wasn’t always clear what places these requests would come from and what parameters they could contain. As careful as we were, sometimes we wouldn’t include a parameter in the white list and cause an exception to be (erroneously) raised. Our teams were quick to fix these issues, but it was breaking functionality. How could we avoid this?

What if we didn’t raise an error, and instead logged when these events happened? The user wouldn’t see any difference in the experience and we could learn what parameters we had missed. Our team went to work on overriding methods on the fork of our gem and sending a signal to our favorite service, Airbrake. After pushing our changes to a new set of controllers we patiently waited. Lo and behold, we received an airbrake error in the first day. Requests were coming in from a new feature that we hadn’t anticipated, yet the feature still worked. Our team could easily log it a bug and continue with their current development work without interruption. Smooth sailing.

It is our assumption that this feature would be useful for other Rails developers as well. Therefore we took our modifications to the StrongParameters gem and made it into our own gem, ParamsPatrol. Please feel free to share with others and make a merge request if you’d like to add any features. Happy upgrading.