Skip to main content

Go Workspaces

Introduction

Go workspaces (introduced in Go 1.18) solve a common problem for developers working with multiple related modules. Before workspaces, managing dependencies between local modules required complex workflows involving replace directives or publishing modules to remote repositories. Go workspaces simplify this process, allowing you to work seamlessly with multiple modules in a single development environment.

In this guide, you'll learn:

  • What Go workspaces are and why they're useful
  • How to create and manage workspaces
  • How to add, modify, and remove modules within workspaces
  • Real-world examples of using workspaces in projects

What Are Go Workspaces?

A Go workspace is a development environment that lets you work with multiple Go modules simultaneously. It allows the Go toolchain to recognize and resolve dependencies between your local modules without requiring replace directives in each go.mod file.

Workspaces are defined by a go.work file at the root of your workspace directory. This file specifies which modules are part of the workspace and where they're located.

Why Use Go Workspaces?

Workspaces are particularly useful when:

  1. Developing multiple related modules simultaneously
  2. Working on libraries and applications that depend on each other
  3. Making changes across multiple modules in a monorepo-like structure
  4. Testing changes to a library in the context of an application that uses it

Creating a Go Workspace

Let's explore how to create and use a Go workspace with a practical example.

Step 1: Creating a Workspace

To create a workspace, use the go work init command followed by the directories containing the modules you want to include:

bash
mkdir my-go-project
cd my-go-project
go work init

This creates a go.work file in your current directory.

Step 2: Creating Modules

Let's create two modules for our example: a library and an application that uses it.

First, let's create a simple math library:

bash
mkdir -p mathlib
cd mathlib
go mod init example.com/mathlib

Create a file mathlib/math.go:

go
package mathlib

// Add returns the sum of two integers
func Add(a, b int) int {
return a + b
}

// Multiply returns the product of two integers
func Multiply(a, b int) int {
return a * b
}

Now, let's create an application that will use this library:

bash
cd ..
mkdir -p calculator
cd calculator
go mod init example.com/calculator

Create a file calculator/main.go:

go
package main

import (
"fmt"
"example.com/mathlib"
)

func main() {
a, b := 5, 3

sum := mathlib.Add(a, b)
product := mathlib.Multiply(a, b)

fmt.Printf("%d + %d = %d
", a, b, sum)
fmt.Printf("%d * %d = %d
", a, b, product)
}

Step 3: Adding Modules to the Workspace

Now, add both modules to your workspace:

bash
cd ..
go work use ./mathlib ./calculator

This command adds the modules to your workspace by updating the go.work file:

go 1.18

use (
./mathlib
./calculator
)

Step 4: Running the Application

Now you can run the application directly, and Go will resolve the example.com/mathlib dependency using your local module:

bash
go run example.com/calculator

Output:

5 + 3 = 8
5 * 3 = 15

Notice how we didn't need to use replace directives or publish our library to use it in the calculator application. The workspace handles this automatically.

Workspace Commands

Go provides several commands for managing workspaces:

CommandDescription
go work initInitializes a new workspace
go work use [dir]Adds a module to the workspace
go work editEdits the go.work file
go work syncSyncs dependencies from the workspace to module go.mod files

Adding and Removing Modules

To add a module to your workspace:

bash
go work use ./path/to/module

To remove a module, you can use the -r flag:

bash
go work use -r ./path/to/module

Advanced Workspace Features

Module Versions in Workspaces

A common question is: what happens when my workspace contains different versions of the same dependency?

In a workspace, modules in the workspace take precedence over modules in the module cache. If multiple workspace modules require different versions of the same dependency, Go uses the version from the main module or the highest version required by any workspace module.

The replace Directive in Workspaces

You can use the replace directive in the go.work file to replace a module with another version or location:

go 1.18

use (
./mathlib
./calculator
)

replace example.com/external-module => ../my-fork/external-module

This replacement applies workspace-wide, affecting all modules in the workspace.

Real-World Example: Microservices Development

Let's see how workspaces can benefit a microservices project:

graph TD A[Common Library] --> B[Auth Service] A --> C[User Service] A --> D[Product Service] B --> E[API Gateway] C --> E D --> E

In this scenario, we have several services that share a common library. Let's set up a workspace for this structure:

bash
mkdir microservices-project
cd microservices-project
go work init

# Create modules
mkdir -p common auth users products gateway
cd common
go mod init example.com/common
cd ../auth
go mod init example.com/auth
cd ../users
go mod init example.com/users
cd ../products
go mod init example.com/products
cd ../gateway
go mod init example.com/gateway
cd ..

# Add modules to workspace
go work use ./common ./auth ./users ./products ./gateway

Now, when you make changes to the common library, all services will immediately see those changes without needing to update go.mod files or use replace directives.

Workspace Limitations and Considerations

While workspaces are powerful, there are some important considerations:

  1. Deployment: The go.work file is meant for local development only. When building for production, the Go toolchain ignores the workspace configuration.

  2. Versioning: Workspace modules don't have explicit versions. When you publish your modules, you need to handle versioning separately.

  3. Collaboration: Team members need to set up their workspaces individually, as go.work files shouldn't be committed to version control (they contain absolute paths specific to your environment).

  4. IDE Support: Most modern Go IDEs support workspaces, but older tools might not recognize the workspace structure properly.

Summary

Go workspaces provide a convenient way to work with multiple related modules during development. They simplify dependency management by allowing the Go toolchain to automatically resolve dependencies between your local modules.

Key benefits of workspaces include:

  • Easier multi-module development
  • No need for replace directives for local development
  • Better separation of concerns with modular code
  • Simplified testing of interdependent modules

Workspaces are particularly valuable for larger projects with multiple components, microservices architectures, or when developing libraries alongside the applications that use them.

Additional Resources

Exercises

  1. Create a workspace with three modules: a utility library, a data model library, and an application that uses both libraries.

  2. Convert an existing project that uses replace directives to use workspaces instead.

  3. Create a workspace for a simple microservices architecture with at least two services and a shared library.

  4. Practice making changes to a shared library in a workspace and observe how those changes affect the applications that use it.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)