I just finished Clean Code: A Handbook of Agile Software Craftsmanship book, which is a compilation of patterns on writing clean/maintainable code and is written by uncle Bob and other folks from his consulting company Object Mentors including Tim Ottinger, Michael Feathers (author of Working Effectively with Legacy Code. This book is similar to Implementation Patterns by Kent Beck that I recently read on similar coding practices. Though, this book is a lot thicker with seventeen chapters, however there are plenty of pages filled with tedious listing of Java code.
The first chapter shares thoughts on good code from a number innovators and authors such as Kent Beck, Bjarne Stroustrup, Grady Booch, Dave Thomas, etc. They mention various attributes of good code such as easy to read, efficient, DRY, focused, literate, minimal, error handling and warn of bad code that leads to messy code or broken windows mentality.
The chapter 2 talks about golden advice of using intention-revealing names to improve the readability and following principle of least surprise. This chapter denounces use of hungarian notation or member prefix such as _ or m_.
The chapter 3 describes functions and methods and advises to use small, cohesive functions. It suggests using one level of abstraction to facilitate reading code from top to bottom. It also recommends using polymorphic methods as opposed to switch statement, if-else or functions that take flag/boolean arguments. The chapter discourages functions with side effects or the one that create temporal coupling. This chapter describes an aged old advice of command query separation, though it skips its roots from Bertrand Meyer’s design by contract. Finally, this chapter urges use of exceptions as opposed to error codes.
The chapter 4 describes writing good comments that focus on intent and dissuades against redundant and misleading comments.
The chapter 5 illustrates use of good formatting such as indentation, horizontal, vertical spacing, etc.
The chapter 6 describes use of objects and data structures. It encompasses advice on polymorphism, law of demeter, encapsulation, DTO/value objects, etc.
The chapter 7 discusses error handling. Again it encourages use of exceptions rather than return codes or error codes. It prohibits use of checked exceptions as it violates open/closed principle. It also discourages returning or passing null and recommends exceptions or empty collection instead of returning null.
The chapter 8 explains defining boundaries between components.
The chapter 9 describes advice on unit testing such as TDD, keeping tests clean, one assert per test, single concept per test, etc. Though, such advice should be taken with caution as one assert per test may not capture a unit of testing properly.
The chapter 10 is similar to chapter 6 and contains recommendations on writing classes such as encapsulation, classes should be small, single responsibility principle, cohesion, dependency inversion principle, etc.
The chapter 11 encompasses advice on building systems and managing complexity. It suggests dependency injection and use of factories. It also advocates use of AOP for managing cross cutting concerns.
The chapter 12 talks about emergent design, the concept I first heard from Andy Hunt. This chapter describes advice from Kent Beck: run all tests, refactoring, no duplication, express intents of the programmer and minimize the number of classes and methods. This chapter advocates use of template methods to remove duplication and command/visitor patterns to express design to other developers.
The chapter 13 discusses concurrency and suggests use of single-responsbility principle to keep concurrent code separate from other code, limiting scope of data, keeping threads independent, and use of immutable oobjects or copies of data. It recommends keeping critical section small. The chapter also holds advice on testing threaded code and suggests making threaded code plugable.
The chapter 14 shows an excercise on how to incrementally improve code.
The chapter 15 describes JUnit framework and walks reader through improving tests.
The chapter 16 walks reader through another refactor exercise.
The chapter 17 covers a number of smells, heuristics and anti-patterns. It deters use of poorly written comments, builds/tests that require more than one step. The chapter prohibits passing too many arguments to functions or use of output/flag arguments to functions. It encourages testing boundary conditions and respecting overridden safeties. It also proscribes writing code at wrong level of abstraction, i.e., exposing low-level logic through interface. The chapter also forbids exposing derivatives to base classes, having too much information in interface. Other smells include artificial coupling (sharing constants), feature envy (coupling formatting logic), flag arguments. This chapter also holds advice of Kent Beck about using explanatory (local/temporary) variables. The chapter recommends use of constants, structure over convention, encapsulate conditionals, avoiding temporal couplings, keeping functions at same level of abstraction and keeping configurable data at high levels. The chapter also contains Java specific is advice such as avoiding wildcards in imports, avoiding use of interface to inherit constants, etc. Finally, the chapter cautions against insufficient tests, use of coverage tool, testing boundary conditions, etc.
In conclusion, this book contains advice from wide range of authors on writing good maintainable code. As a coder with over 15 years of experience, I can attest that writing good code requires a lot of micro decisions and detailed attention to details and you generally have to continually improve and refactor your code for good design. Sometime it’s hard to maintain a good design when delivering features under tight deadlines so you may have to take shortcuts. Kent often talks about courage in XP and programmers needs to have courage to fight for writing maintainable code and take time to refactor existing code. Finally, I would caution against using these practices arbitrarily as another favorite rule of mine is that every practice or pattern depends on the situation. Unfortunately, there are lot of people in software industry including uncle bob who use these rules in draconian fashion such as always stressing on 100% test coverage or interfaces separate from implementation (DIP). Such advice may bring more money from consulting work or publication, but is disingenuous for practical use in real projects.