CI/CD cuando la IA escribe el código
Si el código se genera más rápido de lo que se entiende, el pipeline pasa a ser el revisor principal. Siete capas pensadas para transporte y medios de pago, con el código de cada una.
El code review asume que el autor entendió el diff.
Todo proceso de code review descansa en un contrato implícito: el autor entendió lo que escribió, y el revisor busca lo que al autor se le escapó.
Con código generado por IA, ese contrato se debilita. Se puede iterar cinco veces con un asistente, quedarse con la versión que pasó la prueba manual y abrir un MR sin haber trazado las consecuencias del diff final. No es negligencia: la velocidad de generación se separó de la velocidad de comprensión.
El pipeline tiene que compensar lo que el autor no verificó.
Dos perfiles, dos maneras de fallar.
Un cambio que pasa lint, tipos y pruebas E2E puede romper cualquiera de las dos, y solo con carga o con reintentos reales.
~300 TPS
Sostenido, todos los días
1,200 conductores activos reportando ubicación cada 3 a 5 segundos. No es un pico: es carga continua.
+67%
En el pico de demanda
De 15 a 25 TPS en fechas de alta demanda. Cada transacción es dinero real: cero tolerancia a pérdida de datos.
LO QUE NO VEN LINT, TIPOS NI E2E
- Un cobro duplicado por un reintento
- Un timeout ausente que se vuelve caída en cascada
- Una consulta sin límite que se cae con millones de filas
Siete capas, ordenadas por costo.
Lo barato falla primero y lo caro corre solo donde el riesgo lo justifica. Todos los jobs corren en pipelines de merge request.
Integridad del gate
Detecta supresiones, tests saltados y umbrales bajados. Un ratchet compara con la rama base: el número solo puede bajar.
Ratchet en CIForma
Imports alucinados, dead code y deriva de estilo. Con --max-warnings 0, cada advertencia cuenta como fallo.
ESLint · BiomeTipos
Un any de escape y contratos que cambian en silencio, exigidos en modo estricto solo sobre lo que cambió el MR.
tsc --strictSeguridad
Secretos en el rango del MR, patrones inseguros y dependencias de producción, con reglas propias para pagos.
Gitleaks · Semgrep · pnpm auditComportamiento
Regresiones, idempotencia bajo concurrencia y tests que nunca podrían fallar.
Jest · StrykerCarga y resiliencia
Degradación a escala con el perfil real de la plataforma. Si p95 o los errores cruzan el umbral, el MR no avanza.
k6Entrega
Lo que se escapó de todo lo anterior: canary con análisis contra el SLO y rollback automático.
Argo RolloutsNingún gate llega a cero. Lo que separa a un equipo con experiencia de uno sin ella no es evitar el 100% de los incidentes, sino cuánto tarda en detectarlos y en recuperarse.
La pregunta que importa es cómo se recupera.
Capa por capa, con su código
Configuración lista para copiar. Los umbrales son de ejemplo: se fijan contra el SLO real de cada plataforma.
El gate también se protege
Un agente que persigue el check en verde puede llegar por el camino corto: un @ts-ignore, un it.skip, un umbral más bajo. El ratchet compara con la rama base y solo deja bajar el conteo. El pipeline vive en un proyecto central con versión fija.
ratchet:
stage: fast
variables:
GIT_DEPTH: "0"
script:
- |
B=$CI_MERGE_REQUEST_DIFF_BASE_SHA
S='eslint-disable|@ts-ignore|(it|test)\.skip'
n() { git grep -hE "$S" $1 -- src test | wc -l; }
test $(n HEAD) -le $(n $B)Tipos estrictos solo sobre lo que cambió
Pasar archivos a tsc por línea de comandos ignora el tsconfig y da falsos errores en alias como @/ (lo probamos). Se genera un tsconfig temporal que extiende el base e incluye solo los archivos del MR.
strict-changed:
stage: fast
variables:
GIT_DEPTH: "0"
rules:
- changes: ["**/*.ts"]
script:
- |
git diff --name-only --diff-filter=d \
$CI_MERGE_REQUEST_DIFF_BASE_SHA HEAD -- '*.ts' \
| jq -R . | jq -s '{extends:"./tsconfig.json",
compilerOptions:{strict:true},include:.}' \
> tsconfig.pr.json
pnpm exec tsc -p tsconfig.pr.json --noEmitSeguridad, con una regla propia para pagos
Gitleaks escanea el rango de commits del MR y detecta un token agregado y borrado en el commit siguiente. Semgrep suma reglas propias, como exigir Idempotency-Key en todo endpoint de cobro.
rules:
- id: charge-endpoint-without-idempotency-key
languages: [typescript]
severity: ERROR
message: Los endpoints de cobro deben leer el header Idempotency-Key
paths:
include:
- "**/src/payments/**"
patterns:
- pattern: |
@Post(...)
async $FN(...) { ... }
- pattern-not: |
@Post(...)
async $FN(..., @Headers('idempotency-key') $K, ...) { ... }Idempotencia bajo concurrencia
Una idempotencia ingenua revisa si existe y luego inserta: con un request funciona, con tres reintentos simultáneos cobra tres veces. En un ejemplo mínimo que probamos, un test sin aserciones dio 100% de cobertura y 20% de mutation score.
import { randomUUID } from 'crypto';
it('mismo Idempotency-Key: un solo cargo', async () => {
const key = randomUUID();
const send = () =>
api
.post('/charges')
.set('Idempotency-Key', key)
.send({ amount: 1500, currency: 'PEN' });
const res = await Promise.all([send(), send(), send()]);
expect(new Set(res.map((r) => r.body.id)).size).toBe(1);
expect(await ledger.countByKey(key)).toBe(1);
});Carga con el perfil real
1,200 conductores que reportan cada 4 segundos son ~300 requests por segundo. Cuando p95 o la tasa de error cruzan el umbral, k6 termina con código 99 y el MR no avanza. Umbrales de ejemplo.
import http from 'k6/http';
import { sleep } from 'k6';
export const options = {
vus: 1200,
duration: '5m',
thresholds: {
http_req_failed: ['rate<0.001'],
http_req_duration: ['p(95)<250'],
},
};
const body = JSON.stringify({ lat: -6.77, lng: -79.84 });
export default () => {
http.post(`${__ENV.API}/tracking`, body);
sleep(4);
};Entrega progresiva y rollback por SLO
El cambio recibe un porcentaje pequeño del tráfico, se mide contra el SLO y, si se degrada, se revierte solo. Ningún gate llega a cero: importa cuánto tarda la recuperación.
strategy:
canary:
steps:
- setWeight: 5
- pause: { duration: 5m }
- analysis:
templates:
- templateName: slo-p95
- setWeight: 50
- pause: { duration: 10m }Más notas del equipo
/ QUIERO ALGO SIMILAR
Tu próximo proyectoempieza hoy.
Cuéntanos qué necesitas — te decimos cómo lo resolvemos, sin vueltas ni letra chica.
HablemosO escríbenos directo → hola@identity.pe


