By Frances Advincula
(A disclaimer: It has been so long since I wrote a book report, so this will be a super informal review that pushes on the verge of being a mini-summary, but I hope you find it useful anyway, although I am certain I did not do justice to the book.)
For me, the book was divided into 4 parts. Chapter 1 gives us an introduction on what is bad code and its consequences, as well as experts’ definitions of what makes code clean.
From page 3, the author writes about a company that was at its height in the 80s, only to suffer from bad code which ultimately led to its demise:
“The answer confirmed my fears. They had rushed the product to market and had made a huge mess in the code. As they added more and more features, the code got worse and worse until they simply could not manage it any longer. It was the bad code that brought the company down.”
It’s no trivial matter, folks! But we are all guilty of writing bad code at one point or another. We do it because we justify it in our heads, that we have to meet that deadline, or that we have a bazillion other things to finish, or, gasp!, that it’s “good enough.” Well, no, it’s not… and the book does us good by reminding us of LeBlanc’s Law, “Later equals never.” Clean your code before you commit, and clean it now!
Anyway, the book goes on and quotes several experts on what they think makes clean code, but my favorite was Michael Feathers’s. He is the author of Working Effectively with Legacy Code.
“I could list all of the qualities that I notice in clean code, but there is one overarching quality that leads to all of them. Clean code always looks like it was written by someone who cares. There is nothing obvious that you can do to make it better. All of those things were thought about by the code’s author, and if you try to imagine improvements, you’re led back to where you are, sitting in appreciation of the code someone left for you – code left by someone who deeply cares about the craft.”
And I know you care! You are reading this blog, after all. I also know that if you are a newbie engineer like me, you want to transition from being an apprentice to being a craftsman too, so let’s read on, shall we?
Chapter 2 talks about using meaningful words when naming your classes, methods, and variables. Don’t make others have a hard time figuring out what you mean, or have to scroll back up so many times to remember what “x” actually represents. Name your stuff to be descriptive and pronounceable which will help when you are talking to other developers. It seems almost like a “duh” chapter, but it serves as a good reminder nonetheless. Again, I will quote that which I cannot state any better. From page 25:
“In general programmers are pretty smart people. Smart people sometimes like to show off their smarts by demonstrating their mental judging abilities…After all, if you can reliably remember that r is the lower-cased version of the url with the host and scheme removed, then you must be clearly be very smart.
One difference between a smart programmer and a professional programmer is that the professional understands that clarity is king. Professionals use their powers for good and write code that others understand.”
Chapter 3 is about functions. Of course when we write them, they start out long and hairy, but we should refactor until they are short, only do one thing, is clearly named, have the least number of arguments as possible, have no side effects, and use error handling wisely.
Chapter 4 is about comments. Make sure your comment is necessary and not a restatement of the code. Be careful with commenting since it can be an excuse not to refine or refactor code that can be written better. Finally, if subversion can track down the changes for you, don’t even think about commenting about the change you made.
Chapter 5 is about formatting. Basically, use common sense, make your code easy to the eyes (like a good book), and your team should sit down and agree on your project’s formatting rules to keep everything consistent.
Chapter 6 details the difference between objects and data structures and urges us to think very carefully on which one would satisfy our needs for a given problem better. Use objects when you need to hide data but expose behavior, since they allow you to add new objects with ease without modifying existing behavior. The reverse is true for data structures. They usually don’t have behavior and expose data, so use them to add new behaviors to already existing data structures, since it’s harder to add new data structures to behavior that already exists.
Chapter 7 deals with error handling and emphasizes that clean code is robust code. It walks through rules such as using exceptions versus return codes, not returning/passing null, using unchecked exceptions, providing specific expectations that the person debugging can easily start tracing in a call stack, and techniques on having a clear separation between the business logic and error handling.
Chapter 8 deals with boundaries and how to work with third-party code. It chides us to write tests as we learn how to use the third-party code. The book calls them “learning tests” and describes how useful they are in making sure that when the third-party vendor releases updates, your code will still be compatible, etc. It also tells us to manage and minimize the places where we use third-party code by using patterns such as the Adapter pattern. From page 120:
“Either way, our code speaks to us better, promotes internally consistent usage across the boundary, and has fewer maintenance points when the third-party code changes.”
Chapter 9 talks about writing clean unit tests. (This is by far my most favorite chapter, as they really grill us in test driven development at work! A post on TDD is on it’s way!) The book says that test code is just as important, if not more important than, production code. Without tests, you will be scared to change and improve anything in your code base, and your code will start to rot. From page 124:
“Test code is just as important as production code. It is not a second-class citizen. It requires thought, design, and care. It must be kept as clean as production code.”
Why you may ask? Again, from page 124:
“It is unit tests that keep our code flexible, maintainable, and reusable. The reason is simple. If you have tests, you do not fear making changes to the code! Without tests, every change is a possible bug. No matter how flexible your architecture is, no matter how nicely partitioned your design, without tests you will be reluctant to make changes because of the far that you will introduce undetected bugs.”
It then lists the rules of TDD.
- Write a failing test before you write production code.
- Write just enough for the unit test to fail.
- Write just enough production code to make the test pass.
And also five rules of what makes a test clean (FIRST): Fast, Independent (can be run in any order, etc.), Repeatable in any environment, and Self-Validating (they either just pass or fail).
Chapter 10 is all about classes. Basically, they should be small and follow the Single Responsibility Principle, or that a class should only have one reason to change.
Chapter 11 is about designing systems. The intent of the system, at all levels, should be clear. Only use standards if they add value, and always, always use the simplest thing that would work. My favorite from the chapter has to be the advice on making decisions. From page 168:
“…it is also best to postpone decisions until the last possible moment… A premature decision is a decisions made with suboptimal knowledge. We will have that much less customer feedback, mental reflection on the project, and experience with our implementation choices if we decide too soon.”
Chapter 12 lists four simple rules that will lead you to a good, clean, simple design.
- Write good tests and run them all.
- Refactor. (Again we have tests so we can change code to be better and not have to worry about introducing bugs.)
- Avoid duplication of any kind.
- Write code that people understand, so that it can be maintained with ease (which are basically clear about the intent of your code, having good naming conventions, commenting correctly, and keeping classes and methods small).
Chapters 14 to 16 are case studies wherein the book walks you through applications or parts of applications that have bad code and refactor them as you watch – to make the code pristine, clean, and just plain ol’ awesome. You’ll be walked through detailed critique and refactoring of a command-line argument parser, JUnit, and the class SerialDate in the JCommon library.
Chapter 17 is a cross reference of all the “rules” and heuristics that were applied to what was refactored in the previous chapters. I love it in a sense that it provides a master list of things to remember and abide by.
In fact, I might print a page or two of it and tack it on my cubicle.
Happy Friday and happy coding, Everyone!