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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A dedicated FlagSet lets you parse arguments without touching global state, which makes the logic testable.
- 2Validating parsed values immediately turns silent bad input into clear, actionable errors.
- 3Returning errors instead of exiting keeps parsing reusable; the caller decides how to react.
Related explainers
javascript
'use server' import { revalidatePath } from 'next/cache' import { redirect } from 'next/navigation'
How a Next.js Server Action updates a post
server-actions
authorization
validation
Intermediate
7 steps
java
public class ThumbnailProcessor { private static final int MAX_CONCURRENCY = 4;
Bounded parallel thumbnail rendering in Java
concurrency
thread-pool
futures
Intermediate
7 steps
php
<?php namespace App\Support;
Merging query params onto a URL in PHP
url-parsing
query-strings
immutability
Intermediate
8 steps
go
package cache import ( "container/list"
Building a generic LRU cache in Go
lru-cache
generics
linked-list
Intermediate
8 steps
go
package model import ( "encoding/json"
Custom JSON marshaling in Go
json
serialization
interfaces
Intermediate
5 steps
javascript
const transitions = { cart: { checkout: 'shipping' }, shipping: { submitAddress: 'payment', back: 'cart' }, payment: { submitPayment: 'review', back: 'shipping' },
A finite state machine for checkout flow
state-machine
event-driven
data-driven-design
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/parsing-and-validating-cli-flags-in-go-explained-go-4977/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.