Where to Define Command-Line Flags in Go

When building a command-line tool in Go,
you’ll eventually want to accept arguments as flags.

If you’re using the standard library’s flag package,
you’ll have to decide where in your package
to define your flags.
The only requirement of flag
is that the flags be defined
before flag.Parse() is called.

After reviewing many existing packages,
the most common way to define command-line flags
can be demonstrated with this small program:

packagemainimport("flag""net/http")var(httpAddr=flag.String("http",":5050","HTTP service address"))funcmain(){flag.Parse()serve()}funcserve(){http.ListenAndServe(*httpAddr,nil)}

This style defines flags as package variables,
parses them at the start of main,
and dereferences their values as necessary
within the package’s functions.

But this is not the only way to set up your package’s flags.
Consider the following alternative of the program above:

funcmain(){var(httpAddr=flag.String("http",":5050","HTTP service address"))flag.Parse()serve(*httpAddr)}funcserve(addrstring){http.ListenAndServe(addr,nil)}

In the second version,
we’ve moved our flag
declaration into the main function.
This is better, because the flag now lives next to
where the flags are parsed.

We also needed to update our serve function
to accept the the address to listen on as a parameter.
This is great,
because serve no longer depends on package state,
but instead declares all of its dependencies.
Previously,
if we wanted to test serve,
we would need to set the flag’s value before each test case,
and be careful to reset its value afterward.
For this reason,
we also would not have been able
to run our tests concurrently.
Now each test case can provide its own value
as input to the function,
without worrying about contention or contamination.

Finally,
it’s a nice bonus
that we were able to dereference the flag value
before calling serve.
Dealing with pointers to builtin types
like string, int, or float
is cumbersome,
and often a smell since they can be nil,
or accidentally be modified in unsafe ways.

Now, this doesn’t make a huge difference in our small example,
but if you approach all your main methods like this,
you’ll arrive at a better (and more testable!) design,
because it forces you to deal explicitly with dependencies,
instead of being able to fall back on external state.