ariejan de vroom

Rails: Prevent Accidental Debugging Commits

15 October 2014

Your Rails app has grown over time, multiple developers have worked on the code and you are about to make a small change in the code.

Unfortunately this change applies to code that allows you to view a financial overview of last year’s data. It’s legally not allowed to expose the current year’s data, but the change you need to make applies only to 2014. We should shift time to 2015. Problem?

There are several things you could do.

  1. Change your system date to 2015. This has the downside that many apps get confused, including, most likely, your backup tools.
  2. Use a gem like TimeCop to fake time. This works great for tests, but it feels a bit like cheating in development.
  3. Change @financial_year = 1.year.ago to @financial_year = 2014.

Side note: This feature gets tested by both unit and integration tests using TimeCop. The problem here is viewing and testing the change in my browser, prototyping the change and showing it to the customer.

Option 3 seems like the easiest. In the scope of the financial overview, let’s pretend we’re always viewing data for 2014.

The real issue here is that you forget you made this change and commit it. Although there are several process in place to detect this: peer-review, QA-testing and custom acceptance. There still is a chance this change makes it to production. Whoops!

Because this is Rails there are lots of awesome goodies around, like rake notes. It finds comments that start with OPTIMIZE, FIXME or TODO and lists them for you. It’s not unlike what many IDE’s do you as well.

Instead of this change:

- @financial_year = 1.year.ago
+ @financial_year = 2014

I could make a change like this:

- @financial_year = 1.year.ago
+ # TODO: Change this back to 1.year.ago!!!
+ @financial_year = 2014

Then, before committing, I should run rake notes or rake notes:todo and check if there’s anything that needs to be reverted back. But I’m a programmer, dammit. I can automate this! I should automate this!

Using a combination of a git pre-commit hook and a custom annotation using Rails’ SourceAnnotationExtractor, the core of rake notes, it should be possible to simply reject a commit. Rejecting a commit for pending TODO’s doesn’t seem useful. Instead, let’s use a custom annotation: NOCOMMIT. The code change is like this:

- @financial_year = 1.year.ago
+ # NOCOMMIT: Change this back to 1.year.ago!!!
+ @financial_year = 2014

And with the following in .git/hooks/pre-commit:


nocommit_notes=$(ANNOTATION=NOCOMMIT rake notes:custom)

if [[ -n "$nocommit_notes" ]]; then
    echo "Error: You have NOCOMMIT notes in your code."
    echo "$nocommit_notes"
    exit 1

Using the NOCOMMIT tag will prevent you from accidentally committing changes you do not want committed. This is an extension to other practices, like interactively staging your changes and reviewing what goes into a commit, code reviews and acceptance testing. It’s yet another safe guard agains my own stupidity.

Check out railties/lib/rails/tasks/annotations.rake for details on the rake notes task.