The problem with instance variables

Lucian Ghinda, who curates the excellent Short Ruby Newsletter, recently wrote about how he prefers using getter methods instead of direct instance variable access in his Ruby classes.

The crux of the argument is that getter methods are safer because if you access an undefined method, Ruby will raise a NoMethodError but if you access an undefined instance variable, Ruby will return nil.

This behaviour allows even the smallest typos in your instance variables to go undetected. When you do detect an issue, it can take a while to work out where it came from because the error is several steps removed from the source.

Despite this drawback, my own preference is to use direct instance variable access anyway.

Instance variables stand out visually — they always start with an @ and they are uniquely highlighted by text editors. This makes it easy to spot raw data access as distinct from calculations or behaviour triggered by method calls.

When we see an instance variable, we know immediately that it has no side effects, no behaviour and essentially no performance cost. That’s not the case when we see a method call, which could trigger some side effect or exercise some expensive calculation.

It’s a small thing, but I’ve come to value this distinction and how quickly I can make this distinction without having to look anywhere else in the code.

As I was thinking about this, I realised if I could automatically guard each instance variable with a defined? check, that would give me the same safety benefits as getter methods.

What I want to do is take code like this:

@name

And convert it to this:

(raise NameError unless defined?(@name); @name)

Note, this new guard is intentionally on one line so the exception points to the correct location in the original source code and it doesn’t shift any subsequent lines.

Introducing Strict Ivars

Over the weekend, I was able to make a Ruby pre-processor that applies these transformations to code loaded from specific paths. I wrapped it all up in a gem called Strict Ivars.

With this gem configured, you can continue using instance variables as normal but with the benefit that it will raise a NameError if you access an undefined variable.

It’s still super experimental but check it out and let me know what you think.