Showing posts with label Language. Show all posts
Showing posts with label Language. Show all posts

Tuesday, March 1, 2011

Label Considered Harmful

Code is straightforward and logical, but because it's written by human beings, it's as vulnerable to superstition as anything else. One of the most detrimental superstitions is the fear of early return. Convoluted nested ifs and pointless temporary flags are created just to avoid early returns. Early returns are feared because they are believed to be the same as gotos, but more awful code has been written to avoid early returns than was ever written with the goto statement. Although the return statement is superficially like a goto, it was never the goto statement that was the real problem with goto programs. If we look at why the rule forbidding goto was made, we'll see the real culprit.

Superstition begins when a rule's reason is forgotten, and the rule is blindly obeyed. Most programmers today know that goto is wrong, but never worked on a goto program, so don't know why goto is forbidden. The answer has to do with readability and information hiding. Each statement in a program changes the state of the program. How easy it is to understand the state change depends on what statement is called. Let's look at some examples.

b = 10;
c = a – b;
if (a <= 25) return;

After these statements execute, we know that a is greater than 25, b is ten, and c is ten less than a, so c is greater than 15. Notice that the return statement actually increased our knowledge about program state, by eliminating any a's less than 25. If we replace the return statement with a goto statement, we can reason similarly about program state.

b = 10;
c = a – b;
if (a <= 25) goto label1;

The goto statement is not quite as meaningful as return. A return means we're done with whatever the current method is trying to do, so if the current method has a meaningful name, we understand what the return means. A goto has different meanings depending on where it goes. Is it going back in the program to try and get a better value of a? Is it going to a special routine to calculate something different? Is it skipping over the next chunk of code since it doesn't have a good value for a? But even though there's this ambiguity with the goto statement, we can still reason fairly well about program state.

Now look at the label statement, the statement that gets executed after a goto.

  b = 10;
  c = a – b;
label2:

After label2 is executed, what do we know about program state? Nothing. The statement 'goto label2' could be anywhere in the program and before that statement a, b and c could be set to anything. We would have to find every instance of 'goto label2' in the program and read the code around it before we could have any idea what the program state is. This could be exceedingly hard in languages where labels are numbers and can be computed. This is what makes goto programming so difficult to understand and debug. It was never the goto statement itself that caused these problems; instead it was the passive label that received the goto.

And here's where the human mind gets into trouble. Because people are naturally attracted to an action like goto instead of a passive statement like label, all the negative press is directed toward goto. But the real problem with unstructured programming is having spots in the program that can be gotten to from anywhere else in the program.

Tuesday, November 9, 2010

Code Is Meaningless

Computer languages are great for describing how to do something, but they can't explain why. Why add two to the personCounter? Why is statusField set to the empty string when counterField is null? Questions like these don't get answered by code. Code is very good at answering what and how questions, but why questions are completely impossible for it. So documentation was invented to provide a way to explain why something is the way it is. Unfortunately, few use it this way.

Instead, for programmers we have what's called echo documentation:

/**
* Gets a value.
*
* @return value the value returned
*/

public int getValue() {
   return this.value;
}

Many programmers will document their code by simply repeating what's there. There's no indication of what anything is used for, or why it was designed the way it was. Instead of explaining anything they echo the code. This isn't really documentation.

For users, we have a numbered list of exact instructions, in other words, a program:

  1. Click the button labeled “Excute”.
  2. In the popup window that appears, select the radio button labeled “Process Records”.
  3. Click the OK button.
  4. Now select “File/Open...” from the menu.
This style is marginally useful, but it still doesn't do what documentation is supposed to do: answer the questions the interface itself can't answer. It doesn't give the user a chance to do anything more than mindlessly follow directions. This is great for computers but human beings need something different.

Your brain needs information to be balanced between how and why just as your body needs nutritionally balanced food. Eating unhealthy food can leave you with the uncomfortable sensation of being full and hungry at the same time. This same thing happens to your brain when confronted with mounds of data and little explanation. You feel dizzy from the sheer volume of information, but are still confused as to exactly what's going on. And just like junk food that passes through your system without contributing much, most data bereft of reasons for its existence will be quickly forgotten. Documentation that's heavy on information but light on explanation creates more confusion than it prevents.

Why is why necessary? Answering why is so important because it helps human beings store information. To understand something, the human mind needs to create a mental model of it to hang details on. Without the mental model, the mass of details overwhelms the brain's capacity to store them, and the brain forgets most of what was said. People feel lost and confused when this happens, because they have no mental map to orient themselves on. But once they have that mental model to organize things around, human beings can remember an enormous amount of information. Although few people can memorize more than ten phone numbers, most people know more than ten thousand words. Meaning creates the conditions necessary for understanding.

Think of the mental model as a data structure that someone builds in their brain. You don't want to shove information into someone, overflowing their buffer, causing them to forget most of what you tell them. Instead you need to start out telling people how to construct the data structure they need to understand your system. Then you can add details, letting the user know where in their mental model the details fit. This way the human brain can store a lot more information, because it has someplace to put it. When someone has a mental model of something, they say they “understand” it, meaning if you gave them more information they would have somewhere to store it.

To see how different something looks once you have a mental model for it already built, look at the following interface:

Afsefioj:
Jheisp:
Diep:
Bivi:

It's just some fields that seem to have nothing to do with each other. Nothing really suggests what should be entered in them or how they would be used. Now look at the same interface labeled correctly:

Protocol:
Server:
Port:
Path:

Now, because you already know what a URL is, the fields make sense. You see how they relate to each other and can think of some possible values. The structure in your brain that stores what a URL is helps you understand this interface. Without that information in your brain, the interface wouldn't help you understand what the program is about.

This is radically different from the way programs work. Computer programs always have the internal data structure already decided on, and coded into the program. Human beings, on the other hand, have no preset structure, so the first thing you must do is give them the structure that the rest of the data will be a part of.

Documentation should be about creating mental models. The docs don't even have to be complete; once a human being has a mental model, they can discover a lot of information on their own without instructions. But without the model a person is lost and can't do much more than mindlessly follow directions.





Monday, October 25, 2010

Everything Is Interface

There’s a myth that programmers are divided into flighty, sensitive types who can create beautiful user interface but can’t do any heavy lifting, and code jocks who can write multithreaded servers but don’t understand usability.  In truth, everything is user interface.  For your program to do anything, it must interface with something else, be it the operating system, a server, another program, or a human being.  And someone is going to have to learn that interface to use your program.  Source code itself is an interface between the programmer who writes it and the poor slob who has to maintain it.  Computer languages are interfaces between what you can understand and what a computer can do.  If your style of user interface is illogical and inefficient, then your coding style is probably the same.  As a programmer, you should strive to write code that is as slick and easy to use as a good user interface. 

Since all code is user interface, anything forbidden to user interface is forbidden to code and vice versa.  If you wouldn’t cover a screen with dozens of buttons, you shouldn’t write a class with dozens of public methods.  If you don’t use goto because it makes your code confusing and buggy, you shouldn’t force your users to visit screens at random to get their work done.  If you don’t want your users entering their data in a huge free-form field, then you shouldn’t store data in some giant string that needs to be parsed.  If you wouldn’t make your function’s actions depend on a mysterious global variable that’s squirreled away in some header file, you shouldn’t make your program’s behavior depend on a global setting that’s hidden in some inaccessible menu. 

This idea of code as interface goes deeper than just the surfaces I’ve been talking about so far.  When you write a method, the method signature is an interface between the code in that method and everything that calls it.  The public methods of a class are an interface between that class and the rest of your program.  Your logging statements are an interface between the program and whoever has to debug it.  What structure you choose to store data in is an interface between the values stored and the code which uses those values.  Everything is interface, and you must always keep that in mind as you code. 

Code itself is an interface because code is written to be read.  You will read your code months after you wrote it, maintenance programmers will read your code to fix all the bugs in it, and if you do things right, your testers and doc writers will know how to read code and be doing that to do their jobs.  Code doesn’t exist just to perform some action, code exists to communicate what the action is and why we’re doing it.  If code wasn’t meant to be read we’d use _1 and aBc as variable names.  Code is meant to be read so must be readable. 

Programmers have trouble coding these interfaces because they believe they’re interfacing with a computer.  But interfaces almost always boil down to an interface between people.  You may never meet this other person, they could be a language designer or a maintenance programmer; they could be using your server as a backend for their own web site or installing your software on their desktop.  Whoever they are, the decisions you make need to keep them in mind.  Programs are limited not by the computers that run them, but by the humans that use them. 

How many projects fail because these human factors are never considered?  Cool ideas get run into the ground all the time because nobody can understand the implementation.  Projects get patched to death by maintenance programmers who can’t understand them because the original developer is too busy or unavailable to explain it.  Programmers forget why they made some decision in the code and so revisit the same questions again and again and again with management.  Clumsy interfaces generate excessive calls to support, preventing a project from scaling beyond a handful of customers.  The success or failure of any project depends on whether any attention is given to the human beings involved. 

This is why I’ve called my blog Hacking the Ultimate Machine.  The human brain is the ultimate machine and understanding what it can and can’t do is central to being a good programmer.  Ignoring human capabilities and limits means ignoring the capabilities and limits of your own work.  And that means putting out software that is no better than the average half-baked junk.  Paying attention to the human interfaces that make up your code is necessary to achieve anything worthwhile in programming.