Lately, I have been reignited in my desire to learn more about software architecture. While I think of myself as a pretty good "problem solver," I definitely lack a solid understanding of how things should be put together. In a recent stint of research and development on the topic, I came across a fascinating talk titled, "Architecture: The Lost Years," by Robert Martin. While I've heard the name, "Uncle Bob" before, I wasn't too familiar with who Robert Martin was. I was so intrigued by his presentation, however, that I had to immediately go out and buy his book, Clean Code: A Handbook of Agile Software Craftsmanship.
The book is an opinionated (in a good way) view of how to create clean code. It outlines the approaches that Robert Martin has found, over 40 years of programming, to be successful. It was fun to read through each recommendation and think to myself either, "Cool, I already do that," or, "Oh Chickens! I need to start doing that immediately!"
But, the most powerful part of the book, for me, was actually the conclusion of one of the last chapters. I think it speaks directly to many of the fears and anxieties that I have about the way that I currently program:
It is not enough for code to work. Code that works is often badly broken. Programmers who satisfy themselves with merely working code are behaving unprofessionally. They may fear that they don't have time to improve the structure and design of their code, but I disagree. Nothing has a more profound and long-term degrading effect upon a development project than bad code. Bad schedules can be redone, bad requirements can be redefined. Bad team dynamics can be repaired. But bad code rots and ferments, becoming an inexorable weight that drags the team down. Time and time again I have seen teams grind to a crawl because, in their haste, they created a malignant morass of code that forever thereafter dominated their destiny.
Of course bad code can be cleaned up. But it's very expensive. As code rots, the modules insinuate themselves into each other, creating lots of hidden and tangled dependencies. Finding and breaking old dependencies is a long and arduous taks. On the other hand, keeping code clean is relatively easy. If you made a mess in a module in the morning, it is easy to clean it up in the afternoon. Better yet, if you made a mess five minutes ago, it's very easy to clean it up right now.
So the solution is to continuously keep your code as clean and simple as it can be. Never let the rot get started. (Page 250 of 316)
This segment feels like a serious kick to the head! And, worst of all, I think it touches on a deep anxiety that I have, that my current approach to software development is, "unprofessional." But, at least I can take comfort in knowing that this anxiety stems from a deep-seated desire to improve - to always be getting better. And it's books like this - Clean Code - that help me make continual progress on that front.
While the above segment probably struck the deepest chord, I'd like to share a few other segments that I happen to really like for their architectural suggestions:
Flag arguments are ugly. Passing a boolean into a function is a truly terrible practice. It immediately complicates the signature of the method, loudly proclaiming that this function does more than one thing. It does one thing if the flag is true and another if the flag is false. (Page 41 of 316)
Command Query Separation
Functions should either do something or answer something, but not both. Either your function should change the state of an object, or it should return some information about that object. Doing both often leads to confusion. (Page 45 of 316)
Prefer Exceptions to Returning Error Codes
Returning error codes from command functions is a subtle violation of command query separation. (Page 46 of 316)
Don't Repeat Yourself
Duplication may be the root of all evil in software. Many principles and practices have been created for the purpose of controlling or eliminating it. Consider, for example, that all of Codd's database normal forms serve to eliminate duplication in data. Consider also how object-oriented programming serves to concentrate code into base classes that would otherwise be redundant. Structured programming, Aspect Oriented Programming, Component Oriented Programming, are all, in part, strategies for eliminating duplication. It would appear that since the invention of the subroutine, innovations in software development have been an ongoing attempt to eliminate duplication from our source code. (Page 47-48 of 316)
Don't Return Null
If you are tempted to return null from a method, consider throwing an exception or returning a SPECIAL CASE object instead. If you are calling a null-returning method from a third-party API, consider wrapping that method with a method that either throws an exception or returns a special case object. (Page 110 of 316)
The Single Responsibility Principle
To restate the former points for emphasis: We want our systems to be composed of many small classes, not a few large ones. Each small class encapsulates a single responsibility, has a single reason to change, and collaborates with a few other to achieve the desired system behaviors. (Page 140 of 316)
Concurrency Defense Principles
Recommendation: Keep your concurrency-related code separate from other code. (Page 181 of 316)
How Did I Do This?
If we have learned anything over the last couple of decades, it is that programming is a craft more than it is a science. To write clean code, you must first write dirty code and then clean it. (Page 200 of 316)
I really love that last line, "To write clean code, you must first write dirty code and then clean it." One of the great things that Uncle Bob talks about is how iterative programming is - how on one pass, he'll factor logic out into its own function; then, on a second pass, decide that it would be better to inline the logic back into the original function.
I think I tend to have this picture in my head that "master programmers" just get it "right" on the first pass. It's incredibly comforting to know that being a great programming is not about never making mistakes; rather, it's about knowing how to continually iterate and improve on a solution. And probably, to know about a lot of good design patterns to begin with.
I read Clean Code on the Kindle App on my iPad. And, when I was reading reviews of the book, I saw a number of people say to stay away from the Kindle version. After having finished the book, I see what they were talking about. The first half of the book is completely fine, covering many design patterns and recommendations. The second half of the book, however, deals more with code samples and refactoring case studies, and is definitely a bit wonky at parts.
Overall, however, the book is definitely a worthwhile read (hopefully evident from the excerpts above) and one that I would highly recommend. It's a lot to take in and I'm sure it will serve a good reference manual that I'll have to periodically bring back into my consciousness.