SpikeMe
← Comparativos
Go

Cobra vs urfave/cli

atualizado em 14 de julho de 2026

Cobra vs urfave/cli

Cobra e urfave/cli comparados por estrutura de comandos, geração de código (cobra-cli), tratamento de flags, autocompletar de shell, geração de docs e ecossistema, para escolher o framework de CLI em Go.

FatoCobraurfave/cli
Versão atual1.10.22.27.7
LicençaApache-2.0MIT
CritérioCobraurfave/cli
Estrutura de comandosárvore de *cobra.Command com AddCommandcli.App com slice de *cli.Command aninhável
Scaffoldingcobra-cli gera projeto e comandos por linha de comandosem gerador — você escreve a struct do App direto
Flagspflag (flags estilo POSIX/GNU, --flag e -f)cli.Flag tipado por struct, com Aliases
Autocompletar de shellbash, zsh, fish e powershell embutidosbash e zsh via script gerado
Geração de docsgera man pages e Markdown a partir dos comandosgera Markdown/man via pacote docs
Config e ambientecasa com Viper (spf13) para flags + env + arquivoEnvVars direto na definição da flag
Ecossistemapadrão de facto: kubectl, Hugo, GitHub CLI, Helmleve e popular para CLIs de porte pequeno a médio

Contexto

Cobra e urfave/cli são as duas bibliotecas dominantes para construir aplicações de linha de comando em Go, e ambas resolvem o mesmo núcleo: parsear argumentos, organizar subcomandos, tratar flags e imprimir help. A diferença de fundo é de peso e de postura. Cobra é um framework robusto, com um gerador de scaffolding (cobra-cli), integração próxima com o Viper para configuração, geração de man pages e autocompletar para quatro shells — é a fundação por trás de kubectl, hugo, gh (GitHub CLI) e helm, o que faz dele o padrão de facto para CLIs grandes. urfave/cli é mais enxuto: você declara a aplicação inteira como uma struct cli.App, sem gerador e com menos superfície, o que a torna especialmente confortável para ferramentas de porte pequeno a médio.

A pergunta que decide não é "qual parseia flags melhor" — os dois fazem isso bem. É quanto da sua CLI vai crescer e quanta infraestrutura você quer de graça desde o começo. Uma árvore profunda de subcomandos com autocompletar polido, páginas de manual e configuração em camadas (flag, variável de ambiente e arquivo) é território natural de Cobra. Uma ferramenta focada, com meia dúzia de comandos e poucas dependências, tende a ficar mais legível e direta em urfave/cli, onde a definição inteira cabe numa struct que você lê de cima a baixo.

Quando escolher Cobra

Cobra se paga quando a CLI é grande ou tem ambição de crescer: muitos subcomandos, subcomandos aninhados e a necessidade de uma experiência de terminal polida. O cobra-cli gera o esqueleto do projeto e novos comandos por linha de comando, o autocompletar embutido cobre bash, zsh, fish e powershell, e a biblioteca gera man pages e Markdown a partir da própria árvore de comandos. Some a integração com o Viper (mesmo autor) e você tem configuração em camadas — flag sobrepõe variável de ambiente, que sobrepõe arquivo — sem escrever a lógica de precedência à mão.

package main

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
)

func main() {
	var verbose bool

	root := &cobra.Command{
		Use:   "app",
		Short: "app faz coisas",
	}
	root.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "saída detalhada")

	greet := &cobra.Command{
		Use:   "greet [nome]",
		Short: "cumprimenta um usuário",
		Args:  cobra.ExactArgs(1),
		Run: func(cmd *cobra.Command, args []string) {
			if verbose {
				fmt.Fprintln(os.Stderr, "cumprimentando...")
			}
			fmt.Printf("olá, %s\n", args[0])
		},
	}
	root.AddCommand(greet)

	if err := root.Execute(); err != nil {
		os.Exit(1)
	}
}

O exemplo mostra o padrão de Cobra: cada comando é um *cobra.Command que você compõe numa árvore com AddCommand, PersistentFlags propaga uma flag para todos os subcomandos, e validadores como cobra.ExactArgs(1) cuidam da aridade dos argumentos antes de o Run executar. É mais verboso que a alternativa, e essa é justamente a contrapartida: para uma CLI de dois comandos, o boilerplate parece exagero. O retorno aparece na escala — quando a árvore tem dezenas de comandos, o scaffolding, o autocompletar de quatro shells, as man pages e o Viper deixam de ser luxo e viram a infraestrutura que segura a complexidade. Não é coincidência que as maiores CLIs do ecossistema Go rodem sobre Cobra.

Quando escolher urfave/cli

urfave/cli é a escolha certa quando você quer expressar uma ferramenta inteira de forma declarativa, com o mínimo de cerimônia e poucas dependências. A aplicação é uma única struct cli.App: nome, uso, flags e comandos ficam todos num lugar que você lê de uma vez. Não há gerador de código porque não é preciso — a struct já é a especificação. Para uma ferramenta pequena ou média, essa densidade é uma vantagem real: menos arquivos, menos indireção, e a definição inteira visível numa tela.

package main

import (
	"fmt"
	"log"
	"os"

	"github.com/urfave/cli/v2"
)

func main() {
	app := &cli.App{
		Name:  "app",
		Usage: "app faz coisas",
		Flags: []cli.Flag{
			&cli.BoolFlag{Name: "verbose", Aliases: []string{"v"}},
		},
		Commands: []*cli.Command{
			{
				Name:  "greet",
				Usage: "cumprimenta um usuário",
				Action: func(c *cli.Context) error {
					if c.Bool("verbose") {
						fmt.Fprintln(os.Stderr, "cumprimentando...")
					}
					fmt.Printf("olá, %s\n", c.Args().First())
					return nil
				},
			},
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}

A diferença de ergonomia é clara: em urfave/cli, Action retorna error (mais idiomático em Go do que o Run sem retorno de Cobra), as flags são structs tipadas com Aliases, e você mapeia variáveis de ambiente colocando EnvVars direto na definição da flag, sem trazer uma biblioteca de configuração à parte. O custo é o teto de recursos: o autocompletar cobre bash e zsh via script gerado (sem fish/powershell embutidos), não há um cobra-cli para gerar esqueleto, e a integração com configuração em camadas é mais manual. Para a maioria das ferramentas internas, scripts de build e CLIs de produto de porte moderado, nada disso pesa — e a economia de dependências e de boilerplate é um ganho concreto.

Veredito

A decisão acompanha o tamanho e a ambição da CLI. Para ferramentas grandes, com árvores profundas de subcomandos e expectativa de experiência de terminal de primeira — autocompletar em vários shells, man pages, configuração em camadas via Viper — Cobra é a escolha comprovada. O boilerplate inicial é o preço de entrada de uma infraestrutura que as maiores CLIs de Go (kubectl, Hugo, gh, Helm) usam justamente porque escala. Se você está construindo uma plataforma de linha de comando para durar e crescer, comece por Cobra.

Para ferramentas pequenas e médias, urfave/cli entrega mais com menos: uma struct declarativa que você lê de ponta a ponta, Action retornando error no estilo idiomático de Go, EnvVars na própria flag e um grafo de dependências enxuto. Você abre mão de geradores e de alguns recursos avançados, mas ganha simplicidade e velocidade de escrita onde elas importam mais. Regra prática: se você hesita entre os dois porque a CLI é pequena hoje mas pode virar grande, pergunte se os subcomandos vão realmente se multiplicar. Se sim, o investimento em Cobra se paga; se não, urfave/cli mantém o código mais leve e mais legível.

Cobra

Escolha Cobra se…

Escolha Cobra para CLIs grandes com muitos subcomandos, onde scaffolding, autocompletar, man pages e a integração com Viper compensam o boilerplate inicial.

urfave/cli

Escolha urfave/cli se…

Escolha urfave/cli para ferramentas pequenas e médias, onde uma única struct declarativa e menos dependências valem mais que geradores e recursos avançados.

Este comparativo é genérico: a resposta certa depende das versões, do time e do que já existe no seu projeto.

Gere o spike para a SUA stack

Compartilhar

XLinkedIn