A dependency is an external component or runtime resource that a program requires. In Go, dependencies can include command-line arguments, output streams, operating-system behavior, or client integrations. section 5.2.2 section 1.3.2
Explicitly grouping dependencies makes program logic easier to change and test. An env struct can hold abstract outputs through io.Writer, arguments through []string, and flags such as dryRun. section 5.2.2 section 5.3
Dependency injection improves a command-line program by:
- Moving core logic from main into a run function with explicit inputs and outputs. section 5.3.1
- Replacing os.Args with injected arguments and os.Stdout with an io.Writer such as os.Stdout or strings.Builder. section 5.3.1
- Returning errors instead of calling os.Exit inside the testable logic. section 5.3.1
Injecting command-line dependencies
main supplies real operating-system resources, while run receives an env value and remains independent of globals. Tests can substitute arguments, writers, and errors, then inspect behavior without exiting the process. section 5.3.1 section 5.6
Listing 5.1 The env type (hit/cmd/hit/hit.go)
package main
import (
"io"
. . .
)
type env struct {
stdout io.Writer #1
stderr io.Writer #2
args []string #3
dryRun bool #4
}
The env type provides a compact dependency boundary for output, errors, arguments, and dry-run behavior. section 5.2.2
Listing 5.2 Refactoring the main function (hit/cmd/hit.go)
func main() {
if err := run(&env{
stdout: os.Stdout, #1
stderr: os.Stderr, #1
args: os.Args, #1
}); err != nil {
os.Exit(1) #2
}
}
func run(e *env) error { #3
c := config{
n: 100,
c: 1,
}
if err := parseArgs(
&c,
e.args[1:], #4
e.stderr, #5
); err != nil {
return err #6
}
fmt.Fprintf( #7
e.stdout, #7
"%s\n\nSending %d requests to %q (concurrency: %d)\n",
logo, c.n, c.url, c.c,
)
return nil
}
The refactored command shows how main supplies real dependencies while run consumes abstractions and returns errors. section 5.3.1


























