CommunityProgramación y desarrollogithub.com

addyosmani/observabilidad-e-instrumentación

Instrumenta el código para que el comportamiento en producción sea visible y diagnosticable. Úsalo al añadir registro de eventos, métricas, trazado o alertas. Úsalo al enviar cualquier funcionalidad que se ejecute en producción y necesites evidencia de que funciona. Úsalo cuando se reporten problemas de producción pero no puedas determinar qué ocurrió a partir de los datos disponibles.

¿Qué es observabilidad-e-instrumentación?

Esta habilidad establece un proceso sistemático para instrumentar código de producción, desde la definición de preguntas de guardia hasta la verificación de la telemetría. Se aplica a cualquier plataforma o stack tecnológico, pero los ejemplos concretos usan Node.js/TypeScript. Enseña a elegir la señal correcta (logs estructurados, métricas, trazas) según la pregunta que se quiere responder, siguiendo patrones RED/USE. Cubre logging estructurado con IDs de correlación, métricas con histograms y control de cardinalidad, trazado distribuido con OpenTelemetry, creación de alertas basadas en síntomas, y runbooks mínimos viables. Incluye una lista de verificación pre-lanzamiento para garantizar que la instrumentación es suficiente antes de enviar a producción.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/addyosmani/agent-skills/tree/main/skills/observability-and-instrumentation

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Observabilidad e Instrumentación

Resumen

El código que no se puede observar es código que no se puede operar. La observabilidad es la capacidad de responder "¿qué está haciendo el sistema y por qué?" desde fuera, usando la telemetría que el código emite. La instrumentación no es un complemento posterior al lanzamiento: se escribe junto con la funcionalidad, de la misma manera que las pruebas. Si una funcionalidad se envía sin telemetría, el primer error reportado por un usuario se convierte en arqueología en lugar de una consulta.

Cuándo Usarla

  • Construir cualquier funcionalidad que se ejecutará en producción
  • Añadir un nuevo servicio, endpoint, trabajo en segundo plano o integración externa
  • Un incidente de producción tardó demasiado en diagnosticarse ("no pudimos determinar qué ocurrió")
  • Configurar o revisar reglas de alertas
  • Revisar un PR que añade E/S, reintentos, colas o llamadas entre servicios

NO para:

  • Diagnosticar una falla que está ocurriendo ahora mismo: usa la habilidad debugging-and-error-recovery (la observabilidad es lo que hace que esa habilidad sea rápida la próxima vez)
  • Perfilar y optimizar lentitud medida: usa la habilidad performance-optimization
  • Listas de verificación de monitoreo del día de lanzamiento y disparadores de reversión: consulta la habilidad shipping-and-launch; esta habilidad cubre la instrumentación que los alimenta

Proceso

1. Definir "funciona" antes de instrumentar

Telemetría sin una pregunta es ruido. Antes de añadir cualquier instrumentación, escribe de 2 a 4 preguntas que un ingeniero de guardia hará sobre esta funcionalidad:

FEATURE: reintento de pago en checkout
QUESTIONS ON-CALL WILL ASK:
1. ¿Qué fracción de pagos se realizan con éxito en el primer intento vs después del reintento?
2. Cuando un pago falla permanentemente, ¿por qué? (¿error del proveedor? ¿timeout? ¿validación?)
3. ¿El proveedor de pagos es más lento de lo habitual?
→ Cada señal a continuación debe ayudar a responder una de estas.

Si no puedes nombrar las preguntas, no estás listo para instrumentar: registrarás todo y no aprenderás nada.

2. Elegir la señal correcta para cada pregunta

SeñalRespondePerfil de costoEjemplo
Log estructurado"¿Qué ocurrió en este caso específico?"Por evento; crece con el tráficopayment_failed con código de error del proveedor
Métrica"¿Con qué frecuencia / qué tan rápido, en agregado?"Fijo por serie; barato de consultarlatencia p99 de llamadas al proveedor
Traza"¿Dónde se fue el tiempo entre servicios?"Por solicitud; generalmente muestreadoUn checkout lento, desglosado por salto

Regla general: las métricas te dicen que algo está mal, las trazas te dicen dónde, los logs te dicen por qué.

3. Registro estructurado

Registra eventos, no prosa. Cada línea de log es un objeto JSON con un nombre de evento estable y campos legibles por máquina:

// MAL: interpolación de cadenas — no consultable, inconsistente
logger.info(`Payment ${id} failed for user ${userId} after ${n} retries`);

// BIEN: nombre de evento estable + campos estructurados
logger.warn({
  event: 'payment_failed',
  paymentId: id,
  provider: 'stripe',
  errorCode: err.code,
  attempt: n,
}, 'payment failed');

Niveles de log — úsalos de manera consistente:

NivelSignificadoAcción de guardia
errorInvariante rota; alguien puede necesitar actuarInvestigar
warnDegradado pero manejado (reintento exitoso, respaldo usado)Observar tendencias
infoEvento de negocio significativo (pedido realizado, trabajo terminado)Ninguna
debugDetalle de diagnósticoDesactivado en producción por defecto

Los IDs de correlación son obligatorios. Genera (o acepta) un ID de solicitud en el límite del sistema y adjúnalo a cada línea de log, tramo y llamada saliente. Sin él, no puedes reconstruir una sola solicitud a partir de logs entremezclados:

// Express: logger hijo por solicitud, ID propagado aguas abajo
app.use((req, res, next) => {
  req.id = req.headers['x-request-id'] ?? crypto.randomUUID();
  req.log = logger.child({ requestId: req.id });
  res.setHeader('x-request-id', req.id);
  next();
});

Cuando varios puntos de entrada escriben en un solo log, nombra el punto de entrada. Un ID de correlación identifica una ejecución; no dice qué ruta de código la inició. El mismo trabajo alcanzado por un planificador, por un endpoint de reproducción y por una ejecución manual de CLI produce líneas intercambiables en un solo sumidero, por lo que atribuir una línea se reduce a eliminación – leer el historial del planificador, la tabla de procesos, un log de despliegue – y ese argumento solo se sostiene mientras esos registros externos aún existan. Marca el punto de entrada donde comienza la ejecución, junto al ID de correlación, y propaga ambos de la misma manera:

// Un helper para cada punto de entrada: el logger propio de la ejecución lleva ambos campos.
// `entryPoint`, no `source` — ECS reserva `source.*` para campos de red.
export const runLog = (entryPoint: 'scheduler' | 'replay_endpoint' | 'cli', runId: string) =>
  logger.child({ entryPoint, requestId: runId });

// tick del planificador        -> runLog('scheduler', crypto.randomUUID())
// POST /jobs/:id/replay -> runLog('replay_endpoint', req.id)
// invocación CLI        -> runLog('cli', process.env.RUN_ID ?? crypto.randomUUID())

Ambos campos deben cruzar los mismos límites que el ID de correlación – metadatos de cola, encabezados HTTP – o un trabajador re-deriva el punto de entrada y adivina. Un campo que meramente se correlaciona con un punto de entrada es una pista, no una atribución: cualquier cosa que pueda invocar el trabajo puede reproducirlo.

Nunca registres secretos, tokens, contraseñas o PII completa. Esta es una regla estricta de la habilidad security-and-hardening: los pipelines de telemetría son una ruta clásica de fuga de datos. Haz una lista blanca de campos; no registres cuerpos de solicitud completos.

4. Métricas

Para servicios orientados a solicitudes, instrumenta RED en cada endpoint y cada dependencia externa: Rate (solicitudes/segundo), Errors (tasa de fallos), Duration (histograma de latencia, no promedio). Para recursos (colas, pools, hosts), usa USE: Utilization, Saturation, Errors.

Al igual que con el trazado, el camino independiente del proveedor es la API de métricas de OpenTelemetry (mismo SDK y contexto que el paso 5). El siguiente ejemplo usa prom-client de Prometheus — una opción de backend común, no la única; las reglas de RED/USE y cardinalidad son idénticas en ambos casos.

import { Histogram } from 'prom-client';

const httpDuration = new Histogram({
  name: 'http_request_duration_seconds',
  help: 'HTTP request duration',
  labelNames: ['method', 'route', 'status_class'],  // '2xx', no '200'
  buckets: [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5],
});

La cardinalidad es el modo de fallo. Cada combinación única de etiquetas es una serie temporal separada. Las etiquetas deben provenir de conjuntos pequeños y fijos (plantilla de ruta, clase de estado, nombre del proveedor). Nunca uses IDs de usuario, URLs completas, mensajes de error u otros valores no acotados como etiquetas – eso pertenece a los logs y trazas.

OK como etiqueta:    route="/api/tasks/:id"   status_class="5xx"   provider="stripe"
NUNCA una etiqueta:  user_id, email, request_id, URL completa, texto del mensaje de error

Nunca promedios, siempre percentiles: un promedio oculta el 1% de usuarios que están teniendo una experiencia terrible. Usa histogramas y lee p50/p95/p99.

5. Trazado distribuido

Usa OpenTelemetry — es el estándar independiente del proveedor, y la auto-instrumentación cubre HTTP, gRPC y clientes de BD comunes con código casi nulo:

// tracing.ts — debe importarse antes que cualquier otra cosa
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';

const sdk = new NodeSDK({
  serviceName: 'checkout-service',
  instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();

Añade tramos manuales solo alrededor de unidades de trabajo internas significativas (por ejemplo, applyDiscounts, chargeProvider) y adjunta los atributos por los que la guardia filtrará. Propaga el contexto a través de cada límite asíncrono – encabezados HTTP, metadatos de mensajes de cola – o la traza muere en la brecha. Muestrea basado en cabecera a una tasa baja por defecto; mantén el 100% de los errores si tu backend soporta muestreo de cola.

6. Alertas

Alerta sobre síntomas que sienten los usuarios, no sobre causas:

SÍNTOMA (digno de paginar):           CAUSA (panel, no una página):
tasa de error > 1% durante 5 min        CPU al 85%
latencia p99 > 2s                       un pod se reinició
edad de cola > 10 min                   disco al 70%

Las alertas basadas en causa se disparan cuando no pasa nada y pasan por alto fallos que no predijiste. Las alertas basadas en síntoma se disparan exactamente cuando los usuarios están afectados, independientemente de la causa.

Reglas para cada alerta que crees:

  1. Debe ser procesable. Si la respuesta es "ignorarlo, se autocura", elimina la alerta.
  2. Enlaza a un runbook — incluso tres líneas: qué significa, primera consulta a ejecutar, ruta de escalado.
  3. Tiene un umbral y una duración justificados por el SLO o por datos históricos, no por una suposición.
  4. Usa solo dos niveles de gravedad: página (afecta al usuario, actúa ahora) y ticket (degradación, actúa esta semana). Un tercer nivel se convierte en ruido que entrena a las personas a ignorar todo.

Escribir Runbooks

La regla 2 anterior requiere que cada alerta enlace a un runbook. El trabajo de un runbook es responder tres preguntas sin que el lector tenga que pensar: qué está pasando, qué revisar primero, y a quién llamar si eso no lo resuelve. Almacena en docs/runbooks/ nombrados según la alerta.

Runbook mínimo viable (tres líneas):

# Runbook: Alta Tasa de Error en /api/tasks
**Significa:** El pool de conexiones de la BD probablemente está agotado, o un mal despliegue.
**Primera comprobación:** `SELECT count(*) FROM pg_stat_activity WHERE backend_type = 'client backend';`
  — si el recuento > límite del pool, consulta el Paso 2. (Reemplaza con el equivalente para tu base de datos.)
**Escalar a:** #db-oncall o rotación de guardia de ingeniería.

Cuándo expandir más allá de tres líneas: añade pasos solo cuando la primera comprobación por sí sola no sea suficiente para decidir. Un runbook de cinco pasos que cubra las tres causas más comunes es mejor que un documento de veinte pasos que cubra cada caso extremo y que se lea por encima.

Mantén los runbooks actualizados. Actualiza el runbook como parte del cierre de cada incidente en el que se usó – un runbook desactualizado genera falsa confianza. Si un paso fue incorrecto o faltaba, arréglalo antes de marcar el incidente como resuelto.

7. Verificar la propia telemetría

La instrumentación es código; puede estar mal. Antes de dar por terminado el trabajo, dispara las rutas y mira la salida real:

  • Fuerza un error en staging → encuéntralo en los logs por requestId, confirma que los campos están estructurados (no [object Object])
  • Envía tráfico de prueba → confirma que las series métricas aparecen con las etiquetas esperadas y valores sensatos
  • Sigue una solicitud entre servicios en la UI de trazado → sin tramos rotos
  • Dispara cada nueva alerta una vez (baja el umbral temporalmente) → confirma que llega al canal correcto y que el enlace del runbook funciona

Racionalizaciones Comunes

RacionalizaciónRealidad
"Añadiré logging después de que funcione""Después" se convierte en "después del primer incidente", que es el momento más caro para descubrir que estás ciego. Instrumenta mientras construyes.
"Más logs = más observabilidad"El ruido no estructurado hace que los incidentes sean más lentos, no más rápidos. Tres eventos consultables vencen a trescientas líneas de prosa.
"console.log está bien por ahora"La salida no estructurada no se puede filtrar, correlacionar ni alertar. El logger estructurado cuesta cinco minutos extra una vez.
"Podemos simplemente mirar los paneles cuando algo se rompa"Los paneles construidos sin preguntas definidas te muestran todo excepto la respuesta. Empieza por las preguntas de guardia.
"Alerta sobre todo lo importante, lo ajustaremos después"Un buscapersonas ruidoso entrena a las personas a ignorarlo. El ajuste nunca ocurre; la página real perdida, sí.
"El ID de usuario como etiqueta de métrica facilita la depuración"También hace que tu backend de métricas se caiga. Las búsquedas de alta cardinalidad pertenecen a logs y trazas.
"El trazado es excesivo para nuestros dos servicios"Dos servicios ya significa preguntas de latencia entre servicios que los logs no pueden responder. La auto-instrumentación hace que el costo sea trivial.

Señales de Alarma

  • Un PR de funcionalidad con reintentos, colas o llamadas externas y cero telemetría nueva
  • Líneas de log construidas con interpolación de cadenas en lugar de campos estructurados
  • Sin ID de correlación/solicitud – cada línea de log es un huérfano
  • Un flujo de log alimentado por un planificador, un webhook y ejecuciones manuales, sin un campo que nombre cuál produjo la línea
  • Métricas etiquetadas con IDs de usuario, URLs completas o texto de mensajes de error (bomba de cardinalidad)
  • Latencia rastreada como un promedio sin percentiles
  • Alertas que se disparan a diario y se reconocen sin acción
  • Alertas sobre causas (CPU, memoria) que pagan humanos mientras la tasa de error visible para el usuario no está monitoreada
  • Secretos, tokens o cuerpos de solicitud completos que aparecen en logs
  • "Funciona en mi máquina" como la única evidencia de que una funcionalidad de producción está sana

Verificación

Después de instrumentar una funcionalidad, confirma:

  • Las preguntas de guardia para esta funcionalidad están escritas, y cada señal se asigna a una
  • Toda la salida de log está estructurada (JSON), con nombres de evento estables y un ID de correlación en cada línea
  • Cada sumidero de log escrito por más de un punto de entrada lleva un campo de punto de entrada, establecido donde comienza la ejecución y propagado con el ID de correlación en lugar de inferirse aguas abajo
  • No hay secretos, tokens ni PII no redactada en ninguna línea de log (verificación puntual de la salida real)
  • Existen métricas RED para cada nuevo endpoint y cada dependencia externa, con conjuntos de etiquetas acotados
  • La latencia es un histograma; p95/p99 son consultables
  • Una sola solicitud se puede seguir de principio a fin en la UI de trazado sin tramos rotos
  • Cada nueva alerta está basada en síntoma, tiene un enlace a runbook y se disparó una vez en prueba
  • Una falla inducida en staging se localizó solo a través de telemetría, sin leer el código fuente

Para la versión resumida de esta lista, incluyendo la compuerta de instrumentación previa al lanzamiento, consulta ../../references/observability-checklist.md.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

addyosmani/agent-skills

Builds production-quality, accessible, responsive user-facing UIs. Use when building or modifying interfaces and pages, creating components, implementing layouts, meeting WCAG accessibility requirements, managing state, or when the output needs to look and feel production-quality rather than AI-generated.

addyosmani/agent-skills

Prepares production launches. Use when preparing to deploy to production, or when asking what needs to be in place before shipping. Use when you need a pre-launch checklist, when setting up monitoring, when planning a staged rollout, or when you need a rollback strategy.

addyosmani/agent-skills

Guides stable API and interface design. Use when designing APIs, module boundaries, or any public interface. Use when creating REST or GraphQL endpoints, defining type contracts between modules, or establishing boundaries between frontend and backend.

addyosmani/browser-testing-with-devtools

Tests in real browsers via Chrome DevTools MCP. Use when building or debugging anything that runs in a browser. Use when you need to inspect the DOM, capture console errors, analyze network requests, profile performance, or verify visual output with real runtime data. Requires the chrome-devtools MCP server to be configured.

addyosmani/ci-cd-and-automation

Automates CI/CD pipeline setup. Use when setting up or modifying build and deployment pipelines. Use when you need to automate quality gates, configure test runners in CI, or establish deployment strategies.

addyosmani/code-review-and-quality

Conducts multi-axis code review. Use before merging any change. Use when reviewing code written by yourself, another agent, or a human. Use when you need to assess code quality across multiple dimensions before it enters the main branch.

addyosmani/code-simplification

Simplifies code for clarity. Use when refactoring code for clarity without changing behavior. Use when code works but is harder to read, maintain, or extend than it should be. Use when reviewing code that has accumulated unnecessary complexity.

addyosmani/constraint-driven-development

Establishes a project's quality bar as a written contract and stops agents quietly lowering it. Interviews the user on which dimensions matter, supplies sane default thresholds when they have no number in mind, records everything in CONSTRAINTS.md, and watches the diff for a weakened bar — new @ts-ignore or eslint-disable suppressions, skipped or deleted tests, assertions stripped out, unimplemented stubs, thresholds edited down. Use when no quality bar is written down, when the user says "set up constraints" or "define our standards", when the user wants dimensions they care about — accessibility, web performance, coverage — set up as enforced constraints, when an agent keeps silencing checks or skipping tests to get to green, when you need a coverage or performance threshold and don't know what number to pick, or when an agent writes more code than anyone will read.

addyosmani/debugging-and-error-recovery

Guides systematic root-cause debugging. Use when tests fail, builds break, behavior doesn't match expectations, or you encounter any unexpected error. Use when you need a systematic approach to finding and fixing the root cause rather than guessing.

addyosmani/deprecation-and-migration

Manages deprecation and migration. Use when removing old systems, APIs, or features. Use when migrating users from one implementation to another. Use when deciding whether to maintain or sunset existing code.

addyosmani/documentation-and-adrs

Records decisions and documentation. Use when making architectural decisions, changing public APIs, shipping features, or when you need to record context that future engineers and agents will need to understand the codebase.

addyosmani/doubt-driven-development

Subjects every non-trivial decision to a fresh-context adversarial review before it stands. Use when correctness matters more than speed, when working in unfamiliar code, when stakes are high (production, security-sensitive logic, irreversible operations), or any time a confident output would be cheaper to verify now than to debug later.

addyosmani/git-workflow-and-versioning

Structures git workflow practices. Use when making any code change. Use when committing, branching, resolving conflicts, or when you need to organize work across multiple parallel streams.

addyosmani/idea-refine

Refines raw ideas into sharp, actionable concepts through structured divergent and convergent thinking. Use when an idea is still vague, when you need to stress-test assumptions before committing to a plan, or when you want to expand options before converging on one. Triggers on "ideate", "refine this idea", or "stress-test my plan".

addyosmani/incremental-implementation

Delivers changes incrementally. Use when implementing any feature or change that touches more than one file. Use when you're about to write a large amount of code at once, or when a task feels too big to land in one step.

addyosmani/interview-me

Extracts what the user actually wants instead of what they think they should want. Achieves this through one-question-at-a-time interview until ~95% confidence about the underlying intent. Use when an ask is underspecified ("build me X" without "for whom" or "why now"), when the user explicitly invokes ("interview me", "grill me", "are we sure?", "stress-test my thinking"), or when you catch yourself silently filling in ambiguous requirements before any plan, spec, or code exists.

addyosmani/performance-optimization

Optimizes application performance. Use when performance requirements exist, when you suspect performance regressions, or when Core Web Vitals or load times need improvement. Use when profiling reveals bottlenecks that need fixing.

addyosmani/planning-and-task-breakdown

Breaks work into ordered tasks. Use when you have a spec or clear requirements and need to break work into implementable tasks. Use when a task feels too large to start, when you need to estimate scope, or when parallel work is possible.

addyosmani/security-and-hardening

Hardens code against vulnerabilities. Use when handling user input, authentication, data storage, or external integrations. Use when building any feature that accepts untrusted data, manages user sessions, or interacts with third-party services.

addyosmani/source-driven-development

Grounds every implementation decision in official documentation. Use when you want authoritative, source-cited code free from outdated patterns. Use when building with any framework or library where correctness matters.

Skills relacionados