go-flags

A reflection-based command-line option parser for Go that turns struct field tags into fully documented flags, subcommands, and INI-file configuration.

Library
Go
vv1.6.1
2,695stars
BSD 3-Clause License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
47/100Fair
Development Activity0
Maintenance20
Community68
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
73/100Good
Architecture78
Code Quality82
Innovation58
Learning Curve75

go-flags is a command-line argument parsing library for Go that goes well beyond the standard library’s flag package. Instead of imperatively registering each flag, you declare a plain Go struct and annotate its fields with tags such as short, long, description, default, env, and required; the library uses reflection to turn that struct into a fully-featured parser, including generated --help output.

Beyond basic flags, go-flags supports nested option groups, namespaced sub-parsers, git-style subcommands (with aliases and arbitrarily deep nesting), positional argument structs, maps and slices as option values, custom types via the Marshaler/Unmarshaler interfaces, and reading defaults from environment variables or an INI file. It also ships a builtin shell-completion mode and Windows-style /flag option syntax as an alternative to POSIX-style dashes.

The project has been stable for over a decade (created in 2012, BSD-3-Clause licensed) and is widely vendored as a dependency inside other popular Go CLIs and tools, making it one of the more battle-tested alternatives to spf13/cobra and spf13/pflag for teams that prefer a struct-tag-driven, declarative style over an imperative builder API.

What You Get

  • Declarative struct-tag API — short (-v) and long (--verbose) option names, descriptions, defaults, and required flags all defined as Go struct tags instead of imperative registration calls
  • Automatic, well-formatted --help and man-page generation derived directly from the same struct tags used for parsing
  • First-class subcommand support (AddCommand or a command: struct tag) with aliases, arbitrary nesting, and a Commander interface for wiring up Execute handlers
  • Rich value support: slices (repeatable flags), maps (key:value syntax), pointers, function callbacks, and choice-restricted string enums via the choice tag
  • Configuration layering — flag values can default from environment variables (env tag, with env-delim for slices/maps) or be read from and written to INI files
  • Built-in bash completion mode (GO_FLAGS_COMPLETION=1) with a Completer interface for custom value completion, plus Windows-style /flag syntax as a build-time alternative to POSIX dashes

Common Use Cases

  • Building a single-binary CLI tool that needs typed, validated flags (ints, durations, custom types) without hand-writing conversion and validation code
  • Building git-style multi-command CLIs (mytool add, mytool rm) where each subcommand has its own option set plus inherited parent flags
  • Tools that need to support configuration from multiple sources — command line, environment variables, and an INI config file — with a single struct as the source of truth
  • CLIs that need to ship shell completion (bash) for flags, subcommands, and argument values without maintaining a separate completion script generator
  • Projects standardizing on a struct-tag/reflection style of flag definition instead of pflag’s imperative Var-style registration

Under The Hood

Architecture go-flags is organized as a layered set of cooperating types rather than a monolith: Parser (in parser.go) embeds *Command (in command.go), which embeds *Group (in group.go), so a parser is itself the root command and root group — this lets subcommands (also *Command) recursively contain their own groups and further subcommands with no special-casing. Each declared struct field becomes an Option (option.go) built from a multiTag (multitag.go), a small hand-rolled tag-string scanner independent of reflect.StructTag.Lookup. Parsing walks the args slice consuming short/long options and dispatching into the active Command’s options, sub-Groups, or a matched subCommand, so the core abstraction that would break the whole design if changed is the Group/Command/Option embedding chain — every feature (namespaces, INI I/O, help/man generation, completion) reads from that same tree rather than a separate flag registry.

Tech Stack The library targets Go 1.20 and has exactly two runtime dependencies: golang.org/x/sys (for terminal width detection used by termsize.go’s platform-specific termsize_windows.go/termsize_nosysioctl.go files) and github.com/sergi/go-diff (used only in tests, for diffing expected vs. actual help output). Everything else — INI parsing (ini.go), man-page generation (man.go), bash completion (completion.go), and its own Levenshtein-distance implementation for “did you mean” suggestions (closest.go) — is hand-written rather than pulled in from third-party packages, keeping the dependency footprint minimal for a library meant to be vendored into many other CLIs.

Code Quality The repository is unusually thoroughly tested for its size: 16 _test.go files sit alongside roughly 20 non-test .go files, with dedicated suites for parser behavior, help/man output, INI round-tripping, completion, commands, groups, and short/long option edge cases. CI (.github/workflows/) runs go build, go test -v ./..., gofmt -l (fails on unformatted files), and go vet across Ubuntu, Windows, and macOS runners, so cross-platform behavior (notably the Windows /flag option style) is exercised on every push. Errors are represented as a typed Error with an ErrorType enum (error.go) rather than ad hoc string errors, giving callers a way to branch on failure kind (e.g. ErrRequired, ErrTag).

What Makes It Unique Relative to imperative Go flag libraries, go-flags’ distinguishing choice is deriving the entire parser — names, defaults, required-ness, environment fallback, INI serialization, and generated help — from a single struct definition via reflection and tags, rather than a builder API where each of those concerns is registered separately and can drift out of sync. Its command/group embedding lets subcommands, nested namespaces, and INI configuration compose through the same tree instead of bolt-on subsystems, and it includes conveniences (typo-suggestion via Levenshtein distance, Windows-style /flag syntax, built-in man-page generation) that most comparable libraries leave to the application author.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search