Why CLI Still Dominates the DevOps and Backend World?
90% of today’s most popular cloud-native tools like Docker, Kubernetes (kubectl), or Terraform are written in Go. There is a simple reason: engineers prioritize CLIs because of their processing speed, low resource consumption, and the highly flexible integration into CI/CD pipelines compared to traditional graphical user interfaces (GUIs).
The Problem: When the Default Flag Library Becomes Overloaded
The flag package in Go’s Standard Library works fine for simple scripts under 50 lines of code. However, as soon as your application needs to break features down into subcommands like git commit or git push, parameter management quickly turns into a mess.
Writing custom logic to parse commands and handle input errors is time-consuming. Instead of wasting 3-4 hours just building the application skeleton, we should use a more professional framework.
Cobra – The Engine Behind Leading Tools
Cobra is the gold standard for building CLIs in the Go ecosystem. It defines a clear structure: Commands (actions), Args (objects), and Flags (modifiers). Cobra’s biggest advantage is its ability to automatically generate help pages and shell auto-completion — key factors in making your tool look as “pro” as a commercial product.
Hands-on: Building the “itfz” CLI in 5 Minutes
We will build the itfz (ItFromZero) tool to manage personal technical notes directly from the terminal.
1. Initialize the Project
mkdir itfz-cli
cd itfz-cli
go mod init github.com/user/itfz-cli
go get -u github.com/spf13/cobra@latest
To speed up development, you should install cobra-cli to generate boilerplate code automatically:
go install github.com/spf13/cobra-cli@latest
cobra-cli init
A standard directory structure will appear:
.
├── cmd/
│ └── root.go
├── main.go
└── go.mod
2. Add Your First Subcommand
Want to create the itfz note command to quickly save knowledge? Just type:
cobra-cli add note
Cobra will automatically generate the cmd/note.go file. Your only task is to focus on writing the processing logic inside the Run function.
3. Distinguish Between Persistent and Local Flags
Understanding these two types of flags will make your tool more logical:
- Persistent Flags: Applied to the current command and all subcommands (e.g., the
--verboseflag). - Local Flags: Applied only to the command where they are defined.
For example, add a flag to choose the file format for notes:
// In file cmd/note.go
func init() {
rootCmd.AddCommand(noteCmd)
noteCmd.Flags().StringP("format", "f", "markdown", "File format (txt/md)")
}
4. Implement the Processing Logic
Here is how to retrieve the flag value and process the user input:
Run: func(cmd *cobra.Command, args []string) {
format, _ := cmd.Flags().GetString("format")
if len(args) == 0 {
fmt.Println("Error: Please enter the note content!")
return
}
fmt.Printf("Note saved [%s]: %s\n", format, args[0])
},
Case Study: 40% Reduction in Operational Errors Thanks to CLI
In a project managing over 20 microservices, our team once struggled because every developer used their own set of Bash scripts. After unifying them into a single CLI tool built with Go, configuration errors during deployment dropped by 40%.
Go compiles into a single executable, eliminating the need for complex Python or Node.js environment setups on servers. By integrating Auto-completion, new members can simply hit Tab to see available commands without reading through dozens of pages of documentation.
Advanced Feature: Auto-completion
You can export command completion files for Zsh or Bash very easily:
# Generate suggestions for zsh
./itfz completion zsh > ~/.oh-my-zsh/completions/_itfz
source ~/.zshrc
The experience of typing itfz [TAB] and seeing subcommands appear will completely change how you work with the terminal.
Conclusion
Building a CLI with Go and Cobra is actually quite intuitive once you master the Command-Flag structure. Don’t just stop at using other people’s tools. Try building a small tool to automate your daily repetitive tasks for yourself and your colleagues.

