Por Lucas Yoris · Performate
Pruebas de contrato vs pruebas de rendimiento: cómo mantener ambas sin duplicar esfuerzo
Separa corrección de esquema del comportamiento bajo concurrencia—comparte artefactos, no suites duplicadas que derivan.
Tu suite Pact está verde, el diff de OpenAPI limpio y staging despliega cada hora—y luego el p99 de checkout se duplica en una oferta modesta porque el nuevo serializador asigna el doble por request. Las pruebas de contrato demostraron la forma del mensaje; nunca demostraron comportamiento paralelo en pools de conexión, cachés o GC.
Contrato y rendimiento responden riesgos distintos. Duplicar catálogos completos de requests en repos separados garantiza deriva en dos sprints. En esta guía verás por qué esquema en verde no es SLO en verde, cómo un script k6 reutiliza los mismos payloads que valida tu suite de contrato, y qué reglas de decisión mantienen ambos gates sin duplicar mantenimiento.
Por qué contrato en verde no es rendimiento en verde
Las pruebas de contrato (Pact, JSON Schema, compatibilidad protobuf) demuestran que productores y consumidores acuerdan formas de mensaje entre versiones. Las pruebas de rendimiento demuestran latencia, throughput y comportamiento ante fallos cuando muchos clientes golpean esas formas a la vez (k6 introduction).
- Los contratos son muestras puntuales: un par request/response no ejerce agotamiento de pools ni contención de locks.
- El rendimiento necesita mix realista: ratios lectura/escritura, refresh de auth y think time cambian la cola de latencia con el mismo esquema.
- Los splits de versión importan:
/v1y/v2pueden pasar checks de esquema mientras el routing los envía por clusters distintos (versionado de API bajo carga). - Las suites duplicadas derivan: dos equipos manteniendo carpetas Postman «contrato» vs «carga» divergen en headers, auth y defaults de campos en semanas.
Piensa en los contratos como verificar el plano; las pruebas de rendimiento estresan el edificio cuando todos los ascensores corren a la vez.
Cuando CI pasa ambas suites pero producción sigue sufriendo
Los equipos suelen correr contratos en pull requests y carga manualmente antes de releases grandes—y se preguntan por qué los incidentes siguen agrupándose en checkout. La brecha suele ser divergencia de artefactos: el script de carga aún postea { "sku": 123 } mientras Pact espera { "sku": "SKU-123" } como string, o la corrida de perf omite el handshake OAuth que hacen tus consumidores. Comparte una spec OpenAPI o colección Postman como fuente, luego deriva ambos gates (Postman a k6).
Implementación práctica con k6: un payload, dos gates
Las pruebas de carga deben reutilizar los mismos cuerpos JSON y headers que valida tu suite de contrato—luego añade concurrencia, umbrales de percentil y rutas etiquetadas. El script siguiente postea payloads de checkout que coinciden con un contrato Pact ficticio mientras mide cola de latencia bajo tráfico paralelo.
Ejemplo de script (ilustrativo—no listo para producción). El fragmento usa URLs, tokens y números SLO ficticios. Adapta base URL, auth, payloads y umbrales a tu entorno.
Qué demuestra este ejemplo:
- Forma de contrato compartida: campos del body (
sku,qty,currency) reflejan lo que afirman los tests de esquema—los fallos de perf no son 400 misteriosos por deriva. - Checks de contrato + umbrales de perf:
check()ligero en status y claves JSON requeridas; gates SLO enp(95)/p(99)y tasa de error. - Rutas etiquetadas:
route:checkoutsepara resúmenes de perf de futuros escenarios de lectura en el mismo script. - Base URL vía env: el mismo script corre contra stub de contrato, staging y env de perf sin edits.
import http from 'k6/http';
import { check, sleep } from 'k6';
const BASE = __ENV.API_BASE || 'https://staging.example.com';
const TOKEN = __ENV.TOKEN || 'staging-token-replace-me';
// Forma validada por tests de contrato (Pact / JSON Schema)
function checkoutPayload(sku) {
return JSON.stringify({
sku: String(sku),
qty: 1,
currency: 'USD',
});
}
export const options = {
scenarios: {
checkout_contract_shape: {
executor: 'constant-arrival-rate',
rate: Number(__ENV.CHECKOUT_RPS || 10),
timeUnit: '1s',
duration: '6m',
preAllocatedVUs: 12,
maxVUs: 50,
tags: { route: 'checkout', gate: 'perf' },
exec: 'checkoutUnderLoad',
},
},
thresholds: {
// Gate de rendimiento — no cubierto por CI de contrato
'http_req_duration{route:checkout}': ['p(95)<750', 'p(99)<1100'],
http_req_failed: ['rate<0.01'],
// Métrica custom de aserciones estilo contrato
'checks{route:checkout}': ['rate>0.99'],
},
};
export function checkoutUnderLoad() {
const sku = `SKU-${100 + (__VU % 50)}`;
const res = http.post(`${BASE}/checkout`, checkoutPayload(sku), {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${TOKEN}`,
Accept: 'application/json',
},
tags: { route: 'checkout', gate: 'perf' },
});
// Checks adyacentes al contrato bajo carga — presencia de esquema, no replay Pact completo
check(res, {
'status 2xx': (r) => r.status >= 200 && r.status < 300,
'has orderId': (r) => {
try {
const body = r.json();
return typeof body.orderId === 'string' && body.orderId.length > 0;
} catch {
return false;
}
},
'currency echoed': (r) => {
try {
return r.json('currency') === 'USD';
} catch {
return false;
}
},
});
sleep(0.4);
}
Patrones que funcionan
- Genera k6 desde OpenAPI o importa Postman una vez; exporta ejemplos de contrato de la misma carpeta (parametrización).
- Corre contrato en CI en cada PR; smoke de perf a baja RPS en merge—soak completo queda programado (CI/CD).
- Etiqueta
gate:contractvsgate:perfcuando un repo aloja ambos tipos de smoke para claridad en dashboards. - Combina con tests de versionado cuando coexisten familias de API (escenarios mix de versión).
Anti-patrones a evitar
- Mantener dos colecciones Postman que divergen en headers de auth y tipos de campo.
- Afirmar cada campo de respuesta en corridas de alto throughput—reserva validación profunda de esquema para CI de contrato.
- Saltar perf porque «Pact está verde»—corrección de esquema no predice cola de latencia.
- Probar mocks que devuelven 200 instantáneos sin backend—ocultan comportamiento real de pools.
Pro tip (comando de ejemplo): la línea siguiente muestra cómo fallar CI en umbrales de perf mientras imprimes tasas de checks—útil cuando contrato y perf comparten un job nocturno.
k6 run checkout-perf.js --summary-export=summary.json --thresholds-on-summary
Qué demuestra este comando: el JSON exportado captura percentiles de http_req_duration y tasas de pass de checks—los revisores de release ven fallos adyacentes al esquema y brechas SLO en un solo artefacto.
Marco de decisión: solo contrato vs solo perf vs ambos
| Situación | Acción recomendada |
|---|---|
| Endpoint nuevo, esquema aún cambia | Contratos en CI de PR; difiere perf hasta que la forma se estabilice |
| Esquema congelado, tráfico desconocido | Contrato verde + smoke de perf a baja RPS con umbrales de percentil |
| Paths críticos checkout/pago | Ambos gates obligatorios; fixtures compartidos desde ejemplos OpenAPI |
| Servicio interno sin consumidores externos | JSON Schema o compat protobuf en CI; perf vía escenarios k6 etiquetados |
| Ventana de deprecación con dos versiones API | Contrato por versión + perf mix de versión (guía versionado) |
Usa solo pruebas de contrato si la integración es temprana y los breaking changes son diarios—los números de perf no tendrían sentido.
Usa solo pruebas de rendimiento si el esquema se impone en otro lado (tipado fuerte, codegen) y tu riesgo es concurrencia—no renombres de campos.
Usa ambas con artefactos compartidos si hay consumidores externos y el path lleva ingresos o penalidades SLA—la mayoría de APIs de producto caen aquí.
Observabilidad, documentación y próximos pasos
Los gates duales solo ayudan cuando los equipos saben qué modo de fallo arreglan. Antes de escalar tráfico:
- Documenta qué ejemplos OpenAPI o fixtures Pact mapean a cada payload de escenario k6.
- Separa dashboards de CI de contrato (PR) vs smoke de perf (merge/nightly)—cadencia y umbrales distintos.
- Alerta sobre divergencia de
p(99)en perf aunque las suites de contrato pasen en el mismo commit. - Correlaciona rutas etiquetadas de k6 con APM durante fallos de perf (correlation IDs).
- Archiva versión de publish de contrato + hash de script k6 por release para trazar regresiones a esquema vs infra.
Cómo Performate mantiene contrato y perf alineados
Bifurcar colecciones para «contratos QA» y «scripts de carga» es cómo empieza la deriva. Abajo un ejemplo concreto de flujo para la API checkout de este artículo.
Ejemplo: una colección, dos gates
- Importa la misma colección Postman (o OpenAPI) que ya mantiene tu equipo de contrato—carpeta
checkoutcon cuerpos de ejemplo. Problema resuelto: una fuente de verdad; perf y Pact tiran de formas de campo idénticas. - Crea un escenario de perf a baja RPS en el editor visual—p. ej. 10 req/s de llegada constante por seis minutos—con el request checkout. Problema resuelto: smoke de perf sin reescribir HTTP desde cero.
- Añade checks en el panel de escenario para presencia de
orderIdy status 2xx—refleja aserciones de contrato de forma ligera. Problema resuelto: las corridas de carga detectan deriva grosera de esquema antes de jobs Pact nocturnos. - Define umbrales p95/p99 en el escenario checkout; deja validación profunda de contrato en CI existente. Problema resuelto: separación clara—forma vs concurrencia—sin repos duplicados.
- Exporta k6 para pipeline de merge a tasa reducida; soak completo queda en calendario. Problema resuelto: gate de perf GitOps coincide con lo que ingeniería afinó localmente.
- Compara corridas entre releases en el reporte integrado cuando sube versión de contrato—observa latencia, no solo badges Pact verdes. Problema resuelto: finanzas y backend debaten una exportación cuando esquema y perf se mueven juntos.
Ese flujo mapea al cta: más confianza en rendimiento sin mantener bosques de scripts paralelos.
Conclusión
Las pruebas de contrato protegen la corrección de integración; las de rendimiento protegen la experiencia del cliente bajo carga paralela. Comparte artefactos, separa gates y nunca trates un publish Pact verde como prueba de que checkout sobrevive una oferta.
Importa una colección, añade umbrales de percentil a los mismos payloads que valida tu suite de esquema y programa un smoke a baja RPS en cada merge—luego anota qué fallos son de forma vs concurrencia.
¿Listo para optimizar el rendimiento de tu API?
Explora cómo Performate simplifica las pruebas de carga con k6, desde imports hasta resultados, para que tu equipo publique con más confianza en rendimiento.