9  0.5. La terminal, en serio

Donde vive el backend, las cosas que nunca debes hacer, y tus nuevos compañeros de AI

NotaEn una frase

La terminal es el escritorio del backend: este capítulo te da el tour esencial (navegar, pipes, procesos), las 12 cosas que jamás debes hacer y un mapa de los agentes de AI que ahora viven ahí.

9.1 El problema real

Tu aplicación funciona — en tu máquina, con VS Code abierto y npm run dev corriendo en una terminal integrada que cerras cuando terminas. Entonces llega el día: “hay que desplegarlo”. Y de repente todo es terminal: el servidor no tiene pantalla, Docker no tiene botones, los logs son un stream infinito, y la única forma de entrar a producción es ssh.

Un log es el diario del programa: qué pasó, cuándo, con qué request. Estructurado = en JSON con campos (request_id, user_id), para filtrar por máquina y no con los ojos.

La terminal (consola, línea de comandos) es la interfaz de texto con el sistema operativo: escribes comandos, lees resultados. Es como hablarle a la computadora por cartas en vez de señalar con el mouse.

En frontend la terminal era un accesorio. En backend es el escritorio donde trabajas. Este capítulo corto te pone cómodo ahí — y te dice qué cosas jamás hacer, porque la terminal no tiene Ctrl+Z.

9.2 Cómo se resuelve en un equipo

  • Nadie memoriza comandos raros. Los equipos escriben runbooks: archivos con los comandos exactos para desplegar, ver logs, reiniciar.
  • Los READMEs arrancan con comandos, no con prosa: “clona, corre esto, abre este puerto”.

Un puerto es una puerta numerada dentro de una computadora: la IP llega al edificio, el puerto al departamento. :3000 = “la app que escucha en el departamento 3000”. Postgres suele usar 5432, HTTP el 80, HTTPS el 443.

  • Desde hace poco, la terminal tiene nuevos habitantes: los agentes de AI (Devin CLI, Claude Code, Copilot, Gemini CLI) viven ahí — porque la terminal es la interfaz universal donde se leen archivos y se ejecutan cosas.

Un CLI (Command Line Interface) es un programa que se usa escribiendo comandos en la terminal: git, npm, docker son CLIs. Lo contrario es una GUI (interfaz gráfica con botones).

9.3 Conceptos nuevos

  • La diferencia entre terminal, shell y consola (son tres cosas).
  • Qué es el PATH y por qué command not found existe.
  • Exit codes: el contrato silencioso de todo comando.
  • Pipes y redirección: componer programas como compones funciones.
  • Las reglas de oro de lo que nunca se hace.
  • Qué son los agentes CLI de AI y cómo trabajar con ellos sin que te rompan nada.

9.4 Explicación visual: tres capas que la gente confunde

flowchart TB
  subgraph W["Terminal — la ventana (GUI)"]
    subgraph S["Shell — el intérprete (bash / zsh / PowerShell)"]
      C["tus comandos"]
    end
  end
  W --> K["Sistema operativo: procesos, archivos, red, permisos"]
  C -->|"exec()"| K

  • Terminal: solo la ventana que dibuja texto (Windows Terminal, iTerm, la pestaña de VS Code). No entiende nada.
  • Shell: el programa que interpreta lo que escribes — bash, zsh, fish, pwsh. El mismo comando puede significar cosas distintas en cada shell.
  • SO: quien realmente ejecuta: crea procesos, abre puertos, borra archivos.

Por eso export PORT=8000 funciona en bash pero en PowerShell es $env:PORT = "8000". Misma intención, distinto intérprete.

Esta decisión es material: necesitas un shell — pero cuál es como elegir el tipo de cimentación: todas sostienen, cambia el terreno donde mejor funcionan.

Alternativa Qué es Qué te cuesta Cuándo elegirla
bash El estándar universal Sintaxis vieja, quirks históricos Scripts portables, servidores Linux — default seguro
zsh bash mejorado (default en macOS) Casi nada — compatible con bash Uso diario en Mac/Linux
PowerShell / pwsh Shell de objetos (no texto) Sintaxis distinta; menos portable a Linux Windows nativo, automatización .NET
fish Autocompletado y UX moderna NO compatible con sintaxis bash Terminal interactiva, no scripts
Nushell Datos estructurados en pipes Joven; ecosistema pequeño Curiosos, pipelines de datos
Git Bash (Windows) bash emulado en Windows No es Linux real — casos borde Windows sin WSL; lo que usa este libro

Y la terminal (la ventana): Windows Terminal, iTerm2, la integrada de VS Code — es decoración, elige la que te guste. El shell es lo que importa para los comandos.

9.5 El tour esencial

Nada de memorizar — esto es lo que usarás cada día, agrupado por intención:

9.5.1 Orientarse y archivos

pwd                    # dónde estoy
ls -la                 # qué hay aquí (con ocultos)
cd .. && cd -          # subir / volver al directorio anterior
mkdir -p src/services  # crear carpetas anidadas
cp .env .env.example   # copiar
mv old.ts new.ts       # mover/renombrar
cat app.log            # ver un archivo
tail -f app.log        # ver logs EN VIVO (el más usado en backend)

9.5.2 Componer con pipes (la idea más poderosa)

cat app.log | grep ERROR | wc -l    # cuántos errores hay
history | grep docker               # qué comandos docker usé
curl -s localhost:8000/health       # probar tu API sin navegador
echo "hello" > file.txt             # escribir (sobreescribe)
echo "more" >> file.txt             # añadir (append)

| conecta la salida de un programa con la entrada del siguiente — es .map().filter() pero entre procesos completos.

Un bucle repite una acción por cada elemento o hasta cumplir una condición: for task in tasks hace algo con cada tarea. map y filter son bucles disfrazados de funciones — transforman/filtran colecciones sin for explícito.

Un diccionario (dict en Python, map en Go, objeto en JS) guarda parejas clave→valor: {"ana": 30}. Es la estructura que más aparece en JSON — un objeto JS es un diccionario.

Un proceso es un programa en ejecución: el sistema operativo le da memoria propia y un lugar en la fila del CPU. Tu API es un proceso; Postgres es otro; el navegador es varios. Que “se caiga el proceso” = el programa murió.

9.5.3 Procesos, puertos y exit codes

Ctrl+C                 # SIGTERM amable: "termina por favor"
kill -9 1234           # SIGKILL: mata sin dejar limpiar (último recurso)
netstat -ano | grep 8000     # Windows: quién ocupa el puerto
lsof -i :8000                # Unix: lo mismo
echo $? / echo $LASTEXITCODE # exit code del comando anterior (0 = ok)

9.5.4 Variables de entorno

export PORT=8000       # bash/zsh
$env:PORT = "8000"     # PowerShell
env | grep PORT        # ver qué existe

Las variables de entorno son cómo la config llega a tu app sin hardcodearla — el Cap. 24 vive de esto. Y tu repo ya lo usa: el .env con tu API key.

Una variable es una caja con nombre donde guardas un valor: let total = 42 guarda el 42 bajo el nombre total. Puedes leerla y cambiarla después (total = 50). const = caja que no se puede reemplazar.

Una variable de entorno es configuración que vive fuera del código: DATABASE_URL, JWT_SECRET. El mismo binario corre en dev y prod con distintas vars — los secretos nunca se escriben en el código.

Un entorno es una instancia completa donde corre tu app con su propia config y datos: local (tu máquina), staging (réplica de prueba), producción (la real). Cada entorno tiene sus propias llaves y su propia base de datos.

Un repositorio (repo) es la carpeta del proyecto con todo su historial Git adentro. GitHub/GitLab son servicios que hospedan repos para compartirlos y respaldarlos.

9.6 Las cosas que NUNCA debes hacer

AdvertenciaReglas de oro — la terminal no tiene Ctrl+Z
  1. rm -rf sin mirar el path dos veces. Jamás con variables que puedan estar vacías: rm -rf "$DIR/" con DIR vacío borra desde la raíz. Regla: echo el comando primero si hay variables.

  2. curl ... | bash sin leer el script. Estás ejecutando código arbitrario de internet, a veces como root. Descarga, lee, después ejecuta. Los instaladores serios te dejan revisar.

  3. Commitear secretos. .env, keys, tokens, PEMs. Borrar el archivo después NO lo quita del historial de git — si se fue, hay que rotar la credencial (esto ya te pasó con una API key en este proyecto).

  4. sudo por costumbre. Si necesitas sudo para todo, algo está mal configurado (npm global, permisos de carpeta). sudo es el bisturí, no el martillo.

  5. chmod 777 para “arreglar” permisos. “Funcionó” = le diste todo a todos. Entiende qué permiso falta (644 archivos, 755 ejecutables).

  6. git push --force a ramas compartidas (main, develop). Reescribes la historia de otros. --force-with-lease en TU rama es el límite aceptable.

  7. Pegar comandos que no entiendes — especialmente de foros o con sudo. Si no sabes qué hace, pregúntale a un AI CLI primero (“explícame este comando”) — para eso están.

  8. kill -9 como primera opción. SIGKILL no deja al proceso cerrar conexiones ni escribir el último log. Primero Ctrl+C o kill (SIGTERM); -9 solo si ignora lo demás.

  9. Poner secretos en la línea de comandos: mysql -pSuperSecret queda en history y es visible en ps. Usa .env, flags interactivos o gestores de secretos.

  10. Rutas con espacios sin comillas: cd C:\Repositorios\Grupo umbral falla (dos argumentos); cd "C:\Repositorios\Grupo umbral" funciona. Ironía: este repo lo tiene.

  11. “Solo voy a probar rápido” en producción. No hay sandbox en prod: DROP DATABASE no pregunta “¿estás seguro?”. Cualquier comando destructivo se ensaya en staging primero.

  12. docker run --privileged o montar / por curiosidad. Le diste al contenedor tu máquina entera.

9.7 La nueva realidad: los CLI de AI

La terminal dejó de ser solo para comandos: desde hace unos años también es donde viven los agentes de AI — programas que leen tu repo, proponen cambios, editan archivos y ejecutan comandos por ti.

CLI Comando Qué es
Devin CLI devin Agente autónomo (el que está escribiendo este libro contigo)
Claude Code claude Agente de Anthropic en tu repo
Gemini CLI gemini Agente de Google, free tier generoso
Copilot CLI gh copilot / copilot Sugerencias y agente de GitHub
Aider aider Pair-programming open source por terminal

¿Por qué viven en la terminal y no en una app con botones? Porque la terminal es la interfaz universal: todo lo que un dev puede hacer — leer archivos, correr tests, git, ssh — es un comando. Un agente que sabe usar la terminal sabe usar todo lo que tú usas.

9.7.1 Las reglas de oro aplican al agente también

Un agente es un junior muy rápido que a veces se equivoca con total confianza:

  • Tú apruebas lo que ejecuta. Si propone rm -rf o un --force, las reglas de arriba aplican igual — la responsabilidad es tuya.
  • Revisa el diff antes de commitear lo que escribió, como harías con el PR de un compañero.
  • Nunca pegues secretos en el prompt. .env y gestores de secretos, igual que con cualquier herramienta.
  • Dale contexto, no órdenes vagas: “agrega validación al endpoint POST /tasks de src/routes/tasks.ts” funciona; “arregla el backend” no.
Explícame este comando antes de que lo ejecute: [pegar comando].
Quiero: qué hace cada flag y cada pipe, qué archivos/procesos toca, si
es destructivo o reversible, y la forma segura de lograr lo mismo si es
peligroso. No lo ejecutes — solo explícalo.

9.8 Errores comunes

  • command not found: el programa no está en el PATH. Revisa si está instalado y si tu terminal es vieja (el PATH se carga al abrirla — ya nos pasó con quarto).

PATH es la lista de carpetas donde la terminal busca programas cuando escribes un comando. Si node no está en el PATH, la terminal responde “command not found” aunque esté instalado en algún lado.

  • “Cerré la terminal y sigue corriendo” (o al revés): procesos hijos, nohup, servicios — Ctrl+C mata el foreground, no la familia.
  • Mezclar sintaxis de shells: export no existe en PowerShell, $env: no existe en bash. Mira qué shell tienes antes de pegar comandos de un tutorial.

9.9 Buenas prácticas

  • El README empieza con los 3 comandos para correr el proyecto. Si necesitas más, ponlos en scripts/ o un Makefile.
  • history + Ctrl+R: tu memoria está a una búsqueda de distancia.
  • Antes de un comando destructivo, la versión inofensiva: ls donde iría el rm, --dry-run si existe, echo donde hay variables.
  • Scripts para lo que repites: si lo escribiste tres veces, es un archivo .sh/.ps1 commiteado.

EXTRA Los roadmaps piden dos cosas: comandos básicos de Git (clone, add, commit, push, pull, branch, merge) y servicios de alojamiento de repositorios:

  • GitHub — la comunidad más grande; donde vive el open source y tu portafolio
  • GitLab — ciclo completo en una plataforma (repos + CI/CD + issues)
  • Bitbucket — el clásico del ecosistema Atlassian
  • AWS CodeCommit — repos Git privados administrados por Amazon

El cometario que vale: tu perfil de GitHub es tu portafolio — los equipos que contratan lo miran antes que el CV.

9.10 Ejercicio

Sin salir de la terminal: (1) navega a un directorio temporal, (2) crea playground/logs/, (3) escribe tres líneas en app.log con >>, (4) cuenta las líneas con un pipe, (5) levanta python -m http.server 8000 (o npx serve), (6) pruébalo con curl desde otra terminal, (7) mátalo con Ctrl+C.

9.11 Mini reto

Un compañero te pega esto y te dice “córralo”:

cd /tmp && curl -sL https://example.com/setup.sh | bash && rm -rf $OLD_BUILD

Antes de tocar nada: enumera las tres reglas de oro que viola.

9.12 Vocabulario técnico del capítulo

Una función es una receta reutilizable: recibe ingredientes (parámetros), hace pasos y devuelve un plato (return). La escribes una vez y la llamas mil veces: add(2, 3) → 5.

El parámetro es el hueco declarado en la función (def f(x) — x es parámetro); el argumento es el valor concreto que le pasas (f(42) — 42 es argumento). Mismo dato, dos momentos: declaración vs uso.

Un objeto es una colección de datos con nombre: {name: "Ana", age: 30} — cada dato es una propiedad (clave → valor). Python los llama dict, Go los arma con struct, TS con objetos/interface.

La autorización responde “¿qué puedes hacer?”: eres usuario válido (autenticado), pero ¿puedes borrar esta tarea? Se decide por rol o por ownership — y se verifica en cada request, no se recuerda.

Un token es una credencial portable: una cadena que dice quién eres y hasta cuándo. El servidor la emite tras el login; el cliente la presenta en cada request en vez de la contraseña.

Git es el historial de tu proyecto: cada commit es una foto del código con mensaje. Permite volver atrás, trabajar en ramas paralelas y fusionar el trabajo de varias personas sin pisarse.

localhost significa “esta misma máquina” — es la dirección que tu computadora usa para hablarse a sí misma. Cuando desarrollas, el “servidor” y el “cliente” viven en tu laptop: por eso todo es localhost:3000.

La memoria RAM es el escritorio de trabajo del programa: rápida, pero se borra al apagar. Los datos que deben sobrevivir van a disco o a la base de datos. Por eso let tasks = [] pierde todo al reiniciar.

9.13 Lo que deberías saber hacer ahora

Ejercicio (bash):

cd /tmp && mkdir -p playground/logs && cd playground
echo "INFO start" >> logs/app.log
echo "ERROR oops" >> logs/app.log
echo "INFO done" >> logs/app.log
cat logs/app.log | wc -l          # 3
python -m http.server 8000 &      # en otra terminal: curl localhost:8000
# Ctrl+C para detener

Mini reto — las tres reglas violadas:

  1. curl | bash ejecuta código no revisado de internet (regla 2).
  2. rm -rf $OLD_BUILD usa una variable que puede estar vacía → rm -rf / (regla 1).
  3. Y la meta-regla 7: correr algo que no entendiste. El flujo correcto: curl -sL .../setup.sh -o setup.sh, leerlo, entonces decidir.

Pregunta de reflexión: ¿por qué crees que los agentes de AI viven en la terminal y no en una app gráfica? Si tu respuesta es “porque la terminal puede hacer todo lo que un dev hace”, entendiste el capítulo.