What to focus on in a Go project

Just getting the code to do what you want it is of course one of the key features of your project. But ignoring the other aspects will lead your project to either rot, discourage users from starting to use it or even stopping to use it, or worst of all: make you, the maintainer, get tired of the project.

Focus on the “customer”

Focusing on the features at hand may seem like the obvious choice, but it’s actually not the best choice. If you only att features with the mindset of solving the problems, you risk your project getting bloated and eventually difficult to add new features at all.

What to do focus on is trying to always trying to get into the perspective of the consumer/customer/user of your project.

Do one thing, and do it well

The classical “Unix philosophy” that is echoed all over the place and phrased in different ways. The concept is also commonly referred to as the Single Responsibility Principle (SRP)1 or the Common Closure Principle (CCP)2, and in turn relies on that you design the system by Volatility decomposition.

The idea is that your package should have a goal to solve, and it should not try to touch anything outside its own scope. The term “bloat” often comes up when a project fails to comply to this principle.

As your project grows, you probably want to extend its functionality anyway. This should not be condemned, but you should then look into solving that with proper integration or extensability instead.

Integration support

A good package is a package that can integrate well with other existing solutions.

Top example of this is password management. Instead of letting your project deal with passwords, tokens, or other credentials itself, you should let the user be able to integrate their existing password manage tools. For example, consider the following config file:

# ~/.config/mypkg.yaml

# BAD
apiKey: "7WGvzuQuONfPb7xT8GKgFb+edoD+nbyA"

# GOOD
apiKeyCommand: "pass show 'mypassword'"

This will mean you have to write more code, but results in giving the users more options to choose from.

Other examples:

  • Supporting multiple database providers, choosing between: “in memory”, MySQL, MsSQL, PostgreSQL, etc.

  • Supporting sending notifications via email, sms, as arguments to an arbitrary command, etc.

  • Hosting the service inside Docker, Kubernetes, on “raw-metal”, etc.

  • Reading configuration through environment variables (os.LookupEnv), config files on disk, and command-line arguments.

Extensability

Keeping a project extensible is a though task. This involves either supporting some kind of plugin support or extensive configuration.

Prime examples are syntax highlighting libraries. It’s very rare to find one that only supports a handfull of languages, and that’s it. Instead, they let you as a user to create any new arbitrary syntax definitions that tells the parser how to do its lexing/tokenization and parsing, so the syntax highlighter can even support your domain-specific language (DSL).

For Go library projects in particular, this means using interfaces properly so your library consumers can switch out behaviors as they need.

References

  1. Martin, R. (2008). The Single Responsibility Principle. In Clean Code: A Handbook of Agile Software Craftmanship (Robert C. Martin Series) (1st ed., pp. 138-140). Pearson.

    ↩︎
  2. Martin, R. (2017). The Common Closure Principle. In Clean Architecture: A Craftsman’s Guide to Software Structure and Design (Robert C. Martin Series) (1st ed., pp. 105–107). Pearson.

    ↩︎