Apéndice F — F. Checklists del profesional
Antes de exponer un endpoint / una migración / un deploy / decir que está listo
Las listas de verificación completas que el libro fue construyendo capítulo a capítulo — reunidas para uso diario: antes de mergear, antes de migrar, antes de desplegar, antes de decir “está listo”.
F.1 Antes de exponer un endpoint nuevo
CONTRATO
[ ] Schema de entrada validado (tipos, required, límites) — cap. 4
[ ] Schema de salida: solo campos del contrato, nada interno — cap. 5
[ ] Status code correcto: 200/201/204, nunca 200-con-error — cap. 6
AUTH
[ ] ¿Requiere sesión? → middleware/Depends aplicado — cap. 16
[ ] ¿Requiere rol? → requireRole o verificación en la query — cap. 17
[ ] ¿Toca un recurso ajeno? → ownership EN el WHERE + 404 — cap. 17
ERRORES
[ ] Errores de dominio mapeados al contrato {error:{code}} — cap. 25
[ ] Nada de stack traces, nada de mensajes internos — cap. 25
[ ] 422 para input inválido con detalle por campo — cap. 4
DATOS
[ ] Query parametrizada — cero concatenación — cap. 9
[ ] ¿Más de una escritura? → transacción — cap. 13
[ ] ¿Lista? → ORDER BY + LIMIT 21 (paginación) — cap. 12
[ ] Índice que cubre el patrón de acceso — EXPLAIN — cap. 12
TEST
[ ] Camino feliz — cap. 28
[ ] 401 sin sesión, 403 rol débil, 404 recurso ajeno — cap. 28
[ ] 422 input inválido — cap. 28
[ ] Fuga de campos internos (password_hash, team_id…) — cap. 28
F.2 Antes de correr una migración
[ ] Tiene up Y down probados — cap. 11
[ ] No edita una migración ya aplicada en ningún entorno — cap. 11
[ ] Compatible con la versión VIEJA del código (deploy escalonado) — cap. 35
[ ] CREATE INDEX CONCURRENTLY en tablas grandes — cap. 12/35
[ ] NOT NULL solo con DEFAULT o en dos pasos — cap. 35
[ ] Corre en CI antes del deploy (migrate-gate) — cap. 34
[ ] Tiempo estimado medido en staging con datos reales — cap. 39
F.3 Antes de un deploy
[ ] Tests verdes en CI — cap. 34
[ ] Imagen construida una vez, la MISMA para staging y prod — cap. 32
[ ] Secretos en env vars / secret manager, nunca en la imagen — cap. 24/32
[ ] La app corre como non-root, multi-stage, imagen mínima — cap. 32
[ ] /health responde y /health/deep toca la BD — cap. 33
[ ] Graceful shutdown: deja de aceptar, drena, cierra pool — cap. 33
[ ] La migración ya corrió (o es expand-phase) — cap. 34/35
[ ] Logs JSON + request_id funcionando — cap. 23
[ ] Plan de rollback conocido ANTES de apretar el botón — cap. 35
F.4 Antes de decir “está listo”
SEGURIDAD (cap. 26 auditada por endpoint)
[ ] Cada endpoint: auth ✓ rol ✓ ownership ✓ exposición ✓
[ ] Login/register con rate limit — cap. 18
[ ] Cookies httpOnly+Secure+SameSite / CORS con orígenes exactos — cap. 15/18
[ ] Webhooks verifican firma sobre body crudo + dedupe — cap. 22
[ ] WebSocket autenticado y autorizado igual que REST — cap. 38
ROBUSTEZ
[ ] Errores → handler global único, formato estable — cap. 25
[ ] Config validada al boot (fail-fast, sin defaults secretos) — cap. 24
[ ] Llamadas externas: timeout + retry + degradación — cap. 19
[ ] Lo que no espera el usuario va a un job — cap. 21
[ ] Logs reconstruyen una request sin reproducirla — cap. 23
DATOS
[ ] Migraciones versionadas y aplicadas — cap. 11
[ ] Índices verificados con EXPLAIN + volumen real — cap. 12/39
[ ] Operaciones multi-paso dentro de tx — cap. 13
[ ] Lo versionado es append-only con puntero — cap. 37
PRUEBAS Y DOCS
[ ] Pirámide: servicios > API > e2e — cap. 27
[ ] Suite verde de corrido en máquina limpia — cap. 28
[ ] README levanta el proyecto sin ti — cap. 39
[ ] openapi.json publicado + runbook de 5 líneas — cap. 39
[ ] ADR escrito para cada decisión que costó — cap. 36
F.5 Mini reto
Pasar la checklist completa a un proyecto tuyo anterior y contar los hallazgos.
La experiencia universal: el primer pase marca 10–20 casillas en rojo — endpoints sin ownership en la query, secretos con default, listas sin paginar, errores que filtran formato interno. No es que tu proyecto fuera malo: es que “funciona” y “está listo” son estándares distintos y esta lista es la distancia entre ambos. La segunda vez que escribes un endpoint, la lista ya corre en tu cabeza — para eso era.
F.6 Vocabulario técnico del capítulo
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.
Un array es una colección ordenada de elementos accedidos por posición: ["a","b","c"][0] es "a" (se cuenta desde 0). Python las llama listas, Go slices — misma idea, distinto acento.
Un secreto es un dato que no puede publicarse: contraseñas de BD, llaves de API, el secreto que firma los JWT. Viven en .env (fuera de git) o en un secret manager — nunca en el código ni en el repo.
Un deploy es poner tu código a correr en el servidor: copiar la imagen, iniciar el proceso, verificar que responde. El objetivo es que sea aburrido — automático, repetible, con rollback.
Un dominio es el nombre que compras (midominio.com) y apuntas a tu servidor por DNS. Sin él, tus usuarios tendrían que memorizar una IP. El HTTPS serio requiere dominio.
Una imagen es la plantilla inmutable de la que nacen contenedores: la foto del disco + cómo arrancar. Se construye en capas (Dockerfile), se versiona con tags, se publica en un registry.
WebSocket es una conexión que queda viva: en vez de preguntar- responder-cerrar como HTTP, ambos pueden hablar cuando quieran. Es como el servidor puede empujar — por eso sirve para chat y colaboración en vivo.
Un job es trabajo que se hace sin que el usuario espere: mandar el email, generar el PDF. Va a una cola y un worker lo procesa en segundo plano — la respuesta HTTP sale inmediata.
Vertical: máquina más gorda (más CPU/RAM) — fácil, tiene techo. Horizontal: más máquinas — el camino de internet, pero requiere que la app no guarde estado local (por eso todo va a BD/Redis).
Staging es el ensayo general: una copia de producción (misma config, datos falsos o anonimizados) donde pruebas el deploy antes de hacerlo de verdad. Si falla ahí, falló gratis.
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.
Una base de datos es el programa que guarda datos de forma permanente y los responde rápido. Relacional (Postgres): tablas con relaciones. No relacional: documentos, clave-valor, grafos — cada una para una forma de dato distinta.
Una query es la pregunta que le haces a la base de datos en SQL: SELECT * FROM tasks WHERE done = false. La BD traduce la pregunta a un plan de búsqueda — por eso los índices importan.
Una migración es un cambio versionado del esquema: un archivo que dice “crea esta columna” (up) y “bórrala” (down). Son el historial Git de la estructura de la BD — se aplican en orden y no se editan una vez aplicadas.
Una transacción es un grupo de operaciones que se confirman juntas o no se confirma ninguna: transferir dinero = restar de A y sumar a B. Si falla a la mitad sin transacción, el dinero desapareció.
Un pool es el staff de conexiones a la BD: abrir una conexión por request es caro, así que el pool mantiene ~10–20 abiertas y las presta. El request la usa, la devuelve, y la siguiente la reutiliza.
La autenticación responde “¿quién eres?” — login, contraseña, token. Se confunde con autorización (“¿qué puedes hacer?”), que es la pregunta siguiente. Primero te identificas, luego te dejan o no pasar.
Una firma (HMAC) prueba que un mensaje es auténtico y no fue tocado: se calcula con un secreto sobre el contenido. Los webhooks la usan para que verifiques “esto realmente vino de Stripe”.
Un middleware es un filtro en la cadena del request: pasa por él antes de llegar a tu handler. Auth es middleware — “verifica el token” vive una vez y protege todas las rutas, no se copia en cada una.
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.