skip to content

In a Go module, what does the ./... pattern match, and what does go build ./... leave on disk?

level: middleimportance: should knowfreq 50%

answer

  1. a wildcard over path elements, not a shell glob
  2. main module only, dependencies excluded
  3. some directories never match
  4. several packages means check-only
  5. -o takes a directory

basics

~20 s

The ./... pattern matches every package in the current directory and its subdirectories that belongs to the main module. Building several packages at once only checks that they compile: go build discards the executables and writes no files.

solid answer

~50 s

`...` is the go command's wildcard for path elements, so `./...` expands to the package in the working directory plus every package beneath it — but only packages of the **main module**, never dependencies. Directories with no Go files are skipped, as are directories named `testdata` and any whose name starts with `.` or `_`, which is why a repository can keep deliberately-broken fixture files without breaking the build. The output rule surprises people: when `go build` is given more than one package it compiles them purely as a check and **throws the executables away**, so `go build ./...` in a multi-package repo produces no files. To keep binaries, either install them with `go install ./cmd/...`, or give `-o` a directory — `go build -o bin/ ./cmd/...` — which writes one executable per main package into it. `-o` naming a single file with several main packages is an error.

code

text · 3 lines
text
$ go build ./...                 # compiles everything, writes nothing
$ go build -o bin/ ./cmd/...     # one executable per main package, into bin/
$ go install ./cmd/...           # same set, into $GOBIN

go deeper

for a junior

Recall that ./... means this directory and everything below it within your own module, and that go build over many packages is a compile check rather than a way to produce binaries.

for a middle

Explain the expansion rules — main module only, skipped testdata and dot/underscore directories — and the exact remedy for keeping executables, including -o with a directory.

for a senior

Show where each form belongs in a pipeline: a whole-tree compile gate, a release step that collects commands, and why a package with no tests still needs a build step to be covered.

for a principal

Set the convention once — which patterns the build scripts use and where artefacts land — so CI, local builds and release automation do not each diverge on what gets compiled.

## The pattern language The go command's arguments are **package patterns**, not file paths. `.` is the package in the working directory, `./cmd/api` is a package below it, and `...` is a wildcard that matches any sequence of path elements. So `./...` means "this package and everything under it", and `./cmd/...` narrows that to the commands. Two boundaries matter: - **`./...` stays inside the main module.** It expands over the directory tree of the module you are in, so dependencies are never matched — they are compiled as needed, but they are not part of the pattern. The pattern that spans dependencies is `all`, which means every package transitively imported by the main module's packages. - **Some directories are skipped.** A directory with no Go files contributes no package. Directories named `testdata`, and any directory whose name begins with `.` or `_`, are ignored by pattern expansion entirely. This is what lets a repository keep intentionally-uncompilable fixtures under `testdata/` without `go build ./...` ever tripping over them. ## The output rule that surprises people `go build` writes an executable only when it is given **exactly one main package**. Give it several packages — which `./...` almost always does — and it compiles everything and **discards the results**. The documented purpose of that mode is to serve as a check that the packages build. So `go build ./...` is an excellent, cheap "does the whole tree still compile" step and a completely ineffective way to produce artefacts. Engineers new to Go run it, see no error, look for binaries and find none. A non-main package behaves the same way for a different reason: there is nothing to link, so building it can only ever be a check. ## Getting the binaries you wanted There are two honest ways to turn a pattern into executables: - **`go install ./cmd/...`** builds every main package under `cmd/` and installs each into `$GOBIN` (or `$GOPATH/bin`), named after the last element of its package path. - **`go build -o bin/ ./cmd/...`** writes them into a directory of your choosing instead. `-o` accepts a directory, and each main package produces one file inside it. Passing a **single file name** with several main packages is rejected — the go command will not silently pick one or overwrite in a loop. For a single command, `go build -o bin/api ./cmd/api` remains the direct form. ## Where this shows up in real repositories - **CI compile gate:** `go build ./...` as a fast step that fails on a package nobody imports from a test — a package with no tests and no importers is otherwise never compiled by `go test ./...`. - **Check-only builds:** `go build -o /dev/null ./...` makes the discard explicit for readers who do not know the multi-package rule. - **Release step:** `go build -o dist/ ./cmd/...` to collect every command of a repository in one directory. - **Selective work:** `./internal/...` or `./cmd/...` when a full-tree build is slower than you want. ## Common misreadings - Believing `./...` includes dependencies. It does not; `all` is that pattern. - Believing `go build ./...` writes one executable per main package into the current directory. It writes nothing. - Believing `...` is a shell glob. It is interpreted by the go command, so quoting rarely matters and `./…`-style patterns work identically on every platform. A shell glob such as `./cmd/*` would expand to directories and behave differently. - Forgetting that a broken file in `testdata/` is invisible to the build, and then being confused about why an editor reports errors the build does not. ## Why interviewers ask It separates people who have run Go commands from people who have only read them. The pattern rules and the discard rule are exactly the two things that bite when you write a Makefile or a CI job for the first time, and being able to state both — plus the `-o <dir>` remedy — shows the candidate has built and shipped something rather than just run `go run .`.

  • How do you get an executable for every main package under cmd/ in one command?
    `go build -o bin/ ./cmd/...` writes one executable per main package into `bin/`, each named after the last element of its package path; `go install ./cmd/...` does the same into `$GOBIN`. What you cannot do is give `-o` a single file name with several main packages — that is an error rather than a silent overwrite.
  • Does ./... include the packages of your dependencies?
    No. `./...` expands only over the main module's own directory tree. Dependencies are still compiled when your packages import them, but they are not matched by the pattern. The pattern that names dependencies is `all`, meaning every package transitively imported by the main module's packages — which is why `go list all` prints far more than `go list ./...`.
  • Why doesn't go build ./... fail on deliberately-broken Go files kept under testdata/?
    Because pattern expansion skips directories named `testdata` outright, along with any directory whose name begins with `.` or `_`, and directories containing no Go files. That is exactly what makes `testdata` the conventional home for fixtures that must not compile — golden inputs for a parser, for instance.

saying these in an interview costs you the question

  • Says ./... also matches packages in dependencies
  • Expects go build ./... to leave one binary per main package
  • Thinks ... is expanded by the shell
  • Assumes testdata directories are compiled like any other
  • Passes -o a single file name for several main packages