Recently I read a book “Code Simplicity” written by Max Kanat-Alexander. This is a very book aiming at helping improving code quality. After finishing this thin book, I want to share something here to benefit more junior programmers like me.
Code’s simplicity determines code’s quality
Code changes from time to time, and the factors causing code to change usually are not predictable. Thus we don’t know how our code is going to change in the future. Though currently we may have a very good design, there is some degree of potential that we need a new feature in the future that is not compatible with current design. As a result, when we consider the cost of developing a software, we not only need to consider the cost of implementation, but also have to take maintenance cost into account. Consequently, if our project is going to last for a long time, we may put weight more efforts into the maintenance side than the developing aspect. If our project is a disposable software, it is OK (usually preferable) to make a software that just works currently. However, our projects usually are not transient falling stars, we have to maintain them for a long time to keep serving people or other projects. What’s more, if your software works currently and we have no future change any more, how can we measure whether your code is good or not? Is it really meaningful to say that your code is poor since it is hard to understand? But your project helps us to solve problems and we don’t expect more from your project. That’s good enough already! Only when your code will change from time to time will we start to care about whether you have a good code quality. As a result, code’s quality is closely relating to software’s maintainability. Thus reducing the maintenance cost is the key to improve code’s quality, not the implementation or how perfectly current design meets our need.
Maintenance cost is heavily depend on the cost for programmers to understand code. Programmers are smart persons, it is a piece of cake for them to comprehend a complex software project as long as you give them enough time. However, this is not the case in real life. We have budget, programmers deadlines, too. When you order a programmer to add a feature, they just want things to be done before deadline. The most likely situation is that they try to modify the current code and add some codes to make it work. Every feature is handled in this way, after two months, you modified code 20 times. The design becomes ugly, and the complexity of your code is soaring. Now it’s damn hard to add any feature. Yet the changes are still a lot and needed, you can’t follow the changes any more because your code is so complicated that even adding a small feature is a big big task. Building the code with simplicity is needed! Even if it may increase the cost of implementation, it keeps your project alive and avoid your project to become a gigantic monster that you have to kill. Practically speaking, being simple is flexible to future changes and reduces your maintenance cost, thus improves your code quality.
If you want to change your code, before changing it, think carefully that whether you will greatly lift the complexity level of your project. If the answer is YES, please try to think that whether adjusting your design will help you reduce the complexity. If the answer is YES, don’t hesitate to do this. It may seems that you spend a lot of time for a small change, you save a lot of time for the future and keep your project maintainable, which extends your project’s life to serve more people or other projects.
Don’t write code that isn’t needed
Remember, we can’t predict future. It is not necessary to save currently unused code for future. You may think that writing those codes may save future time, actually in most cases, it just increases your burden to understand it and debug it in future. Changes are definitely going to happen in the future, it is understandable that a programmer wants to do something for future use. Yet we are not foreseers, we just guess our future. The code you leave for the future only has certain chances to be used. However, it adds your code’s complexity because it requires more time to understand what your code is doing. It increases both implementation cost and maintenance cost. Moreover, when you are evolving your project, the unused code are unlikely to be tested over time. As a result, if “fortunately” those out-dated codes will be put into use in the future, we are likely to introduce strange bugs because that those codes are not compatible with current version. We spend a lot of time to debug it. What’s worse is that when encountering a bug, we may first doubt our newly-written code since we assume that old code are less likely to be buggy, which also increase our debugging time. It is more acceptable that we write a new one, an up-to-date version.
Use incremental design
I have a list of requirements, and now I am going to design a project. There are two strategies, I deliver a grand design including all the features, or I implement a feature first with a simple design and then add features one by one. For the former one, since we stand higher level, we usually tend to build a complex design that every part in this design is intricately related with others, and this design will perfectly meets the current requirement. However, if there is any fault in the design or that any feature changes in the future, it’s hard to adjust since we already start to build the big framework. Changing girders for a house is not a tiny issue. For incremental design (the latter one), we are doing project like we are gradually adding features for our current project. Every time we introduce a small change, we are possibly refining our design. By this way, we also keeps our design meeting tightly with required features. More importantly, we tend to write high quality code since we tend to have a simpler design and implementation. Once there is a change of feature, we may roll back to the previous version and try to re-implement this. Or we can regard it as another new feature, and add this change to our feature list, which will not cause a development catastrophe – replacing the whole blueprint. And we may have another rule to support incremental design: the whole project’s simplicity level is decided by every individual piece’s simplicity.
Never “fix” anything unless it’s a real problem
Compare optimizing your code and implementing a new feature, which one is more desirable? Of course the latter one, budget is limit, we can’t afford spending time on invisible improvement. Moreover, there is a development rule saying that the defect you introduce is proportional to the size of your changes. Unless you are simplifying your code, which reduces the maintenance cost in the long run, you should not change your code without seeing any problem. We don’t need to change, if your project works.
Code’s quality closely relates to maintenance cost. If there is no change in the future and your projects works, how can we know whether your code quality is good or poor. Maintenance cost embodies code’s quality, and simplicity of your code determines your maintenance cost. How to keep your code simple? That’s a big question, and we have a lot of useful design patterns, code conventions, good comment habits and so on to help us achieve this goal. Maybe next time I will try to share some detailed skills about making code simple. Right now I share some general concepts. If you have some thoughts about this topic, please feel free to discuss with us. Sharing is a virtue.