Clean code digest (Emergence)

Getting Clean via Emergent Design

What if there were four simple rules that you could follow that would help you create good designs as you worked? What if by following these rules you gained insights into the structure and design of your code, making it easier to apply principles such as SRP and DIP? What if these four rules facilitated the emergence of good designs?

Many of us feel that Kent Beck’s four rules of Simple Design1 are of significant help in
creating well-designed software.

According to Kent, a design is “simple” if it follows these rules:

  • Runs all the tests
  • Contains no duplication
  • Expresses the intent of the programmer
  • Minimizes the number of classes and methods

The rules are given in order of importance.

Writing tests leads to better designs.

The fact that we have these tests eliminates the fear that cleaning up the code will break it!

But the most important way to be expressive is to try. All too often we get our code working and then move on to the next problem without giving sufficient thought to making that code easy for the next person to read. Remember, the most likely next person to read the code will be you.

So take a little pride in your workmanship. Spend a little time with each of your functions and classes. Choose better names, split large functions into smaller functions, and generally just take care of what you’ve created. Care is a precious resource.

 

Clean code digest (Systems)

Separate Constructing a System from Using It

construction is a very different process from use

The separation of concerns is one of the oldest and most important design techniques in our craft.

It is a myth that we can get systems “right the first time.” Instead, we should implement only today’s stories, then refactor and expand the system to implement new stories tomorrow. This is the essence of iterative and incremental agility. Test-driven development, refactoring, and the clean code they produce make this work at the code level.

Software systems are unique compared to physical systems. Their architectures can grow incrementally, if we maintain the proper separation of concerns.

It is not necessary to do a Big Design Up Front18 (BDUF). In fact, BDUF is even harmful because it inhibits adapting to change, due to the psychological resistance to discarding prior effort and because of the way architecture choices influence subsequent thinking about the design.

A good API should largely disappear from view most of the time, so the team expends the majority of its creative efforts focused on the user stories being implemented. If not, then the architectural constraints will inhibit the efficient delivery of optimal value to the customer.

Modularity and separation of concerns make decentralized management and decision making possible.

We all know it is best to give responsibilities to the most qualified persons. We often forget that it is also best to postpone decisions until the last possible moment. This isn’t lazy or irresponsible; it lets us make informed choices with the best possible information. A premature decision is a decision made with suboptimal knowledge.

Systems Need Domain-Specific Languages

A good DSL minimizes the “communication gap” between a domain concept and the code that implements it

When the domain logic is obscured, quality suffers because bugs find it easier to hide and stories become harder to implement.

 

Clean code digest (Classes)

Following the standard Java convention, a class should begin with a list of variables. Public static constants, if any, should come first. Then private static variables, followed by private instance variables. There is seldom a good reason to have a public variable.

The first rule of classes is that they should be small. The second rule of classes is that they should be smaller than that.

If we cannot derive a concise name for a class, then it’s likely too large.

We should also be able to write a brief description of the class in about 25 words

The Single Responsibility Principle (SRP)2 states that a class or module should have one, and only one, reason to change.

Classes should have one responsibility—one reason to change.

The problem is that too many of us think that we are done once the program works. We fail to switch to the other concern of organization and cleanliness. We move on to the next problem rather than going back and breaking the overstuffed classes into decoupled units with single responsibilities.

Every sizable system will contain a large amount of logic and complexity. The primary goal in managing such complexity is to organize it so that a developer knows where to look to find things and need only understand the directly affected complexity at any given time. In contrast, a system with larger, multipurpose classes always hampers us by insisting we wade through lots of things we don’t need to know right now.

We want our systems to be composed of many small classes, not a few large ones. Each small class encapsulates a single responsibility, has a single reason to change, and collaborates with a few others to achieve the desired system behaviors.

In essence, the DIP says that our classes should depend upon abstractions, not on concrete details.

Clean code digest (Unit Tests)

The Three Laws of TDD

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.

Test code is just as important as production code.

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. No matter how flexible your architecture is, no matter how nicely partitioned your design, without tests you will be reluctant to make changes because of the fear that you will introduce undetected bugs.

But with tests that fear virtually disappears. The higher your test coverage, the less your fear.

Indeed, you can improve that architecture and design without fear!

The dirtier your tests, the dirtier your code becomes. Eventually you lose the tests, and your code rots.

What makes a clean test? Three things. Readability, readability, and readability.

The BUILD-OPERATE-CHECK pattern is made obvious by the structure of these tests. Each of the tests is clearly split into three parts. The first part builds up the test data, the second part operates on that test data, and the third part checks that the operation yielded the expected results.

The tests in Listing 9-2 demonstrate the technique of building a domain-specific language for your tests. Rather than using the APIs that programmers use to manipulate the system, we build up a set of functions and utilities that make use of those APIs and that make the tests more convenient to write and easier to read. These functions and utilities become a specialized API used by the tests. They are a testing language that programmers use to help themselves to write their tests and to help those who must read those tests later on.

disciplined developers refactor their test code into more succinct and expressive forms.

the number of asserts in a test ought to be minimized.

a better rule is that we want to test a single concept in each test function.

F.I.R.S.T.

Clean tests follow five other rules

Fast

Independent

Repeatable

Self-Validating. You should not have to read through a log file to tell whether the tests pass. You should not have to manually compare two different text files to see whether the tests pass.

Timely.  Unit tests should be written just before the production code that makes them pass. If you write tests after the production code, then you may find the production code to be hard to test.

Tests are as important to the health of a project as the production code is. Perhaps they are even more important, because tests preserve and enhance the flexibility, maintainability, and reusability of the production code. So keep your tests constantly clean. Work to make them expressive and succinct. Invent testing APIs that act as domain-specific language that helps you write the tests.

If you let the tests rot, then your code will rot too. Keep your tests clean.

 

Clean code digest (Boundaries)

There is a natural tension between the provider of an interface and the user of an interface. Providers of third-party packages and frameworks strive for broad applicability so they can work in many environments and appeal to a wide audience. Users, on the other hand, want an interface that is focused on their particular needs. This tension can cause problems at the boundaries of our systems.

we could write some tests to explore our understanding of the third-party code. Jim Newkirk calls such tests learning tests.

We should avoid letting too much of our code know about the third-party particulars. It’s better to depend on something you control than on something you don’t control, lest it end up controlling you.

 

Clean code digest (Error Handling)

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.

Error handling is important, but if it obscures logic, it’s wrong.

In a way, try blocks are like transactions. Your catch has to leave your program in a consistent state, no matter what happens in the try. For this reason it is good practice to start with a try-catch-finally statement when you are writing code that could throw exceptions.

Try to write tests that force exceptions, and then add behavior 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.

The price of checked exceptions is an Open/Closed Principle1 violation. If you throw a checked exception from a method in your code and the catch is three levels above, you must declare that exception in the signature of each method between you and the catch. This means that a change at a low level of the software can force signature changes on many higher levels. The changed modules must be rebuilt and redeployed, even though nothing they care about changed.

Checked exceptions can sometimes be useful if you are writing a critical library: You must catch them. But in general application development the dependency costs outweigh the benefits.

In fact, wrapping third-party APIs is a best practice. When you wrap a third-party API, you minimize your dependencies upon it: You can choose to move to a different library in the future without much penalty. Wrapping also makes it easier to mock out third-party calls when you are testing your own code.

Don’t Return Null

If you are tempted to return null from a method, consider throwing an exception or returning a SPECIAL CASE object instead. If you are calling a null-returning method from a third-party API, consider wrapping that method with a method that either throws an exception or returns a special case object.

 

Clean code digest (Objects and Data Structures)

Hiding implementation is about abstractions! A class does not simply push its variables out through getters and setters. Rather it exposes abstract interfaces that allow its users to manipulate the essence of the data, without having to know its implementation.

Serious thought needs to be put into the best way to represent the data that an object contains. The worst option is to blithely add getters and setters.

Procedural code (code using data structures) makes it easy to add new functions without changing the existing data structures. OO code, on the other hand, makes it easy to add new classes without changing existing functions.

Procedural code makes it hard to add new data structures because all the functions must change. OO code makes it hard to add new functions because all the classes must change.

Mature programmers know that the idea that everything is an object is a myth.

hybrids make it hard to add new functions but also make it hard to add new data structures. They are the worst of both worlds. Avoid creating them.

The quintessential form of a data structure is a class with public variables and no functions. This is sometimes called a data transfer object, or DTO. DTOs are very useful structures, especially when communicating with databases or parsing messages from sockets, and so on.

The quasi-encapsulation of beans seems to make some OO purists feel better but usually provides no other benefit.

 

Clean code digest (Formatting)

Code formatting is important.

Code formatting is about communication, and communication is the professional developer’s first order of business.

Your style and discipline survives, even though your code does not.

Concepts that are closely related should be kept vertically close to each other.

The important thing is for the instance variables to be declared in one well-known place. Everybody should know where to go to see the declarations.

If one function calls another, they should be vertically close, and the caller should be above the callee, if at all possible.

As in newspaper articles, we expect the most important concepts to come first, and we expect them to be expressed with the least amount of polluting detail. We expect the low-level details to come last. This allows us to skim source files, getting the gist from the first few functions, without having to immerse ourselves in the details.

Programmers clearly prefer short lines.

I personally set my limit at 120.

Every programmer has his own favorite formatting rules, but if he works in a team, then the team rules.

 

Clean code digests (Comments)

“Don’t comment bad code—rewrite it.” —Brian W. Kernighan and P. J. Plaugher1

Indeed, comments are, at best, a necessary evil. If our programming languages were expressive enough, or if we had the talent to subtly wield those languages to express our intent, we would not need comments very much?aperhaps not at all.

The proper use of comments is to compensate for our failure to express ourself in code. Note that I used the word failure. I meant it. Comments are always failures. We must have them because we cannot always figure out how to express ourselves without them, but their use is not a cause for celebration.

Therefore, though comments are sometimes necessary, we will expend significant energy to minimize them.

Rather than spend your time writing the comments that explain the mess you’ve made, spend it cleaning that mess.

In many cases it’s simply a matter of creating a function that says the same thing as the comment you want to write.

Keep in mind, however, that the only truly good comment is the comment you found a way not to write.

If you are writing a public API, then you should certainly write good javadocs for it. But keep in mind the rest of the advice in this chapter. Javadocs can be just as misleading, nonlocal, and dishonest as any other kind of comment.

Any comment that forces you to look in another module for the meaning of that comment has failed to communicate to you and is not worth the bits it consumes.

It is just plain silly to have a rule that says that every function must have a javadoc, or every variable must have a comment. Comments like this just clutter up the code, propagate lies, and lend to general confusion and disorganization.

Long ago there was a good reason to create and maintain these log entries at the start of every module. We didn’t have source code control systems that did it for us. Nowadays, however, these long journals are just more clutter to obfuscate the module. They should be completely removed.

We don’t have to comment it out any more. Just delete the code. We won’t lose it. Promise.

It is a pity when a comment needs its own explanation.

Short functions don’t need much description. A well-chosen name for a small function that does one thing is usually better than a comment header.

Generating javadoc pages for the classes and functions inside a system is not generally useful, and the extra formality of the javadoc comments amounts to little more than cruft and distraction.