Go Command — Junior Level¶
Table of Contents¶
- Introduction
- Prerequisites
- Glossary
- Core Concepts
- Pros & Cons
- Use Cases
- Code Examples
- Coding Patterns
- Clean Code
- Product Use / Feature
- Error Handling
- Security Considerations
- Performance Tips
- Metrics & Analytics
- Best Practices
- Edge Cases & Pitfalls
- Common Mistakes
- Tricky Points
- Test
- Tricky Questions
- Cheat Sheet
- Summary
- What You Can Build
- Further Reading
- Related Topics
- Diagrams & Visual Aids
Introduction¶
Focus: "What is it?" and "How to use it?"
The go command is the primary tool for managing Go source code. It is a single binary that bundles compiling, testing, formatting, dependency management, and much more. Unlike many other languages that require a separate build tool (Maven, Gradle, npm), Go ships everything you need in one CLI.
Every Go developer interacts with the go command dozens of times per day, so understanding its sub-commands is essential from day one.
Prerequisites¶
- Required: Go installed on your system (
go1.21+) — all commands depend on a working Go installation - Required: Basic terminal / command-line skills — you will run all commands in a terminal
- Helpful but not required: Understanding of what a compiler does (turns source code into an executable)
Glossary¶
| Term | Definition |
|---|---|
| Module | A collection of Go packages with a go.mod file at its root |
| Package | A directory of .go files that compile together |
| Binary | The executable file produced by go build |
| Dependency | An external module your code imports |
| go.mod | The file that declares the module path and its dependency requirements |
| go.sum | The file that stores cryptographic checksums of dependencies |
| GOPATH | The workspace directory for Go code (less important since Go modules) |
| GOROOT | The directory where Go itself is installed |
Core Concepts¶
Concept 1: go run — Run a program¶
go run compiles and immediately executes one or more .go files. It does not produce a permanent binary — the compiled output is placed in a temporary directory and deleted after execution.
go run main.go
go run . # run the package in the current directory
go run ./cmd/server # run a specific package
Concept 2: go build — Compile a binary¶
go build compiles your code into an executable binary. By default the binary is named after the directory (or the -o flag overrides this).
go build # produces binary named after directory
go build -o myapp # produces binary named "myapp"
go build ./... # compile all packages (check for errors)
Concept 3: go fmt — Format code¶
go fmt rewrites your .go files to follow the standard Go formatting style. There is no configuration — every Go project looks the same.
Concept 4: go vet — Find suspicious code¶
go vet runs static analysis to find common mistakes that compile fine but are probably bugs.
Concept 5: go test — Run tests¶
go test compiles and runs test functions (functions named TestXxx in *_test.go files).
go test ./... # test all packages
go test -v ./... # verbose output
go test -run TestFoo # run only tests matching "TestFoo"
Concept 6: go mod init — Initialize a module¶
Creates a new go.mod file in the current directory, declaring a new module.
Concept 7: go mod tidy — Clean up dependencies¶
Adds missing dependencies and removes unused ones from go.mod and go.sum.
Concept 8: go get — Add or update dependencies¶
Downloads and installs packages and their dependencies, updating go.mod.
go get github.com/gin-gonic/gin # add latest version
go get github.com/gin-gonic/gin@v1.9.1 # add specific version
go get -u ./... # update all dependencies
Concept 9: go install — Install a binary¶
Compiles and installs a binary to $GOPATH/bin (or $GOBIN).
Concept 10: go doc — View documentation¶
Shows documentation for a package, function, type, or method.
go doc fmt # package-level doc
go doc fmt.Println # function doc
go doc -all fmt # everything in the package
Concept 11: go version and go env¶
go version # prints Go version: go version go1.22.0 linux/amd64
go env # prints all Go environment variables
go env GOPATH # print a specific variable
Real-World Analogies¶
| Concept | Analogy |
|---|---|
go run | Like pressing "Play" in an IDE — it compiles and runs in one step, but does not save the compiled result |
go build | Like baking a cake and keeping it — you get a finished product (binary) you can share |
go fmt | Like a spell-checker for code style — it automatically fixes formatting so everyone writes code the same way |
go mod tidy | Like cleaning out your fridge — removes expired items (unused deps) and adds missing ingredients (needed deps) |
Mental Models¶
The intuition: Think of the go command as a Swiss Army knife. Each sub-command (run, build, test, fmt, vet, etc.) is a different blade. You carry one tool, but it handles everything.
Why this model helps: It prevents beginners from looking for separate tools for each task. There is no separate formatter binary, no separate test runner, no separate package manager — the go command does it all.
Pros & Cons¶
| Pros | Cons |
|---|---|
| All-in-one tool — no need for Makefiles or build tools | Limited customization — cannot change formatting rules |
| Fast compilation — Go compiles faster than most languages | go get behavior changed between Go versions (confusing history) |
| Built-in testing and benchmarking | No built-in task runner (like npm run dev) |
| Cross-compilation out of the box | Error messages can be cryptic for beginners |
Standardized formatting with go fmt | No built-in watch mode (need third-party tools) |
When to use:¶
- Always — the
gocommand is how you work with Go code
When NOT to use:¶
- When you need a complex multi-language build system — use Make, Bazel, or similar alongside the
gocommand
Use Cases¶
- Use Case 1: Quickly running a script —
go run main.golets you test ideas without a compile step - Use Case 2: Building a production binary —
go build -o serverproduces a deployable executable - Use Case 3: Managing dependencies —
go mod init,go get,go mod tidyhandle your module's imports
Code Examples¶
Example 1: Hello World with go run¶
What it does: Prints "Hello, World!" to the terminal. How to run: go run main.go
Example 2: Building and running a binary¶
// Save as main.go
package main
import (
"fmt"
"os"
)
func main() {
name := "Go Developer"
if len(os.Args) > 1 {
name = os.Args[1]
}
fmt.Printf("Hello, %s!\n", name)
}
What it does: Greets the user by name (defaults to "Go Developer"). How to run:
Example 3: Writing and running a test¶
// Save as math_test.go
package main
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Add(2, 3) = %d; want 5", result)
}
}
How to run: go test -v Output:
Example 4: Initializing a module and adding a dependency¶
# Create a new project
mkdir myproject && cd myproject
go mod init github.com/user/myproject
# Create main.go
cat > main.go << 'EOF'
package main
import (
"fmt"
"github.com/fatih/color"
)
func main() {
color.Green("Hello in green!")
fmt.Println("Regular text")
}
EOF
# Download the dependency
go mod tidy
# Run the program
go run main.go
Coding Patterns¶
Pattern 1: Build-then-run workflow¶
Intent: Separate compilation from execution for repeatable deployments. When to use: When deploying to production or distributing binaries.
Diagram:
Remember: Use go run for development, go build for production.
Pattern 2: Test-format-vet cycle¶
Intent: Catch bugs and style issues before committing code. When to use: Before every git commit.
Diagram:
Clean Code¶
Naming¶
// Bad naming
func r(p string) {}
var m = make(map[string]string)
// Clean naming
func readConfig(path string) {}
var userCache = make(map[string]string)
Rules: - Variables: describe WHAT they hold (userCount, not n, x, tmp) - Functions: describe WHAT they do (buildBinary, not do, run) - Booleans: use is, has, can prefix (isValid, hasTests)
Functions¶
// Too long, does too many things
func deploy(code string) error {
// 60+ lines: build, test, push, notify...
return nil
}
// Single responsibility
func buildBinary(src string) (string, error) { return "", nil }
func runTests(dir string) error { return nil }
func pushToRegistry(bin string) error { return nil }
Rule: If you need to scroll to see a function — it does too much. Aim for 20 lines or fewer.
Comments¶
// Noise comment (states the obvious)
// build the binary
go build -o server
// Explains WHY, not WHAT
// Use -trimpath to remove local file paths from the binary (for security)
go build -trimpath -o server
Rule: Good code explains itself. Comments explain why, not what.
Product Use / Feature¶
1. Docker Multi-Stage Builds¶
- How it uses Go commands:
go buildcompiles a static binary inside a Docker builder stage, then the binary is copied to a minimalscratchoralpineimage. - Why it matters: Produces tiny container images (5-15 MB instead of 300+ MB).
2. CI/CD Pipelines (GitHub Actions, GitLab CI)¶
- How it uses Go commands:
go test ./...,go vet ./..., andgo buildare the standard CI steps. - Why it matters: Automated quality checks catch bugs before they reach production.
3. VS Code / GoLand IDE Integration¶
- How it uses Go commands: IDEs run
go fmt,go vet,go test, andgo docautomatically in the background. - Why it matters: Developers get instant feedback as they type.
Error Handling¶
Error 1: go: cannot find main module¶
Why it happens: You are in a directory without a go.mod file. How to fix:
Error 2: package xxx is not in std¶
Why it happens: The dependency has not been downloaded. How to fix:
Error 3: cannot find package after renaming¶
Why it happens: The import path does not match the module path in go.mod. How to fix:
// Make sure go.mod says:
module github.com/user/myproject
// Then import as:
import "github.com/user/myproject/utils"
Error Handling Pattern¶
# Always check if the command succeeded
go build -o server ./cmd/server
if [ $? -ne 0 ]; then
echo "Build failed!"
exit 1
fi
Security Considerations¶
1. Dependency supply-chain attacks¶
# Insecure — blindly adding dependencies
go get github.com/random-user/unverified-lib
# Secure — verify checksums and use the Go checksum database
go get github.com/well-known/trusted-lib
go mod verify # verify downloaded modules match go.sum
Risk: Malicious code hidden in dependencies can steal data or install backdoors. Mitigation: Use go mod verify, review go.sum changes, and enable GONOSUMCHECK only for private modules.
2. Embedding sensitive data in binaries¶
# Insecure — hardcoded secrets compiled into binary
go build -ldflags="-X main.apiKey=secret123" -o server
# Secure — read secrets from environment at runtime
Risk: Anyone who decompiles the binary can extract secrets. Mitigation: Never embed secrets via -ldflags. Use environment variables or a secrets manager.
Performance Tips¶
Tip 1: Use go build ./... to check compilation without producing binaries¶
# Slow approach — build each package separately
go build ./pkg/auth
go build ./pkg/db
# Faster approach — build all at once
go build ./...
Why it's faster: Go only compiles each package once and caches the results.
Tip 2: The build cache¶
Go automatically caches compiled packages. To see cache statistics:
Why it matters: Second builds are near-instant because unchanged packages use the cache.
Metrics & Analytics¶
What to Measure¶
| Metric | Why it matters | Tool |
|---|---|---|
| Build time | Slow builds reduce productivity | time go build ./... |
| Test execution time | Slow tests discourage running them | go test -v ./... 2>&1 \| tail |
| Binary size | Affects deployment speed and container image size | ls -lh ./server |
Basic Instrumentation¶
# Measure build time
time go build -o server ./cmd/server
# Measure test time
go test -v -count=1 ./... 2>&1 | grep -E "ok|FAIL"
Best Practices¶
- Always run
go fmt ./...before committing — keeps code style consistent - Always run
go vet ./...in CI — catches bugs that compile fine but are wrong - Use
go mod tidyregularly — keepsgo.modclean and accurate - Use
go test -race ./...in CI — detects data races that cause intermittent bugs - Never commit
vendor/unless required —go mod downloadreproduces it
Edge Cases & Pitfalls¶
Pitfall 1: go run with multiple files¶
# This fails if main() uses functions from other files
go run main.go
# Error: undefined: helperFunction
# Fix: include all files
go run main.go helpers.go
# Better: run the whole package
go run .
What happens: go run main.go only compiles main.go, not other files in the package. How to fix: Use go run . to compile the entire package.
Pitfall 2: go get inside vs outside a module¶
# Inside a module directory (has go.mod) — adds dependency to go.mod
go get github.com/pkg/errors
# Outside a module directory — installs binary (Go 1.17+, use go install instead)
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
Common Mistakes¶
Mistake 1: Forgetting go mod tidy after adding imports¶
// You add a new import:
import "github.com/sirupsen/logrus"
// But forget to run:
// go mod tidy
// Result: build fails with "missing go.sum entry"
Mistake 2: Using go get to install tools (deprecated since Go 1.17)¶
# Wrong way (deprecated)
go get golang.org/x/tools/gopls
# Correct way
go install golang.org/x/tools/gopls@latest
Mistake 3: Running go test without ./...¶
Common Misconceptions¶
Misconception 1: "go run is the same as go build + running the binary"¶
Reality: go run creates a temporary binary in a temp directory and deletes it afterward. It is NOT the same as go build -o app && ./app because the binary path and caching behavior differ.
Why people think this: The output looks the same, so it seems identical.
Misconception 2: "go fmt is optional"¶
Reality: While go fmt does not affect compilation, it is considered mandatory in the Go community. Most CI pipelines reject code that is not properly formatted.
Why people think this: Other languages treat formatting as a preference, but Go enforces a single standard.
Tricky Points¶
Tricky Point 1: go build produces no output for library packages¶
Why it's tricky: Beginners expect a binary to appear. go build on a non-main package only checks for compilation errors. Key takeaway: Only package main with func main() produces an executable.
Tricky Point 2: go test caches results¶
go test ./... # runs tests
go test ./... # uses cached results (prints "ok (cached)")
go test -count=1 ./... # forces re-run
Why it's tricky: You might think tests ran again, but they used cached results. Key takeaway: Use -count=1 to force fresh test execution.
Test¶
Multiple Choice¶
1. What does go run main.go do?
- A) Compiles main.go and saves the binary in the current directory
- B) Compiles main.go into a temporary binary and executes it
- C) Interprets main.go line by line (like Python)
- D) Downloads dependencies and then runs main.go
Answer
**B)** — `go run` compiles to a temporary directory and executes the result. It does NOT save a binary in the current directory (that's `go build`), and Go is NOT an interpreted language.True or False¶
2. go fmt can be configured to use tabs instead of spaces (or vice versa).
Answer
**False** — `go fmt` has no configuration options. It always uses tabs for indentation. This is intentional — it eliminates style debates.What's the Output?¶
3. What happens when you run this?
mkdir newproject && cd newproject
echo 'package main; import "fmt"; func main() { fmt.Println("hi") }' > main.go
go build
Answer
Output: `go: cannot find main module` (or similar error about missing go.mod). You must run `go mod init` first to create a module.4. What does this command produce?
Answer
It compiles all packages but discards the output (no binary is written). This is useful for checking that code compiles without errors.5. Which command adds missing dependencies AND removes unused ones?
- A)
go get ./... - B)
go mod download - C)
go mod tidy - D)
go mod verify
Answer
**C)** — `go mod tidy` both adds missing and removes unused dependencies. `go get` only adds, `go mod download` only downloads, and `go mod verify` only checks checksums."What If?" Scenarios¶
What if you delete go.sum and run go build? - You might think: The build will fail because checksums are missing. - But actually: Go will re-download modules and regenerate go.sum. The build succeeds, but you should commit the new go.sum.
What if you run go fmt on a file with syntax errors? - You might think: go fmt will format it anyway. - But actually: go fmt will print the syntax error and leave the file unchanged. It only formats valid Go code.
Tricky Questions¶
1. What is the difference between go build and go install?
- A) They are identical
- B)
go buildproduces a binary in the current directory;go installputs it in$GOPATH/bin - C)
go installalso downloads dependencies;go builddoes not - D)
go buildonly works forpackage main
Answer
**B)** — `go build` places the binary in the current directory (or a specified path with `-o`), while `go install` places it in `$GOPATH/bin` (or `$GOBIN`). Both compile; the difference is where the output goes.2. You run go test ./... twice in a row. The second run finishes instantly. Why?
- A) Go is very fast at compiling
- B) Go caches test results and skips re-running unchanged tests
- C) The tests are empty
- D) The test binary is cached from the first run
Answer
**B)** — Go caches test results based on the test binary, inputs, and environment. If nothing changed, it prints `(cached)` instead of re-running tests.Cheat Sheet¶
| What | Syntax / Command | Example |
|---|---|---|
| Run a program | go run <files> | go run . |
| Build a binary | go build -o <name> | go build -o server |
| Format code | go fmt <packages> | go fmt ./... |
| Static analysis | go vet <packages> | go vet ./... |
| Run tests | go test <packages> | go test -v ./... |
| Init a module | go mod init <path> | go mod init github.com/user/app |
| Add dependency | go get <module> | go get github.com/gin-gonic/gin |
| Clean deps | go mod tidy | go mod tidy |
| Install tool | go install <module>@<ver> | go install golang.org/x/tools/gopls@latest |
| View docs | go doc <symbol> | go doc fmt.Println |
| Check Go version | go version | go version |
| Show env vars | go env <var> | go env GOPATH |
Self-Assessment Checklist¶
I can explain:¶
- What the
gocommand is and why it is an all-in-one tool - The difference between
go run,go build, andgo install - What
go.modandgo.sumare for
I can do:¶
- Initialize a new Go module from scratch
- Add and remove dependencies using
go getandgo mod tidy - Run tests, format code, and vet code with one command each
- Build a production binary with
go build -o
I can answer:¶
- All multiple choice questions in this document
Summary¶
- The
gocommand is Go's all-in-one tool for building, testing, formatting, and managing dependencies go run= compile + execute (temporary).go build= compile + save binary.go install= compile + save to$GOPATH/bingo fmt,go vet, andgo testare the quality trifecta — run them before every commitgo mod init,go mod tidy, andgo getmanage your module's dependencies
Next step: Learn about Go packages and imports — how to organize code across multiple files and directories.
What You Can Build¶
Projects you can create:¶
- CLI calculator: Practice
go buildandgo runwith a command-line tool - Unit-tested string library: Practice
go testwith table-driven tests - Multi-file web server: Practice module initialization and dependency management with
go get
Learning path — what to study next:¶
Further Reading¶
- Official docs: Command go — comprehensive reference for all sub-commands
- Blog post: Using Go Modules — official guide to module workflow
- Video: The Go Command — GopherCon — deep dive into the go tool
Related Topics¶
- Packages & Imports — how Go organizes code that the
gocommand compiles - Go Modules — deep dive into
go.mod, versioning, and dependency management
Diagrams & Visual Aids¶
Mind Map¶
Go Command Workflow¶
Build Pipeline — ASCII¶
+----------------+ +----------------+ +----------------+
| Source Code | | go build | | Binary |
| (.go files) | --> | (compiler) | --> | (executable) |
+----------------+ +----------------+ +----------------+
| |
v v
+----------------+ +----------------+
| go.mod | | Deploy to |
| go.sum | | server/Docker |
+----------------+ +----------------+