A post the other day on /r/ruby asked about the reasoning behind the recommendation that instance variables be accessed through methods instead of directly throughout code where they are in scope. Most of the responses dealt the fact that this helps make your code more maintainable, which is true, but I think there’s a more significant reason for this: it adds data to your class’s public interface, so it encourages healthy design habits and forces some introspection.

In a carefully designed class, changes to interface are taken seriously. Before you add, remove, or change it, you should ask some questions, the most important (in my opinion) of which are, “Is this method reasonable, given the stated purpose of this class?” and “Is this a method something that users can trust to be there and work properly in future releases of this library?” When you expose an instance variable as a method, you implicitly respond to both of those statements in the affirmative, so think carefully about whether those questions are also true of that instance variable!

When you find yourself in the position that you have an instance variable that shouldn’t be exposed as part of the public API, consider that you might have a class with too much responsibility or an instance variable that you don’t need. Unneeded instance variables often come from doing this:

def my_process
  get_foo
  use_foo_here
  use_foo_again
  return_value
end

def get_foo
  @foo = ObjectLookup.find_by(quality: 'foo')
end

def use_foo_here
  @bar = @foo + 10
end

def use_foo_again
  @baz = @bar + 20
end

def return_value
  @bar > 40 ? @bar : @foo
end

…when something like this is healthier in every way:

def my_process
  foo = get_foo
  bar = plus_10(foo)
  baz = plus_20(bar)
  return_value(foo, baz)
end


def get_foo
  ObjectLookup.find_by(quality: 'foo')
end

def plus_10(obj)
  obj + 10
end

def plus_20(obj)
  obj + 20
end

def return_value(starting, ending)
  ending > 40 ? ending : starting
end

Yes, there are times whenyou want to reuse an expensive query multiple times internally within an object and exposing it to the public API would be inappropriate. That’s fine, just mark that method private. But that should be your last result, not an excuse for extra data. Changes to the public API are an opportunity to question your approach and improve the quality of your code, so face them head on!