Skip to content

Go Style Guidelines

This guide describes how to structure Go projects within our monorepo and outlines some best practices for writing Go code.

It is not meant to be comprehensive and should rather supplement established patterns and best practices. A good reference for the latter is Google's Go style guide. We try to explicitly document instances in which we intentionally break with this style guide.

Project Structure

To simplify buliding, testing etc. all Go projects in the monorepo should follow a similar layout. A simple example could look as follows:

my-project
|- Makefile
|- go.mod
|- go.sum
|- cmd
|  |- server
|  |  `- main.go
|  `- cli
|     `- main.go
|- foo
|  `- foo.go
|- internal
|  `- bar
|     |- doc.go
|     |- something.go
|     |- something_test.go
|     |- some_other_thing.go
|     `- some_other_thing_test.go
`- testsintegration
   |- test_main_test.go
   `- server_test.go

Aspects of this are explained in the following subsections.

Makefile

We use shared Make rules for all Go projects so you don't have to write these from scratch every time. Thus your Makefile should likely only contain the following unless you have some very special needs that can't be generalized:

include $(LYC_PATH_REPO_ROOT)/make/go.mk

go.mod

The "tidyness" of go.mod should be verified automatically, other than that:

  • Include lyceum.technology in your module name, e.g. lyceum.technology/my-project (also note that we use hyphens, not underscores).

  • If you want to use our shared daedalus Go library (adjust the relative path to the correct location):

require (
    ...
    lyceum.technology/daedalus v0.0.0
)

replace lyceum.techology/daedalus => ../daedalus_go

Binaries, Public and Internal Packages

  • Place binaries to build under cmd. If using our shared Make rules, it is not required to add any additional .gitignore patterns.
  • Create a directory per package that you want to make available to other projects (more likely than not you will have none of these)
  • Place all other packages under internal.
  • Package names should contain neither hyphens nor underscores and directory names and packages names should match exactly
  • Place package doc comments in a doc.go file. An exception being packages that contain only a single file. In this case it's okay to give the file the same name as the package directory and omit doc.go, e.g. foo/foo.go above.

Integration tests

Place all integration tests (any that require external components such as test containers) in a testsintegration (and thus a testsintegration package).

These must not run when running go test with -short. To achieve this you can use the following minimal test_main_test.go template:

package testsintegration

import (
    "flag"
    "log"
    "os"
    "testing"
)

func TestMain(m *testing.M) {
    flag.Parse()

    if testing.Short() {
        log.Println("Skipping integration tests in short mode")
        os.Exit(0)
    }

    os.Exit(m.Run())
}

Naming Conventions

Aside from following typical Go conventions please note the following when naming things (variables, functions etc.) in your code:

  • Name boolean-returning functions so that they naturally read as predicates; Prefer prefixes such as Is, Has or Can. For example: IsActive(), HasChild(), CanRetry(), ...