Part IV — Real-world builds: terminal
Theory and internals are done — Parts I–III gave you everything the next three parts use. This one and the two after it are capstones: full, opinionated builds rather than isolated examples. This one stays on the terminal: a production-grade CLI, then two larger capstones (a job queue, a multi-GB log analyzer) that combine concurrency, I/O, and error handling into something closer to what you'd actually ship.
40. Building production CLIs: flag, subcommands, config precedence
40.1 The standard library's flag package
var (
verbose = flag.Bool("verbose", false, "enable verbose logging")
port = flag.Int("port", 8080, "port to listen on")
config = flag.String("config", "", "path to config file")
)
func main() {
flag.Parse()
args := flag.Args() // positional args after all flags
}
40.2 Subcommands without a framework
flag.NewFlagSet gives each subcommand its own independent flag scope —
this is literally how cobra-style CLIs (git commit -m, docker run -it) are built underneath, and it's often enough on its own for an
internal tool:
func main() {
if len(os.Args) < 2 {
fmt.Println("usage: toolbelt <serve|migrate|version>")
os.Exit(1)
}
switch os.Args[1] {
case "serve":
serveCmd := flag.NewFlagSet("serve", flag.ExitOnError)
port := serveCmd.Int("port", 8080, "port")
serveCmd.Parse(os.Args[2:])
runServe(*port)
case "migrate":
migrateCmd := flag.NewFlagSet("migrate", flag.ExitOnError)
dryRun := migrateCmd.Bool("dry-run", false, "preview only")
migrateCmd.Parse(os.Args[2:])
runMigrate(*dryRun)
case "version":
fmt.Println("toolbelt v1.0.0")
default:
fmt.Printf("unknown command %q\n", os.Args[1])
os.Exit(1)
}
}
For richer needs — nested subcommands, auto-generated help, shell
completion — reach for github.com/spf13/cobra, which is flag.FlagSet
composed at scale, not a different paradigm.
40.3 Config precedence: flags > env > file > defaults
The convention nearly every well-built CLI follows, highest priority first:
func resolvePort(flagVal *int, flagSet bool) int {
if flagSet {
return *flagVal // 1. explicit flag wins
}
if v := os.Getenv("APP_PORT"); v != "" { // 2. environment variable
if p, err := strconv.Atoi(v); err == nil {
return p
}
}
if cfg.Port != 0 { // 3. config file value
return cfg.Port
}
return 8080 // 4. hardcoded default
}
40.4 Exit codes and stderr vs. stdout
Reserve stdout for a program's actual output (so it composes with | and
scripting); send diagnostics, warnings, and errors to stderr; and return
a non-zero exit code (os.Exit(1)) on failure so shell scripts and CI
can detect it:
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}