A duller knife for the production Rails console

Note: This is a re-write of a post that was lost in a blog migration. Same tool, new content. Yay!

It was a Monday like any other. I’m standing at my desk, drinking coffee, when suddenly a worrying Slack notification pops up

Oh no. I think I just ran User.update(admin: true) instead of user.update(admin: true)

My heart stops, panic sets in.

We were able to recover from this situation. But it was the first time that a production console change bit hard enough to make me question everything.

So post recovery. How do we move on?

Enter - Safeconsole

I had a wild idea. What if we wrapped every console session in a database transaction?

Turns out, with Pry, it was actually quite easy!

Few hooks and a large lib file later, I had it working!

Today

Today we still use the lib file version, even after I pulled the idea out into a gem and open sourced it three years ago. There’s a few more quality of life changes I’ve added, but the basic concept still remains the exact same.

Production console, with the knife dulled a bit.

The basics

gem 'safeconsole'

Then bin/rails safeconsole instead of bin/rails console.

Every session opens inside a transaction. Do the thing, look at the result, leave. Nothing was written.

user = User.find(42)
user.update!(admin: true)
user.reload.admin
# => true

exit
# rolled back. user 42 is not an admin.

If you meant it, say so before you leave:

user.update!(admin: true)

commit
exit

The rest of the commands:

  • commit: set the changes in the transaction to be committed to the database
  • nevermind: set the changes in the transaction to be discarded
  • refresh: start a new transaction, discarding or committing based on your config
  • stats: some stats on the current session
  • commands: print the commands available in your session

There’s a bit of configuration too, app name, session and command timeouts, and a current_env override for when Rails.env says production in a staging box. That’s all in the README.

← All Ramblings