Skip to main content

Command Palette

Search for a command to run...

Environments (dev / staging / prod)

Updated
7 min read
Environments (dev / staging / prod)
A

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"?


More from this blog

Alejandro Parra's Blog

16 posts