DEV Community

Cover image for Book: Clean Code
Everton Schneider
Everton Schneider

Posted on • Updated on

Book: Clean Code

The book Clean Code is a classic. Every programmer should have it on their shelf. Every programmer should read it. Every programmer should know it.

After years of experience, I finally got the time to take some time to read this book. I felt like I should have read it years ago.
The reading progresses smoothly with some nice code examples in an enjoyable journey with tips to learn, to have some reflection and questions about how you can code better.

The chapters of the book are very well divided into categories and the more you proceed with its contents, more you want to continue.

Below I will leave a few of my favourite topics and some distinct phrases that I liked from it, which I turn to sometimes to remember some good practices we all should follow.

Image description

Chapter 1: Clean Code

The Boy Scout Rule

It's not enough to write code well. The code has to be kept clean over time.
If we all check in our code a little cleaner than when we checked it out, the code simply could not rot.

Chapter 2: Meaningful Names

Don't Pun

Avoid using the same word for two purposes. Using the same term for two different ideas is essentially a pun.
Follow the "one word per concept" rule.

Use Solution Domain Names

Remember that the people who read your code will be programmers. So go ahead and use computer science (CS) terms, algorithm names, pattern names, math terms, and so forth.

Don't Add Gratuitous Context

Don't prefix your classes or methods with some type of "project initials". If you have a project called "Corner Shop Cash Flow" it is a bad idea do prefix every class with CSCF. When you type C in your IDE, it will give you a long list of all class, not filtering anything you need. Let your IDE work for you.

Chapter 3: Functions


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

Blocks and Indenting

This means that the blocks within if statements, else statements, while statements, and so on should be one line long. Probably that line should be a function call.

Do One Thing

Functions should do one thing. They should do it well. They should do it only.

If the function does only those steps that are one level below the stated name of the function, then the function is doing one thing. After all, the reason we write functions is to decompose a larger concept into a set of steps at the next level of abstraction.

Switch Statements

We cannot always avoid using switch statements, but we can make sure that each switch statement is buried in a low-level class and is never repeated. We do this with polymorphism.
The switch statements can be tolerated if they only appear once in the code.

Use Descriptive Names

You know you're working on clean code when each routine turns out to be pretty much what you expected.

Don't be afraid to make a name long. A long descriptive name is better than a short enigmatic name. A long descriptive name is better than a long descriptive comment.

Function Arguments

The ideal number of arguments for a function is zero. Next comes one, followed by two. Three arguments should be avoided where possible. More than three required very special justification - and then shouldn't be used anyway.

Arguments are even harder from a testing point of view. It's hard to write test cases for all combinations that different values passed by parameters can execute. If there are no arguments, it's clear. If there's one argument, is not too hard. If there are two arguments the problem is already a bit challenging. More than that it starts to be complex.

Flag Arguments

Flag arguments are ugly. Passing a boolean into a function is a truly terrible practice. It complicates the signature of the method and it declares that the function does more than one thing. It does one thing if the flag is true and it does another thing if the flag is false.

Argument Objects

When a function needs more than three or four arguments, it is likely that one or more arguments are wrapped into an object.

It looks like cheating, but it's not. The object is part of a clear context that is passed to the function, not just a list of different single values.

Have no side-effects

Functions must promise to do one thing only. They cannot do hidden things that may lead to unexpected behaviours.

Command Query Separation

Functions should either do something or answer something, but not both.

This gets easier to understand with an example:

public boolean set(String value, int size)  // it should not set a value and return a boolean.
Enter fullscreen mode Exit fullscreen mode

Imagine the readability of this:

if (set("height", 10)) {...}  // is it a question about what was set? or is it a question about what is set now?
Enter fullscreen mode Exit fullscreen mode
Extract Try/Catch Blocks

Just like mentioned before about Blocks and Indenting, the try/catch blocks are ugly because they mix error handling with business code. It's better to extract the bodies of the try and catch into functions of their own.

Don't Repeat Yourself

Duplication may be the root of all evil in software. Use OO wisely as a strategy for eliminating duplication.


Master programmers think of systems as stories to be told rather than programs to be written.

Chapter 4: Comments

Don't comment bad code - rewrite it!

Nothing can be more damaging as an old comment that propagates lies and misinformation.
The older the comment is, the farther away it is from the code it describes, the more likely it is to be plain wrong.

Comments Do Not Make Up for Bad Code

A good reason to write comments is bad code. It's used when the code is a mess. Someone thinks "Ah, I'd better comment that!". What should be done? Better clean it!

Explain Yourself in Code

This deserves just an example.

// Check if an employee is eligible for full benefits
if ((emplioyee.flags && HOURLY_FLAG) && (employee.age > 65)) {...}
Enter fullscreen mode Exit fullscreen mode

The above comment is not necessary. Just change the condition to be a meaningful named function.

if (employee.isEligibleForFullBenefits) {...}
Enter fullscreen mode Exit fullscreen mode
Bad Comments

Most comments fall into this category. Usually, they are excuses for poor code or justification for insufficient decisions, amounting to little more than the programmer talking to himself.

Any comment that required you to look into another module to check the meaning of that comment has failed to communicate to you and is not worth the bits it consumes.

Don't Use a Comment When You Can Use a Function or a Variable

This title speaks for itself. As seen above, replace comments with a meaningful named function or variable! #profit

Chapter 5: Formatting

The Purpose of Formatting

Code formatting is too important to be ignored. It is about communication, and communication is the professional developer's first order of business.

Vertical Formatting

How big should a source file be?

There are many source analyzers that can try to help when managing the size of classes and methods. The book Clean Code brings a graph with the average file length in different projects. It is interesting to have an idea of the files with the smaller and biggest length, even when it is for a personal project.

Vertical Openness Between Concepts

Nearly all code is read left to right and top to bottom. Each line represents an expression or clause, and each group of lines represents a complete thought. Those thoughts should be separated from each other with blank lines.

Vertical Distance

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

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

Vertical Ordering

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 once flow down the source code module from high level to low level.

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

Chapter 6: Objects and Structures

Hiding implementation is not a matter of putting a layer of functions between the variables. Hiding the 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 data, without having to know its implementation.

Data/Object Anti-Symmentry

Object hide their data behind abstractions and expose functions that operate on that data. Data structure expose their data and have no meaningful functions.

Below the dichotomy between objects and data structures:

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. Sometimes you really do want simple data structures with procedures operating on them.

The Law of Demeter

The method should not invoke methods on objects that are returned by any of the allowed functions. In other words, talk to friends, not to strangers.
The following code appears to violate the Law of Demeter (among other things) because it calls the getScratchDir() function on the return of getOptions() and then calls getAbsolutePath() on the return of value of getScratchDir().

final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
Enter fullscreen mode Exit fullscreen mode
The Wrecks

This kind of code is often called a train wreck because it looks like a bunch of coupled train cars. Chains of calls like this are generally considered to be sloppy style and should be avoided. It's usually best to split them up as follows:

Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();
Enter fullscreen mode Exit fullscreen mode

Whether it is a violation of Demeter depends on whether or not ctxt, Options, and ScratchDir are objects or data structures. If they are objects, then their internal structure should be hidden rather than exposed, and so knowledge of their innards is a clear violation of the Law of Demeter. On the other hand, if ctxt, Options, and ScratchDir are just data structures with no behaviour, then they naturally expose their internal structure, and so Demeter does NOT apply.
The use of accessor functions confuses the issue. If the code had been written as follows, then we probably wouldn't be asking about Demeter violations.

final String outputDir = ctxt.options.scratchDir.absolutePath;
Enter fullscreen mode Exit fullscreen mode

The issue would be a lot less confusing if data structures simply had public variables and no functions, whereas objects had private variables and public functions.

Chapter 7: Error Handling

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

Use Unchecked Exception

When checked exceptions were introduced in Java, they seemed like a great idea. The signature of every method would list all possible exceptions it could throw to its caller. The code would not compile if the signature of the method didn't match what your code could do.
They were thought to be a great idea, but now it's known as not. Other languages like C#, C++, Python or Ruby don't have checked exception and they work well without it.

So, what's the price to use checked exceptions?
It violates the Open/Closed Principle, where software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
If you throw a checked exception from a method in your code and the catch is 3 levels above, you must declare the exception in the signature of the method between you and the catch. It means that a change at a low level of the software can force signature changes on many higher levels.
Also, encapsulation is broken because all functions in the path of a throw must know about details of that low-level exception.

Provide Context with Exceptions

Create informative error messages and pass them along with your exceptions. If you are logging in your applications, pass along enough information to be able to log the error in your catch.

Do NOT Return Null

When we return null, we are essentially creating work for ourselves and foisting problems upon our callers. All it takes is one mission null check to send an application spinning out of control.

Do NOT Pass Null

Returning null from methods is bad, but passing null into methods is worse. Unless you're working with an API which expects you to pass null, you should avoid passing null in your code whenever possible.

Passing null into a method can possibly result in a NullPointerException wherever a null check is missing.

In most programming languages there is no good way to deal with a null that is passed by a caller accidentally. Because this is the case, the rational approach is to forbid passing null by default. When you do, you can code with the knowledge that a null in an argument list is an indication of a problem, and end up with far fewer careless mistakes.

Chapter 8: Boundaries

Third Parties

Third-party implementation (aka libraries) has 2 important goals:

  • it must have broad applicability (intention of providers).
  • it must implement specific needs (intention of the users).

The users may want to use a library or API, but not have all its methods available to their developers.
The book brings an example of the java.util.Map, which demonstrate that the Map API can have many methods available to who uses it, even if that's not the intention of the user to provide them.
Let's say no one should remove an object from the Map, what could be done, since the method remove(Object key) is available?
One suggestion is to encapsulate the use of the API in a class containing a private variable and to expose the desired methods by creating proxy methods.

public class Products {

    private Map<Product> products = new HashMap<Product>();

    public Product getById(String: id) {
        return products.get(id)

    public void delete(String: id) {
        products.remove(id)  // fell free to add a try/catch and throw a proper exception if the item does not exist

Enter fullscreen mode Exit fullscreen mode
Code that does not exist

When something needs to be developed to be integrated with a code that does not yet exist, it's possible to create a fake interface to mock the API with the external code. It helps to test the code and to keep the internal consistency, without impact from methods and variables names or method signatures.

Clean Boundaries

Boundaries with third-party must be coded in very few places to refer them. One of the advantages is that the code in the boundary has fewer maintenance points when the third-party changes.

Chapter 9: Tests

Test code must be implemented with the same standards of quality than production code. There must not be any license to break the rules in tests codebase.

Having dirty tests is a horrible practice. The problem is that tests must change as the production code evolves. The dirtier the tests, the harder they are to change.

A code must have tests to ensure that changes in the production code work as expected. If there aren't good tests, their maintainability becomes difficult, the number of tests decreases, the bugs may increase after releases, fear of changes make users believe that they will bring more harm than good. The code starts to rot.

What's make a good test?

Readability, readability, readability.

Single concept per test

We don't want long test functions that go testing one miscellaneous thing after another.

Each assertion deserves one test. If that's not possible, they should be grouped by the concept.


F: Fast
I: Independent
R: Repeatable
S: Self-Validating (output must be true/false, pass/fail - nothing in between)
T: Timely (written before production code)

Chapter 10: Classes

  • Variables and utility functions must be kept private.
  • If a test in the same package must call a function, it can be made protected or package scope.

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

A class must have only one responsibility.

We should be able to write a brief description of the class in about 25 words, without using "if", "and", "or" or "but".

The Single Responsibility Principle

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

  • Classes should have one responsibility - one reason to change.
  • Trying to identify responsibilities (reasons to change) often helps us recognise and create better abstractions in our code.

... SRP is often the most abused class design principle.

Maintaining separation of concerns is just as important in our programming activities as it is in our programs.

Do you want your tools organised into toolboxes with many small drawers each containing well-defined and well-labelled components? Or do you want a few drawers that you just toss everything into?


Cohesion must be high. When cohesion is high, it means that the methods and variables of the class are co-dependent and hang together as a logical whole.


Any modifications in the class have the potential of breaking other code in the class. It must be fully retested.

Coupled vs Decoupled

Coupled code: code connected to many others - where it depends on others and others depend on it.
Decoupled code: code isolated.

A system decoupled will be more flexible and promote more reuse. The lack of coupling means that the elements of our system are better isolated from each other and from change. This isolation makes it easier to understand each element of the system.

Chapter 11: Systems

  • It is a myth that we can get systems "right the first time".
  • Software systems are unique compared to physical systems. Their architectures can grow incrementally, IF we maintain the proper separation of concerns.

BDUF: Big Design Up Front.

The book mentions it is not necessary to do BDUF and that it 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 design.

In a sufficiently large system, whether it is a city or a software project, no one person can make all the decisions.

Use standards wisely

Standards must add demonstrable value. Teams cannot be obsessed with various strongly hyped standards and lose focus on implementing value for their customers.

Chapter 12: Emergence

Kent Beck defines "simple" design with the following rules:

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

The rules are in order of importance.

Run all tests

Making a system testable pushes developers toward a design where classes are small and single purpose. It's easier to test classes that conform to the SRP. The more tests we write, the more we'll continue to push toward things that are simpler to test.

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

We can apply anything from the body of knowledge about good software design. We can increase cohesion, decrease coupling, separate concerns, modularise concerns, shrink our functions and classes, choose better names, etc.

No duplication

Duplication is the primary enemy of a well-designed system. It represents additional work, additional risk, and additional unnecessary complexity.


When software becomes big and complex, it demands more time from a developer to understand it, and there is even a greater opportunity to misunderstanding it.

The clearer the author can make the code, the less time others will have to spend understanding it.

Below what can reduce defects and shrink the cost of maintenance.

  • You can express yourself by choosing good names.
  • You can express yourself by keeping your functions and methods small.
  • You can express yourself by using standard nomenclature.
  • You can have well-written unit tests that are expressive.

Spend a little time with each of your functions and classes. Choose better names, spit large functions into smaller functions, and generally just take care of what you've created. Care is a precious resource.

Minimal Classes and Methods

The goal is to keep the overall system small the functions and classes are also kept small. This is the lowest priority of the four rules od Simple Design.

So, although it's important to keep class and function count low, it's more important to have tests, eliminate duplication, and express yourself.

Chapter 13: Concurrency

Writing clean concurrent programs is hard - very hard.

  • Correct concurrency is complex, even for simple problems.
Concurrency Defense Principles - SRP

The Single Responsibility Principle states that a given method/class/component should have a single reason to change. Concurrency design is complex enough to be a reason to change in its own right and therefore deserves to be separated from the rest of the code.

Recommendation: Keep your concurrency-related code separated from other code.

Know Your Library

A few things to consider when writing threaded code in Java:

  • Use the provided thread-safe collections.
  • Use the executor framework for executing unrelated tasks.
  • Use non-blocking solutions when possible.
  • Several library classes are not thread safe.

Recommendation 1: Review the classes available to you. In case of Java, become familiar with the java.util.concurrent, java.util.concurrent.atomic and java.util.concurrent.locks.

Recommendation 2: Learn the basic algorithms and understand their solutions.

Synchronized blocks

"... if there is more than on synchronized method on the same shared class, then your system may be written incorrectly"

The synchronized keyword introduces a lock. All sections of code guarded by the same lock are guaranteed to have only one thread executing through them at any given time.

Recommendation: Keep your synchronized sections as small as possible.

Testing Threaded Code

Write tests that have the potential to expose problems and then run them frequently, with different programmatic configurations and system configurations and load. If tests ever fail, track down the failure. Don't ignore a failure just because the tests pass on a subsequent run.

Get Your Nonthreaded Code Working First

This may seem obvious, but it doesn't hurt to reinforce it.
So not try to chase down non-threading bugs and threading bugs at the same time. Make sure your code works outside of threads first.

Chapter 14: Successive Refinement

This chapter of the book is a case study. Below there are a few aspects I thought interesting mentioning.

  • To write clean code, you must first write dirty code and then clean it.
  • Much of good software design is simply about partitioning - creating appropriate places to put a different kind of code. This separation of concerns makes the code much simpler to understand and maintain.
  • Programmers who satisfy themself with merely working code are behaving unprofessionally.

Nothing has more profound and long-term degrading effect upon a development project than bad code.

Of course, bad code can be cleaned up. But it's very expensive. As code rots, the modules insinuate themselves into each other, creating lots of hidden and tangled dependencies.
On the other hand, keeping the code clean is relatively easy. If you made a mess in a module in the morning, it is easy to clean up in the afternoon.

The solution is to continuously keep your code as clean and simple as it can be. Bevel let the code rot.

Chapter 15: JUnit Internals

This chapter is somehow similar to Chapter 14. The JUnit framework code is analysed by the book author and criticised.
Is not relevant to write down all the suggestions he's making to change JUnit code.

Below the conclusion from the author of the book:

... we have satisfied the Scout Boy Rule. We have left this module a bit cleaner than we found it. Not that it wasn't clean already. The authors (of JUnit) had done an excellent job with it. Bit no module is immune from improvement, and each of us has the responsibility to leave the code a little better than we found it.

Chapter 16: Refactoring SerialDate

This chapter is very similar to Chapter 15, but the code inspected was from the class SerialDate. Just like in the previous chapter, it shows areas of potential improvement in the code.

Chapter 17: Smells and Heuristics

This chapter is a summary of many things seen throughout the book.


Obsolete Comment

Comments get old quickly. It is best not to write a comment that will become obsolete.

Redundant Comment

Comments should say things that the code cannot say for itself.

Poorly Written Comment

If you're gonna write a comment, take the time to make sure it is the best comment you can write.

Commented-Out Code

That code sits there and rots, getting less and less relevant with every passing day. Commented-out code is an abomination.
When you see commented-out code, delete it. Don't worry, the source code control system still remembers it.


Build Required More Than One Step

You should be able to check out the system with one simple command and then issue one simple command to build it.

Tests Require More Than One Step

Being able to run all tests is so fundamental and so important that it should be quick, easy and obvious to do.


Too Many Arguments

Functions should have a small number of arguments. More than three is very questionable.

Output Arguments

Output arguments are counterintuitive.

Flag Arguments

Boolean arguments loudly declare that the function does more than one thing.

Dead Function

Keeping dead code around is wasteful.


Multiple Languages in One Source Code

The idea is for a source file to contain one, and only one, language. Realistically, we will probably have to use more than one. But we should take pains to minimise both the number and extent of extra languages in our source files.

Obvious Behaviour Is Unimplemented

When an obvious behaviour is not implemented, readers and users of the code can no longer depend on their intuition about function names.

Override Safeties

Turning off certain compiler warning (or all warnings) may help you get the build to succeed but at all risk of endless debugging sessions. Turning off failing tests and telling yourself you're getting them to pass later is as bad as pretending your credit cards are free money.


This is one of the most important rules in this book and it should be taken very seriously. Andy Hunt and Dave Thomas call it DRY (Don't Repeat Yourself). Kent Beck made it one of the core principles of Extreme Programming and called it: "Once and only once".

Every time you see duplication in the code, it represents a missed opportunity for abstraction.

Still more subtle are the modules that have similar algorithms, but that don't share similar lines of code. This is still duplication and should be addressed by using the TEMPLATE METHOD it STRATEGY pattern.

Base Classes Depending on Their Derivatives

The most common reason for partitioning concepts into base and derivative classes is so that the highest level base class concepts can be independent of the lower level derivative class concepts.

Too Much Information

A well-defined interface does not offer very many functions to depend upon, so coupling is low. A poorly defined interface provides lots of functions that you must call, so coupling is high.

Dead Code

Dead code is code that isn't executed. After a while, it starts to smell. That happens because dead code isn't updated when the designs change. It still compiles but does not follow the newer conventions or rules.


If you do something a certain way, do all similar things in the same way.


Clutters are useless things in the code (my words).

  • Functions that are never called.
  • Variables that are never used.
  • Comments that are useless.
  • Default constructor with no implementation.

All these things are clutters and should be removed.

Missplaced Responsibility

One of the most important decisions a software developer can make is where to put code.

Code should be placed where a reader would naturally expect it to be.

Innapropriate Static

If you want a function to be static, make sure that there is no chance that you'll want it to behave polymorphically.

Use Explanatory Variables

One of the more powerful ways to make a program readable is to break the calculations up into intermediate values that are held in variables with meaningful names.

Function Names Should Say What They Do

In the code below:

Date newDate = date.add(5);
Enter fullscreen mode Exit fullscreen mode

What do you expect this code to add to the date?

  • 5 seconds
  • 5 minutes
  • 5 hours
  • 5 days
  • 5 weeks
  • 5 months
  • 5 years
  • ...

Variable names must be named to have an easy understanding for the reader.
A better name for the code above would be name the method addDaysTo(n) or increaseByDays(n).

Prefer Polymorphism to If/Else or Switch/Case

I use the following "ONE SWITCH" rule. There may be no more than one switch statement for a given type selection. The cases in that switch statement must create polymorphic objects that take the place of other such switch statements in the rest of the system.

Follow Standard Conventions

Every team should follow a coding standard based on common industry norms.

Replace Magic Numbers with Named Constants

This is probably one of the oldest rules in software development.

Be Precise

When you make a decision in your code, make sure you make it precisely. Know why you have made it and how you will deal with any exceptions.

Structure over Convention

Naming conventions are good, but they are inferior to structures that force compliance.

Encapsulate Conditionals

For example:
if (shouldBeDeleted(timer)) {...}
is preferable to
if (timer.hasExpired() && !timer.isRecurrect()) {...}

Avoid Negative Conditionals

For example:
if (buffer.shouldCompact())
is preferable to
if (!buffer.shouldNotCompact())

Functions Should Do One Thing
public void pay() {
  for (Employee e: employees) {
    if (e.isPayday()) {
      Money pay = e.calculatePay();
Enter fullscreen mode Exit fullscreen mode

should be split in

public void pay() {
  for (Employee e: employees) {

private void payIfNecessary(Employee e) {
  if (e.isPayday) {

private calculateAndDeliverPay(Employee e) {
  Money pay = e.calculatePay();
Enter fullscreen mode Exit fullscreen mode
Encapsulate Boundary Conditions

Observe that the snippet level + 1 appears twice in the code below.

if (level + 1 < tags.length) {
  parts = new Parse(body, tags, level + 1, offset + endTag)
Enter fullscreen mode Exit fullscreen mode

This is a boundary condition that should be encapsulated into a variable.

int nextLevel = level + 1;
if (nextLevel < tags.length) {
  parts = new Parse(body, tags, nextLevel, offset + endTag)
Enter fullscreen mode Exit fullscreen mode


Constants versus Enums

Enums exist in Java for a while. Use them. Don't keep using the old trick public static void final ints. The meaning of int's can get lost. The meaning of enum's cannot because they belong to an enumeration that is named.


Choose Descriptive Names

Make sure the name is descriptive.
This is not just a feel-good recommendation. Names in software are 90 percent of what makes software readable.

Names are too important to treat carelessly.


Insufficient Tests

A test suite should test everything that can possibly break.

Don't Skip Trivial Tests

They are easy to write and their documentary value is higher than the cost to produce them.

Exhaustively Test Near Bugs

When you find a bug in a function, it is wise to do an exhaustive test if that function. You'll probably find that the bug was not alone.

Tests Should Be Fast

A slow test is a test that won't get run.

Final Note

Professionalism and craftsmanship come from values that drive disciplines.

Top comments (0)