Avoiding nil in Ruby programs (and NULL in databases)

Tute Costa November 15, 2019

Most Ruby developers see the following exception in production, no matter how well taken care their codebase is:

1
NoMethodError: undefined method `name' for nil:NilClass

And the search begins. Was it a user? Or another object that would respond to #name if present? Was it a typo? Many different statements can return nil:

1
2
3
4
5
6
session[:blog_pozt]    # => nil
session[:current_user] # => nil
@currentz_uzer         # => nil
array[length + 1]      # => nil
if false then 1 end    # => nil
empty_method()         # => nil

Our preference is to return anything but nil. A quick improvement is to return a symbol! Authentication Ruby gems return nil on current_user by default, but we can do better:

1
2
3
4
5
6
def current_user
  super || :guest_user
end

# Now the error would read:
NoMethodError: undefined method `name' for :guest_user:Symbol

A symbol shows the source of the problem within the (otherwise generic) error message.

See also Rails Refactoring Example: Introduce Null Object.


NOT NULL database constraints, or Rails validations?

Both! Our preference is to add a null: false constraint to any new column in migrations that store required values. The Rails presence validation ensures forms and error messages work as intended, while the DB constraint will ensure that no console session, bug, factory, ourselves when sleepy, or any connection to our DB store invalid data.

Even when the field is not required, a constraint with a default value is better than allowing nil (null: false, default: ""). That way we ensure we always deal with instances of String and not also of NilClass, avoiding type checks and potential bugs.

See also If You Gaze Into nil, nil Gazes Also Into You.


Enjoy!


Tute Costa is a Principal Engineer at Epion Health. He focuses on building infrastructure and architecture for Epion’s Patient Engagement Platform. When not coding, you can find him riding his bike around the small mountains of Córdoba (Argentina).