Skip to content

Quality and Best Practices

The quality of code has a practical impact on both your agility and the cost of development:1

  • you can't change buggy and/or bloated code fast enough to be truly agile
  • existing bugs can easily increase development costs (and time) by 10x
  • the mess eventually becomes so big and so deep that you cannot clean it up anymore2

Productivity vs Time

Productivity vs Time

All developers with more than a few years experience know that previous messes slow them down. And yet all developers feel the pressure to make messes in order to meet deadlines. In short, they don’t take the time to go fast! You will not make the deadline by making a mess. Indeed, the mess will slow you down instantly, and will force you to miss the deadline. — Robert C. Martin

Bottom-Up Development

Breaking a problem down into small, coherent fragments lends itself to organization. Start with the basic low-level components and then proceed to higher-level abstractions.

Bottom-up development emphasizes coding and early testing, which can begin before having a detailed understanding of the final system. In practice, this may never be the case as requirements are constantly evolving.

Advantages of the bottom-up approach are component reusability, agility, and testability.

I compared Mel's hand-optimized programs with the same code massaged by the optimizing assembler program, and Mel's always ran faster. That was because the “top-down” method of program design hadn't been invented yet, and Mel wouldn't have used it anyway. He wrote the innermost parts of his program loops first. — The Story of Mel

Opportunistic Refactoring

We encourage developers to refactor existing code when they notice a specific issue, even though this may seem difficult when working with a distributed team, branches, and pull requests due to potential merge conflicts and delayed feedback.

It is best to do this while you are working on the same component anyway, for example to implement a feature or enhancement. This way you can easily validate if the proposed changes make sense and you avoid conflicts with others.

Releasing imperfect code is not a problem as long as it is accompanied by automated tests. This makes it easy to refactor later without breaking anything or requiring detailed knowledge of the requirements and a lot of time for manual testing. Be pragmatic. Done is better than perfect.

Potential security issues are an important exception. These should never be ignored. If you find a problem, please report it to us immediately so we can fix it.

Feel free to think ahead, just don't code ahead. But also, don't feel the need to decide so many details ahead. Learn enough to get started and build only what you need. — J. B. Rainsberger

Premature Optimization

One of the hardest parts of software development is knowing what to work on. Don't get carried away implementing unnecessary abstractions and focusing on scalability optimization before you've even validated the functionality of a feature or component.

Instead of spending a lot of time on something you may not need, focus on user needs and test automation. That way, you'll make sure you're developing the right functionality, and you can refactor it later for scalability and other non-functional aspects without breaking anything.

Also keep in mind that it's much easier and less effort to maintain small amounts of duplicate code than to choose the wrong abstraction.

Premature optimization is the root of all evil. — Donald Knuth

Be Careful with Caching

There are two hard things in computer science: cache invalidation and naming things.

A cache is just a memory leak you haven't met yet. — Dave Cheney

Go Slow Before You Go Fast 🐰

Read the docs, understand the context, and talk to others to gather missing information before you start coding. Write tests. Stay focused.

Don't worry that this will take too long. Take your time. It's the fastest and only sustainable way to get things done. You have to go slow before you can go fast.

Simple, elegant solutions are more effective, but they are harder to find than complex ones, and they require more time, which we too often believe to be unaffordable. — Niklaus Wirth, Feb 1985

Effectiveness > Efficiency

Optimize for effectiveness before efficiency when prioritizing tasks:

  • Effectiveness is about achieving a specific outcome, such as providing the features that best help users solve their problems.
  • Efficiency means doing things in an optimal way, for example, faster and cheaper. We all strive to be efficient, but that's worthless if it doesn't contribute to effectiveness.

In contrast, a feature factory focuses on the quantity of new features rather than their quality:

Feature Factory

It is fundamentally the confusion between effectiveness and efficiency that stands between doing the right things and doing things right. There is surely nothing quite so useless as doing with great efficiency what should not be done at all. — Peter Drucker

Test Automation Guidelines

We strive for complete test coverage as it is a useful tool for finding untested parts of our code base. Test coverage is of limited use as a numerical statement of how good our tests are.

The F.I.R.S.T. Principle includes five rules that good tests should follow:2

  • Fast. If tests are slow, you won't run them frequently, which makes them much less useful and increases the cost of development.
  • Independent. You should be able to run each test independently and run the tests in any order you like. When tests depend on each other, then the first one to fail causes a cascade of downstream failures, making diagnosis difficult and hiding downstream defects.
  • Repeatable. If your tests aren’t repeatable in any environment, then you’ll always have an excuse for why they fail. You’ll also find yourself unable to run the tests when the environment isn’t available.
  • Self-Validating. You should not have to read through a log file to tell whether the tests pass. If the tests aren’t self-validating, then failure can become subjective and running the tests can require a long manual evaluation.
  • Timely. If you write the tests after the production code, you will generally find that the production code is difficult to test. Instead, add tests at implementation time to ensure that the code is testable, does what you expect it to do, and meets the requirements.

Code that cannot be tested is flawed. — Anonymous

Code Quality Reports

goreportcard.com generates reports on the quality of Open Source Go projects. It uses several measures, including gofmt, go vet, go lint and gocyclo. If you find this helpful and also use the tool for your own projects, you can support the developers on Patreon.

Code Quality

Use reports for inspiration when you need some, keeping in mind that not every issue reported needs to be resolved immediately.

Security Best Practices

The Open Source Security Foundation (OpenSSF) maintains standardized security criteria and awards a badge upon completion of the questionnaire:

CII Best Practices