Welcome to Giant Robots Smashing Into Other Giant Robots — a weblog about development, business, design and technology — written by thoughtbot.
save bang your head, active record will drive you mad
So in my never-ending quest to remove conditional logic from code, I began writing my Rails actions like:
1 2 3 4 5 6 7 8 |
def create @user = User.new params[:user] @user.save! redirect_to user_path(@user) rescue ActiveRecord::RecordNotSaved flash[:notice] = 'Unable to create user' render :action => :new end |
Instead of the traditional way using conditional logic like:
1 2 3 4 5 6 7 8 9 |
def create @user = User.new params[:user] if @user.save redirect_to user_path(@user) else flash[:notice] = 'Unable to create user' render :action => :new end end |
I did this until one of my co-workers saw this and said:
exceptions should not be expected
What?
When writing an application you expect invalid input from users. Since we expect invalid input we should NOT be handling it via exceptions because exceptions should only be used for unexpected situations.
Well what’s an unexpected situation?
- Losing a connection to your database.
- Running out of memory
- Some obscure IO/socket error
Now I’m not going to write error handling for those unexpected circumstances because I don’t expect them to happen. If they do then I want the user to see an error page (500) and the developers to be notified via email about the exception (using exception notifier in Rails) (if i cared then yes I’d have to write error handling code but trying to recover from situations such as an out of memory is not going to be easy, if at all possible, so I want my application to fail in such a rare situation).
Let’s look at an example of reading from a file from the pickaxe.
1 2 3 4 5 |
File.open('testfile') do |file| while line = file.gets puts line end end |
IO#gets returns a String or nil. That is correct because reaching the end of the file is NOT an unexpected situation and should be handled with conditional logic.
What if ruby raised some EOFError instead:
1 2 3 4 5 6 7 |
File.open('testfile') do |file| while line = file.gets puts line end rescue EOFError return end |
Raising an EOFError would mean reaching the end of the file would be unexpected. That’s crazy talk.
I think ActiveRecord::Base#save! and ActiveRecord::Base.update_attributes! should be pulled from the public API. All they do is save the object and raise an exception if any validation fails. I doubt there are any web applications out there that do NOT expect invalid input. So there is no point in using #save! and #update_attributes! over their true/false equivalents #save and #update_attributes.
However, there is one case where they both have to be used. In transactions. Apparently the only way to roll back a transaction is to raise an exception. I say there should be some #rollback method so you could do something like:
1 2 3 4 |
User.transaction do user.save || rollback another_user.save || rollback end |
Following this convention we could also return the ’!’ back to its original meaning of ‘modifying the receiver’. Rails, with its #save! and #update_attributes!, has pushed the ’!’ to mean ‘something dangerous’ in order to justify the exception raising of methods like #save! and #update_attributes!.
Another Ruby idiom that is an abuse of exceptions is something along the lines of:
1 2 3 |
user.address.street rescue '' # or user.address.street rescue nil |
Code like this says that an address is optional for a user. Since a user without an address is not an exceptional situation it should be handled with conditional logic instead of exceptions.
1 2 3 |
if ! user.address.nil? user.address.street end |
I can’t stand that ‘rescue nil’ hack. Quit being lazy and write the ‘if’ statement.
About this entry
You're reading an entry on GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS, the company weblog of thoughtbot, inc.
- Author:
- Jared Carroll
- Published:
- September 25th 11:23 PM
- Updated:
- September 30th 09:57 AM
- Sections:
- Development
thoughtbot is hiring
We are hiring web developers and web designers in both Boston and New York, NY.
What are we up to?
We built Shoulda, an eclectic set of additions to Test::Unit; Paperclip to manage uploaded files without hassle; Jester, a REST/ActiveResource client library written in Javascript, and Squirrel, an enhancement for ActiveRecord's find syntax; — amongst some other projects.

Chad (President) and Jon (CTO) co-authored a technical book titled Pro Active Record: Databases with Ruby and Rails, which explores the ins and outs of the ActiveRecord ruby library. You can buy it today at Amazon.com.
About thoughtbot, inc.
We are a small web application development consulting business, with offices in Boston, MA and New York, NY. If you're looking to find a team for your next web development project or your new web application — get in touch.
19 comments
Jump to comment form