Skip to main content
In this guide, we’ll learn about multi-module workspaces that were introduced in Go 1.18.

What are Workspaces?

Workspaces allow us to work with multiple modules simultaneously without having to edit go.mod files for each module. Each module within a workspace is treated as a root module when resolving dependencies.
This is a very useful feature from Go 1.18 that simplifies development when working with multiple related modules.

Creating a Workspace

Let’s understand workspaces better by creating a practical example.
1

Create a hello module

First, let’s create a new module:
$ mkdir workspaces && cd workspaces
$ mkdir hello && cd hello
$ go mod init hello
2

Add code with an external dependency

Create a main.go file and install an example package:
package main

import (
  "fmt"

  "golang.org/x/example/stringutil"
)

func main() {
  result := stringutil.Reverse("Hello Workspace")
  fmt.Println(result)
}
Install the dependency:
$ go get golang.org/x/example
go: downloading golang.org/x/example v0.0.0-20220412213650-2e68773dfca0
go: added golang.org/x/example v0.0.0-20220412213650-2e68773dfca0
3

Test the module

Run the code to verify it works:
$ go run main.go
ecapskroW olleH

The Problem Workspaces Solve

What if we want to modify the stringutil module that our code depends on? Until Go 1.18, we had to use the replace directive in the go.mod file. Now, let’s see how workspaces provide a better solution.

Using go.work

1

Initialize a workspace

In the workspaces directory, create a new workspace:
$ go work init
This creates a go.work file:
$ cat go.work
go 1.18
2

Add the hello module to the workspace

$ go work use ./hello
This updates the go.work file:
go 1.18

use ./hello
3

Clone and modify the dependency

Download and modify the stringutil package:
$ git clone https://go.googlesource.com/example
Cloning into 'example'...
remote: Total 204 (delta 39), reused 204 (delta 39)
Receiving objects: 100% (204/204), 467.53 KiB | 363.00 KiB/s, done.
Resolving deltas: 100% (39/39), done.
Modify example/stringutil/reverse.go:
func Reverse(s string) string {
  return fmt.Sprintf("I can do whatever!! %s", s)
}
4

Add the modified package to the workspace

$ go work use ./example
$ cat go.work
go 1.18

use (
  ./example
  ./hello
)
5

Test the workspace

Run the hello module and see the modified function in action:
$ go run hello
I can do whatever!! Hello Workspace

Workspace Commands

go work init

Initializes a new workspace in the current directory:
$ go work init

go work use

Adds a module to the workspace:
$ go work use ./path/to/module
You can add multiple modules at once:
$ go work use ./module1 ./module2 ./module3

go work sync

Synchronizes the workspace’s build list back to the modules:
$ go work sync

Use Cases

Workspaces are particularly useful when:
  • Developing multiple related modules simultaneously
  • Testing changes across module boundaries
  • Working on a dependency and the code that uses it
  • Prototyping changes to external dependencies
  • Avoiding the need for replace directives in go.mod
The go.work file should typically not be committed to version control, as it’s specific to your local development environment.

Key Takeaways

  • Workspaces were introduced in Go 1.18
  • They allow working with multiple modules without editing go.mod files
  • The go.work file defines which modules are part of the workspace
  • Each module in a workspace is treated as a root module
  • Workspaces are ideal for local development with multiple modules
  • Use go work init, go work use, and go work sync to manage workspaces

Build docs developers (and LLMs) love