Some code (and results) of a survey of Go flags/getopts libraries

heads

tip
browse log

clone

read-only
https://hg.sr.ht/~ser/flag-libs-analysis
read/write
ssh://hg@hg.sr.ht/~ser/flag-libs-analysis

#Go Flag Libraries

#Abstract

Go comes with a basic flag library. It lacks some features, or has some anti- features, that has resulted in a proliferation of third-party flag libraries. This is a summarization of some of those libraries.

#Data

Sorting is: Deps, ABC, LOC

Library LOC Deps ABC Score Complexity
clapper 303 0 119 76
sircmpwn-getopt 501 0 154 60
cosiner-argv 462 0 155 94
claptrap 458 0 235 152
droundy-goopt 596 0 243 162
namsral-flag 764 0 299 162
ogier-pflag 1112 0 438 97
opflag 1161 0 461 118
integrii-flaggy 1732 0 659 303
spf13-pflag 3856 0 1464 583
jessevdk-go-flags 3529 0 1604 1045
dmulholland-args 437 1 199 97
thatisuday-commando 640 1 213 110
mwitkow-go-flagz 1461 1 487 265
cosiner-flag 1821 1 713 463
moogar0880-venom 2029 2 604 303
stevenroose-gonfig 1169 5 540 375
peterbourgon-ff 1060 6 308 231
cobra 4529 6 1507 808
dc0d-argify 348 14 139 96

LOC -- no tests, no comments scc -i go -M _test --no-cocomo
ABC Score -- ABC complexity for the project (excluding dependencies) abcgo -format summary -path . -no-test

Here's the real image: graphed And here's a hack work-around because sr.ht serves the wrong mime-type for SVG files: graphed

The Complexity column is SCC's metric, which is a sort of cyclomatic complexity derived from counting branches. ABC is a better metric, and the SCC metric is included just because.

ABC metrics are probably the most predictive of the amount of extra size a library will add to code. LOC can be manipulated by formatting and is affected by coding style, but ABC counts branches, assignments, and conditionals, which don't change no matter how many if statements you cram together on a single line. It's also a pretty good indication of actual complexity-for-guessing-potential-for-bugs.

#Subjective Features Analysis

lib short long combined inverted env vars types choices commands varargs good api mand/opt files
claptrap Y Y Y Y N Y Y Y Y Y Y N
clapper Y Y N Y N N N Y Y Y N
droundy Y Y Y N Y Y Y N
sircmpwn Y N Y N N N N N Y Y Y N
opflag Y Y Y N N Y N N Y Y N N
namsral Y Y Y Y Y Y Y
integrii Y Y y Y Y Y Y
jessevdk Y Y Y N N Y Y Y Y N Y N

Env vars would be nice, but something like GoBike could add env vars and config files, and github.com/subpop/go-ini could add support for config files.

#Note

The purpose of this repo is this file. The scripts merely generate it from a template, calculating and inserting the metrics table. The scripts might be useful to you; generate.sh in particular will generate the Data table above for any set of Go projects you check out into the directory. Do read it, and especially check the header, because it uses some tools that aren't installed by default on all Linuxi.

#Background

I do this thing every few months where I look for a good flags library for Go. I finally consolidated the process to a script; this is the result.

I mostly like the Go flags library; it works well in trivial cases, it has a great API, and it's small. If you look at the profusion of third-party flags libraries for Go, even just on Github, it's obvious to a casual observer that the stdlib package falls short for many people. A casual inspection of the libraries shows that what people are looking for varies greatly; if there's one common theme, it's that Go's choice to diverge from POSIX getopt standards is unpopular.

Having your Go tools handle arguments differently than most of your other tools is unpleasant. While it wasn't possible for Go to conform to standards for every platform, choosing to not conform to the most widely used standard was an odd choice.

Looking at the popular options, it's obvious that many developers don't care if the size of their flags library eclipses the size of the rest of their program. For instance, Cobra -- one of the popular libaries -- is over 4 thousand lines of non-comment, non-test libraries, and has a total ABC complexity of 1,575 and a max cyclomatic complexity of 48 ("complex, high risk").

But who cares about metrics? They're just numbers. Go's flag library adds no external dependencies and is usually fine for small, on-off projects, but I too usually find anything non-trivial gets hard to manage with the stdlib. What I look for in a flags library:

  • An API similar to flags; I don't want to have to build structures to parse into, or use something that feels more like a framework than a library.
  • Small. I don't want a flags library to double the size of my program.
  • POSIX-ish; this means
    • short flags
    • long flags
    • combined short flags (-a -b ~= -ab)
    • varargs (-n 1 -n 2 -n 3)
  • commands (this is where I do think beyond-POSIX has benefit: sub-commands are user friendly)

In more involved applications, there are some other features that are pretty useful:

  • inverted flags (--recurse gives you --no-recurse)
  • types -- the library does data type validation based on the spec
  • choices -- limits valid arguments
  • mandatory arguments

The last three are easily done in code outside of the flag library, but having the data validation built-in reduces errors.

There are two other features which I really like, but start to feel like feature envy, and -- indeed -- libraries exist for supporting these outside of flags.

  • env vars -- allows passing arguments through environment variables, necessary to comply with 12-factor practices. The library GoBike-envflag extends the stdlib flags library to support 12-factor env variable support, which is awesome! It fails as soon as you start using a third party flags library, though.
  • config files -- there's a substantial advantage to having the flags integrated with the configuration file; syncing command line arguments with file configuration can require a lot of error-prone code.

What's the moral? If you want something done right, do it yourself. I ended up writing claptrap, which implements everything I want, how I want, without being a monster framework disguised as a library. Claptrap actually started out as a set of PRs for clapper, but in the end with maybe 20% of the original code and 60% of the original API, I just forked it off into a different project.