Controlling complexity is the essence of computer programming. --Brian Kernigan

Rule #4 - Be Explicit

Rule #4 of The Programmer's Code

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.

 

Version: 6.0.20200920.1535