The Art of Clean Code
Anyone unfamiliar with the process and profession of software development could understandably assume that computer programmers spend most of their work time writing new code. However, they actually spend most of their time reading existing code — written by themselves and, if part of a project and team, written by other programmers. The speed and accuracy with which they can read and understand that code depends very much upon its quality. High quality code is characterized by, for instance, generous use of explanatory comments, focused modular functions, and descriptive variable names. In contrast, low quality code may have no comments and terribly cryptic variable names — making it a nightmare for programmers to debug and enhance that codebase.
As one would expect, better programmers have a strong interest in continuously improving the quality of the code they write, because doing so will increase future comprehension of their code, enhance their reputation on the team and in their technical community, and increase their marketability. In turn, those elite programmers can be big fans of those few books which share the best coding practices learned by slogging through awful code and vowing never to torture themselves or others by allowing their development standards to sink that low. Books such as Code Complete, by Steve McConnell, are popular recommendations. One possible new entrant in this genre is The Art of Clean Code: Best Practices to Eliminate Complexity and Simplify Your Life. Its author, Christian Mayer, is a computer scientist, the founder of the Python educational website Finxter, and the author of other technical books, on Python. This particular book was published on 2 August 2022 by No Starch Press under the ISBN 9781718502185. On the publisher's Web page, visitors will find some information about the book and a link to a free sample chapter (the second one, on the 80/20 Principle). The book is available in electronic format, as well as a print version spanning 176 pages.
The bulk of the book is organized into nine chapters, each of which explores a particular principle. The first one, "How Complexity Harms Your Productivity", argues that the primary impediment to programmer productivity is complexity. In view of the book's title, one would think that this chapter would then present techniques for minimizing complexity in one's individual code fragments and overall system design. Instead, the chapter oddly claims that the complexity stymieing the newbie coder consists of the many options available for the following choices: the programming language, the project, libraries to use, application platforms (smartphones, websites, virtual reality, smart home devices, etc.), and available programming editors. The author does not address the first one; presumably he would recommend Python, given how the book uses that language exclusively. As for the second purported source of complexity — an initial project for the new coder — the author advises "choose a practical code project [...] and push it to completion. Don’t read coding books or random tutorials on the web before completing a full project. [...] Just set up the project and start coding with the limited skills you have and your common sense." The obvious problem with this advice is that the beginning coder will miss out on all of the time-tested principles of quality programming explained in those unread books and will instead begin reinforcing bad practices through repetition.
The author then reiterates the value of simplicity and the methods of achieving it to be presented in subsequent chapters (largely repeating the material from his Introduction). He then outlines what he means by the term "complexity" — namely, "A whole, made up of parts, that is difficult to analyze, understand, or explain." Confusingly, this new meaning is quite different from what he stated earlier in the chapter and, even worse, it is quite different from the commonly understood meaning, as well as any dictionary definition (e.g., "the quality of being intricate and compounded"). Moreover, complexity can be the quality of something, but not the thing itself (the "whole"). The rest of the chapter discusses the stages of a project's lifecycle (planning, requirements definition, design, building, testing, and deployment), software complexity (algorithmic, cyclomatic, and cognitive), and minimizing complexity in your life to maximize deep work. For a book purporting to teach best practices, it was disappointing to see the testing section using "print" statements instead of "assert" statements. However, the most baffling claim is in regards to building a software product, namely, "A small spelling mistake can undermine the viability of the whole software product." If the spelling mistake is in a code comment, it will have no impact on the functionality of the compiled code. If the spelling mistake is in the code itself, then it will result in either a compilation error or a fixable bug. How could any of these scenarios be equated to undermining the viability of an entire product?
The second chapter, "The 80/20 Principle", explains the well-known Pareto Principle and how it applies to improving programmer productivity. Yet it is difficult to imagine any technical reader nowadays not being thoroughly familiar with that principle. Also, how to apply it in order to increase one's productivity is, in most cases, fairly obvious. Much of the text in the early part of this chapter seems like padding, and several of the figures simply belabor the point. More alarming, however, is the poor advice found in the statement "One of the vital few activities to focus on is to write more lines of code." Actually, the best programmers generally write fewer lines of code, of higher quality and thus a lower risk of needing to be rewritten. In addition, they are more adept at reusing existing code in libraries (either internal to the project or outside in the form of open-source code), thereby avoiding the need to write it from scratch. It's the code quality and maintainability that matter, and not the number of lines of code.
In the next chapter, "Build a Minimum Viable Product", readers learn the value of first building their product without any unnecessary or at least secondary features, thereby devoting their limited time and energy to creating only that which is needed to validate that the intended users would want to use the product. The author presents six of the most common pitfalls when working on a project in "stealth mode", in which one does not receive feedback from the market or even a handful of individual testers. Incidentally, the excellent observation "Ideas are cheap; execution is king" applies not just to software product ideas, but a myriad of artistic endeavors, such as screenplays. In the realm of contract work and freelancing, that observation can be an antidote to people insisting that you sign a nondisclosure agreement before they share with you what they're convinced is a revolutionary idea. Overall, this particular chapter was noticeably more interesting than any of the earlier ones. Its only questionable advice is "Spend more time thinking about the next feature to implement than you spend actually implementing each feature", which sounds like a recipe for writing rushed and sloppy code.
Speaking of which, the fourth chapter, "Write Clean and Simple Code", addresses the topic one would have expected to encounter much earlier, given the book's title. The author presents a worthy discussion of — as well as many of the benefits of — high code quality, frequent refactoring, robust system architecture, leveraging proven libraries, descriptive names (of variables, classes, etc.), helpful code comments, and other smart coding practices. Experienced developers will be able to relate to the code quality metric of "WTFs per minute, intended to measure your code readers’ frustration." In his sample code, in the conditional expressions, the author removes all optional spaces, thus jamming together all the operators and making the expressions difficult to read. (I've never seen that awful style anywhere else.) But the biggest flaw was the statement "Don’t use inline comments These can be avoided entirely by choosing meaningful variable names." That is certainly not always the case, as some difficult code can be made much more comprehensible with brief inline comments explaining the processing being done, step-by-step. The lines of code are adjacent to one another, which can ease reading, through the use of in-line comments instead of block comments, which separate the lines.
Given how little time that most programmers — working individually or as a team — devote to any sort of optimization, it may seem odd that the author devotes an entire chapter to the topic ("Premature Optimization Is the Root of All Evil"). That was a critical factor decades ago, in the era of computing resources far more constrained than those of today — but much less so in our current era of gigabytes of RAM, terabytes of SSD storage, and multi-core and multi-threading CPUs running at multi-gigahertz speeds. In support of his warnings, the author quotes from one of the pioneers of programming, Donald Knuth, but the quote is from 1974, almost half a century in the past. Mayer briefly describes six areas where he sees premature optimization (code functions, features, planning, scalability, test design, and object-oriented world building). But only the first of the six items is what programmers refer to as premature optimization, and the remaining five are merely bad practices, some of which were already discussed earlier in the book. Planning and implementing unneeded features and future scalability, creating inappropriate unit tests, and crippling the performance of object-oriented code with excessive inheritance, are all mistakes to be avoided, but none of them are examples of premature optimization. The author then presents a lengthy and pointless example of premature optimization, but then partly redeems the chapter with a solid discussion of appropriate optimization.
The sixth chapter, "Flow", is similar to the second one in that it covers an idea that most readers should already be quite familiar with. After summarizing the highlights of what it means for a person to be in a state of flow, the author presents some worthwhile advice on how software developers can maximize their chances of achieving flow and thus working with greater enjoyment and productivity. At the statement "Try to cook a tasty meal with decayed food — almost impossible!", readers may chuckle, particularly if they enjoy eating sauerkraut, aged beef, and other intentionally decayed foods. Also, they may groan at the unsubstantiated hyperbole that flow can "boost your work productivity by an order of magnitude". Nonetheless, the productivity and health advice may be brief but is always appropriate for coders and other knowledge workers.
Experienced programmers are familiar with the concept of each function performing a simple task and doing it well — which confers many benefits, including reducing complexity, easing the development of unit test functions, and increasing the odds of that function being reused (optimally, as part of a library). This best practice is one of 15 that the author has gleaned from the world of Unix in general and from the well-respected developers Eric Raymond and Mike Gancarz in particular. These commendable principles are explained and illustrated with sample code, in the seventh chapter — making it arguably the meatiest and most useful one in the book. One minor flaw is that much of the HTML markup containing element attributes incorrectly use single quotes for enclosing the attribute values, when in fact only double quotes are valid. (Perhaps a copyeditor at the publisher is not familiar with HTML.) The chapter's conclusion merely repeats what was stated earlier, and thus could be excised from the book.
The brief eighth chapter, "Less Is More in Design", advocates for minimalism in the crafting of user interfaces and experiences — with a focus on what is known as the "material design" school of thought, which employs user interface elements that correspond in appearance and (virtual) functionality with everyday items, such as cards and buttons. Specifically, he advocates for maximizing the whitespace that can improve the readability of websites and applications, and warns against feature creep, excessive variation in fonts and colors, design inconsistency, and unnecessary elements. Ironically, Figure 8-7 uses ten boxes, each containing text (with some text marked out), ten checkmarks and crosses, and one arrow icon, just to convey the principle that ideal design contains only important and very important elements.
The final chapter promises to tell the reader how to focus. However, it doesn't actually do so. Instead, it explains the concept of entropy (yet another concept that most if not all readers will already understand), illustrates it with a simple example of a house made of large dots, presents a useless (and almost childish) illustration of not focusing on a goal, and then rehashes most of the best practices already covered earlier in the book. But nowhere in the chapter does the author provide any advice on how programmers can truly focus — such as the use of white noise, Pomodoro work sessions, and other techniques.
In terms of writing style, the credibility of the text was weakened by an excessive use of exclamation marks. There were some mistakes in phrasing: "increases productivity by magnitudes" should instead read "increases productivity by orders of magnitude"; "lead measures" should instead read "leading measures" (or "leading indicators"); the term "depreciated" should instead read "deprecated"; and "cumulate" should instead read "compound" (or something synonymous). Each use of the term "that" to refer to people should instead use "who". There were a couple punctuation mistakes, such as using a dash to separate complete clauses, as well as an extraneous question mark in the phrase "algorithms and data structures?.". In Figure 7-3, the y-axis label reads "Number of years left" but the sample code uses
plt.ylabel('No. Years Left'). But all of these were minor blemishes.
Overall, the narrative was straightforward and not difficult to understand. Even though the book is quite Python-centric, it could be read with profit by programmers using other languages. The value of the book's content generally increased the further that one progressed into it, except for the ending. Much of the earlier material wasn't as substantial or well thought out as later material. In other words, the reader shouldn't give up after reading the first two or three chapters.
However, this book has a few major problems:
- The repetition of ideas was egregious. Most of the book consists of several best practices — some of which are quite obvious — restated too many times. The book could have been easily boiled down to a single blog post. This is confirmed at the very end of the book, in a "Letter from the Author", where he provides a link to a PDF file that summarizes the book on a single page.
- Much of its actual content was disappointing, being a combination of somewhat disparate topics: obvious advice on the value of simplicity and focus, enthusiasm for Python and machine learning, career advice for freelancers, and an assortment of observations about software projects. Several sections, like the last chapter, failed to deliver on their promises.
- The book's title is misleading, as only one chapter tries to inform the reader about how to write clean code. When proposing the book project or submitting the final manuscript, the author may have suggested a more accurate title, overridden by the publisher. (One possibility would be Simplifying Software Projects.)
- Its real purpose was not made explicit. The discussion about the stages of the life cycle would suggest that the book was really intended as an introduction to software product development for the newbie coder, and not a guide to writing clean code. But in that case a great deal of important and needed material is missing. It is clearly aimed at beginners (as an experienced developer, I learned nothing from it).
I wanted to like this book a lot more, given that the author comes across as a good guy and the book's title and marketing copy suggest the best of intentions. Nonetheless, this book could perhaps serve the beginning programmer as a brief overview of many of the pitfalls to be avoided when planning and developing a software product.