- Uma referência em documento open source que organiza princípios de design de programas CLI e diretrizes concretas como uma releitura moderna da filosofia UNIX tradicional, tendo como principal público desenvolvedores que criam ferramentas de linha de comando
- A CLI evoluiu de uma simples plataforma de scripts para uma interface de texto centrada em humanos, e os princípios de design também precisam ser atualizados para acompanhar essa mudança
- Composability e facilidade de uso para humanos não são incompatíveis; seguindo convenções UNIX como entrada/saída padrão, pipes e códigos de saída, é possível alcançar ambas ao mesmo tempo
- Oferece recomendações concretas até para detalhes frequentemente negligenciados na prática, como texto de ajuda, mensagens de erro, formato de saída, interatividade e sistema de configuração
- A compatibilidade futura e a confiança do usuário em ferramentas CLI são definidas pela estabilidade da interface e pela transparência dos dados analíticos, e este guia apresenta essa linha de base
Filosofia (Philosophy)
Design centrado no ser humano
- Os comandos UNIX tradicionais eram projetados principalmente supondo uso por outros programas, mas hoje a maior parte das CLIs é usada diretamente por pessoas, então é necessário um design com prioridade para humanos
- No passado, a CLI era "machine-first", mas hoje evoluiu para uma UI textual "human-first"
Pequenos componentes combináveis
- O núcleo da filosofia UNIX é combinar programas pequenos e simples para formar sistemas maiores, e isso continua válido hoje
stdin/stdout/stderrpadrão, sinais e códigos de saída garantem a conexão entre programas, e JSON oferece suporte a troca de dados mais estruturada- Todo software inevitavelmente se torna parte de um sistema maior, e ser um componente que funciona bem ou não é algo decidido na fase de design
Consistência
- Usuários de terminal já estão acostumados com convenções existentes, então recomenda-se que a CLI siga padrões já estabelecidos
- Ainda assim, se a consistência prejudicar a usabilidade, é possível quebrar a convenção com cuidado
Quantidade adequada de informação
- Se um comando fica esperando por vários minutos sem qualquer saída, há informação "de menos"; se despeja uma enorme quantidade de logs de debug, há informação "demais"
- O equilíbrio na quantidade de informação é absolutamente importante para que o software apoie o usuário
Facilidade de descoberta (Ease of Discovery)
- GUIs mostram todas as funções na tela, enquanto CLIs muitas vezes são mal interpretadas como algo que depende apenas de memória
- Com texto de ajuda abrangente, exemplos ricos e sugestões do próximo comando, a CLI também pode ser fácil de aprender ao adotar técnicas de GUI
CLI como conversa
- O uso da CLI tem uma estrutura de conversa baseada em tentativas e erros repetidos; sugestões para corrigir erros, indicação de estado intermediário e confirmação antes de ações perigosas são técnicas de design que aproveitam essa característica
- A pior interação é uma conversa hostil que deixa o usuário impotente; a melhor é uma troca agradável que gera sensação de realização
Robustez (Robustness)
- O software precisa ser robusto na prática e na percepção
- Tratar entradas inesperadas com elegância, manter a idempotência, informar o progresso e evitar expor stack traces são pontos centrais
- Reduzir casos especiais complexos e manter tudo simples aumenta a robustez
Empatia (Empathy)
- Ferramentas CLI são instrumentos criativos para desenvolvedores e devem ser agradáveis de usar
- O design deve ser pensado o suficiente para que o usuário sinta que a ferramenta está do seu lado
Caos (Chaos)
- O mundo do terminal é cheio de inconsistências, mas esse caos também é fonte de criação livre
- "Se um padrão for claramente prejudicial à produtividade ou à satisfação do usuário, abandone esse padrão" — Jef Raskin
Diretrizes — O básico (The Basics)
- Use uma biblioteca de parsing de argumentos: entre as recomendadas por linguagem estão Go(Cobra, cli), Python(Click, Typer, Argparse), Rust(clap), Node(oclif) e várias outras
- Retorne código de saída 0 em caso de sucesso e código diferente de 0 em caso de falha — esse é o critério usado por scripts para distinguir sucesso e erro
- A saída padrão deve ir para
stdout, enquanto mensagens como logs e erros devem ir parastderr
Diretrizes — Ajuda (Help)
- Exiba texto de ajuda detalhado com a flag
-hou--help, e aplique o mesmo a subcomandos - Ao executar sem argumentos, mostre uma ajuda concisa (incluindo descrição, 1–2 exemplos, explicação das flags e indicação de
--help)jqé citado como um bom exemplo dessa implementação
- Dê suporte a várias formas de pedir ajuda, como
--help,-hehelp subcommand - No topo do texto de ajuda, forneça link para a documentação web e caminho para feedback
- Mostre os exemplos primeiro — recomenda-se uma progressão em forma de história até chegar a casos de uso mais complexos
- Coloque flags e comandos mais usados no topo do texto de ajuda (consulte a organização do
git) - Use formatação como títulos em negrito para facilitar a leitura rápida, mas de forma independente do terminal
- Quando o usuário digitar algo errado, é possível inferir a intenção e sugerir correções — porém a execução automática deve ser decidida com cautela
- Uma entrada errada pode não ser só um typo, mas um erro lógico; além disso, ao corrigir automaticamente, surge o ônus de dar suporte permanente à sintaxe em questão
Diretrizes — Documentação (Documentation)
- Forneça documentação baseada na web — essencial para busca e compartilhamento de links
- Forneça documentação no terminal — sincronizada com a versão instalada e acessível offline
- Considere fornecer páginas man — podem ser geradas com ferramentas como
ronn, e recomenda-se permitir acesso por subcomando, como emnpm help ls
Diretrizes — Saída (Output)
- Legibilidade para humanos em primeiro lugar — use TTY para determinar se a saída será lida por pessoas
- Fluxos de texto são a interface universal do UNIX, então também deve haver suporte a saída legível por máquina
- Se uma saída amigável para humanos prejudicar a compatibilidade com pipes, forneça saída em texto simples com a flag
--plain - Quando a flag
--jsonfor passada, ofereça saída em formato JSON - Em caso de sucesso, a saída deve ser concisa e, se não for necessária, ausente — para scripts, ofereça a opção
-qpara suprimir a saída - Avise o usuário quando houver mudança de estado —
git pushé um bom exemplo ao exibir o estado do branch remoto - Estruture a saída para que seja fácil verificar o estado atual do sistema e também saber o próximo passo, como em
git status - Use cores de forma intencional e desative-as obrigatoriamente em condições como pipe,
NO_COLOR,TERM=dumbe--no-color - Em ambientes sem TTY, não mostre animações nem spinners (para evitar poluir logs de CI)
- Use emoji e símbolos apenas quando eles aumentarem a clareza (
yubikey-agenté citado como exemplo) - Informações compreensíveis só para desenvolvedores devem ficar fora da saída padrão e aparecer apenas no modo verbose
- Não use
stderrcomo se fosse um arquivo de log — em geral, evite rótulos de nível de log comoERReWARN - Em saídas longas, considere usar um pager como
less— ative apenas em TTY e recomenda-se a opçãoless -FIRX
Diretrizes — Erros (Errors)
- Reescreva erros previsíveis em mensagens compreensíveis para humanos (ex.: "é preciso executar
chmod +w file.txt") - Mantenha uma boa relação sinal/ruído — erros do mesmo tipo devem ser agrupados sob um único cabeçalho
- Coloque informações importantes no final da saída — texto vermelho deve ser usado de forma intencional e rara
- Em caso de erro inesperado, inclua informações de debug e instruções para enviar um bug report
- Monte a URL do bug report com informações preenchidas automaticamente para facilitar o envio
Diretrizes — Argumentos e flags (Arguments and Flags)
- Argumentos (
args) são posicionais, flags são nomeadas — prefira flags a argumentos - Forneça uma versão com nome completo para todas as flags (ex.: suporte simultâneo a
-he--help) - Flags de um único caractere devem ser limitadas às mais usadas
- Quando houver padrão, use nomes de flags padronizados (
-f/--force,-q/--quiet,-v,--jsonetc.) - Defina valores padrão que sejam adequados para a maioria dos usuários
- Se argumentos ou flags não forem fornecidos, peça a entrada via prompt, mas nunca force prompt em ambientes não interativos
- Antes de ações perigosas, peça confirmação — conforme o nível de risco, use confirmação
y/n, ofereça dry-run ou exija digitação direta de texto- O risco é dividido em mild (exclusão de arquivo), moderate (exclusão de diretório, alteração de recurso remoto) e severe (exclusão de um servidor inteiro)
- Em entrada e saída de arquivos, aceite
-para ler destdine escrever emstdout(ex.:curl ... | tar xvf -) - Não receba segredos diretamente por flags — recomenda-se usar uma flag como
--password-fileoustdin(devido ao risco de exposição em saída depse no histórico do shell)
Diretrizes — Interatividade (Interactivity)
- Prompts e elementos interativos devem ser exibidos somente quando
stdinfor TTY - Quando
--no-inputfor passado, desative todos os prompts - Ao inserir senha, desative o echo (não mostrar o conteúdo digitado na tela)
- Oriente claramente para que o usuário possa sair a qualquer momento — Ctrl-C deve sempre continuar funcionando
Diretrizes — Subcomandos (Subcommands)
- Mantenha consistência nos nomes de flags e no formato de saída entre subcomandos
- Ferramentas complexas devem usar uma estrutura de subcomandos em dois níveis no formato
noun verbouverb noun(ex.:docker container create) - Evite subcomandos com nomes ambíguos ou parecidos (ex.: evitar usar
updateeupgradeao mesmo tempo)
Diretrizes — Robustez (Robustness Guidelines)
- Faça a validação de entrada logo no início e encerre cedo com erro compreensível quando os dados forem inválidos
- Responsividade é mais importante que velocidade — mostre alguma coisa em até 100 ms
- Para tarefas demoradas, forneça uma barra de progresso (progress bar) — é possível usar bibliotecas como Python(
tqdm), Go(schollz/progressbar) e Node(node-progress) - Ao processar em paralelo, tome cuidado para que a saída não se embaralhe
- Configure timeouts de rede — incluindo valores padrão, para evitar espera infinita
- Depois de um erro temporário, o design deve permitir que uma nova tentativa retome do estado anterior
- Design crash-only — estrutura capaz de encerrar imediatamente sem etapa de limpeza, garantindo idempotência
Diretrizes — Compatibilidade futura (Future-proofing)
- Mantenha mudanças em formato aditivo e compatível com versões anteriores
- Antes de mudanças que quebrem compatibilidade, mostre avisos prévios dentro do programa
- Mudanças na saída para humanos geralmente são aceitáveis — para scripts, incentive o uso de
--plaine--json - Proíba subcomandos catch-all — depois eles impedem a adição de subcomandos reais com aquele nome
- Não permita automaticamente abreviações de subcomandos — só aliases explícitos, mantidos de forma estável
- Proíba “bombas-relógio” — minimize dependências externas para que a ferramenta ainda possa funcionar daqui a 20 anos
Diretrizes — Sinais e caracteres de controle (Signals)
- Ao receber Ctrl-C (sinal INT), encerre imediatamente e defina timeout para rotinas de limpeza
- Durante a limpeza, deixe claro que um novo Ctrl-C pode forçar o encerramento (ver o exemplo do Docker Compose)
- O programa deve ser projetado assumindo que pode iniciar sem que uma limpeza anterior tenha sido concluída
Diretrizes — Configuração (Configuration)
Prioridade de aplicação da configuração (maior → menor):
- flags → variáveis de ambiente do shell atual → configuração no nível do projeto (
.env) → configuração no nível do usuário → configuração de todo o sistema
Recomendações por tipo de configuração:
-
Configurações que mudam a cada chamada (nível de debug, dry-run): use flags
-
Configurações que variam por projeto ou máquina (caminho, cor, proxy HTTP): combinação de flags + variáveis de ambiente
-
Configurações compartilhadas por todo o projeto (tipo Makefile,
package.json): use arquivos versionados -
Siga a especificação XDG Base Directory — recomenda-se caminho de configuração baseado em
~/.config(com suporte emyarn,fish,neovim,tmuxetc.) -
Ao modificar automaticamente o arquivo de configuração de outro programa, é obrigatório obter o consentimento do usuário
Diretrizes — Variáveis de ambiente (Environment Variables)
- Variáveis de ambiente são adequadas para comportamentos que mudam conforme o contexto de execução
- Nos nomes, use apenas letras maiúsculas, números e underscore, sem começar por número
- Recomenda-se valor em uma única linha — múltiplas linhas podem gerar incompatibilidade com o comando
env - Verifique primeiro variáveis de ambiente genéricas como
NO_COLOR,DEBUG,EDITOR,HTTP_PROXY,SHELL,TMPDIR,HOME,PAGER - Recomenda-se suportar leitura de arquivo
.envpor projeto — mas.envnão é substituto de um arquivo de configuração formal- Limitações de
.env: não entra em controle de versão, não tem histórico, usa apenas o tipo string e é vulnerável a problemas de codificação
- Limitações de
- Não leia segredos de variáveis de ambiente — eles se propagam para todos os processos, podem vazar em logs e ficar expostos em
Docker inspectousystemctl show- Segredos devem ser recebidos apenas por arquivos de credenciais, pipes, sockets
AF_UNIXou serviços de gerenciamento de segredos
- Segredos devem ser recebidos apenas por arquivos de credenciais, pipes, sockets
Diretrizes — Nomenclatura (Naming)
- Use palavras simples e fáceis de lembrar — se forem genéricas demais, há risco de conflito com outros comandos
- Use apenas minúsculas e, quando necessário, hífens (
curlé um bom exemplo,DownloadURLé um mau exemplo) - Mantenha nomes curtos, mas nomes extremamente curtos como
cd,lsepsficam reservados para utilitários genéricos - O caso de renomeação do antecessor do Docker Compose,
plum→fig→docker compose, mostra na prática que a facilidade de digitação é um critério importante de naming
Diretrizes — Distribuição (Distribution)
- Sempre que possível, distribua como binário único — usando ferramentas como PyInstaller
- Se binário único não for viável, use instaladores nativos da plataforma
- Explique como desinstalar no final das instruções de instalação
Diretrizes — Dados analíticos (Analytics)
- Não envie dados de uso nem dados de crash sem o consentimento do usuário
- Ao coletar, divulgue claramente o que será coletado, por quê, como será anonimizado e por quanto tempo será retido
- Recomenda-se opt-in por padrão — se usar opt-out, isso deve ser informado claramente na primeira execução ou no site
- São apresentados três exemplos: Angular.js (opt-in explícito), Homebrew (Google Analytics, FAQ pública) e Next.js (estatísticas anônimas ativadas por padrão)
- Como alternativas à análise, é possível usar instrumentação da documentação web, medição de downloads e entrevistas diretas com usuários
1 comentários
Comentários do Hacker News
stdout.stderré para logging, informação etc., estdoutdeve fornecer uma saída útil independentemente de ser tty ou não.AF_UNIX, serviços de gerenciamento de segredos ou outros mecanismos de IPC.