Table of contents
Principles
Single Level of Abstraction
Maintaining a level of abstraction promotes readability.
Changeability | |
---|---|
Correctness | |
Production efficiency | |
Continuous improvement | |
Single Developer |
A line of code can be at different levels of abstraction. The assignment of a value to a variable is at a lower level of abstraction than a method call, for example. After all, there can be far more logic behind the method call than in the assignment of a variable. Even method calls can be at different levels of abstraction. Calling a method from a framework is at a different level than calling a method from the application.
To ensure that code is easy to read and understand, only one level of abstraction should be used in a method. Otherwise, the reader will find it difficult to distinguish essentials from details. If bit tweaks are required, these should not be mixed with the calling of methods.
A helpful analogy is to look at articles in the daily newspaper: at the top of the page is the most important thing, the headline. It should give a rough idea of what the article is about. In the first sentence of the article, this is described at a high level of abstraction. The further you go in the article, the more details emerge. We can structure our code in the same way. The name of the class is the heading. This is followed by the public methods at a high level of abstraction. These may call methods at a lower level, until finally the "bit wicket methods" remain. This division allows me as the reader of the class to decide what level of detail I want to look at. If I am only roughly interested in how the class works, I only need to look at the public methods. In them, the functionality is solved at a high level of abstraction. If I am interested in more details, I can go deeper and look at the private methods.
Literature sources: Clean Code, page 36ff.
Single Responsibility Principle (SRP)
Focus facilitates understanding. A class with exactly one task is easier to understand than a general store.
Changeability | |
---|---|
Correctness | |
Production efficiency | |
Continuous improvement | |
Single Developer |
The Single Responsibility Principle (SRP) is one of the SOLID Principles. It reads: A class should only one have responsibility.
The background to the single responsibility principle is the idea that changes or extensions to the functionality of an application should be limited to just a few classes. The more classes that have to be adapted, the greater the risk that the necessary changes will cause problems in places that are not essentially related to the extension. A violation of the single responsibility principle leads to coupling and thus to increased complexity, making it more difficult to understand the code.
Separation of Concerns (SoC)
If a code unit does not have a clear task, it is difficult to understand it, apply it and, if necessary, correct or extend it.
Changeability | |
---|---|
Correctness | |
Production efficiency | |
Continuous improvement | |
Team |
Translated as separation of concerns, this principle means that several concerns should not be grouped together in one class. What are concerns? Interests are "completely different" purposes. It is also said that concerns are orthogonal to each other and, above all, orthogonal to the main functionality of a functional unit. Examples of typical concerns are Tracing, logging, transactionality, caching. These concerns should be outsourced to specialized functional units according to the principle of separation of concerns.
The separation of concerns principle is closely related to the single responsibility principle. Concerns are a superset of responsibilities. Ideally, each responsibility consists of exactly one concern, namely its core functionality. However, several concerns are often mixed together in one responsibility. As this is usually technically unavoidable, the principle does not state that a responsibility may only consist of one core, but that the concerns should be separated. Within a method, for example, it should be clearly recognizable that there are several concerns. Furthermore, the concerns should not be scattered throughout the method, but grouped in such a way that it is clear what belongs to a concern.
In domain-driven design, for example, an attempt is made to strictly separate the business domain from the infrastructure. This means that a class from the business domain must not contain any infrastructure, such as for database access, but should only represent the business logic. Persistence is a "concern" that has nothing to do with the business logic. Separation of concerns leads to loose coupling and high cohesion. The individual components are each focused on one task, one concern, and are therefore easy to understand. All parts that make up the component are aligned to this one task, so the parts are closely related (high cohesion). Separation of concerns also leads to components that are easy to test. This is because if the purpose of a code unit is focused, less broad testing is required. Fewer test parameter combinations need to be tested in relation to the code unit to be tested. If the separation of concerns is to be pursued consistently, object orientation must be expanded to include the concept of aspect-oriented programming (AOP). This makes it possible to completely extract aspects such as transactionality, tracing or caching from a method.
Source Code Conventions
Code is read more often than it is written. Therefore, conventions are important that support fast reading and understanding of the code.
Changeability | |
---|---|
Correctness | |
Production efficiency | |
Continuous improvement | |
Team |
We consider the following aspects to be important:
- Naming rules
- Correct commenting
This is not to say that other conventions are unimportant, we just want to start with these two because they seem elementary to us. With all code conventions, one thing is very important to us: it is not so much about the concrete form, but about consistent adherence to the convention. And it is about the awareness that conventions are necessary.
Naming rules
Without naming rules, you have to adapt to the style of individual developers again and again.
Naming rules should help the reader of the code to understand the code. As it is helpful, for example, to distinguish fields from local variables, this could be supported by a naming rule. What such a convention looks like in individual cases is a matter of taste. Some prefer "this.xyz" others "_xyz". Which variant you choose is not important to us. What matters to us is that the convention is adhered to consistently. The necessity of a naming rule for fields, for example, also depends on the context. In a class with 400 lines, a naming rule that emphasizes fields over variables would be very important to us, but in manageable classes it tends to take a back seat. With the help of root cause analysis, the Clean Code Developer gets to the bottom of the actual cause for the necessity of a naming rule.
Comment correctly
Unnecessary or even incorrect comments slow down reading. The code should be so clear and unambiguous that it requires as few comments as possible.
To put it bluntly, a comment in the code is an indication that the code can still be improved. Typical for such cases are 3 lines of code that are overwritten with a comment. At this point, it probably helps to extract the three lines as a method (Refactoring: Extract Method) and use the comment as the name of the method. In general, the need for comments can be reduced by using good names for variables, methods, classes, etc.
Instead of
int length; // in mm
better
int lengthInMM;
Instead of
public double Price() { // Calculates the gross price ... }
better
public Money BruttoPreis() { ... }
You should not comment on what you do, but, if at all, why you do something.
Practices
Issue Tracking
Only what you write down will not be forgotten and can be effectively delegated and followed up.
Changeability | |
---|---|
Correctness | |
Production efficiency | |
Continuous improvement | |
Team |
Structured management of all issues is essential to ensure that nothing gets lost. And only if an overview of all open issues is possible can the issues be prioritized and put in order. This does not necessarily require sophisticated tools; a board with cardboard cards can also serve the purpose. Above all, the focus here should not be on the tool, but on the activity.
See also under Tools.
Automated integration tests
Integration tests ensure that the code does what it is supposed to do. Not automating this recurring activity would be a waste of time.
Changeability | |
---|---|
Correctness | |
Production efficiency | |
Continuous improvement | |
Single Developer |
We have already described the fundamental prerequisite for any changes to the code in the red degree by using a version control system. We can make changes to the code, delete entire files and directories without worry, and everything can be retrieved again thanks to the version control system.
If we now make changes to the code, we should be sure that we are not breaking anything. And we can only achieve this certainty if we test whether the application still behaves as before after the change. Performing these tests manually after every change would not be practical; we have to automate them. One of the great evils of software development is the fear of overlooking something when making changes to the code, of not taking a detail into account and thereby causing an error in code that previously worked. As a rule, it doesn't even matter whether the changes are intended to improve the code (refactoring) or to implement additional requirements. As long as we are not sure after making a change that everything still works as before, the fear remains. This leads us to leave code as it is in case of doubt, because it works. Necessary refactoring is omitted for fear of making mistakes.
So that we can also be involved in ongoing projects (so-called Brownfield projects, in contrast to Greenfield "greenfield") to create this safety net, we need procedures that can be applied to existing code. Automated integration tests are suitable for this. They either start at the very top of the user interface and test the application through all layers or start further down. In either case, several functional units are tested in interaction.
So before we make changes or extensions to the code, we create integration tests for the affected code areas. Tools and techniques such as WatiN, UI Automation, etc. can be used for this. Of course, unit tests that test individual functional units in isolation are also desirable. To do this, however, the code must fulfill requirements that are probably not always met: the code must already contain the Single responsibility principle must be taken into account. Otherwise, the dependencies between the functional units (components, classes or methods) are so great that they cannot be tested in isolation. The long-term goal is, of course, a code base in which unit tests are possible. What's more: in future, we will create the tests before implementation (Test first). But to get there through refactoring, integration tests are first required to ensure that the application still behaves as it did before refactoring.
See also under Tools.
Read, Read, Read
Reading educates!
Changeability | |
---|---|
Correctness | |
Production efficiency | |
Continuous improvement | |
Single Developer |
Reading educates - we are firmly convinced that this also applies to software developers. Software technology continues to evolve. In addition to the major development steps such as procedural programming, object-oriented programming, functional programming, aspect-oriented programming, etc., there are constant developments on a smaller scale that a professional software developer has to deal with. On the one hand, there are techniques such as Dependency Injection or Object Relational Mapper. But there are also development steps within these techniques, such as Domain Specific Languages (DSLs) for configuration vs. XML-based configuration. In addition to the technical aspects of software development, the process is also constantly evolving. The realization that waterfall models do not work has become established, and various agile processes are being developed. The clean code developer must keep all of this in mind.
We therefore suggest reading at least 6 specialist books per year. Furthermore, periodicals should be read regularly, and by this we mean blogs as well as specialist journals.
You can find suggestions in the Bibliography.
Reviews
Four eyes see more than two. When one developer explains his code to the other, details usually emerge that have not yet been considered.
Changeability | |
---|---|
Correctness | |
Production efficiency | |
Continuous improvement | |
Team |
Reviews come in two simplified forms: as a continuous process in pair programming and as an independent process step in code review. The goal is the same in both cases: the code should be reviewed by a second developer. This prevents "operational blindness". The mere fact that a developer presents and describes his code to another developer leads to aha experiences.
As a rule, it is only through discussion with other developers that it becomes clear where the strengths and weaknesses of a code base lie. The process of continuous improvement in particular makes it necessary to engage with the views of other developers.
Of course, it is not only the source code that is a suitable basis for reviews. They offer a good opportunity to check the results of any development activity, provided they lead to a "readable" result. In addition to purely informal reviews, such as pair programming or assessment by a second person, there is also the formal review with a review process and corresponding roles. Other well-known types of review include walkthrough, technical review, peer review and inspection.
Reviews supplement dynamic tests, such as the automatic unit test or the automatic integration test from the yellow degree resp. orange degree. In contrast to these tests, reviews are also very well suited to finding errors in the requirements. They can also be used very early on in the development process and errors can therefore be found very early on. And the earlier errors are found, the cheaper it is to eliminate them.
Sources
Source | Author | Brief description |
---|---|---|
Basic knowledge of software testing, training and further education for | T. Linz and A. Spillner | The textbook for Certified Tester Foundation Level according to ISTQB |
Certified Tester Foundation Level according to ISTQB standard |
The orange degree is followed by the Yellow degree to.