24 pontos por xguru 2020-12-28 | 3 comentários | Compartilhar no WhatsApp

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 stdin for 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 --plain para permitir integração com grep / awk etc.

    ✓ 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 -q para 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 status mostra git 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 stdout nã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 stderr como 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 foo e cp foo bar são diferentes

    ✓ flag: parâmetro nomeado. Um caractere como -r ou 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 de stdin ou escrever em stdout

      $ 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 stdin for um terminal interativo

    ✓ se --no-input for 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 que Ctrl-C funcione. Se, por conta da execução de programas como ssh, tmux etc., Ctrl-C nã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 update e upgrade

    → 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-C durante 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 .env para 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

 
jonnung 2021-01-09

Obrigado pelo ótimo conteúdo.

 
xguru 2020-12-28

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.

Criá-las também está ficando cada vez mais fácil e mais poderoso.

 
xguru 2020-12-28

Aprendi bastante enquanto fazia uma tradução simples. Depois de terminar, fiquei pensando se não teria sido melhor traduzir o repositório inteiro. ^^;;