Rule #4 - Be Explicit
Rule #4 of The Programmer's Code
By
Steve on
Wednesday, January 16, 2013
Updated
Friday, April 22, 2016
Viewed
21,299 times. (
0 times today.)
When I was 16 years old I wrecked my car.
It was a cold, Missouri morning when I climbed into the driver's seat. The windows
were all glazed with ice and I couldn't see a thing.
I scraped the front windshield and waited for a small spot to defrost.
I decided to forgo scraping the back windows because, well, because I was going to
be driving forwards rather than backwards. Mostly, anyway.
My car was parked nose first in front of the barn door. I would have to back up
fifty feet before turning around.
Impatient as I was, I decided to get started even though the back windshield was
still glazed over. I opened my driver side door, poked my head outside, looked back
and pressed the accelerator with confidence.
About one second later the door caught the dirt embankment and buckled like a knee
bending backwards. The door was nearly ripped off the hinges before I could stop
the car.
I was distraught. I had just spent $3000 on this car only one month earlier. For
a sixteen year old kid who had been saving for years, that was a lot of money.
The car was perfect. And now--now, it's wrecked. Would the door even shut? Could
I still drive it?
The body metal near the hinges was splayed and torn every which way. The door could
still swing on the hinges, but there was too much buckled metal to open and close
all the way. Eventually, I managed to pound it flat enough the door could open and
close.
I was very disappointed. With no more money to get the door fixed and wanting to
avoid having to explain to a body shop what I had done, I drove that car for several
years with that disfigured door.
When I did finally get the courage and the capital to fix the door, I first found
a replacement door from a junkyard. It was in good shape, no rust, dents or dings.
The only problem was that it was green and my car was red.
So I drove my car, with the replacement door, to a local body shop. Here I was,
this fresh faced, skinny kid asking them for an estimate and hoping I could afford
it.
I told them that I wanted them to paint the door so it matched the rest of the car.
When I got the quote I was surprised by how inexpensive the paint job would be.
It was all I could afford, but I had expected something out of my price range. So
I confirmed. "You will match the existing car color?" They said they would do their
best.
When I got my car back I had a dark red car with a much brighter red door. It stuck
out like a sore thumb.
They tried to tell me that the door would fade in time and everything would be perfect.
I wasn't buying it. I was upset and told them so. They shrugged their shoulders
and said they did the best they could. I continued to complain, but they quickly
tired of me and demanded I pay up if I wanted the the car back. I felt helpless.
What could I do? They claimed they did their best. Clearly they were lying.
Reluctantly, I paid them and drove a two-tone car for the next few years. I liked
my damaged door better.
Everything is a Contract
So why would I tell you this story of misfortune? Because I learned a very valuable
lesson that day. A lesson that became especially important later in life when I
starting writing software for a living. It's a lesson I'm going to pass along to
you.
What I learned is that I would have to be more explicit about exactly what I wanted.
If I was to have any hope of ensuring positive outcomes I would need to make absolutely
certain that all parties in any contract or agreement understood clearly and precisely
what was expected and that I had solid proof of their agreement.
You would think after that revelation that I would have become an attorney. But
no. The law is no match for software. Indeed, every line and every interaction in
software is a contract. There are big contracts where one program calls another
one via a sophisticated API--both programs are successful when they agree on how
they will communicate.
And then there are small contracts such as setting the value of an integer variable
to 6. Your line of code succeeds when the 6, an integer, is assigned to an integer
variable. If you try to assign a string, such as "6", there is no meeting of the
minds, and your contract is broken. Immediately.
Each of these software contracts is critically important for several reasons.
First, computers can't think. It is both good and bad that computers can't think.
As human beings we know how easy it is for us to read into what someone says and
to think that we understand when we really don't. Your computer is not capable of
making sensible assumptions. On the other hand, your computer doesn't even try
to make assumptions most of the time and so it just breaks. If your contract is
not very, very clear...if there is any room for ambiguity...your program just breaks.
Second, programming contracts are important because computers are disturbingly
precise. While lawyers can argue for months or years over the language
in a property contract, computers do not have that luxury. An army of lawyers
could argue about whether "6" is really an integer or a string, but the computer
will simply fail. It says it's a string so it's a string.
The third reason you need precise contracts in your software is because you will
forget. Imagine a software API that allowed you to call a method with a parameter
that could be any of the following:
- 6
- "6"
- "six"
- "half a dozen"
- "a few"
That flexibility is not in itself bad. But what if one place you used the number
6 and then somewhere else called with the phrase "half a dozen." You might find
yourself debugging the code a few months on asking if there was some reason you
did it differently in the two places. Is it possible that the API responds differently
to those two inputs? Will it respond with 12 or "an even dozen"?
Perhaps when you wrote the code you knew there was no difference, but a few months
later and you are having to relearn what you forgot. If the API only allowed integers,
then I would not be struggling with this question. In this situation, too much flexibility
in the API is not good because it increases ambiguity.
In a similar vein, the final reason for explicit contracts in your code is because
without it
others will struggle to maintain your code. They will waste hours, days or weeks
trying to learn what the hell your code was supposed to do. If you are having trouble
figuring out what your code did a few months after you wrote it, then others will be
struggling much more.
Implicit Typing is a Weak Contract
Above I described the difficulty presented by weak or ambiguous APIs. Such animals
do indeed exist in the software world. I see them every day when looking at other
people's code...okay, mine too.
But I want to discuss one of my favorite weak contracts.
Many software languages support something called implicit typing in which a variable's
type is determined by context.
Javascript is the most widely used modern language that not only allows implicit
typing, but requires it. Every variable is declared simply as a var and its type
is determined by whatever is assigned to it. Furthermore, the type can change if
you assign something different to the same variable.
And C# more recently, to my chagrin, introduced the var keyword. Fortunately,
they had the wisdom to greatly restrict it. Specifically var has only method scope
and the var object retains its type once it has been established.
For example, the following C# code will actually generate a compiler error. Not
so with Javascript.
The following C# code causes a compile-time error.
var a = 17;
a = "bugs bunny";
So, in C#, var is simply a bucket that holds something that is well defined with
a consistent type that does not change.
Javascript, on the other hand, is way more flexible.
I'm not going to judge the merits of Javascript or any other language here. They
all have their strengths and weaknesses.
I will only say that being more explicit is better than being less. Type-changing
variables are weak contracts and leave room for interpretation based on context.
This is like my body shop saying they would match the color, but then because it
was Tuesday and raining, they decided to paint the door blue instead of red.
You could argue that I should have known that doors are always painted blue on rainy
Tuesdays, but it is an unnecessary complication that increases the knowledge load
and over complicates the contract. This leaves our code prone to errors and misunderstandings.
Javascript's rules are precise and deterministic, but the tremendous flexibility
makes it very easy to write code that is buggy and difficult to maintain because
there is too much room for interpretation.
Some people argue that using the var keyword forces you be more careful about your
variable naming. Yes, really, I've seen this argument many, many times. But it
is a ridiculous argument. First, it doesn't force you to do anything. Lazy developers
will still use var along with bad names. And second, it is an admission that using
var reduces the clarity of the code.
If I had my way, I would consider banning the var keyword in Javascript and C#
both. But that is probably too extreme. var does serve a purpose in C# for handling
anonymous types. But I would restrict it, as recommended by Microsoft, for only
that situation.
The Antidote for Ambiguity
So, let's say I'm stuck with Javascript. Obviously, I have to use the var keyword.
I refer to Rule #1 of the Programmer's Code, Be Consistent.
Explicit and consistent naming habits can be your antidote when dealing with weak
and ambiguous contracts.
For example, if you declare a variable with the name percentComplete, make sure
that the value assigned to it is always numeric. Avoid actions and operations where
its type could purposely or inadvertently be changed to something that would no longer
make sense in an arithmetic calculation.
The following Javascript snippet is perfectly legal.
var percentComplete = 0;
function OnProgress(progress)
{
percentComplete = progress;
if (percentComplete >= 100)
{
percentComplete = "Done.";
}
}
function GetProgressMessage()
{
return "Progress = " + percentComplete;
}
But what if you want to use percentComplete to calculate the progress in MB? Everything works great
until the last step where percentComplete has been turned into a string and the MB calculation now fails.
function GetProgressInMegaBytes()
{
return "MB = " + streamLength * percentComplete/100;
}
If I'm the one who has to maintain your code, I'm going to be very annoyed that a
variable named percentComplete is no longer a numeric value.
I'm certain that any good Javascript programmer would never write the code
samples above because it is not explicit, unambiguous code.
Rather, by using the name percentComplete they are telling other developers
that this variable will contain a numeric percent value and they will be consistent
about that in their code.
So even though the language allows you to be less explicit, you must have the
discipline to enforce consistency yourself.
Conclusion
In this article I have compared lines of code to legal contracts. Each needs to
be explicit and clear; but it's even more important in software. One way to be explicit
in your code is to use strong typing and avoid the var keyword unless absolutely
necessary such as when working with anonymous types in C#. Other ways to be explicit
are to use consistent naming and simple, unambiguous APIs.
And now, in order to avoid all ambiguity, this article will end at the next period.