gopherpath
0%

go build is the whole build system

30 min · lesson 1 of 2 · #toolchain · #deps

You are used to a build tool that is also a dependency manager that is also a plugin host. Go splits that into one tool that does all three badly-named things, plus gofmt which ends every formatting argument you have ever had.

The command map

Maven / Gradle Go Notes
mvn compile go build ./... compiles to a throwaway cache, not target/
mvn package go build -o bin/app ./cmd/app produces one static binary
mvn test go test ./... tests are source files, not a plugin
mvn dependency:tree go mod graph far smaller, because there is no transitive config
mvn versions:display-dependency-updates go list -m -u all
mvn spring-boot:run go run ./cmd/app no devtools restart needed
mvn deploy / Nexus go get module@version + commit go.sum the module proxy is the registry
checkstyle + spotless gofmt -l . one canonical format, zero config
pom.xml go.mod + go.sum 10 lines vs 200
-Xmx2g at runtime build flags / GOMEMLIMIT tuning is a build+runtime knob, covered in M9

Dependencies are pinned by content hash in go.sum and by the Minimum Version Selection rule: your build uses the maximum of the minimum versions anyone asked for. There is no "nearest-wins" conflict resolution to reason about, which is why Go projects do not have dependency hell stories.

The same program, three toolchains

Go
package main

import "fmt"

func main() {
	fmt.Println("hello from a static binary")
}
Java
public final class Hello {
    public static void main(String[] args) {
        System.out.println("hello from the JVM");
    }
}
TypeScript
// needs tsconfig + node + a bundler (or tsx) to become runnable
const greet = (): string => 'hello from node';
console.log(greet());

Build and run each:

Shell
# Go: 8 MB self-contained binary, no runtime on the target
go build -o bin/hello . && ./bin/hello && file bin/hello

# Java: 5 KB class + a 200 MB JVM on every host
javac Hello.java && java Hello

# TS: transpile, then run under node
npx tsx index.ts

Module layout you will actually use

go.mod is the whole project config:

Text
module github.com/vlad/gopherpath

go 1.27

require (
	github.com/yuin/goldmark v1.7.4
	github.com/alecthomas/chroma/v2 v2.15.0
)

The go 1.27 line is not cosmetic — it sets language semantics for the module (loop-variable scoping, generics behaviour). Bumping it is a deliberate, reviewed change, the way you treat <maven.compiler.release>.

Trap: go get writes to your go.mod. There is no separate "install" step and no lockfile to regenerate by hand. If a build works on your machine and not in CI, the answer is almost always a missing go.sum entry — commit both files.

Proof checkpoint

  1. go mod init example.com/hello in an empty dir, paste the Go program above into main.go, run go build -o bin/hello . and file bin/hello — note "statically linked".
  2. Run go vet ./... and gofmt -l . — both should be silent. These two commands are your spotlessApply.
  3. Copy the binary to another directory (or a scratch container) and run it there with no Go installed.
  4. Break the formatting on purpose, run gofmt -w ., and look at the diff. That is the last formatting debate you will have.
stored in this browser only