building-backends
Esta es la habilidad fundamental para el trabajo de backend. Léela antes de escribir cualquier código de servidor: establece las configuraciones por defecto y los puntos no negociables para que cada servicio sea consistente, luego delega a las habilidades específicas para los procedimientos detallados. Si una regla aquí entra en conflicto con una convención local del repositorio, gana el repositorio; si no hay conflicto, síguela.
Primero, comprende antes de construir
- Lee el código existente antes de añadirle. Sigue el marco de trabajo, modelo de errores, capas, nomenclatura y estilo de pruebas del repositorio. Un patrón "correcto" que lucha contra el código base es el patrón equivocado. Introducir una segunda forma de hacer algo que el repositorio ya hace es un defecto, no una mejora.
- Clarifica el contrato, no la implementación. Conoce las entradas, salidas, casos de error, quién lo llama y las expectativas de consistencia/latencia antes de elegir un diseño. La mayoría del retrabajo en backend proviene de construir el contrato equivocado rápido.
- Prefiere lo aburrido. Soluciones probadas, legibles y obvias por encima de las ingeniosas. El backend funciona sin supervisión a las 3 a.m.; optimiza para el ingeniero de guardia que tiene que entenderlo medio dormido, no para la elegancia.
Arquitectura — las reglas
- Capas con dependencias apuntando hacia adentro.
transporte (http) → servicios (casos de uso) → dominio (entidades/reglas), yservicios → repositorios (E/S) → dominio. El dominio no importa nada hacia afuera y no realiza E/S. Si una flecha de importación apunta en la dirección equivocada, el diseño está roto. - Los handlers orquestan, los servicios deciden, los repositorios hacen E/S. Un handler analiza/valida la entrada, llama a un servicio, mapea el resultado a una respuesta — sin lógica de negocio. Un repositorio es el único lugar que toca una base de datos, cola o HTTP externo.
- Mantén el dominio testeable sin infraestructura. Si no puedes hacer pruebas unitarias de la lógica central sin una base de datos o red, la lógica está en la capa equivocada.
- Un único punto de composición. Construye la configuración, conecta las dependencias e inicia el servidor en un solo lugar. Todo lo demás recibe sus dependencias; nada busca construir singletons globales.
- Diseña sin estado. El estado de sesión/scratch vive en un almacén compartido (BD/caché), no en la memoria del proceso, para que el servicio escale horizontalmente y sobreviva a un reinicio. Si estás cacheando en una variable de nivel de módulo, detente y piensa.
→ Para levantar el esqueleto con estas capas ya conectadas, usa api-service-scaffold.
Diseño de API — las reglas
- Contratos explícitos y versionados. Versiona la API (URL o encabezado) desde el primer día; nunca hagas un cambio incompatible en un contrato publicado — añade la nueva forma junto a la anterior y deprecia la antigua.
- Valida cada entrada en el límite a un valor tipado. El núcleo nunca ve un cuerpo de solicitud sin procesar. Rechaza la entrada inválida con 422 y detalle a nivel de campo; nunca confíes en el cliente.
- Un modelo de error, mapeado una vez. El código de dominio lanza errores tipados (
NotFoundError,ConflictError,ValidationError,UnauthorizedError…); un único mapeador de límites los convierte en estado +{ código, mensaje, detalles? }. Nunca filtres trazas de pila o mensajes internos.404para "no encontrado" y "no es tuyo" — distinguirlos filtra la existencia. - Formas consistentes. Mismo envoltorio para éxito, mismo envoltorio para errores, paginación consistente (cursor en lugar de desplazamiento para conjuntos grandes), mismo uso de mayúsculas/minúsculas en los campos. Un llamante debería aprender tus convenciones una vez.
- Idempotencia para operaciones no seguras.
PUT/DELETEson idempotentes por definición; haz que losPOSTque pueden reintentarse sean seguros con una clave de idempotencia. Las redes reintentan — un cliente cobrado dos veces es un error de diseño. - Códigos de estado correctos. 2xx éxito, 4xx error del llamante, 5xx error tuyo. Una falla de validación es 422, no 200 con un cuerpo de error; una falla de autenticación es 401/403, no 400.
La capa de datos — las reglas
- La base de datos es la fuente de verdad; aplica las invariantes allí. Las claves foráneas, restricciones de unicidad,
NOT NULLy comprobaciones pertenecen al esquema, no solo al código de la aplicación — el código de la aplicación tiene errores y condiciones de carrera; la BD no miente. - Las transacciones envuelven invariantes y son tan cortas como sea posible. Todo lo que debe ser todo o nada va en una transacción; no hagas trabajo lento (llamadas externas, cómputo pesado) mientras mantienes una. Las transacciones largas mantienen bloqueos y privan a los demás.
- Espera concurrencia. Dos solicitudes golpearán la misma fila a la vez. Usa bloqueo optimista (una columna de versión) o aislamiento apropiado; nunca leas-modifiques-escribas a través de un viaje de ida y vuelta sin protección.
- Las migraciones son compatibles hacia atrás y reversibles. Nunca cambies el significado de una columna en un solo despliegue; expande→migra→contrae. Nunca tomes un bloqueo que bloquee una tabla en una tabla caliente. Cada migración se envía con un rollback probado.
- Vigila el N+1. Obtén los datos relacionados en un conjunto, no en un bucle de consultas. Perfila el número de consultas en los endpoints reales, no solo la latencia.
- Indexa las consultas que realmente ejecutas — y solo esas. Un índice no utilizado es amplificación de escritura sin beneficio.
→ Para los mecanismos de migración (bloqueos, expandir-contratar, rellenos, rollbacks), usa db-migration-guardian.
Seguridad — la línea base (no negociable)
- Autentica luego autoriza, en cada solicitud, del lado del servidor. Nunca confíes en una afirmación del cliente sobre quién es o qué puede hacer. Verifica la propiedad en cada acceso a un recurso — "Tengo un token válido" ≠ "Este registro es mío".
- Parametriza cada consulta. Nunca construyas SQL con cadenas. Lo mismo para cualquier interfaz inyectable (shell, LDAP, plantilla).
- Los secretos vienen del entorno/gestor de secretos, nunca del código. Nada sensible en el código fuente, registros o mensajes de error. (Consulta la habilidad
secrets-management). - Mínimo privilegio en todas partes — usuarios de BD, credenciales de servicio, roles en la nube limitados exactamente a lo que el servicio necesita.
- Limita la tasa y establece tiempos de espera en los endpoints de entrada y llamadas de salida. Una llamada sin límite o un endpoint sin límite es una denegación de servicio esperando a suceder.
- Valida y codifica en los límites para prevenir inyección y SSRF; trata toda entrada externa, incluida la de otros servicios internos, como no confiable.
Concurrencia, trabajos y fallos
- Asume que cada llamada externa falla. Establece tiempos de espera, añade reintentos acotados con retroceso y fluctuación, y un interruptor de circuito para una dependencia inestable. Un reintento sin tiempo de espera es cómo una dependencia lenta derriba todo el servicio.
- Haz que las operaciones sean idempotentes para que un reintento (tuyo o del llamante) sea seguro.
- El trabajo en segundo plano va a una cola, no en la ruta de la solicitud. La solicitud encola; un trabajador procesa; los fallos van a una cola de mensajes fallidos para inspección, no a un
catch {}silencioso. - Falla fuerte, degrada con gracia. Expón los errores a la telemetría; cuando una dependencia no crítica esté caída, degrada (sirve datos obsoletos, omite el enriquecimiento) en lugar de devolver 500 para toda la solicitud.
La observabilidad es parte de "hecho", no una ocurrencia tardía
Un servicio que no puedes depurar en producción no está terminado. Los registros estructurados, las métricas RED/USE y el rastreo vinculados a un único id de correlación se incluyen mientras construyes, no después del primer incidente.
→ Usa backend-observability para los detalles de instrumentación y las barreras de cardinalidad/PII.
Postura de pruebas
- Prueba el comportamiento a través de la superficie pública (el endpoint / el caso de uso), no los internos privados — así las pruebas sobreviven a las refactorizaciones.
- Una pirámide, no un cono de helado: muchas pruebas unitarias rápidas sobre la lógica de dominio/servicio, un conjunto enfocado de pruebas de integración sobre la BD real y el cableado, unas pocas pruebas de extremo a extremo en los caminos críticos.
- Cada corrección de error incluye una prueba de regresión que falla antes de la corrección y pasa después.
- Pruebas deterministas. Sin dependencia del reloj de pared, red o orden; siembra los datos explícitamente; aísla las pruebas entre sí.
Procedimiento para cualquier tarea de backend
- Lee el código circundante; fija el contrato (entradas/salidas/errores/llamantes/consistencia).
- Coloca el trabajo en la capa correcta; mantén el dominio libre de E/S.
- Valida la entrada en el borde; usa el modelo de error único; devuelve el estado correcto.
- Pon las invariantes en la BD + una transacción corta; maneja la concurrencia; planifica las migraciones como expandir-contratar.
- Aplica la línea base de seguridad (autorización + propiedad, consultas parametrizadas, secretos, tiempos de espera/límites).
- Haz que las llamadas externas y los trabajos sean resilientes e idempotentes.
- Instrumenta mientras avanzas; añade pruebas en la capa correcta, incluida una prueba de regresión para cualquier error.
- Confirma contra la
Definición de hechode la habilidad específica relevante.
Definición de hecho
- Capas respetadas (el dominio no realiza E/S; las dependencias apuntan hacia adentro); un único punto de composición.
- Cada entrada validada en el límite; un modelo de error mapeado una vez; códigos de estado correctos; contrato versionado.
- Invariantes aplicadas en la BD; transacciones cortas; concurrencia manejada; migraciones compatibles hacia atrás y reversibles.
- Línea base de seguridad cumplida: autorización + comprobaciones de propiedad, consultas parametrizadas, secretos externos, tiempos de espera/límites de tasa presentes.
- Llamadas/trabajos externos idempotentes y resilientes; trabajo en segundo plano fuera de la ruta de la solicitud.
- Observabilidad cableada (registros/métricas/trazas con un id de correlación); pruebas en las capas correctas, verdes, con cobertura de regresión.