Go Workspaces (multi-module)
Tags: go, workspace, multimodule, programming
Published: 2022-11-21 07:48:39

## 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 <strong>issue</strong>: 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.](https://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.
   ```console
   $ 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.

   ```console
   $ 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.

   ```console
   $ go get golang.org/x/example
   ```

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

   ```go
   package main

   import (
    "fmt"
    "golang.org/x/example/stringutil"
   )

   func main() {
     fmt.Println(stringutil.Reverse("Hello"))
   }
   ```

   Now, run the hello program:

   ```go
   $ 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:

```console
$ 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:

```console
$ 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:

   ```console
   $ git clone https://go.googlesource.com/example
   ```

2. Add the module to the workspace

   ```console
   $ 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:

   ```go
   package stringutil

   import "strings"

   // ToUpper uppercases argument string.
   func ToUpper(s string) string {
     return strings.ToUpper(s)
   }
   ```

#### Run the code in the workspace


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qbjmx4cdd6dnjlm2h6fm.png)

## References

- [Go Workspaces](https://go.dev/doc/tutorial/workspaces)
- [Go Modules](https://go.dev/ref/mod#workspaces)

Get the full source code from [this github repository](https://github.com/datumbrain/kss-go-workspace).