When I exercise my introspectional skills I often think fondly of my ancestors. I imagine their first grunted discussions of values, time and the meaning of soccer. And I believe these discussions closely resembled those we see about good code nowadays. But without the discussion we'd probably still be fighting naked over the affection of women. Um...
In this article I'll try to define good code from a business value perspective. I'll come to this conclusion:
Good Code executes a set of features correctly in a specified time (present value) and maximizes future value (minimizes future cost) by adhering to the dynamic nature of code through an ROI-oriented design, a test suite, process automation and VCS-usage.
I would like to investigate the topic of good code by looking at the code's value. From a financial point of view the present value of Bilbo's ring is the sum of discounted future payments. Those include negative payments (being lectured by Gandalf) and, of course, positive payments (becoming a hero is not that bad, after all).
But how does this relate to code? What future payments can you get out of a computer program? There is a myriad of ways to get value from a computer program:
- Sell it for money.
- Change it and sell it for even more money.
- Learn from it.
- Have fun while changing it.
- Have fun using it.
Without further discourse, I'll head straight for the topics that cover the money making aspects.
You can sell software. The value of the software today is the expected sum of discounted future income from this software. So if you can sell the software without changing it ever again, a binary-only version that meets your needs may be a very good piece of code. The present value of software is a set of features it executes correctly in a specified time, and is therefore independent of the source code.
It's interesting that the current value is already based on expectations. Software value is not like a piece of gold, it's more like an investment that pays dividends. As such, software is risky and highly speculative. And since our expectations are usually not good at predicting the future and changing software is a lot easier than changing hardware, the software is going to be changed. A lot. And this is why software is grown rather than built.
Expected Value Growth
All those expected future payment series become a lot more complicated when you plan to increase the software's value over time by changing it. Now you have not only an expected series of growing payments, but also an expected series of cost for maintenance. The expectations become even fuzzier when you realize that you have to guess the future growth of value without knowing the present value (remember that this is an expectation, too) or the future requirements.
But there's still light at the end of the tunnel: you don't have to calculate the expected value, you just have to maximize it. You don't need to know all the details before you can make a plan. There are a few things about those future payments that are rather obvious.
The value of software grows if the growth of the present value is greater than the maintenance cost.
So the faster new features get into the code the better. Since most of the time the expected future value growth is a lot bigger than the present value, the most important aspect good code must have is:
Good code is easy to change.
How can you design your code in a way that it is easy to change? Is it only the code? No. Code that is easy to change is a lot more than just a few written statements in a programming language. It's everything that's involved in the build process and all the tools that handle the code or your documentation. So what is needed for good code? What are the minimum requirements for the environment?
Design to maximize the return of investment. Make the code easy to change. This means modularity, abstraction, low coupling and high cohesion and all the other wisdoms of software development that are known for ages.
And of course it is highly dependent on the people involved whether code is easy to change. Some people can't read perl, others don't like the structure of python. Some can only work efficiently with static typing, other people like dynamically typed languages. Most of the time you'll hear people say that a feature of their most valued tool is the only way to produce good code. Usually you can safely ignore it.
Good design is always in the eye of the beholder. Take low coupling and high cohesion for example. Modularization is the art of minimizing your interfaces. But most of the time you have more than one option to split up your design. There may be two or more ways to break up dependencies, and different people will find different solutions easier to understand, because they have different models in their minds. Some people envision a graphical representation of the system in their mind. Others think of names and relationships.
Find the model that fits best for your team and make the code easy to change so that the poor souls that will maintain your code when you leave earth on your mission to Pluto are able to refactor it to make change even more easy for themselves.
Automated Test Suite
An automated test suite makes it easier to change the system without breaking it. Even if you never implemented a new feature and later realized that you've broken a different feature, remember that you write code not for yourself, but for other people who have to maintain your code. Sometimes I myself feel like a different person when I read my code from six months ago. You can find out more about automated tests in Kent's book Test Driven Development and read why Martin Fowler regards all code that is not unit tested as legacy code in his book Refactoring.
Human beings inherently suck at doing complicated things. We make errors. If you have to do stuff that could also be done by a small program, you're wasting resources, increasing maintenance cost and therefore diminishing value. Good code comes with a completely automated build process. The output of that build process is a package that is ready for release.
A lot of people have written on what a good VCS tool should do. Today, there are many good VCS tools out there. A good VCS tool helps you to increase the value of your code by being able to work efficiently in teams and to access historic information about your code.
Some of the possibilities to analyze the historic information are active research topics today. Silvia Breu and Thomas Zimmermann work on the extraction of cross-cutting concerns from version history. There is a lot of information waiting in your VCS to be used. If you don't use a VCS tool you loose this information, which can help you understand the reason why your big god-class evolved the way it did. And this may help you to improve your design more easily.
The code itself is the most important piece in the puzzle. But the processes around the code are what keeps it in good shape. There is a lot more to good code than a few design principles or using the best programming language or paradigms. Good code involves everything needed to keep your code easy to change and maintain in the future.
Quality With A Name
What's good software, anyway