It is not enough to just write code that is clean and self-documenting. Eliminating comments and replacing them with clear variable and method names, will tell the reader of your code clearly what it is doing, but it will not properly express why it is doing it.
If you recall from my original post on Elegant code, I stated that elegant code is:
Something that is simple yet effective, delivered with grace.
By writing self-documenting code we are hopefully able to achieve some of the aspects of simplicity and grace, but it tells us nothing about the effectiveness of the code.
Bringing the dead to life
Throwing out comments is a debatable topic. It raises quite a bit of controversy, as you can see from some of the comments on my post about the subject. The reason, perhaps, that so many people are so staunchly opposed to the idea, is because they are worried, and rightly so, that throwing out comments will reduce the documentation of what the code is doing.
Think about that for a second. Is documentation of what the code is doing important? It better be. What we are trying to accomplish by writing self-documenting code instead of comments, is to take the vessel, which is the code itself, and make that same vessel be the documentation of what the code does. It is by doing this that we creating a living representation of the functionality of the code. Comments are akin to a dead representation of the functionality of the code because they do not change automatically with the code.
I diverge to talk about comments so that I can draw a parallel to another form of documentation which is valuable and should be considered necessary. That other set of documentation is requirements. Have you ever written requirements documents? Have you ever captured requirements from the customer and put them into UML diagrams, or perhaps plopped them into some templated Word document, which is never updated again, and no one reads, because no one trusts it is correct? Wouldn’t it be great if we would replace that dead document or set of documents, with a living one?
The good news is we can, and some of us have. And I fully expect this recommendation will be just as controversial, because I am suggesting that we don’t have to write low level specifications of the system at all, instead we can write good unit tests. When we do this, we are taking a dead form of document and bringing it to life.
Unit tests tell us why and how our code is effective
i = 5; // Widget count defaults to five.
is replaced with
widgetCount = DEFAULT_WIDGET_COUNT;
1.1.5 When a new widget is created, the widget count should be increased by one.
is replaced with
public void When_NewWidgetIsCreated_Then_WidgetCountIsIncreasedByOne()
WidgetFactory.Count = 3;
The unit test is replacing the low level requirement, which is somewhat open to interpretation and untied to the code, with an absolute and completely tied-to-the-code, specific requirement.
The unit test is telling us why our code is built the way it is. It is telling us what requirement that particular structure of code is trying to address. It is telling us what end result that code is trying to accomplish and what the expectations are on the results of a given condition.
The unit test is telling us how the code is to be used in order to achieve its result. It is serving as a living reference to the syntax and use of the code we built. Instead of providing instructions on how to use our API, we are providing a living example, and proof that it will indeed work. Examples are often much more effective than instructions anyway.
What exactly are we replacing?
At this point we are not trying to replace all the documentation of the system (at this point).
With self-documenting code, we sought to eliminate comments, and replace them with good variable and method names.
With unit tests, we seek to eliminate technical documentation and requirements of the system, that is targeted at a developer or technical audience.
We are NOT trying to eliminate high level specifications of the system, or use cases. Unit tests DO NOT replace that kind of documentation! We will talk about replacing that kind of documentation when we talk about automated functional or system tests.
Simply put, any kind of documentation that you would give to a developer so they understand how the system works and how to use the code or APIs, can be easily replaced by writing good unit tests.
Our simple goal is to replace the “dead” documentation (meaning that it does not update automatically with the thing it is documenting), with “living” documentation in the form of unit tests.
One last point. I want to be clear that I am not saying that having unit tests automatically results in the ability to replace technical documentation. Just like having long-named methods and variables doesn’t automatically replace the need for comments. It is a matter of the quality of the unit tests that are written, just as it is a matter of the quality of the self-documenting code.
As always, you can subscribe to this RSS feed to follow my posts on elegant code. Feel free to check out my main personal blog at http://simpleprogrammer.com, which has a wider range of posts, updated 2-3 times a week. Also, you can follow me on twitter here.