Private Is For Humans
A friend told me a tale about a technical job interview from years
ago. The feedback he received from the interviewer was that he had
used private
incorrectly. The code was something like this:
class FizzBuzz
def generate
# ...
end
private
FIZZ = 3
BUZZ = 5
# ...
end
Constants can be made private using private_constant
.
The interviewer had correctly identified that the FIZZ
and BUZZ
constants were still publicly accessible. In Ruby, private
does not
affect constants.
# constants are accessible from outside the class
FizzBuzz::FIZZ #=> 3
Class methods are also not affected by private
, but can be made
private using private_class_method
or class << self
syntax.
Even so, this usage of private
is not “incorrect”. What the
interviewer failed to identify is that private
is for humans.
Let’s look at why, on today’s episode of This Ain’t It, Chief.
The Purpose Of private
Let’s start by looking at the purpose of private
.
In a nutshell, the purpose of private
is to communicate stability
and safety. It discourages developers from using certain bits of code
that are likely to have breaking changes in the future — or perhaps
that have nasty, non-obvious side effects. This helps to prevent bugs
and facilitates refactoring.
Private methods can be accessed with #send
and #instance_exec
.
Private constants can be accessed with #const_get
.
I say it “discourages” instead of “prevents”, because Ruby can not
guarantee that private things are not accessed from outside of the
class. On the contrary, Ruby provides built-in metaprogramming
functionality that makes it trivial to access private things. In
Ruby, private
is more about communication than enforcement1.
How private
Achieves Its Purpose
While writing code, private
communicates information to the
developer in two ways.
private
communicates information programmatically too, via Ruby’s
metaprogramming functionality, but this is a comparatively rare use
case.
Firstly, it acts as a label. Developers can see whether something is private while reading code or documentation. It is a written warning.
Secondly, it causes errors to be raised when private methods/constants are accessed from outside the class. Developers can use these errors as feedback instead of reading code or documentation, while in a REPL (like IRB, Pry, or Byebug), or while writing tests in a TDD-like workflow.
Is It Wrong To Put Constants Under private
?
With all that in mind, is it wrong to put constants under private
,
despite the fact that they will not raise errors when accessed?
I think that this use of private
has achieved its intended purpose.
It communicates to developers that the constant should not be relied
upon, regardless of whether it raises an error when accessed.
To say that it is wrong implies that the only benefit of private
is
enforcement not communication — that it’s pointless unless it
raises an error. That’s not the case. And since the errors are trivial
to bypass, if enforcement is high on your list of priorities then
you’re probably using the wrong programming language.
It is possible for a developer to use one of these pseudo-private constants accidentally. If they guess the constant name without reading the file it comes from, and everything works during testing, then they might be unaware that the constant was intended to be private. But looking at it pragmatically, this scenario is more or less imaginary. Developers don’t write code that way. If I asked a bunch of Rubyists whether they are concerned about the quality of their codebase being negatively affected by the accidental use of constants which were intended to be private, I’m guessing the most common answer would be “… what?”.
So no, it is not incorrect to place constants underneath private
.
It’s good enough for most purposes. And because the theoretical
negative consequences don’t happen in practice, to insist otherwise is
pedantry. Private is for humans, not for computers.
Footnotes
-
Some people are even of the opinion that public methods should be considered to be private in certain circumstances.
In Practical Object-Oriented Design in Ruby, Sandi Metz says:
If the illusion of control is a comfort, feel free to use the keywords. However, many perfectly competent Ruby programmers omit them and instead use comments or a special method naming convention (Ruby on Rails, for example, adds a leading ‘_’ to private methods) to indicate the public and private parts of interfaces. These strategies are perfectly acceptable and sometimes even preferable. They supply information about method stability without imposing visibility restrictions.
TomDoc is another example of this:
Instead of assuming that all classes and methods are intended for public consumption, TomDoc makes the Public API opt-in. To denote that something is public, all you have to do is preface the main description with “Public:”. By forcing you to explicitly state that a class or method is intended for public consumption, a deliberate and thoughtful Public API is automatically constructed that can inform disciplined version changes according to the tenets of Semantic Versioning.
Got questions? Comments? Milk?
Shoot an email to [email protected] or hit me up on Twitter (@tom_dalling).