← Volver al portal de guías
1. Modelo ISO/IEC 25010 — Performance Efficiency
ISO/IEC 25010:2023 define Performance Efficiency con tres sub-características. Toda prueba de performance debería poder mapearse a una de ellas:
| Sub-característica |
Definición |
Cómo se prueba |
| Time behaviour |
Tiempos de respuesta, procesamiento y throughput cumplen lo esperado |
Load tests con métricas p50/p95/p99, stress tests para identificar el punto de quiebre |
| Resource utilization |
Cantidad y tipos de recursos usados son apropiados |
Monitoreo de CPU, memoria, I/O, red durante load tests; profile de aplicación |
| Capacity |
El sistema soporta el número máximo declarado de usuarios/transacciones |
Stress + scalability testing, identificar saturación |
2. Tipos de pruebas de performance
| Tipo |
Objetivo |
Forma de carga |
Cuándo aplicarlo |
| Smoke |
Validar que el sistema responde bajo carga mínima |
1-5 VUs por pocos minutos |
Cada deploy; gate básico en CI |
| Load |
Comportamiento bajo carga esperada (p95 SLO cumple) |
Carga ramp-up hasta target sostenido |
Pre-release, semanal en staging |
| Stress |
Encontrar el punto de quiebre y observar degradación |
Carga creciente hasta que error rate > 1% o p95 > 2× SLO |
Antes de eventos esperados (Black Friday, lanzamiento) |
| Spike |
Resistencia a picos súbitos de tráfico |
0 → 10× target en < 30s, sostener 1-5 min |
Sistemas con tráfico viral / lanzamientos / campañas |
| Soak / Endurance |
Detectar memory leaks, fugas de connections, degradación gradual |
Carga moderada sostenida por 4-24h |
Pre-release de versiones críticas |
| Scalability |
Verificar que añadir recursos mejora capacidad linealmente |
Load tests con 1, 2, 4, 8 instancias |
Validar autoscaling, capacity planning |
| Volume |
Performance bajo gran volumen de datos (no de tráfico) |
Carga normal con DB de tamaño realista (TB) |
Sistemas con datasets que crecen mucho |
| Frontend / Web Vitals |
Performance percibida por el usuario final (LCP, INP, CLS) |
Lighthouse, RUM (Real User Monitoring) |
Cada PR de UI; continuo en producción |
3. Métricas core
3.1 Latencia: NUNCA promedio, SIEMPRE percentiles
⚠️ Regla fundamental: el promedio de latencia es una mentira estadística. Una API con p50=100ms, p95=200ms, p99=5s tiene "promedio" engañosamente bajo, pero el 1% de tus usuarios tiene una experiencia terrible. Usar siempre p50, p95, p99 (y p99.9 para sistemas críticos).
| Métrica |
Qué mide |
Target típico (web) |
| p50 (mediana) | 50% de requests más rápidos que esto | < 200ms |
| p95 | 95% de requests más rápidos que esto | < 1000ms |
| p99 | 99% de requests — la cola "lenta" | < 2000ms |
| p99.9 | Solo sistemas críticos — la cola extrema | SLO específico |
| Tail ratio | p99 / p50 — qué tan estable es el sistema | < 10× |
3.2 Throughput, error rate, saturation
| Métrica |
Definición |
Cómo medirla |
| RPS / TPS | Requests / transactions per second | k6 http_reqs rate, APM |
| Error rate | % de respuestas con status ≥ 500 o errores aplicativos | k6 http_req_failed, APM |
| Saturation (USE) | % de uso del recurso más restringido (CPU, conn pool, queue) | Prometheus, métricas de aplicación |
| Apdex | Índice de satisfacción 0-1 basado en thresholds T y 4T | APM (New Relic, Datadog) |
3.3 Modelo USE (Brendan Gregg) para diagnóstico
Para cada recurso del sistema (CPU, memoria, disco, red), revisar:
- Utilization — % del tiempo que el recurso está ocupado
- Saturation — cuánto trabajo extra está esperando (queue length)
- Errors — cuántos errores produce ese recurso
4. Web Vitals modernos (Google, 2024)
Las Core Web Vitals de Google son el estándar de la industria para frontend performance. INP reemplazó a FID en marzo 2024, así que si tu material cita FID, está desactualizado.
| Métrica |
Qué mide |
Bueno |
A mejorar |
Pobre |
LCP Largest Contentful Paint |
Tiempo hasta renderizar el elemento más grande visible |
≤ 2.5s |
≤ 4.0s |
> 4.0s |
INP Interaction to Next Paint |
Latencia de la interacción más lenta del usuario (reemplazó FID en mar 2024) |
≤ 200ms |
≤ 500ms |
> 500ms |
CLS Cumulative Layout Shift |
Suma de cambios inesperados de layout |
≤ 0.10 |
≤ 0.25 |
> 0.25 |
FCP First Contentful Paint |
Cuándo aparece el primer texto/imagen (métrica complementaria) |
≤ 1.8s |
≤ 3.0s |
> 3.0s |
TTFB Time to First Byte |
Tiempo del backend hasta el primer byte |
≤ 0.8s |
≤ 1.8s |
> 1.8s |
📌 Lab vs Field: Lighthouse mide lab data (entorno sintético controlado). Para producción usá RUM (Real User Monitoring) — Chrome UX Report, Datadog RUM, Sentry. Las métricas pueden divergir hasta 30%.
5. SLI / SLO / Error Budget
Marco de Site Reliability Engineering (Google SRE Book). Convierte performance de "intentamos que sea rápido" a un compromiso medible:
| Concepto |
Definición |
Ejemplo |
SLI Service Level Indicator |
Métrica observable cuantitativa |
"% de requests con latencia < 500ms" |
SLO Service Level Objective |
Objetivo interno sobre el SLI |
"99% de requests < 500ms en ventana de 28 días" |
SLA Service Level Agreement |
Compromiso contractual con el cliente (con consecuencias) |
"95% de uptime mensual o 10% de crédito" |
| Error budget |
1 - SLO. Cuánto error te podés permitir antes de gatear features. |
SLO 99.9% → budget = 0.1% = 43.2 min/mes de downtime |
📌 Política operativa típica: mientras el error budget no se haya consumido, el equipo lanza features libremente. Si se consume, se pausan features y se prioriza estabilidad. Esto crea un balance objetivo entre velocidad y confiabilidad.
6. Perfiles de carga: Open vs Closed Model
Diferencia fundamental que la mayoría de herramientas viejas (JMeter clásico) ignoran:
| Modelo |
Descripción |
Cuándo usarlo |
Closed model (Virtual Users / VUs) |
N usuarios concurrentes, cada uno espera la respuesta antes de mandar la siguiente. Si el sistema se lentifica, los VUs envían menos. |
Apps internas con número fijo de usuarios concurrentes (CRM, ERP) |
Open model (Arrival rate) |
X requests/segundo entran independientemente del estado del sistema. Si se lentifica, las queues crecen. |
APIs públicas, e-commerce, cualquier sistema con tráfico real (la mayoría) |
⚠️ Coordinated omission: el closed model esconde el peor caso porque los VUs lentos automáticamente reducen la carga. Si tu sistema responde a usuarios que llegan según Poisson (la mayoría), usá open model (en k6: ramping-arrival-rate o constant-arrival-rate).
7. k6 — scripts y patrones recomendados
7.1 Load test básico con thresholds
// load-test.js — load test estándar con SLOs como thresholds
import http from 'k6/http';
import { check, group } from 'k6';
export const options = {
scenarios: {
api_load: {
executor: 'ramping-arrival-rate', // open model
startRate: 10,
timeUnit: '1s',
preAllocatedVUs: 100,
maxVUs: 500,
stages: [
{ target: 50, duration: '2m' }, // ramp-up
{ target: 200, duration: '5m' }, // load
{ target: 200, duration: '10m' }, // sustained
{ target: 0, duration: '2m' }, // ramp-down
],
},
},
thresholds: {
// SLOs como gates: el test FALLA si no se cumplen
'http_req_duration{name:GET /products}': ['p(95)<500', 'p(99)<1500'],
'http_req_duration{name:POST /orders}': ['p(95)<1000', 'p(99)<2500'],
'http_req_failed': ['rate<0.01'], // <1% errores
'checks': ['rate>0.99'],
},
};
export default function () {
group('browse-and-buy', () => {
const r1 = http.get('https://api.example.com/products',
{ tags: { name: 'GET /products' } });
check(r1, { '200 ok': r => r.status === 200,
'has items': r => r.json('items.length') > 0 });
const r2 = http.post('https://api.example.com/orders',
JSON.stringify({ productId: 'p1', qty: 1 }),
{ headers: { 'Content-Type': 'application/json' },
tags: { name: 'POST /orders' } });
check(r2, { '201 created': r => r.status === 201 });
});
}
7.2 Stress test — encontrar el punto de quiebre
export const options = {
executor: 'ramping-arrival-rate',
preAllocatedVUs: 100,
maxVUs: 2000,
stages: [
{ target: 100, duration: '2m' },
{ target: 500, duration: '5m' },
{ target: 1000, duration: '5m' },
{ target: 2000, duration: '5m' },
{ target: 5000, duration: '5m' }, // hasta romper
],
// sin thresholds — queremos VER dónde rompe
};
7.3 Soak test — 8 horas para detectar memory leaks
export const options = {
executor: 'constant-arrival-rate',
rate: 100, timeUnit: '1s',
duration: '8h',
preAllocatedVUs: 200,
thresholds: {
'http_req_duration': ['p(95)<500'], // debe mantenerse 8h
'http_req_failed': ['rate<0.005'],
},
};
8. Lighthouse CI en pipeline
# lighthouserc.json
{
"ci": {
"collect": {
"url": ["https://staging.example.com/", "https://staging.example.com/products"],
"numberOfRuns": 3
},
"assert": {
"assertions": {
"categories:performance": ["error", { "minScore": 0.85 }],
"categories:accessibility": ["error", { "minScore": 0.95 }],
"largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
"interaction-to-next-paint": ["error", { "maxNumericValue": 200 }],
"cumulative-layout-shift": ["error", { "maxNumericValue": 0.10 }]
}
},
"upload": { "target": "temporary-public-storage" }
}
}
9. Observabilidad y APM
Performance testing sin observabilidad es ciego. Stack mínimo recomendado:
| Capa | Open source | SaaS | Para qué |
| Métricas | Prometheus + Grafana | Datadog, New Relic | RPS, latencia p95/p99, error rate, saturation |
| Logs | Loki, OpenSearch | Datadog Logs, Splunk | Errores con contexto, correlación con traces |
| Traces | Jaeger, Tempo | Honeycomb, Datadog APM | Distributed tracing, identificar bottleneck por servicio |
| RUM (frontend) | Sentry self-hosted | Sentry, Datadog RUM | Web Vitals reales del usuario final |
| Profiling | Pyroscope, Parca | Datadog Continuous Profiler | Encontrar hot paths en CPU/memoria |
📌 Estándar emergente: OpenTelemetry (OTel). Define un único protocolo y SDK para métricas, logs y traces. Si vas a empezar de cero, hacelo OTel-native — te desacopla de cualquier vendor.
10. Performance shift-left: presupuestos en PR
El error más común es probar performance solo al final. La mejora 10× viene de romper el build cuando un PR introduce regresiones:
| Etapa | Test | Trigger | Tiempo |
| PR | Lighthouse CI (frontend) + smoke k6 (backend) | Cada commit | < 5 min |
| Merge a main | Load test corto (5 min) | Auto post-merge | < 10 min |
| Pre-release | Load + soak + spike completo | Manual o release branch | 30 min - 2 h |
| Producción | Synthetic monitoring + RUM | Continuo | 24/7 |
10.1 Workflow de presupuesto en PR (GitHub Actions)
name: Performance Budget
on: [pull_request]
jobs:
perf:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Lighthouse CI
run: |
npm i -g @lhci/cli
lhci autorun --config=./lighthouserc.json
- name: k6 smoke
uses: grafana/k6-action@v0.3.1
with:
filename: tests/perf/smoke.js
flags: --vus 10 --duration 2m
11. Plantilla — Performance Test Plan
# Performance Test Plan — <sistema>
## 1. Identificación
- Sistema: …
- Versión: …
- Owner: …
- Fecha: 2026-04-26
## 2. Contexto y objetivos
- Tráfico actual de pico: 1200 RPS
- Crecimiento esperado en 6 meses: 3×
- Objetivo del test: validar que escala a 4000 RPS con SLOs intactos
## 3. SLOs definidos
| Endpoint | SLI | SLO |
|---------------------------|-----------------------------|-----------------------|
| GET /products | latencia p95 | < 300 ms |
| POST /orders | latencia p95 | < 800 ms |
| GET /search | latencia p95 | < 500 ms |
| Toda la API | error rate | < 0.5% |
| Toda la API | disponibilidad mensual | ≥ 99.9% |
## 4. Tipos de test a ejecutar
| Test | Carga | Duración | Criterio éxito |
|------------|-------------------|----------|----------------|
| Smoke | 10 RPS | 2 min | 0 errores 5xx |
| Load | 1500 RPS | 30 min | SLOs cumplidos |
| Stress | 100 → 6000 RPS | 30 min | Identificar quiebre > 4000 |
| Spike | 100 → 5000 en 30s | 10 min | Recovery < 60s |
| Soak | 800 RPS | 8 h | Sin degradación, sin leaks |
## 5. Datos de prueba
- 100k usuarios sintéticos pre-creados
- Catálogo: copia del prod (anonimizado)
- Datasets en CSV + parametrización en k6
## 6. Entornos
- Test ejecutado contra: staging-perf (idéntico a prod, escalado al 100%)
- NO en prod salvo synthetic monitoring continuo
## 7. Observabilidad
- Grafana dashboard "perf-test-suite"
- APM: Datadog, capturando p50/p95/p99 por endpoint
- Profiling: Continuous Profiler activo durante el test
## 8. Criterios de salida
- [ ] Todos los SLOs cumplidos en el load test
- [ ] Stress test identifica punto de quiebre y degradación es graceful
- [ ] Spike test: recovery automático < 60s
- [ ] Soak: memoria estable, sin leaks
- [ ] Reporte HTML + dashboard linkeado en release notes
12. Anti-patterns
- Reportar el promedio de latencia. Solo p50/p95/p99/p99.9 cuentan.
- Probar en una máquina compartida con la aplicación. El generador de carga debe ser independiente.
- Pruebas con datos vacíos. 10 productos en DB vs 10M es un mundo distinto. Volumen real (o sintético equivalente).
- Closed model cuando deberías usar open model. Coordinated omission esconde problemas.
- "Pasamos el load test" sin medir saturación de recursos. Si CPU está al 95% o pool de conexiones al límite, no escala.
- Performance test al final del proyecto. Imposible corregir tarde — Shift-left con presupuestos en PR.
- Lighthouse score como única métrica frontend. Es lab data — agregar RUM en producción.
- No pre-calentar el sistema. JIT warm-up, cache fría, pool sin establecer → primeras métricas son irreales.
- Test "sintético" muy distinto del tráfico real. Distribución de endpoints debe parecerse al pareto real.
- No correlacionar resultados con observabilidad. Sin trace, no sabés POR QUÉ es lento.
📚 Referencias
- ISO/IEC 25010:2023 — System and software quality models
- Google Web Vitals (web.dev/vitals)
- Google SRE Workbook — capítulos sobre SLO/SLI/Error Budget
- k6 documentation — k6.io/docs
- Brendan Gregg — USE method (brendangregg.com/usemethod.html)
- Gil Tene — "How NOT to measure latency" (sobre coordinated omission)
- OpenTelemetry — opentelemetry.io
QA Shift-Left Methodology — Framework operativo integrado por E-Gregorio
Guía Performance — referencia a estándares públicos: ISO/IEC 25010:2023 y Google Web Vitals 2024