Clean Code: A Handbook of Agile Software Craftsmanship
Clean Code: A Handbook of Agile Software Craftsmanship
Remember that code is really the language in which we ultimately express the requirements.
Bad Code
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 bad code that
brought the company down.
The degree of the slowdown can be significant. Over the span of a year or two, teams that were moving very fast at
the beginning of a project can find themselves moving at a snail’s pace.
Over time the mess becomes so big and deep and so tall, they cannot clean it up. There is no way at all.
Furthermore, they and everyone else on the team, are under horrific pressure to increase productivity. So they all
make more and more messes, driving the productivity ever further toward zero.
Now the two teams are in a race. The tiger team must build a new system that does everything that the old system
does.
If you have experienced even one small part of the story I just told you, then you already know that spending time
keeping your code clean is not just cost effective; it’s a matter of professional survival.
I like my code to be elegant and efficient. The logic should be straightforward to make it hard for bugs to hide, the
dependencies minimal to ease maintenance, error handling complete according to an articulated strategy, and
performance close to optimal so as not to tempt people to make the code messy with unprincipled optimizations.
Clean code does one thing well.
Grady Booch, author of Object Oriented Analysis and Design with Applications
Clean code is simple and direct. Clean code reads like well-written prose. Clean code never obscures the designer’s
intent but rather is full of crisp abstractions and straightforward lines of control.
Clean code can be read, and enhanced by a developer other that it original author. It has unit and acceptance tests. It
has meaningful names. It provides one way rather than many ways for doing one thing. It has minimal dependencies,
which are explicitly defined, and provides a clear and minimal API. Code should be literate since depending on the
language, not all necessary information can be expressed clearly in code alone.
I could list all 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 cares
deeply about the craft.
You know you are working on clean code when each routine you read turns out to be pretty much what you expected.
You can call it beautiful code when the code also makes it look like the language was made for the problem.
We Are Authors
You cannot write code if you cannot read the surrounding code. The code you are trying to write today will be hard
or easy to write depending on how hard or easy the surrounding code is to read. So if you want to go fast, if you
want to get done quickly, if you want your code to be easy to write, make it easy to read.
The Boy Scout Rule
If we all checked-in out code a little cleaner than when we checked it out, the code simply could not rot. The cleanup
doesn’t have to be something big. Change one variable name for the better, break up one function that’s a little too
large; eliminate one small bit of duplication.
Meaningful Names
It is easy to say that names should reveal intent.
The name of a variable, function, or class should answer all the big questions. It should tell you why is exists, what it
does, and how it is used. If the name requires a comment, then the name does not reveal its intent.
When there is no “programmer-eese” for what you’re doing, use the name from the problem domain. At least the
programmer who maintains your code can ask a domain expert what it means.
The hardest thing about choosing good names is that it requires good descriptive skills and a shared cultural
background. This is a teaching issue rather than a technical, business or management issue.
People are also afraid of renaming things for fear that some other developers will object. We do not share that fear
and find that we are actually grateful when names change (for the better).
Functions
The first rule of functions is that they should be small.
A function should not be large enough to have a nested structure more than 2 levels deep. Consider splitting it.
The smaller and more focused a function is, the easier it is to choose a descriptive name.
The ideal number of arguments for a function is zero. Testing complexity increases with argument count.
Every system is built from a domain-specific language designed by the programmers to decide that system. Functions
are verbs of that language, and classes are the nouns.
Master programmers think of systems as stories to be told rather than programs to be written. They use the facilities
of their chosen programming language to construct a much richer and more expressive language that can be used to
tell that story.
Comments
Don’t comment bad code – rewrite it- Brian W Kernighan
One of the more common motivations for writing comments is bad code. We write a module and we know it is
confusing and disorganized. So we say to ourselves, “Ooh, I’d better comment that!” No! You’d better clean it!
Consider expressing the software intention in code first and then comment if necessary second. E.g. function and
variable descriptive names.
Does the comment relate to code that could be its own purpose named method instead?
Are there any legal, licence, copyright or privacy considerations to comment or link to?
Review and update comments if you are changing how the software operates.
https://en.wikipedia.org/wiki/Comment_(computer_programming)
Formatting
Code formatting is about communication, and communication is the professional developer’s first order of business.
The functionality that you create today has a good chance of changing in the next release, but the readability of your
code will have a profound effect on all the changes that will ever be made. The coding style and readability set
precedents that continue to affect maintainability and extensibility long after the original code has been changed
beyond recognition. Your style and discipline survives, even though your code does not.
Add a blank line after declarations and functions. This is a visual cue to a new concept.
In general we want function call dependencies to point in the downward direction. That is, a function that is called
should be below a function that does the calling. This creates a nice flow down the source code module from high
level to low level.
In any given system we will sometimes want the flexibility to add new data types, and so we prefer objects for that
part of the system. Other times we will want the flexibility to add new behaviours, and so in that part of the system
we prefer data types and procedures. Good software developers understand these issues without prejudice and
choose the approach that is best for the job at hand.
Error Handling
Error handling is just one of those things that we all have to do when we program. Input can be abnormal and
devices can fail. In short, things can go wrong, and when they do, we as programmers are responsible for making
sure that our code does what it needs to do.
It is good practice to start with a try-catch-finally statement when you are writing code that could throw exceptions.
This helps you define what the user of that code should expect, no matter what goes wrong with the code that is
executed in the try.
Try to write test that force exceptions, and then add behaviour to your handler to satisfy your tests. This will cause
you to build the transaction scope of the try block first and will help you maintain the transaction nature of that
scope.
Each exception that you throw should provide enough context to determine the source and location of an error.
Create informative error messages and pass them along with your exceptions. Mention the operation that failed and
the type of failure.
We can write robust clean code if we see error handling as a separate concern, something that is viewable
independently of our main logic.
Boundaries
We seldom control all the software in our systems. Sometimes we buy third-party packages or use open source.
Other times we depend on teams in our own company to produce components or subsystems for us.
Consider creating a boundary class to handle arguments between your system and a public API.
In learning tests we call the third-party API, as we expect to use it in our application. We’re essentially doing
controlled experiments that check out understanding of that API. The tests focus on what we want out of the API. In
learning tests we call the third-party API, as we expect to use it in our application. We’re essentially doing controlled
experiments that check out understanding of that API. The tests focus on what we want out of the API.
Unit Test
First Law You may not write production code until you have written a failing unit test.
Second Law You may not write more of a unit test than is sufficient to fail, and not compiling is failing.
Third Law You may not write more production code than is sufficient to pass the currently failing test.
These three laws lock you into a cycle that is perhaps thirty seconds long. The tests and the production code are
written together, with the test just a few seconds ahead of the production code.
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.
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.
Readability is perhaps even more important in unit tests than it is in production code.
Perhaps a better rule is that we want to test a single concept in each test function.
F.I.R.S.T
Fast Tests should be fast
Self-Validating The test should have a Boolean output. Either pass or fail.
Classes
The name of a class should describe what responsibilities it fulfils. If we cannot derive a concise name for a class,
then it’s likely too large.
A class should begin with a list of variables, public variables first then private variable.
The second rule of classes is that they should be smaller than that.
With functions we measured size by counting physical lines. With classes we use a different measure. We count
responsibilities.
Each small class encapsulates a single responsibility, has a single reason to change, and collaborates with a few
others to achieve the desired system behaviours.
Each of the methods of a class should manipulate one or more of the class variables. This help with cohesion within a
class and logical structure of the software.
Systems
Complexity kills. It sucks the life out of developers, it makes products difficult to plan, build and test
Software system should separate the Startup process, when the application objects are constructed and the
dependencies are “wired” together, from the runtime logic that takes over after Startup.
An optimal system architecture consists of modularized domains of concern. The different domains are integrated
together with minimally invasive Aspect or Aspect-like tools. This architecture can be test-driven, just like code.
Modularity and separation of concerns make decentralized management and decision making possible. In a
sufficiently large system, whether it is a city or a software project, no person can make all the decisions.
Test the code structure architecture domains of concern and domain logic. Software system intent should be clear
and high and low levels.
Emergence
Simple Design
1. Runs all the tests
2. Contains no duplication
3. Expresses the intent of the programmer
4. Minimizes the number of classes and methods
Code should clearly express the intent of its author. The clearer the author can make the code, the less time others
will have to spend understanding it. This will reduce defects and shrink the cost of maintenance.
Concurrency
Objects are abstractions of processing. Threads are abstractions of schedule.
Concurrency is a decoupling strategy. It helps us decouple what gets done from when it gets done. In single-
threaded applications what and when are so strongly coupled that the state of the entire application can often be
determined by looking at the stack backtrace.
Decoupling what from when can dramatically improve both the throughput and structures of an application.
Consider, for example, the standard “Servlet” model of Web applications. The servlets are executed asynchronously
whenever Web requests come in. In principle, each servlet execution lives in its own little world and is decoupled
from all the other servlet executions.
Write tests that have the potential to expose problems and then run them frequently, with different programmatic
configurations and system configurations and load. It test ever fail, track down the failure. Don’t ignore a failure just
because the tests pass on a subsequent run.
Concurrent code is difficult to get right. Code that is simple to follow can become nightmarish when multiple threads
and shared data get into the mix. If you are faced with writing concurrent code, you need to write clean code with
rigor or else face subtle and infrequent failures.
Successive Refinement
One of the best ways to ruin a program is to make massive changes to its structure in the name of improvement.
Some programs never recover from such “improvements.” The problem is that it’s very hard to get the program
working the same way it worked before the “improvement.”
To avoid this, I use the discipline of Test-Driven Development (TDD). One of the central doctrines of this approach is
to keep the system running at all times. In other words, using TDD, I am not allowed to make a change to the system
that breaks that system. Every change I make must keep the system working as it worked before.
To achieve this, I need a suite of automated test that I can run on a whim and that verifies that the behaviour of the
system is unchanged.
So the solution is to continuously keep your code as clean and simple as it can be. Never let the rot get started.
Smells and Heuristics
Comments
Functions
• Too many arguments
• Output arguments
• Flag arguments (Boolean)
• Dead function (unused)
General
Names
Tests
• Insufficient tests
• Use a coverage tool
• Don’t skip trivial tests
• Test boundary conditions
• Exhaustively test near bugs
• Patterns of failure are revealing
• Test should be fast