go 58 lines · 8 steps

Parsing and validating CLI flags in Go

A self-contained flag parser binds command-line arguments into a config struct and rejects invalid values before the program runs.

Explained by highlit
1package main
2 
3import (
4 "errors"
5 "flag"
6 "fmt"
7 "os"
8 "time"
9)
10 
11type config struct {
12 addr string
13 port int
14 timeout time.Duration
15 verbose bool
16 env string
17}
18 
19func parseConfig(args []string) (*config, error) {
20 cfg := &config{}
21 
22 fs := flag.NewFlagSet("server", flag.ContinueOnError)
23 fs.StringVar(&cfg.addr, "addr", "0.0.0.0", "host address to bind")
24 fs.IntVar(&cfg.port, "port", 8080, "port to listen on")
25 fs.DurationVar(&cfg.timeout, "timeout", 15*time.Second, "request timeout")
26 fs.BoolVar(&cfg.verbose, "verbose", false, "enable verbose logging")
27 fs.StringVar(&cfg.env, "env", "development", "runtime environment")
28 
29 if err := fs.Parse(args); err != nil {
30 return nil, err
31 }
32 
33 if cfg.port < 1 || cfg.port > 65535 {
34 return nil, fmt.Errorf("port must be between 1 and 65535, got %d", cfg.port)
35 }
36 if cfg.timeout <= 0 {
37 return nil, errors.New("timeout must be positive")
38 }
39 switch cfg.env {
40 case "development", "staging", "production":
41 default:
42 return nil, fmt.Errorf("invalid env %q: must be development, staging, or production", cfg.env)
43 }
44 if fs.NArg() > 0 {
45 return nil, fmt.Errorf("unexpected positional arguments: %v", fs.Args())
46 }
47 
48 return cfg, nil
49}
50 
51func main() {
52 cfg, err := parseConfig(os.Args[1:])
53 if err != nil {
54 fmt.Fprintln(os.Stderr, "error:", err)
55 os.Exit(2)
56 }
57 fmt.Printf("listening on %s:%d (env=%s)\n", cfg.addr, cfg.port, cfg.env)
58}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A dedicated FlagSet lets you parse arguments without touching global state, which makes the logic testable.
  2. 2Validating parsed values immediately turns silent bad input into clear, actionable errors.
  3. 3Returning errors instead of exiting keeps parsing reusable; the caller decides how to react.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Parsing and validating CLI flags in Go — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code