Guia open source modernizado, seguindo os princípios tradicionais do Unix
-
Filosofia de design de CLI
→ pessoas em primeiro lugar
→ componentes simples que funcionam juntos
→ manter consistência entre programas
→ falar apenas o necessário (saída nem de menos nem de mais)
→ tornar fácil de descobrir (ajuda abrangente, exemplos, sugestão do próximo comando a executar, sugestão do que fazer quando houver erro)
→ como uma conversa normal
→ de forma robusta
→ ter empatia com o usuário
→ caos: se precisar quebrar as regras, deixe clara a intenção e o objetivo
-
Diretrizes de CLI
→ básico
✓ usar uma biblioteca de parsing de linha de comando: Go (Cobra, cli), Node (oclif), Python (Click, Typer), Ruby (TTY)
✓ retornar 0 em caso de sucesso e um código diferente de 0 em caso de erro
✓ a saída deve ir para
stdout✓ logs, erros e mensagens afins devem ir para
stderr→ ajuda
✓ ao executar sem opções, mostrar ajuda (
-h,--help)✓ por padrão, mostrar uma ajuda concisa
· o que este programa faz · um ou dois exemplos de uso · explicação das flags (se não forem muitas) · `--help` para explicações adicionais✓ com as opções
-h,--help, mostrar a ajuda completa✓ fornecer um canal para receber feedback/issues
✓ na ajuda, fornecer link para a documentação em versão web
✓ explicar com exemplos
✓ se houver muitos exemplos, deixá-los em outro lugar (cheat sheet ou página web)
✓ não se preocupe com páginas
man(são pouco usadas e nem funcionam no Windows)✓ se a ajuda for longa, encaminhá-la para um pager
✓ mostrar no início da ajuda as flags e comandos mais usados
✓ usar formatação na ajuda (negrito)
✓ se o usuário fez algo errado e você conseguir inferir isso, recomende o que fazer
✓ se o seu comando espera receber algo via pipe, mas
stdinfor um terminal interativo, mostre a ajuda e encerre imediatamente→ saída
✓ a saída human-readable (legível para humanos) é a mais importante
✓ se não prejudicar a usabilidade, forneça saída machine-readable
✓ se a saída human-readable impedir a saída machine-readable, ofereça a opção
--plainpara permitir integração comgrep/awketc.✓ ao receber
--json, produzir saída no formato JSON✓ em caso de sucesso, é melhor não haver saída, mas se precisar haver, seja conciso. Ofereça a opção
-qpara ocultar saídas desnecessárias✓ se algo mudar de estado, informe ao usuário (veja a saída de
git push)✓ torne fácil visualizar o estado atual do sistema
✓ recomende comandos que o usuário possa executar (como
git statusmostragit add/restore)✓ ações que vão além do escopo interno do programa devem ser explícitas. Como ler/escrever arquivos sem que o usuário tenha indicado isso (cache), ou conectar-se a um servidor remoto (download de arquivo)
✓ usar arte ASCII para aumentar a densidade de informação
✓ usar cores com intenção. Não abuse
✓ desativar cores quando não for um terminal ou quando o usuário solicitar
✓ se
stdoutnão for um terminal interativo, não mostrar animações✓ usar símbolos/emojis apenas quando ajudarem a deixar algo mais claro
✓ por padrão, não mostrar informações que só façam sentido para quem desenvolveu a ferramenta
✓ não usar
stderrcomo se fosse um arquivo de log (ao menos não como padrão; exiba níveis como ERR, WARN etc. apenas em modo detalhado)✓ se houver muito texto de saída, use uma ferramenta de paginação como
less→ erro
✓ capture os erros e reescreva as mensagens para pessoas
✓ Signal-to-noise ratio (relação sinal-ruído) é importante. Se o mesmo erro ocorrer várias vezes, agrupe-o sob um cabeçalho explicativo
✓ considere para onde o olhar do usuário vai primeiro
✓ se ocorrer um erro inesperado/inexplicável, forneça informações de debug/trace e explique como enviar esse bug aos desenvolvedores
✓ permita enviar relatórios de bug sem esforço extra. (por exemplo, gerar uma URL com todas as informações, de modo que o usuário só precise abri-la no navegador para concluir o envio)
→ Argument & Flags: argumentos e flags
✓ argumento: parâmetro posicional. A ordem importa.
cp bar fooecp foo barsão diferentes✓ flag: parâmetro nomeado. Um caractere como
-rou vários como--recursive. Em geral, a ordem não importa.também pode incluir um valor do usuário. `--file foo.txt` ou `--file=foo.txt`✓ prefira flags a argumentos. Exigem mais digitação, mas são mais claras. Muitos argumentos dificultam expandir recursos depois
✓ tenha sempre a versão curta e a versão longa das flags. Em scripts, usar a versão longa dispensa explicação adicional
✓ use flags de um caractere apenas para as mais usadas
✓ para operações simples, também é possível receber vários argumentos
✓ se forem necessários dois ou mais argumentos distintos, talvez haja algo errado no design
✓ as flags devem usar nomes padronizados (se já existirem)
`-a --all`, `-d --debug`, `-f --force`, `-h --help`, `-o --output`, `-p --port`, `-q --quiet`, `-u --user`✓ use como padrão o que for adequado para a maioria dos usuários
✓ se o usuário passar um argumento/flag que exige entrada, mas nenhum valor tiver sido fornecido, solicite a entrada ao usuário
✓ sempre forneça uma forma de passar valores por argumentos/flags; não exija prompt obrigatoriamente
✓ antes de fazer algo perigoso, sempre peça confirmação
✓ se a entrada ou saída for um arquivo, ofereça suporte a
-para ler destdinou escrever emstdout$ curl https://example.com/something.tar.gz | tar xvf -✓ se a flag puder receber um valor adicional, permita uma palavra especial como
none.ssh -F none✓ sempre que possível, faça argumentos, flags e subcomandos funcionarem independentemente da ordem
✓ permita que valores sensíveis (como senhas) possam ser fornecidos por arquivo
→ interatividade
✓ usar prompt ou recursos interativos apenas quando
stdinfor um terminal interativo✓ se
--no-inputfor passado, não use prompt nem nenhum recurso interativo✓ ao pedir senha, não mostre o que o usuário digita
✓ faça com que o usuário possa sair facilmente (não faça como o
vim). Permita queCtrl-Cfuncione. Se, por conta da execução de programas comossh,tmuxetc.,Ctrl-Cnão puder ser usado, deixe claro que existe uma sequência de escape iniciada por~, como no SSH→ subcomandos
✓ ferramentas complexas podem oferecer subcomandos para reduzir a complexidade
✓ se houver várias ferramentas fortemente relacionadas, elas também podem ser agrupadas em um único comando para facilitar o uso
✓ mantenha consistência entre os subcomandos. A mesma flag deve ter o mesmo significado, com formatos de saída semelhantes
✓ use nomes consistentes entre múltiplos níveis de subcomandos
✓ não inclua comandos com nomes confusos ou parecidos, como
updateeupgrade→ robustez
✓ valide toda a entrada do usuário. Verifique cedo e mostre erros compreensíveis
✓ responsividade é mais importante do que velocidade
✓ se demorar, mostre o progresso
✓ se possível, processe em paralelo. Mas faça isso com cuidado
✓ defina timeouts
✓ torne o programa idempotente (
idempotent). (executar de novo não deve mudar o resultado). Quando houver erro, o usuário deve conseguir apertar seta para cima no shell e retomar a partir do estado anterior✓ torne-o crash-only. É o próximo passo da idempotência. Se não for necessário limpar nada após a operação, ou se a limpeza puder ser adiada até a próxima execução, o programa pode encerrar imediatamente ao falhar ou ser interrompido
✓ as pessoas vão usar mal o seu programa
→ preparação para o futuro
✓ sempre que possível, faça mudanças de forma aditiva. Não quebre compatibilidade alterando funções existentes; adicione novas flags
✓ se a mudança não puder ser aditiva, avise antes
✓ mudanças na saída voltada para pessoas geralmente são OK
✓ mesmo que exista um subcomando usado com frequência, não crie um catch-all subcommand que o execute sem ser explicitamente chamado
✓ não permita abreviações arbitrárias de comandos de subcomando
✓ não crie “bombas-relógio” que um dia vão parar de funcionar
→ sinais e caracteres de controle
✓ se o usuário pressionar
Ctrl-C(sinal INT), interrompa o mais rápido possível✓ se o usuário pressionar
Ctrl-Cdurante uma limpeza demorada, ignore na primeira vez e permita encerramento forçado ao pressionar novamente^CGracefully stopping... (press Ctrl+C again to force)→ configuração
✓ seguir a especificação XDG (X Desktop Group)
✓ se você modificar configurações que não pertencem ao seu programa, peça confirmação ao usuário e explique claramente o que será feito
✓ aplicar parâmetros de configuração na seguinte ordem de prioridade
flags > variáveis de ambiente do shell > configuração em nível de projeto (`.env`) > configuração do usuário > configuração do sistema→ variáveis de ambiente
✓ variáveis de ambiente servem para comportamentos que mudam conforme o contexto em que o comando é executado
✓ para maximizar a portabilidade, variáveis de ambiente devem conter apenas letras maiúsculas, números e sublinhado, e não devem começar com número
✓ se possível, use valores de uma única linha (
single-line) em variáveis de ambiente✓ não use nomes amplamente já utilizados
✓ sempre que possível, verifique e use variáveis de ambiente genéricas
`NO_COLOR`, `DEBUG`, `EDITOR`, `HTTP_PROXY`, `SHELL`, `TERM`, `TERMINFO`, `HOME`, `TMPDIR`, `PAGER`, `LINES` ..✓ se necessário, carregue variáveis de ambiente a partir de
.env✓ não use a extensão
.envpara arquivos de configuração→ nomenclatura
✓ o nome deve ser simples e fácil de lembrar
✓ use apenas minúsculas e só use
-(hífen) quando realmente necessário✓ se possível, seja curto
✓ fácil de digitar no teclado
→ distribuição
✓ se possível, distribua como um binário único
✓ facilite a desinstalação
→ analytics
✓ não envie dados de uso da ferramenta nem de crashes para você sem o consentimento do usuário
3 comentários
Obrigado pelo ótimo conteúdo.
Parece que estão surgindo cada vez mais boas ferramentas de linha de comando graças ao Rust e ao Go, que facilitam bastante a criação de binários únicos.
Coletânea de ferramentas de linha de comando úteis hoje em dia https://pt.news.hada.io/topic?id=793
Utilitários de linha de comando em Rust que aumentam a produtividade https://pt.news.hada.io/topic?id=2958
Criá-las também está ficando cada vez mais fácil e mais poderoso.
Criando aplicativos de linha de comando com Rust https://pt.news.hada.io/topic?id=972
Caporal.js - framework completo para desenvolvimento de CLI em Node https://pt.news.hada.io/topic?id=2378
create-node-cli - criando CLIs facilmente com Node.js https://pt.news.hada.io/topic?id=3268
Gooey - criando GUIs para todas as linguagens e ferramentas de CLI https://pt.news.hada.io/topic?id=582
ink - criando CLIs com React https://pt.news.hada.io/topic?id=2041
Aprendi bastante enquanto fazia uma tradução simples. Depois de terminar, fiquei pensando se não teria sido melhor traduzir o repositório inteiro. ^^;;