What is Clean Code, which values and principles apply during implementation and what are the challenges?
Clean Code Definition
Clean Code is a term from software development and addresses the clear, comprehensible, traceable, logical and disciplined implementation of code. The goal is to produce software efficiently and effectively, and to design the code so that it is easily
- and maintainable.
In today’s software development practice it is not uncommon for working code not necessarily to be “clean”. This quickly leads to errors during adaptations or problems with extensions, for example when a small change leads to a cascade of subsequent changes. The maintenance and further development of software is unnecessarily complicated. The approach propagated by the Clean Code Developer School1 counteracts such problems by
- carrying out an analysis first followed by a design – called Clean Analysis and Clean Design – independently of the type of project (greenfield or brownfield),
- developing incremental in close cooperation with the customer while learning and being reactive (in one word: agile),
- defining guiding concepts – values, principles and practices – which are fundamental to development and must be taken into account accordingly.
This approach avoids technical debt or at least keeps it as low as possible. Although the effort required to implement clean code is comparatively high initially, in practice it pays for itself very quickly over the lifecycle of the software. Clean code is therefore not only technologically sustainable, but also makes economic sense.
Clean Code Development
While the early works of Robert C. Martin “Clean Code: A Handbook of Agile Software Craftsmanship”2 or Dustin Boswell “The Art of Readable Code”3 focused primarily on the simple, textual form of code, the Clean Code Developer School takes a broader approach: it looks at the development process and defines values and numerous principles and practices. Above all, however, it recognises that an agile approach and structural considerations before implementation – namely analysis and design – are necessary. Without an appropriate structure, the implementation of non-functional requirements is very difficult or even impossible.
Furthermore, the structure of a software provides the basis for an evolution based on new requirements. Without structure, the costs for new features increase exponentially. In addition, it is difficult to add a structure at a later stage. In the course of Clean Code Development, the saying applies: First the analysis and design, then the implementation. Or, to put it another way: first think cleanly, then implement cleanly.
Clean Code Values
The approach to implementing clean code is based on a foundation of four values:
- Production Efficiency
- Continuous Improvement
Software lives. Often even longer than originally assumed. It is improved, adapted, extended. The easier a software can be adapted to changing requirements and conditions, the more changeable it is. This evolvability can be increased by using different design principles that influence the software architecture, such as
- the abstraction of information,
- the separation of responsibilities,
- the use of interfaces
- or the coupling of components.
Ideally, changeability is taken into account from the very beginning of software development, because the later it comes into focus, the more complex, expensive and difficult it becomes to adapt.
Correctness as a value may sound banal at first. But there is more behind it than just the correct execution of functions, the correct use of classes and methods, or the application of principles and practices of software development. Correctness also addresses the handling of requirements.
Often developers do not even know the requirements of customers. They do not know any acceptance criteria. They do not know any framework conditions or the system context, no application scenarios and no non-functional requirements. How can one succeed in paying attention to correctness? Instead of trusting (or hoping) that a test department will find possible errors, developers should understand what customers actually want before implementation. Only when they know this can they implement applications correctly.
Production efficiency as a value considers two aspects of software development:
- Software development causes effort and costs. Code that is difficult to read, modify, extend or maintain extends development time and is inefficient. The more extensive and the longer a software is developed, the more these problems become significant. Production efficiency means that a software can be developed for years, because the emphasis is on clean code. The alternative would be a new development instead of an enhancement – and often this is not a very attractive alternative.
- Production efficiency is a measure of the other values. On the one hand, for example, in order to avoid endlessly increasing the effort required to ensure the correctness of software, and on the other hand, aspects such as gold plating, where more is developed with a positive intention than was agreed with the client, or overengineering, where software is developed and delivered in higher quality than required by the client.
The value “continuous improvement” does not address the improvement of the implementation as such, because this aspect is already part of the evolvability. The continuous improvement requires the developers to deal with their way of working. For example, the following questions could come into focus:
- What experiences were made with pair programming or mob programming?
- What are the causes of code smells?
- How can the cooperation within the team and with customers be improved?
- What are our learnings in code reviews?
- What should be improved in the next iteration?
- How can code katas be optimised?
Continuous improvement is therefore a value that revolves around the reflection of the team.
Which of these values should developers look for first? Primarily, the values are not in an explicit order that developers can follow. One value is not more important than another. Nevertheless, changeability is consciously the first priority. It stands for a basic attitude, an openness to future requirements and evolutions, even if these are of course not yet known. Just as Safety First applies in aviation, changeability should be the first thing that applies in software development.4
Clean Code Principles and Practices
Even if the values provide orientation for software developers, they are too abstract for concrete implementation. That is why there are principles and practices:
- Principles are guidelines for structuring software. They complement each other. Ideally, a maximum number of principles is always observed. If software is developed contrary to these principles, the effects will become apparent in the medium term at the latest, for example, through increasing costs for adjustments. An implementation tends to reveal whether a principle has been observed or not.
- Practices are instructions for action. They are methods and techniques that are used continuously. Some practices require the use of tools. And it is not always possible to recognise from the implementation whether a practice has been implemented or not.
Below you will find an overview of the principles:
DRY – Dont’ Repeat Yourself
The DRY principle aims at reducing the repetition of software patterns, replacing them with abstractions or using data normalisation to avoid redundancies.
KISS – Keep It Simple and Stupid
“Make it as simple as possible” the KISS principle says. It demands not to see or implement things too complicated, and always to look for or use the simplest solution to a problem.
Beware of Optimisations
Optimisations often cause high expenses, they are often neither necessary nor useful. A simple rule therefore is: Beware of optimisation. And if you do optimise, do it after a thorough analysis.
SRP – Single Responsibility Principle
The SRP principle addresses the software design according to which a module with its classes, functions, variables and data structures should be responsible to only one actor.
SLA – Single Level of Abstraction
The SLA principle states that different levels of abstraction of functions should not be mixed in order to increase the readability, understandability and maintainability of the code.
Source Code Conventions
Source code conventions define guidelines that help to structurally improve software quality. They include e.g. naming rules, comments, declarations, statements, white spaces etc.
SoC – Separation of Concerns
The SoC principle calls for a class to concentrate on a specific aspect in order to be able to test individual concerns separately and to make adjustments in a manageable way.
DIP – Dependency Inversion Principle
The DIP propagates that modules of higher levels do not depend on modules of lower levels, modules depend on abstractions and abstractions should not depend on details but details on abstractions.
ISP – Interface Segregation Principle
The ISP principle states that interfaces have a high degree of cohesion and should therefore only contain elements that belong together. The aim is to make the coupling between components “lean” and clear.
POLA – Principle of Least Astonishment
The Principle of Least Astonishment, sometimes called POLS – Principle of Least Surprise, states that a user interface should always be designed so that the user experiences as few surprises as possible.
LSP – Liskov Substitution Principle
The CSP principle states that derived subtypes must behave like their basic types, whereby subtypes may extend the functionalities of the basic types but not restrict them.
OCP – Open Closed Principle
The OCP principle deals with the extensibility of modules, classes, methods etc., according to which they should be open for extensions and closed for modifications.
Information Hiding Principle
According to the information hiding principle, the inner workings of components should be encapsulated or hidden by interfaces in order to avoid unnecessary dependencies and extensive adjustments.
LoD – Law of Demeter
The Law of Demeter, also known as the Principle of Least Knowledge, states that objects should only communicate with objects in their immediate environment in order to reduce coupling.
TDA – Tell, Don’t Ask Principle
The TDA principle is about bundling data with the functions that work with this data. Instead of asking an object for data in order to act with this data, the object should be told what it should do.
Design and Implementation don’t overlap
In practice, design and implementation often diverge. Ideally, inconsistencies should be minimised by separating the responsibilities between design or architecture and implementation.
Implementation Reflects Design
Implementation should not exist independently of design or architecture. For example, components defined in the architecture should also be physically separated in the code.
YAGNI – You Ain’t Gonna Need It
The YAGNI principle demands that functionality is only implemented when it is actually needed. Often requirements for features are still too imprecise or they turn out to be unnecessary later. In case of doubt (premature, unnecessary) efforts should therefore be avoided.
Together, the Single Responsibility Principle, the Open Closed Principle, the Liskov Substitution Principle, the Interface Segregation Principle and the Dependency Inversion Principle are also known as the SOLID Principle. In his book “Agile Software Development: Principles, Patterns and Practices” Robert C. Martin considers these principles to be essential for the development of clean code.5
Below you will find an overview of the practices:
Observe Boy Scout Rule
The boy scout rule says: “Always leave a place in a better condition than you found it.” Thus, small things in the code should be improved and bugs should be fixed before they mutate into bigger problems.
Carry Out a Root Cause Analysis
Root Cause Analysis focuses on the elimination of causes rather than the elimination of symptoms. This is much more efficient in the medium term.
Use Version Control System
Version management deals with the administration of files including archiving, logging, recovery and access coordination. Software development without a version control system is unthinkable.
Apply Simple Refactoring Patterns
Refactoring addresses the restructuring of a software while retaining the range of functions. Extracting methods or renaming incomprehensible names are simple refactoring patterns.
Reflection is the prerequisite for active learning. Practice shows, however, that it only takes place on a daily basis if it is actively scheduled in the calendar, otherwise it falls victim to the daily business.
Use Automated Integration Tests
Integration tests check the cooperation of different components, e.g. after a refactoring or extension. Automation provides the required efficiency during testing.
Reviews increase the quality of the code, whether in the course of pair programming, peer reviews or code reviews. The aim is the continuous improvement of the code quality.
Read and Educate Yourself
Software techniques, methods, frameworks, tools – many things evolve in the context of software development. Here it is advisable to read a lot (blogs, articles or books) to stay up to date.
Use Automated Unit Tests
Unit tests check whether the developed components work as intended. For example, individual classes or methods are tested. The automation of the tests reduces the test effort.
For the isolated testing of individual components, dependencies must be eliminated. Mockups or mock objects are test dummies that interact with the component to be tested, for example because the desired object is not yet available.
Analyse Code Coverage
Code coverage expresses what part of the source code – statements, branches, paths and conditions – is executed by test cases. The aim is to find the parts that are not yet tested.
Carry out Complex Refactoring
It is not possible to implement code directly in an optimal form. In addition to the simple ones, there are also more complex refactoring techniques6, whose effect can only be checked by automated tests.
Take Part in Professional Events
In order to educate yourself in the field of software development, in addition to reading technical literature, it is important to exchange ideas with other developers – for example at meetings, in user groups or at conventions.
Pursue Continuous Integration
The continuous integration of components into an application – for example in the form of daily builds – offers the advantage of diagnosing incompatibility and integration problems quickly and not only at the end of an iteration.
Static code analysis helps, for example, to check correctness by means of automated tests or to determine compliance with requirements. The changeability of a software can also be partially determined by metrics.
Use Inversion of Control Container
The IoC Container helps to instantiate and connect many small objects that are created as a result of the SoC principle. It also helps to reconfigure classes for test cases.
Pass on Experience
Having knowledge is good, passing on knowledge is very good. The idea behind it: only through the transfer of knowledge does true reflection and the penetration of a specialist topic take place. And everyone involved benefits from this.
One of the goals of software development is to minimise the number of errors reported by customers after the release of a new version. The comparability of the measurement is more important than precision.
Conduct Iterative Development
Development in iterations and short feedback cycles are two essential success factors of software development today. As a result, the risk of erroneous developments decreases and the quality of the software increases.
Develop in a component-oriented way
Component-oriented development promotes productivity through parallel implementation, improves the clarity of the application and facilitates testing of the individual components.
Test first propagates that interfaces and a correspondingly desired behaviour are described by tests. This approach simultaneously produces specification documentation in the form of executable code that can be automatically checked.
Tips for Using Clean Code
And how do values, principles and practices come together? What are the tips for implementing Clean Code?7
- General tips
Follow the conventions. Reduce complexity as much as possible. The simpler the code, the better. Act like a boy scout and leave the code better than you found it. And always look for the cause and not the symptom of a problem.
- Design tips
Keep configurable data at a high level. Use polymorphism instead of if/else or switch/case. Use separate multithreading code. Avoid over-configurability and use dependency injection. Follow the Law of Demeter.
- Tips for comprehensibility
Be consistent and implement similar things in the same way. Use explanatory variables. Encapsulate boundary conditions, as they are often difficult to understand. Prefer dedicated value objects over primitive types. Avoid logical dependencies, e.g. do not write methods that work correctly depending on something else in the same class. And avoid negative conditions.
- Tips for names
Use descriptive, unambiguous, meaningful, pronounceable and searchable names. Replace magic numbers with named constants and avoid encodings.
- Tips for functions
Keep functions small by doing just one thing. Use descriptive names and use as few arguments as possible. Avoid side effects.
- Tips for comments
Try to let the code speak for itself. Do not comment on your code. If you do comment, do it to explain the intention or to point out possible side effects.
- Tips on the structure of the source code
Declare variables near their usage. Dependent and similar functions should be close together. Place functions in a downward direction, avoid horizontal alignment. Keep lines of code short, use white spaces to associate or disassociate aspects and use on indentations.
- Tips for tests
Make tests readable, fast, independent, repeatable and automated. And only test one aspect per test at a time.
The Continuous Engagement with Clean Code
Changeability, correctness, production efficiency and continuous improvement are the four values of Clean Code. To postulate them as advantages or goals sounds logical and sensible, but in the reality of software development the implementation is anything but easy. Clean code cannot be easily measured and therefore cannot be implemented in passing. This is where the principles and practices come into play. However, this is combined with the willingness to continuously deal with clean code, its meaning and its concrete implementation. Only if the focus is on clean implementation can it succeed in the long term. In this case less is actually less and more is actually more. In modification of one of the principles one could say: You gonna need it!
Impulse to discuss:
Clean code is not more important than functional or performant code, but it is also not less important. And in practice clean code cannot be developed at all.
Notes (some of them in German):
 Here you will find information about the Clean Code Developer School and its view on sustainable productivity in software development.
 Robert C. Martin: Clean Code: A Handbook of Agile Software Craftsmanship
 Dustin Boswell: The Art of Readable Code: Simple an Practical Techniques for Writing Better Code
 Die Clean Code Developer School: Die Anforderungen auf die Füße stellen
 Robert C. Martin: Agile Software Development: Principles, Patterns and Practices
 Martin Fowler: Online Catalog of Refactorings
 Wojtek Lukaszuk: Summary of tips
There is a community on XING where people talk about aspects of clean coding.
Here you can find additional information from the t2informatik blog: