Header

Workspace Deserves a Spotlight

A new feature in Go 1.18 go workspace mode which makes it simple to work with multiple modules. With multi-module workspaces, you can tell the Go command that you’re writing code in multiple modules at the same time and easily build and run code in those modules.

Why Workspace?

In short, the workspace makes the multiple-module dependency and switch easier in local development.

You must have encountered such an issue: The multiple modules in the repo reference each other directly. But your local changes to a module A fail to reflect in the module B that depends on A. What we take to overcome this is

  1. Push the modified code of module A to GitHub, and pull the update in module B.
  2. Replace the module B go.mod file’s dependency on A to a local absolute path.
    replace github.com/hamza/a v0.1 => /Users/hamza/workspace/repo/module-a
    

Both solutions have their own cons, which bring us pain in developing in multi-module mode somehow.

The first method is not designed in the way you like it if you try to avoid releasing new versions before more tests. Besides, after you publish it on Github, more releases and more versions are required for fixing the bug once they occur.

For the second method, you should always remind yourself to revert the changes in the local go.mod file before submitting; otherwise, you might drag your co-developers into trouble.

In light of this, Go released the go worspace mode in its version 1.18.

Set Up Workspace

Let’s test whether the workspace can ease the above-mentioned issues. Make sure that you’ve installed Go at Go 1.18 or later using the links at go.dev/dl.

Create a module for your code

To begin, create a module for the code you’ll write.

  1. Open a command prompt and create a directory for your code called workspace.
    $ mkdir workspace
    $ cd workspace
    
  2. Initialize the module

    In this example, I will create a new module hello that will depend on the golang.org/x/example module.

    $ mkdir hello
    $ cd hello
    $ go mod init example.com/hello
    go: creating new go.mod: module example.com/hello
    

    Add a dependency on the golang.org/x/example module by using go get.

    $ go get golang.org/x/example
    

    Create hello.go in the hello directory with the following contents:

    package main
    
    import (
     "fmt"
     "golang.org/x/example/stringutil"
    )
    
    func main() {
      fmt.Println(stringutil.Reverse("Hello"))
    }
    

    Now, run the hello program:

    $ go run example.com/hello
    olleH
    

Create the workspace

In this step, we’ll create a go.work file to specify a workspace with the module.

Initialize the workspace

In the workspace directory, run:

$ go work init ./hello

The go work init command tells go to create a go.work file for a workspace containing the modules in the ./hello directory.

The go command produces a go.work file that looks like this:

go 1.18

use ./hello

The go.work file has similar syntax to go.mod.

The go directive tells Go which version of Go the file should be interpreted with. It’s similar to the go directive in the go.mod file.

The use directive tells Go that the module in the hello directory should be main modules when doing a build.

So in any subdirectory of workspace the module will be active.

Run the program in the workspace directory

In the workspace directory, run:

$ go run example.com/hello
olleH

The Go command includes all the modules in the workspace as main modules. This allows us to refer to a package in the module, even outside the module. Running the go run command outside the module or the workspace would result in an error because the go command wouldn’t know which modules to use.

Next, we’ll add a local copy of the golang.org/x/example module to the workspace. We’ll then add a new function to the stringutil package that we can use instead of Reverse.

Download and modify the golang.org/x/example module

  1. Clone the repository

    From the workspace directory, run the git command to clone the repository:

    $ git clone https://go.googlesource.com/example
    
  2. Add the module to the workspace

    $ go work use ./example
    

    The go work use command adds a new module to the go.work file. It will now look like this:

    go 1.18
    
    use (
      ./hello
      ./example
    )
    

    The module now includes both the example.com/hello module and the golang.org/x/example module.

  3. Add the new function.

    We’ll add a new function to uppercase a string to the golang.org/x/example/stringutil package.

    Create a new file named toupper.go in the workspace/example/stringutil directory containing the following contents:

    package stringutil
    
    import "strings"
    
    // ToUpper uppercases argument string.
    func ToUpper(s string) string {
      return strings.ToUpper(s)
    }
    

Run the code in the workspace

Output

References

Get the full source code from this github repository.