La pirámide de tests: la base de todo pipeline
El costo de un bug se multiplica exponencialmente mientras más tarde se detecta. Un bug detectado en unit tests cuesta 1x. En integration tests, 10x. En staging, 50x. En producción, 500x. La pirámide de tests define la distribución correcta de cobertura para maximizar la detección temprana con el menor costo de ejecución.
- Unit tests (base de la pirámide, 70%): rápidos (<5 minutos para toda la suite), sin dependencias externas, alta cobertura de lógica de negocio. Si tardan más de 10 minutos, el equipo empieza a saltárselos.
- Integration tests (30%): verifican la interacción con bases de datos, APIs externas y otros servicios internos. Usan contenedores Docker para las dependencias (testcontainers).
- E2E tests (10%): verifican los flows críticos del usuario completos. Solo para los happy paths de mayor valor de negocio — los E2E completos son lentos y frágiles.
Estrategia de branches: Trunk-Based Development para equipos ágiles
Gitflow (develop, release, hotfix branches) era el estándar hace 10 años. Para equipos que quieren deployar múltiples veces por día, Trunk-Based Development es el modelo correcto: todos los desarrolladores integran al main trunk frecuentemente (idealmente diario), y los releases se hacen directamente desde trunk.
# Trunk-Based Development — flujo simplificado
main (trunk)
├── feature/TICKET-123-add-checkout # branch de corta duración (<2 días)
└── feature/TICKET-456-fix-cart # merge a main vía PR con CI obligatorio
# NO: ramas de larga duración
# NO: develop, release, hotfix como modelo de integración
# SÍ: feature flags para desacoplar deploy de releaseLa clave de Trunk-Based Development son los feature flags. El código de una feature incompleta se mergea a trunk protegido por un flag desactivado. El feature se activa en producción cuando está listo, independientemente de cuándo se mergeó el código.
Pipeline de GitLab CI production-grade
stages:
- test
- build
- security
- deploy-staging
- integration-test
- deploy-production
variables:
IMAGE: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}
unit-tests:
stage: test
image: node:20-alpine
cache:
paths: [node_modules/]
script:
- npm ci
- npm run test:unit -- --coverage
coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
build-image:
stage: build
image: docker:24
services: [docker:24-dind]
script:
- docker build --cache-from ${IMAGE}:latest -t ${IMAGE} .
- docker push ${IMAGE}
only: [main]
trivy-scan:
stage: security
image: aquasec/trivy:latest
script:
- trivy image --exit-code 1 --severity HIGH,CRITICAL ${IMAGE}
allow_failure: false # bloquea el pipeline si hay vulnerabilidades críticas
only: [main]
deploy-production:
stage: deploy-production
environment: production
when: manual
script:
- kubectl set image deployment/api api=${IMAGE} -n production
- kubectl rollout status deployment/api -n production --timeout=5m
only: [main]Deployment Strategies: elegir según el riesgo
No todas las features merecen el mismo nivel de precaución en el deploy. Las tres estrategias principales:
Rolling Update
Deploy gradual reemplazando pods uno a uno. Costo de implementación bajo. Rollback tarda N minutos (tiempo de rolling update). Ideal para la mayoría de cambios de bajo riesgo en servicios stateless.
Blue/Green Deployment
Dos environments idénticos (blue=actual, green=nueva versión). El switch es instantáneo a nivel de load balancer. Rollback en segundos. Costo: doble de infra durante el deploy. Ideal para cambios de alto impacto donde el rollback instantáneo justifica el costo.
Canary Deployment
Deploy a un porcentaje pequeño del tráfico (5%, 10%) antes del rollout completo. Detecta problemas reales de producción con impacto mínimo. Requiere feature flags o traffic splitting en el ingress/service mesh. La estrategia más robusta para servicios críticos con alto volumen de usuarios.
# Argo Rollouts — Canary con análisis automático
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: api
spec:
strategy:
canary:
steps:
- setWeight: 5 # 5% del tráfico al canary
- pause: {duration: 10m}
- analysis:
templates:
- templateName: success-rate
- setWeight: 50
- pause: {duration: 5m}
- setWeight: 100
canaryMetadata:
labels:
deployment: canary
stableMetadata:
labels:
deployment: stableSecrets en pipelines: el error más frecuente
Hardcodear secrets en el pipeline (variables de CI sin enmascarar, kubectl apply con tokens en el script) es el error más frecuente en pipelines empresariales. El patrón correcto: las credenciales de deploy viven en el secret manager del cloud (AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault), y el pipeline los obtiene en runtime con credenciales temporales vía OIDC.
# GitLab CI — OIDC con AWS para credenciales temporales sin secrets estáticos
deploy:
id_tokens:
AWS_OIDC_TOKEN:
aud: https://gitlab.com
script:
- export $(aws sts assume-role-with-web-identity
--role-arn ${AWS_DEPLOY_ROLE_ARN}
--role-session-name gitlab-ci-${CI_JOB_ID}
--web-identity-token ${AWS_OIDC_TOKEN}
--query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]'
--output text | awk '{print "AWS_ACCESS_KEY_ID="$1" AWS_SECRET_ACCESS_KEY="$2" AWS_SESSION_TOKEN="$3}')
- kubectl apply -f k8s/Preguntas frecuentes
¿Cuántos deploys a producción por día es razonable para un equipo empresarial?
¿Cómo implemento feature flags sin una herramienta de terceros?
¿Cómo medir la efectividad del pipeline de CI/CD?
¿Cómo manejo los database migrations en el pipeline?
¿Qué tan costoso es implementar un pipeline CI/CD de nivel enterprise?
¿Tu equipo quiere pasar de deploys manuales a un pipeline CI/CD que les dé confianza para iterar rápido? Diseñamos e implementamos el pipeline desde cero.
Habla con el equipo