Compass True North
Published in

Compass True North

Catching Up with the World — Go Modules in a Monorepo

Even Murphy is already using Go Modules

1) Single vs Multi-Module:

2) go.mod Placement:

Ideal single module monorepo directory structure
Compass pre-modules monorepo directory structure
Compass monorepo w/ modules v0
Compass monorepo w/ modules v1

3) Keeping the go.mod/go.sum Stable

  1. `go mod tidy` on pre-push: The `go mod tidy` command helps keep go.mod files clean by removing unused dependencies. So we added a pre-push check that runs `go mod tidy` and blocks `git push` if any uncommitted changes are detected in the go.mod or go.sum as a result of tidying. Changes to those files must be committed before the code can be pushed up for review.
  2. `go mod tidy` in CI: For redundancy, we run that same `go mod tidy` check again in CI (in case the pull request for which CI is running was created through some alternative means that did not run the pre-push check).
  3. `go test -mod=readonly` in CI: When we run the suite of Go unit tests, we have the `-mod=readonly` flag on for the `go test` command. This flag causes the `go test` command to fail if it’s missing a dependency and wants to update the go.mod file. This helps ensure that dependencies are deliberately added. The go.mod must be kept up-to-date so that the next developer who pulls master doesn’t have any unexpected or unrelated go.mod changes.
  4. `go.mod` updates pushed up for review in a cron job: I mentioned before that we would unexpectedly get updates to the go.mod files. To help combat this we have set up a cron job that runs a suite of Go commands (such as `go test` and `go mod tidy`, as well as linting and other tooling). If any go.mod or go.sum changes are found after running these commands, then a pull request containing those changes is generated and pushed up for review. At the moment, we’re running it once a day.


  1. First, we decided whether we wanted a single module or multiple modules. A single module is the Go default, but multiple modules might help with versioning and will give each service more control. We chose the single module approach, given Go’s “1 repo = 1 module” ideal.
  2. Next, we decided where the go.mod file should live. The easiest location is the root of the repository (the Go default), but we didn’t have that flexibility. So we place the go.mod at the root of the Go code (with a go.mod at the root, purely to act as a pointer so we could run Go commands from the root).
  3. Finally, we keep our go.mod/go.sum files stable by having checks for `go mod tidy`, run tests in CI in `-mod=readonly` mode, as well as automation that looks for go.mod updates and pushes them up for review.

Related Links:



Compass Engineering & Product Blog — An inside glimpse at our technology and tools, brought to you by the engineers of the game-changing real estate platform, Compass. Hiring at

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store