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:
- Developing multiple related modules simultaneously
- Working on libraries and applications that depend on each other
- Making changes across multiple modules in a monorepo-like structure
- 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:
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:
mkdir -p mathlib
cd mathlib
go mod init example.com/mathlib
Create a file mathlib/math.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:
cd ..
mkdir -p calculator
cd calculator
go mod init example.com/calculator
Create a file calculator/main.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:
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:
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:
Command | Description |
---|---|
go work init | Initializes a new workspace |
go work use [dir] | Adds a module to the workspace |
go work edit | Edits the go.work file |
go work sync | Syncs dependencies from the workspace to module go.mod files |
Adding and Removing Modules
To add a module to your workspace:
go work use ./path/to/module
To remove a module, you can use the -r
flag:
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:
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:
-
Deployment: The
go.work
file is meant for local development only. When building for production, the Go toolchain ignores the workspace configuration. -
Versioning: Workspace modules don't have explicit versions. When you publish your modules, you need to handle versioning separately.
-
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). -
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
-
Create a workspace with three modules: a utility library, a data model library, and an application that uses both libraries.
-
Convert an existing project that uses
replace
directives to use workspaces instead. -
Create a workspace for a simple microservices architecture with at least two services and a shared library.
-
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! :)