How many programmers, so many definitions, what is clean code. Often, when interviewing, we hear that good code is one that is easy to read. We agree, but as our personal experience suggests, this is just the tip of the iceberg.
Clean Code: Cause & Effect.
The first sign that informs us that the code is no longer clean is an increase in the development time for new functionality and an increase in the regression scale at the slightest change in the system. This is a consequence of the fact that technical debt accumulates, the components in the system are very closely related, and there are no autotests. The reasons for this may be:
- External - such as pressure from the business and its desire to get new functionality faster.
- Internal - poorly established development processes and interaction within the team, lack of control and development standards or a banal lack of competence.
You should not put the work on the quality of code and architecture on the back burner. During a sprint, it is important to be able to separate tasks that have direct business value (tasks with new functionality) and technical tasks that have only an indirect effect on the business. The proportion of the split depends on the current state of the project, the time frame and the number of free hands.
What is Clean Code?
It turns out that to say that the code is clean and the system is designed correctly, easy reading of the code is not enough. It should have other qualities as well:
- The code is easy to modify. With the right design and architecture, extending your code can be done without much time and expense. The entities of the code should not be closely related to each other; the code should be somewhat abstract and self-sufficient. Each entity that we operate during the development should be responsible only for its own part of the functionality.
- The code must be stable, predictable, secure, and reliable. No matter how simple the code is to read, it should be covered by tests. Good code and tests are always there. Moreover, it is not only the number of tests that is important, it is also their quality. With this code, there are no problems when running and debugging, it does not cause changes in the environment.
- Protected code. When writing any code, you must not forget about the overall security of the product. We recommend that you get to know the basic principles of security and stick to them. If we're talking about web projects, we recommend OWASP.
- A good code is a code that isn't there. This does not mean that all the code should be written on one line, and you should definitely be proud of subtle methods. This means that the code should not be duplicated, and some of the common things should remain at the abstraction level. In theory, simplifying your code should lead to fewer defects.
- Reading the code itself is also important. Each developer has their own writing style, and the level of reading depends on our experience. We all want to write a simple, beautiful and concise code.
To get away from the subjective assessment of the quality of your code, in 1961, the term "code smells" or "code with a smell" was introduced. This is a group of rules and guidelines that clearly define whether it's time to refactor. Odor leads to code decay, and developers should always strive to eliminate it. The need for refactoring directly follows from the presence of a code smell, and by preventing the cause, we can avoid the consequence. You can find more information on the key signs that it's time for you to refactor in Martin Fowler book Refactoring: Improving the Design of Existing Code.
Should you write clean code?
It's definitely worth doing it! But it is not always worth paying too much attention to cleanliness.
Do not forget about the feasibility and lifetime of your code. For example, if you are faced with the task of developing a concept - PoC (Proof of Concept), and you prove that the selected technology stack fulfills the task, your code will become irrelevant in a week or two. You shouldn't waste your energy on improving this functionality.
There is an opinion that there is no need to monitor the quality of the code or part of the system that will soon be replaced. And this is not true for several reasons. High quality workmanship will make the transition or integration with new parts easier, smoother and faster. It will surely make life easier in cases where several versions of the code have to be supported at the same time. The number of regression errors with clean code will be several times less. Also, do not forget that nothing is more permanent than a temporary solution. Perhaps the tasks for improving this part of the code will lie in the backlog for several months.
What can help improve your code?
Most programmers dream of writing code quickly and as beautifully as possible, and so that everything works perfectly the first time. But not everyone succeeds in making the code not only workable, but also understandable. How do you succeed in writing clean code? There are two ways - self-organization and teamwork.
Let's look at several possible ways to improve individual code quality. These guidelines are suitable for any developer level.
- 1. Instruments
Pay attention to the tools used, especially the main one - the development environment. A convenient tool is half the battle. The IDE (Integrated Development Environment) not only complements the code on the fly, but also suggests smelling places to tidy up. By extending the IDE with the necessary plugins, you get a Swiss army knife. You do most of the work in this tool - make it as convenient and flexible as possible.
Don't miss out on the opportunity to purchase your favorite product. Fighting licenses will save you some time, and official support will never be superfluous.
- 2. Participation in open source projects.
In teamwork, the ability to work with someone else's code is important. Understanding, reading, and adhering to a style is not always easy. Working with someone else's code, it is often possible to learn about new approaches to solving non-trivial problems. Read and study the code!
- 3. Strict framework
Start developing a small project that solves a specific problem. Design the architecture yourself and implement it. In doing so, you can set yourself technical limits, for example, development using only OOP, the cyclomatic complexity of methods not more than 10, compliance with all development recommendations in a given language, conscious use of design patterns, etc.
Get into the habit of working within limits. After all, there will not always be someone nearby who can monitor the quality of your work.
- 4. Development of abstract thinking
Read and use programming patterns. They are not tied to a specific language and help to solve problems more efficiently. You will have the same understanding of problem solving design with other developers. You will have a better understanding of how third-party tools and libraries work.
Solve programming puzzles. This is a great way to improve your programming skills and learn the intricacies of your chosen language.
- 5. Decision analysis
Do not rush to solve problems head-on. Ask senior developers questions and ask yourself questions too. It is always important to understand the causal relationship of certain decisions. By understanding the problem well, you can effectively solve it.
A good developer is not an artisan who writes code, but an engineer who combines applied research, planning, and design in his work.
- 6. Reading the documentation
Being able to read the documentation is just as important as reading the code. The next step is to learn how to write documentation.
- 7. Practice!
Any experience is better than no experience.
Most of the tasks are solved in a team. It is very important to share responsibility for quality among its participants. The larger the team, the more difficult it is to keep the product in good shape. Let's take a look at several approaches to retaining code in the conditions mentioned above.
- 1. Code review
This is a simple principle to understand and follow. At least two people, including the author of the code, review the code.
There are a few things to keep in mind when reviewing your code:
One is to check if the code violates the rules of the coding convention. This is a process that can and should be automated using static analyzers in CI (Continuous Integration).
Others are code maintainability and error handling that cannot be checked automatically.
Last but not least, the code should be checked for completeness. Does this code snippet contain the entire scope of the function as intended?
- 2. Continuous integration
The essence of continuous integration is that it allows you to get a lot of feedback on the current state of your code quickly.
Continuous integration works when you follow two simple rules:
Product assembly is fast. Avoid slow builds. Continuous Integration improves the quality of your code by providing quick feedback. If the tests fail, the build will fail, and you are instantly notified.
You add static analyzers to your build script that check coding conventions, improve code quality, and check security.
- 3. Coding Conventions
It is important to have a list of coding conventions. But before you start making the list, everyone on the team must understand the significance of this agreement. Do not expect such an agreement to be adopted the first time, there will be a lot of discussions.
Make a list of coding conventions in which you define how variables should be declared, naming conventions, and so on. The number of rules you can add to this list is unlimited and may vary. Just do what works for you and your team. Feel free to add new rules to the agreement list if the team is happy with it. The same goes for removing agreements from the list.
Once you've got your list of coding conventions, it's imperative to stick to them. The preferred way is to check coding conventions with static analyzers and continuous integration, since it doesn't require any manual steps.
Good code can speed up long-term software development. It is reusable and developers don't have to waste time fixing old bugs. It also makes it easier for new people to join the project.
- 4. Tests
The fewer errors in the code, the higher its quality. Rigorous testing filters out critical errors and ensures that the code works as intended.
Having a clear testing strategy is important when it comes to improving code quality. At a minimum, your code should be modular. It's even better if you want to use other methods, such as integration or regression testing.
The largest number of tests in a software project should be unit tests. They are cheap and fast. There are many different tools that can help you create unit tests and code coverage reports. Running a test suite and generating a code coverage report can be done automatically through continuous integration. It is even possible to make a build fail if the code coverage does not meet the required percentage.
- 5. Error analysis
Having bugs in your code is probably inevitable. Therefore, analyzing and handling these errors is very important. If you want to improve your skills, it's important to learn from your mistakes.
When an error occurs, analyze it with a few questions:
Is this a low or high priority bug? If it is high priority, it should be corrected immediately. If the bug is minor and allows the product to complete the task without major problems, the bug may be fixed in future iterations.
- Did something go wrong?
- Why haven't we checked this (right)?
- Where else does this happen?
- And most importantly, how can we prevent this from happening in the future?
Of course, there are tools to help you track errors. You can choose the tracker available on the market that suits your needs.
- 6. Collecting metrics
There are several metrics you can use to quantify the quality of your code. SonarQube easily copes with this task. It will easily help you collect all the important metrics you need:
The number of defects and their severity are important indicators of overall quality. Finding errors can and should be automated, but only partially. The code review remains valid to identify deeper errors in the code's logic itself.
Repetitions of code sections
Each piece of knowledge should have a single, consistent and authoritative representation within the system - the DRY principle (Don’t repeat yourself).
Complexity is often measured using the cyclomatic complexity metric. This is an indicator of the number of linearly independent paths in the program code. There is a correlation between the number of cyclomatic complexity and the defect rate. In theory, simplifying your code should lead to fewer defects.
Availability of required comments
Just a few well-spaced lines of comments, a comment to a module, class or method will be enough to make the code much clearer.
Used when testing software. It shows the percentage of the program's source code that was executed during testing. Set the standard below which the percentage of your tests does not fall.
- Potential errors
Errors in code are akin to a carbon footprint. It is absolutely impossible to avoid, and the extra exhaust by itself will not kill either humanity or the surrounding nature. Nevertheless, it seems like a natural necessity today to reduce the negative effect of your stay on the planet. In much the same way, writing clean code is the responsibility of every developer. Regardless of which path you choose, you should strive to write code that works and is understandable.
It's good if we manage not to turn purity into a fetish, given the lifespan of our code and the feasibility of further improvements. The main thing to remember is people: users who can be let down by a sudden failure of even a small part of the system we have developed, and the engineers who have to support this system.