Clean Code – by Robert C Martin
Publisher Prentice Hall, (2009)
Buy it on Amazon
As the title suggests the book aims to teach programmers how to write better code. The goal is to help create code that is easy to read by humans and also easy to maintain and debug if necessary. The author is one of the creators of the Agile manifesto. “Uncle Bob” is very well known in the industry and really needs very little introduction. With over 40 years of coding experience there’s no doubt that his suggestions are sound and well founded.
The most disappointing aspect of the book is the illustrations, which seem quite unprofessional and somewhat puerile.
Clean code – bad code, total cost of owning a mess. This chapter includes comments from highly respected programmers like:
- Bjarne Stroustrup, invetor of C++
- Grady Booch, author of Object Oriented Analysis
- Dave Thomas, founder of IBM OTI Labs
- Ward Cunningham, inventor of Wiki
Meaningful names – use intention revealing names, pronounceable names add meaningful context. Avoid number series naming e.g. ( myVariable1, myVariable2, …)
– Functions – they should do one thing, perform one task. Also, stick to one level of abstraction and have no side effects or make changes in other parts of the application. Apply the DRY principle – Don’t Repeat Yourself.
– Comments – comments do not make up for bad code instead explain yourself with code. For example:
// check to see if employee is eligible for full benefits
if(employee.flags & HOURLY_FLAG) &&
(employee.age > 65))
Some comments cannot be avoided e.g. legal, informative comments, TODO comments. However these other types should be avoided: mumbling or vague comments, log comments from version control history, and of course commented out code – if the code is not useful simply remove it.
– Formatting – vertical size refers to average file size. How many lines of code in each file. Generally smaller files (less than 500 lines) are easier to understand.Since people read vertically, the top part of the code should give an idea what the rest of the file contains. More details should appear further down the page. Horizontal formatting and indentation improve readability, usually 45 characters per line is a good reference point. Avoid the need to scroll right.
– Objects and Data structures – data abstraction: private variables should not be exposed through getters and setters, since this defeats the whole purpose of being private.
The Law of Demeter: a module should not know about the innards of the objects it manipulates.
– Error handling – use exceptions rather than return codes, it’s better to throw a custom error message to handle errors that may be caused by undefined variables. It is advisable to write your Try-Catch-Finally statement first. In order to avoid repeated null checks – don’t return null. This is good as long as you have full control over all services consumed by your application. When using 3rd party code that does not live by this rule then null checks will still be required.
– Boundaries – When creating components or subsystems that will be used in public APIs it is better to encapsulate interfaces such as Maps. Avoid passing it as an argument or returning it.Learning boundaries – when you need to use a new 3rd party application or API it is advisable to set up unit tests that call the 3rd party code in order to understand how to use the code. This is essentially like running controlled experiments that reveal how the 3rd party API works.
– Unit tests – 3 laws of TDD:
- You may not write production code until you have written a failing unit test
- You may not write more of a unit test than is sufficient to fail, and not compiling is failing
- You may not write more production code than is sufficient to pass the currently failing test
Keep your tests clean, maintain the same quality standard as your production code. Test code must be modified along with production code so it needs to be as easy to maintain.
– Classes – The Java standard convention is that the Class should begin with a list of variables – static then instance. Encapsulation means that variables and functions should be kept private, classes should be small in order to follow the single responsibility principle. Usually developers aim to write code that works without making it clean and well organized. Getting software to work and making clean software are two different activities. It is always better to take the time to clean up code that is bug free and already works.
– Systems – You should separate construction from use – using the analogy of building a hotel – the construction phase involves equipment and construction workers and it is quite different from when the hotel is completed and being occupied by guests and hotel staff. One way to do this is to move construction code into a
main method or modules called by
main. In situations when the application needs to be responsible for when objects are created the Abstract Factory pattern comes into play. Dependency Injection is another mechanism for separating construction from use.
– Emergence – How to get clean via emergent design. Follow the four rules suggested by Kent Beck:
- Run all the tests
- Contain no duplication
- Express the intent of the programmer
- Minimize the number of classes and methods
– Concurrency – This is a decoupling strategy that helps to separate what gets done from when it gets done. It is only applicable in multi threaded languages like Java. Some myths and misconceptions include:
- Concurrency always improves performance – this is not always true
- Design does not change when writing concurrent programs – it can change remarkably
- Understanding concurrency issues is not important when working with a container such as Web or EJB container – you need to know just what your container is doing and when
The fact is that concurrency can be quite complex, even for simple problems
Concurrency defense principles:
Single Responsibility Principle
- Limit the scope of data – apply data encapsulation extensively and use the
synchronizedkeyword for shared data objects
- Use copies of Data – instead of multiple instances treat them as read only
- Bound resources – fixed size or number
- Mutual exclusion – only one thread has access at a time
- Starvation – a thread may be prevented from proceeding forever
- Deadlock – two or more threads waiting on each other
- Livelock – threads simultaneously trying to perform a task and “bumping into each other”
- Producer – Consumer: producer creates work and places in queue for consumers to consume
- Readers-Writers: a source of information for readers that gets updated by writers
- Dining Philosophers: using the analogy of philosophers sitting around a circular dining table. Each one needs two forks to eat and the forks are shared between adjacent philosophers. So each has to wait till the both philosophers next to him have stopped using the forks before he can eat. Each philosopher represents a thread and the forks are like bound resources.
- Obsolete comment – old and irrelevant or incorrect comments
- Redundant comment – describes something self explanatory
- Poorly written comment – a comment worth writing is worth writing well
- Commented out code – when you see commented out code, delete it!
- Tests require more than one step – you should be able to run all tests with just one command
- Dead Function – methods that are never called should be removed
- Duplication – apply the DRY principle (Don’t Repeat Yourself).
- Artificial coupling – things that don’t depend on each other should not be artificially coupled
- Function names should say what they do – if you have to look at the function implementation to know what it does, then it probably needs a better name
Execution Models – there are many ways to partition behavior, the following definitions will help to undertsand
The Execution Models are:
– Successive refinement – Programming is a craft (or an art) more than a science. To write clean code you first need to write dirty code and then clean it up. This chapter is a detailed look at the Args class written by the author. In order to make safe changes to your application it is advisable to use TDD (Test driven Development) approach. The unit and acceptance tests check to make sure the code is doing what it should and nothing else. These tests are always running throughout the process and any changes that fail must be discarded.
– Junit internals – this well known Java testing framework was initially written by Kent Beck and Eric Gamma. This chapter takes an in depth look into the
ComparisonCompactor module of JUnit, which is used to identify string comparison errors.
– Refactoring SerialDate – this class is part of the JCommon open source library by David Gilbert.The refactoring process involves first making the code work by passing all unit tests. Then making it right, by cleaning it up using the strategies discussed earlier in the book.
– Smells and heuristics – how to identify code that smells bad
So there it is, an overview of the entire book except the appendices which contain very intense coding exercises. The author goes through the long arduous process of refactoring some open source code. As the saying goes – practice make perfect. In order to write clean code you need to spend long hours reviewing and refactoring your code until all the "bad code smells" are eradicated.