24 puntos por xguru 2020-12-28 | 3 comentarios | Compartir por WhatsApp

Una guía de código abierto que sigue los principios tradicionales de Unix, actualizada para los tiempos modernos

  • Filosofía de diseño de CLI

→ Priorizar a las personas

→ Piezas simples que funcionen juntas

→ Mantener la consistencia entre programas

→ Decir solo lo necesario (ni muy poca ni demasiada salida)

→ Hacer que sea fácil de descubrir (ayuda completa, ejemplos, sugerencias del siguiente comando a ejecutar, sugerencias sobre qué hacer cuando hay errores)

→ Como una conversación normal

→ Ser robusto

→ Tener empatía con el usuario

→ Caos: si tienes que romper las reglas, deja clara la intención y el propósito

  • Lineamientos para CLI

→ Fundamentos

✓ Usar una biblioteca para parsear la línea de comandos: Go(Cobra,cli), Node(oclif), Python (Click,Typer), Ruby(TTY)

✓ Devolver 0 en caso de éxito y un código distinto de 0 en caso de error

✓ La salida va a stdout

✓ Los mensajes como logs y errores van a stderr

→ Ayuda

✓ Si se ejecuta sin opciones, mostrar ayuda (-h, --help)

✓ Mostrar ayuda concisa por defecto

· Qué hace este programa

· Uno o dos ejemplos de invocación

· Explicación de flags (si no son muchos)

· `--help` para explicación adicional

✓ Con la opción -h, --help mostrar la ayuda completa

✓ Proporcionar una vía para recibir feedback/issues

✓ En la ayuda, ofrecer un enlace a la documentación en versión web

✓ Explicar con ejemplos

✓ Si hay muchos ejemplos, ponerlos en otro lugar (cheat sheet o página web)

✓ No preocuparse por las páginas man (se usan poco y tampoco funcionan en Windows)

✓ Si la ayuda es larga, pasarla a un pager

✓ Mostrar al inicio de la ayuda los flags y comandos más usados

✓ Usar formato en la ayuda (negritas)

✓ Si el usuario hizo algo mal y puedes inferir qué quiso hacer, recomiéndalo

✓ Si tu comando espera recibir algo por pipe, pero stdin es una terminal interactiva, muestra la ayuda y sal de inmediato

→ Salida

✓ La salida human-readable (legible para personas) es lo más importante

✓ Si no afecta la usabilidad, ofrecer salida machine-readable

✓ Si la salida legible para humanos impide la legible para máquinas, ofrecer una opción --plain para integrarse con grep / awk, etc.

✓ Si recibe --json, emitir salida en formato JSON

✓ En caso de éxito, es mejor no tener salida; pero si debe haberla, que sea concisa. Dar soporte a la opción -q para omitir salida innecesaria

✓ Si cambia el estado, díselo al usuario (ver la salida de git push)

✓ Hacer que el estado actual del sistema sea fácil de ver

✓ Recomendar comandos que el usuario pueda ejecutar (como cuando git status muestra git add / restore)

✓ Las acciones que van más allá del interior del programa deben ser explícitas. Por ejemplo, leer o escribir archivos que el usuario no indicó (caché), o conectarse a un servidor remoto (descargar archivos)

✓ Usar ASCII art para aumentar la densidad de información

✓ Usar color con intención. No abusar de él

✓ Desactivar el color si no es una terminal o si el usuario lo solicita

✓ Si stdout no es una terminal interactiva, no mostrar animaciones

✓ Usar símbolos/emojis solo cuando aclaren algo

✓ Por defecto, no mostrar información que solo el creador pueda entender

✓ No usar stderr como si fuera un archivo de logs (al menos no por defecto; muestra niveles como ERR, WARN en modo detallado)

✓ Si vas a mostrar mucho texto, usa una herramienta de paginación como less

→ Error

✓ Captura los errores y reescribe los mensajes para las personas

✓ La relación señal/ruido es importante. Si el mismo error ocurre varias veces, agrúpalo con un encabezado explicativo

✓ Considera cuál es lo primero que va a ver el usuario

✓ Si ocurre un error inesperado/inexplicable, ofrece información de debug/trace y explica cómo enviar ese bug a los desarrolladores

✓ Haz que enviar un bug report no requiera esfuerzo extra. (Por ejemplo, generar una URL con toda la información para que solo tenga que abrirla en el navegador y reportarlo)

→ Argumentos y flags

✓ Argumentos: parámetros posicionales. El orden importa. cp bar foo y cp foo bar son diferentes

✓ Flags: parámetros con nombre. Una letra como -r o varias como --recursive. El orden por lo general no importa.

    También pueden incluir un valor del usuario. `--file foo.txt` o `--file=foo.txt`

✓ Preferir flags sobre argumentos. Requieren más escritura, pero son más claros. Si hay muchos argumentos, luego será difícil ampliar funcionalidades

✓ Tener tanto la versión corta como la completa de los flags. Si usas la versión completa en scripts, no hace falta más explicación

✓ Usar flags de una sola letra solo para los flags de uso frecuente

✓ También es válido recibir varios argumentos para una acción simple

✓ Si necesitas dos o más argumentos diferentes, quizá estés haciendo algo mal

✓ Usa nombres estándar para los flags (si ya existen)

-a `--all` , -d `--debug` , -f `--force` , -h `--help` , -o `--output` , -p `--port` , -q `--quiet` , -u `--user`

✓ El valor por defecto debe ser el adecuado para la mayoría de los usuarios

✓ Si el usuario puso un argumento/flag que requiere entrada pero no recibió un valor, pídele el dato al usuario

✓ Siempre ofrece una forma de pasar valores por argumentos/flags; no obligues a usar prompts

✓ Antes de hacer algo peligroso, pide confirmación siempre

✓ Si la entrada o salida es un archivo, da soporte a - para leer desde stdin o escribir a stdout

$ curl https://example.com/something.tar.gz | tar xvf -

✓ Si un flag puede recibir un valor adicional, permite una palabra especial como none. ssh -F none

✓ Cuando sea posible, haz que argumentos, flags y subcomandos no dependan del orden

✓ Permite que valores sensibles (como contraseñas) puedan ingresarse desde un archivo

→ Interactividad

✓ Usa prompts o funciones interactivas solo cuando stdin sea una terminal interactiva

✓ Si se pasa --no-input, no uses prompts ni ninguna función interactiva

✓ Al pedir contraseñas, no muestres lo que el usuario escribe

✓ Haz que el usuario pueda salir fácilmente (no como vim). Permite que Ctrl-C funcione. Si por la naturaleza de ejecutar programas como ssh, tmux, etc. no se puede usar Ctrl-C, indica claramente que ofreces una secuencia de escape que empieza con ~, como en SSH

→ Subcomandos

✓ Las herramientas complejas pueden ofrecer subcomandos para reducir la complejidad.

✓ Además, si hay varias herramientas muy relacionadas, se pueden agrupar bajo un solo comando para hacerlas más cómodas de usar

✓ Mantén consistencia entre subcomandos. Que el mismo flag tenga el mismo significado y formatos de salida similares

✓ Usa nombres consistentes entre varios niveles de subcomandos

✓ No pongas comandos con nombres confusos o demasiado parecidos, como update y upgrade

→ Robustez

✓ Valida toda la entrada del usuario. Verifica pronto y muestra errores comprensibles

✓ La capacidad de respuesta es más importante que la velocidad

✓ Si va a tardar, muestra el progreso

✓ Si es posible, procesa en paralelo. Pero hazlo con cuidado.

✓ Agrega timeouts

✓ Hazlo idempotente (que el resultado no cambie aunque se vuelva a ejecutar). Si hay un error, que se pueda presionar flecha arriba en el shell y continuar desde antes al volver a ejecutar

✓ Hazlo crash-only. Es el siguiente paso después de la idempotencia. Si no hace falta limpiar después del trabajo, o si la limpieza puede posponerse hasta la siguiente ejecución, el programa puede terminar inmediatamente ante una falla o interrupción.

✓ La gente va a hacer mal uso de tu programa

→ Preparación para el futuro

✓ Si es posible, haz cambios de forma aditiva. No rompas compatibilidad cambiando funciones existentes; agrega nuevos flags

✓ Si el cambio no puede ser aditivo, adviértelo primero

✓ La mayoría de los cambios en la salida para personas están bien

✓ Aunque haya un subcomando que la gente usa mucho, no crees un subcomando catch-all que lo ejecute aunque no se especifique

✓ No permitas abreviaturas arbitrarias de subcomandos

✓ No crees “bombas de tiempo” que en algún momento dejarán de funcionar bien

→ Señales y caracteres de control

✓ Si el usuario presiona Ctrl-C (señal INT), interrumpe lo más rápido posible

✓ Si el usuario presiona Ctrl-C durante una limpieza larga, ignóralo. Si lo vuelve a presionar, permite forzar la salida

 ^CGracefully stopping... (press Ctrl+C again to force)

→ Configuración

✓ Sigue la especificación XDG(X Desktop Group)

✓ Si vas a modificar configuración que no pertenece a tu programa, pide confirmación al usuario y deja claro qué vas a hacer

✓ Aplica los parámetros de configuración por orden de prioridad

flags > variables de entorno del shell > configuración a nivel de proyecto (`.env`) > configuración del usuario > configuración del sistema

→ Variables de entorno

✓ Las variables de entorno son para comportamientos que cambian según el contexto en que se ejecuta el comando

✓ Para maximizar la portabilidad, las variables de entorno solo deben contener mayúsculas/números/guion bajo y no deben comenzar con un número

✓ Si es posible, usa valores de una sola línea en las variables de entorno

✓ No uses nombres de uso amplio

✓ Si es posible, revisa y usa variables de entorno comunes

`NO_COLOR`, `DEBUG`, `EDITOR`, `HTTP_PROXY`, `SHELL`, `TERM`, `TERMINFO`, `HOME`, `TMPDIR`, `PAGER`, `LINES` ..

✓ Si hace falta, carga variables de entorno desde .env

✓ No uses la extensión .env para archivos de configuración

→ Nombres

✓ Usa palabras simples y fáciles de recordar

✓ Usa solo minúsculas y - (guion) solo cuando sea realmente necesario

✓ Si es posible, que sea corto

✓ Que sea fácil de escribir en el teclado

→ Distribución

✓ Si es posible, distribúyelo como un solo binario

✓ Haz que sea fácil de desinstalar

→ Analítica

✓ No envíes datos de uso ni de crashes de la herramienta sin el consentimiento del usuario

3 comentarios

 
jonnung 2021-01-09

Gracias por el buen contenido.

 
xguru 2020-12-28

Parece que cada vez hay más buenas herramientas de línea de comandos gracias a Rust y Go, que permiten crear muy bien binarios únicos.

Crear este tipo de herramientas también se está volviendo más fácil y más potente.

 
xguru 2020-12-28

Aprendí mucho mientras hacía una traducción sencilla. Después de terminarla... también pensé que quizá habría sido mejor traducir el repo completo. ^^;;