Environments (dev / staging / prod)

Platform Engineer + CNCF Ambassador, AWS community builder. I design scalable cloud-native platforms and love making teams faster and safer.
"Solo necesito cambiar la variable ENV a 'prod'" - Spoiler: No
Ayer: cuándo ejecutar tus pipelines. Hoy: dónde ejecutarlos.
Dev, staging, producción. Lo sabes de software tradicional.
Pero en ML, los ambientes son mucho más que código.
Historia:
Data Scientist: "Ya terminé el modelo, súbelo a producción" Yo: "¿Lo probaste en staging?" DS: "No tenemos staging para ML" Yo: "¿Cómo no?" DS: "Es el mismo código, solo cambias la conexión a la base de datos"
3 semanas después:
El modelo en producción degradó un 15%.
¿Por qué? Porque los datos de producción eran diferentes a los de desarrollo.
Nadie lo había probado con datos reales de prod antes del deploy.
El problema fundamental:
En software tradicional:
if ENV == "dev":
db = "localhost:5432"
elif ENV == "prod":
db = "prod-db:5432"
Eso es todo. Mismo código, diferentes configuraciones.
En ML:
if ENV == "dev":
db = "localhost:5432"
dataset = "sample_1000_rows.csv" # Subset
model = "model_v1_local.pkl"
features = ["basic_feature_set"]
monitoring = False
elif ENV == "prod":
db = "prod-db:5432"
dataset = "s3://prod/full_dataset/" # 500GB
model = "s3://models/prod/v2.3.1/"
features = ["basic + advanced + experimental"]
monitoring = True
drift_detection = True
A_B_testing = True
No es solo configuración. Son assets completamente diferentes.
Los 3 tipos de separación necesarios:
1. Separación de Datos
DEV:
├── datasets/
│ ├── sample_1k.parquet # Subset para desarrollo
│ ├── synthetic_data.parquet # Datos sintéticos
│ └── anonymized_prod.parquet # Muestra anonimizada
STAGING:
├── datasets/
│ ├── prod_snapshot.parquet # Snapshot de prod (última semana)
│ └── validation_set.parquet # Set de validación fijo
PROD:
├── datasets/
│ ├── realtime_stream/ # Datos reales en tiempo real
│ └── historical/ # Todo el histórico
¿Por qué?
Dev: necesitas iterar rápido (1k rows, no 500GB)
Staging: necesitas validar con datos realistas
Prod: necesitas todos los datos reales
No puedes entrenar en dev con 1k rows y esperar que funcione en prod con 500GB.
2. Separación de Modelos
# Model Registry por ambiente
DEV:
- Experiments: 1000+ modelos
- No validation estricta
- Cualquiera puede registrar
STAGING:
- Candidates: ~10 modelos pasando validación
- Métricas > baseline required
- Review antes de promover
PROD:
- Active: 1-2 modelos serviendo tráfico
- A/B testing entre versiones
- Rollback automático si falla
```
**El flujo:**
```
DEV → train 100 experiments
→ pick best
→ promote to STAGING
STAGING → validate con prod-like data
→ compare vs prod baseline
→ stress test
→ promote to PROD (if approved)
PROD → shadow mode (sin tráfico real)
→ canary deploy (5% tráfico)
→ gradual rollout (100%)
3. Separación de Features
# Feature Store por ambiente
class FeatureStore:
def get_features(self, env):
if env == "dev":
return [
"basic_features",
"experimental_features" # Puedes romper cosas
]
elif env == "staging":
return [
"basic_features",
"tested_features" # Solo features validadas
]
elif env == "prod":
return [
"basic_features",
"tested_features",
"monitored_features" # + monitoring activo
]
Ejemplo real de pipeline multi-ambiente:
# Pipeline que funciona en todos los ambientes
@pipeline
def training_pipeline(env: str):
# 1. Datos diferentes por ambiente
if env == "dev":
data = load_sample_data(n_rows=1000)
elif env == "staging":
data = load_snapshot("prod_last_week")
else: # prod
data = load_full_dataset()
# 2. Validación diferente por ambiente
if env == "dev":
# Validación light
assert data is not None
elif env == "staging":
# Validación completa
validate_schema(data)
validate_distributions(data)
else: # prod
# Validación + alerting
validate_and_alert(data)
# 3. Training con recursos diferentes
if env == "dev":
model = train(data, resources="small")
else:
model = train(data, resources="large")
# 4. Evaluation con thresholds diferentes
metrics = evaluate(model, data)
if env == "dev":
# Cualquier resultado está OK
register_model(model, env="dev")
elif env == "staging":
# Debe superar baseline
if metrics.accuracy > BASELINE:
register_model(model, env="staging")
else: # prod
# Debe superar baseline + pasar A/B test
if metrics.accuracy > BASELINE:
deploy_canary(model)
if canary_metrics_ok():
promote_to_prod(model)
La infraestructura también cambia:
DEV:
- Local machine o VM compartida
- CPU only (GPUs caras para dev)
- Sin redundancia
- Puede fallar sin problema
STAGING:
- Cluster dedicado
- Similar a prod (pero más pequeño)
- Con monitoring (pero no alerting crítico)
- Datos de prod pero anonymizados
PROD:
- Cluster de alta disponibilidad
- GPUs/recursos necesarios
- Redundancia + auto-scaling
- Monitoring + alerting 24/7
- Backup + disaster recovery
Los errores más comunes:
ERROR 1: "No tenemos staging para ML"
Resultado: Deploy directo a producción
Primera vez que ves datos reales: en prod
Cuando algo falla: afectas usuarios
ERROR 2: "Staging usa los mismos datos que dev"
Resultado: Validas con sample de 1k rows
Funciona en staging (1k rows)
Falla en prod (500GB) por memory/performance
ERROR 3: "Prod y staging comparten infraestructura"
Resultado: Experimento en staging consume recursos
Prod se degrada porque staging le robó memoria
Incident a las 3am
ERROR 4: "Un solo model registry para todo"
Resultado: 1000 experimentos mezclados con modelos de prod
Nadie sabe qué modelo está en producción
Rollback imposible
Arquitectura recomendada:
┌─────────────────────────────────────────────────┐
│ DEV │
│ - Local/shared VM │
│ - Sample data (1k-10k rows) │
│ - Rapid iteration │
│ - No strict validation │
└─────────────┬───────────────────────────────────┘
│ promote best experiment
▼
┌─────────────────────────────────────────────────┐
│ STAGING │
│ - Prod-like cluster (smaller) │
│ - Prod snapshot data (last week/month) │
│ - Full validation pipeline │
│ - Metrics vs prod baseline │
└─────────────┬───────────────────────────────────┘
│ if approved
▼
┌─────────────────────────────────────────────────┐
│ PROD │
│ - HA cluster │
│ - Real-time data │
│ - Shadow → Canary → Full rollout │
│ - 24/7 monitoring │
└─────────────────────────────────────────────────┘
Implementación práctica:
Opción 1: Namespaces (si usas K8s)
# dev namespace
namespace: ml-dev
resources:
memory: 4GB
cpu: 2
# staging namespace
namespace: ml-staging
resources:
memory: 16GB
cpu: 8
# prod namespace
namespace: ml-prod
resources:
memory: 64GB
cpu: 32
replicas: 3
Opción 2: Cuentas separadas (AWS/GCP/Azure)
dev-account/
├── s3://dev-ml-data/
├── dev-model-registry
└── dev-feature-store
staging-account/
├── s3://staging-ml-data/
├── staging-model-registry
└── staging-feature-store
prod-account/
├── s3://prod-ml-data/
├── prod-model-registry
└── prod-feature-store
Opción 3: Prefixes (si no puedes separar infraestructura)
CONFIGS = {
"dev": {
"data_bucket": "ml-data",
"data_prefix": "dev/",
"model_registry": "models/dev/",
},
"staging": {
"data_bucket": "ml-data",
"data_prefix": "staging/",
"model_registry": "models/staging/",
},
"prod": {
"data_bucket": "ml-data-prod", # Bucket separado
"data_prefix": "prod/",
"model_registry": "models/prod/",
}
}
Checklist de separación de ambientes:
Datos:
[ ] Dev usa sample/synthetic data
[ ] Staging usa snapshot de prod
[ ] Prod usa datos reales
[ ] Datos de prod nunca en dev (privacidad)
Modelos:
[ ] Model registry por ambiente
[ ] Promotion workflow claro (dev → staging → prod)
[ ] Rollback fácil en prod
[ ] Experiments solo en dev/staging
Features:
[ ] Feature store por ambiente
[ ] Features experimentales solo en dev
[ ] Monitoring de features en prod
Infraestructura:
[ ] Recursos separados por ambiente
[ ] Prod no comparte recursos con dev/staging
[ ] Credenciales diferentes por ambiente
[ ] Logs/metrics separados
Proceso:
[ ] CI/CD diferente por ambiente
[ ] Approvals para promote a prod
[ ] Testing en staging antes de prod
[ ] Canary/shadow deploys en prod
Mi recomendación como Platform Engineer:
Fase 1 - Mínimo viable:
- Dev: local con sample data
- Prod: cluster con datos reales
- Deploy: manual con checklist
Fase 2 - Agregar staging:
- Staging: snapshot semanal de prod
- Validation automática en staging
- Deploy: semi-automático con approval
Fase 3 - Full separation:
- 3 ambientes completamente separados
- CI/CD automatizado
- Canary deploys
- A/B testing
No intentes hacer Fase 3 desde día 1.
Mañana:
Ya sabes ejecutar pipelines en diferentes ambientes.
Pero hay un problema: todo esto es código que vive en algún lado.
¿Cómo conectas Terraform, Helm, K8s, GitOps con MLOps?
Infrastructure as Code para ML. Y por qué no es solo terraform apply.
Tu turno:
¿Cuántos ambientes tienen para ML?
¿O es "dev es mi laptop y prod es... también mi laptop"?


