Go 1.11 adds experimental modules and a WebAssembly target
Go 1.11 lets dependency identity move beyond GOPATH and compiles programs for JavaScript WebAssembly hosts. Both changes arrive with explicit experimental boundaries.
Go programs have depended on the arrangement of source under GOPATH, even when the application author wanted the repository itself to describe a build. Go 1.11 introduces a way to change that relationship. Its module support records versioned dependencies in the project while allowing development outside the workspace directory. In the same release, a new WebAssembly target lets the compiler produce a program for a JavaScript host.
Andrew Bonventre's release announcement marks both capabilities as experimental. Their availability matters, but so do their limits. Module support adds a dependency model to the existing command. The WebAssembly port includes a Go runtime and an experimental JavaScript interface. Neither change requires a new language syntax.
I find the module experiment especially useful because it makes the source of a dependency decision easier to inspect. Go's appeal depends heavily on understanding a program without reconstructing private conventions. A repository-level declaration can make its build less dependent on the contents of a developer's workspace.
Modules describe the source selected for a build
The Go 1.11 command documentation defines a module as related packages released and versioned together. A go.mod file marks its root and identifies the import-path prefix. Subdirectories belong to that module unless a subtree has its own module declaration.
This makes the module distinct from a package. A Go file still imports packages. The command resolves those imports using a selected set of module versions, called the build list. A module can supply several packages, all associated with the same release identity. That grouping matches cases where multiple packages evolve together without giving every directory independent release metadata.
A new module can begin with a declaration and then acquire requirements as the go command resolves imports. For a directory outside GOPATH/src, the following command creates the declaration.
go mod init example.com/report
That is an initialization example, not a command to replace a project's identity with a placeholder path. The chosen module path becomes the prefix used by consumers. For an existing project, it needs to match how the project's packages should be addressed.
Go 1.11 can also initialize requirements from configurations created by several earlier dependency tools. This recognizes that adoption starts with existing code. An author need not abandon the recorded dependency choices merely to begin expressing them in the new format.
Module mode has a precise activation rule
The temporary GO111MODULE variable selects off, on, or auto. In the default automatic mode, Go 1.11 enables modules when the command runs outside GOPATH/src in a tree with a go.mod. The existence of that file inside GOPATH does not have the same effect under the default rule.
This explains a potentially confusing migration experience. Two copies of the same repository can select different operating modes because their locations differ. Setting the variable explicitly can remove that ambiguity, while leaving it automatic preserves the old workflow in its usual directory. A report about module behavior needs to identify the mode as well as the source files.
Module mode changes how imports obtain their meaning, but GOPATH still has storage roles. Downloaded dependencies go under GOPATH/pkg/mod, and installed commands normally go to GOPATH/bin. Working outside GOPATH therefore does not imply that the variable disappears from all tool behavior. It stops being the directory layout that defines the application's dependency versions.
The distinction is practical when inspecting a machine. A dependency found in the download cache is not necessarily an unversioned workspace checkout. The build list identifies which module version the command selects. Looking only at a familiar workspace directory no longer explains the whole build.
Requirements compose across dependencies
The command recursively follows module requirements and keeps the highest required version for a module identity. This is the minimal version selection approach described in Cox's February design. It selects enough to satisfy the accumulated minimum requirements without treating the latest published release as an automatic update.
Suppose the main module requires D at version 1.2, while another selected dependency requires D at 1.4. The build uses 1.4. Its choice follows from both requirements, so reading only the main module's direct import statements would miss part of the explanation. Publishing D 1.5 alone does not require the unchanged build to start selecting it.
The module convention also distinguishes incompatible major versions through their paths. The earlier semantic import versioning design explains why a second major version normally uses a path ending in /v2. Both versions can then appear in one program as different packages. A dependency can migrate without requiring every other package to change simultaneously.
There is an adoption cost in those names. Authors must keep their declarations and internal imports consistent with the major-version identity. That work has a concrete purpose for downstream users. The source names which incompatible API it expects, and the compiler can distinguish packages with those different paths.
I prefer that explicit cost to an import whose meaning changes invisibly across a large build. The path adds information a maintainer needs when two dependencies migrate at different times. It does not eliminate the work of adapting code to a new API.
The module file is maintained by commands
Ordinary commands can add requirements when an import needs a module not yet recorded. Removing an unnecessary requirement needs a broader view. The versioned help describes go mod tidy as examining packages across build configurations so it can add missing requirements and remove redundant ones.
This difference matters for platform-specific source. A dependency unused in today's build can still be required by files selected on another system. A cleanup operation that considers only the current package and target would lack enough evidence to remove it. The dedicated module command has a wider responsibility.
The main module also controls replacements and exclusions for its own build. The command ignores those directives in dependency modules. That policy lets the application author select a local replacement without allowing every dependency author to impose equivalent overrides on the entire application.
For automation, -mod=readonly prevents ordinary commands from silently updating go.mod when changes are needed. The documented behavior is to fail in that situation. It gives a build system an explicit check that dependency metadata is already complete, while commands intended to edit requirements retain their separate role.
WebAssembly includes the runtime
The release notes identify the new target as js/wasm. A compiled program contains the Go runtime as well as application code. Goroutine scheduling and garbage collection remain part of executing Go. The target is therefore more than an isolated translation of one function into WebAssembly instructions.
The compiler invocation can select the target through its existing environment variables. In a shell supporting command-local environment assignments, a build of a suitable main package takes this form.
env GOOS=js GOARCH=wasm go build -o report.wasm .
Producing the file is one part of execution. The release's browser example loads wasm_exec.js, creates a Go object, and passes its import object when instantiating the module. It then runs the instance through that object. The example shows the required relationship between generated code and its JavaScript support.
The new syscall/js package provides access to the host environment using JavaScript semantics. Its source explicitly describes an experimental interface intended initially to support tests. It is exempt from the normal compatibility promise. Treating its current shape as an established application integration contract would overstate what this release offers.
The target also affects source selection. The release notes explain that filenames ending in _js.go or _wasm.go now receive the corresponding platform interpretation. An existing file with one of those names can disappear from a native build unless it is renamed. That consequence affects projects even when their authors have no intention of compiling for the browser, because adding a target gives previously ordinary filename suffixes a new meaning.
Both experiments make familiar Go tooling operate in a different context. Modules move dependency selection into explicit project metadata. WebAssembly provides a target whose execution includes JavaScript support and the Go runtime. An application can evaluate either capability independently, using the released command's documented mode rules for modules and the supplied runtime support for the WebAssembly binary.