Runtime Environments
Deploy once, run everywhere. One model for dev, QA, and production.
The Problem
Traditional approaches to multi-environment deployment require either:
- Separate models per environment - duplication, drift, maintenance burden
- Configuration files - error-prone, not type-safe, easy to deploy wrong config
- Hard-coded environment logic - brittle, unmaintainable, scattered throughout code
None of these approaches scale. They create risk, slow down deployments, and make onboarding new environments painful.
The Solution: RuntimeEnvironments
RuntimeEnvironments (RTEs) use logical references in your model that are resolved at deployment time via EnvironmentMaps. One model, multiple environments, zero duplication.
How It Works
1. Define EnvironmentMaps
Teams define mappings from logical names to physical resources per environment:
team.add(EnvironmentMap(
keyword="prod",
dataContainers={
frozenset(["customer_db"]): PostgresDatabase(
"CustomerDB_Prod",
hostPort=HostPortPair("prod-db.internal", 5432),
databaseName="customer_production",
productionStatus=ProductionStatus.PRODUCTION
)
},
dtReleaseSelectors={
"transformer_version": VersionPatternReleaseSelector(
"V*.*.*-prod", ReleaseType.STABLE_ONLY
)
}
))
team.add(EnvironmentMap(
keyword="dev",
dataContainers={
frozenset(["customer_db"]): PostgresDatabase(
"CustomerDB_Dev",
hostPort=HostPortPair("dev-db.internal", 5432),
databaseName="customer_dev",
productionStatus=ProductionStatus.NOT_PRODUCTION
)
},
dtReleaseSelectors={
"transformer_version": VersionPatternReleaseSelector(
"V*.*.*-dev", ReleaseType.STABLE_ONLY
)
}
))
2. Use Logical References
Your model uses logical references - no environment-specific values:
Datastore(
"CustomerData",
capture_metadata=SQLSnapshotIngestion(
EnvRefDataContainer("customer_db"), # Logical reference
CronTrigger("Hourly", "0 * * * *"),
Credential("db_reader", CredentialType.USER_PASSWORD)
),
datasets=[...]
)
DataTransformer(
name="MaskCustomerPII",
code=PythonCodeArtifact(
VersionedRepository(
GitHubRepository("myorg/transformers", "main"),
EnvRefReleaseSelector("transformer_version") # Logical reference
)
),
...
)
3. Resolution at Deployment
When each RTE deploys, logical references resolve to environment-specific values:
| Logical Reference | Prod Resolution | Dev Resolution |
|---|---|---|
EnvRefDataContainer("customer_db") |
prod-db.internal:5432 | dev-db.internal:5432 |
EnvRefReleaseSelector("transformer_version") |
V1.0.0-prod | V1.1.0-dev |
Safety Features
The linter validates your model at PR time, preventing common mistakes:
Reference Validation
Every EnvRefDataContainer key must exist in the team's EnvironmentMaps. Missing references fail at lint time, not runtime.
Production Status
Production RTEs only reference production-status DataContainers. Dev/QA RTEs only reference non-production. No accidental cross-contamination.
Complete Coverage
Every RTE must have corresponding EnvironmentMaps for all teams it uses. Incomplete configurations are caught before deployment.
Deployment Workflow
- Develop - Teams develop and test with dev RTE (watches for
-devtags) - Tag for QA - Tag model with
V1.2.3-qafor QA environment testing - QA validates - QA RTE picks up tag, deploys with qa EnvironmentMaps
- Tag for Prod - After validation, tag
V1.2.3-prodfor production - Prod deploys - Prod RTE picks up tag, deploys with prod EnvironmentMaps
Each RTE's infrastructure updates independently within 5-15 minutes of tagging.
Benefits
Single Source of Truth
One model definition for all environments. No duplication, no drift.
Type-Safe Resolution
IDE autocomplete, compile-time validation, no string typos.
Independent Versions
Dev on V1.3.0, prod on V1.2.5. Each environment at its own pace.
Easy Onboarding
New environments just add EnvironmentMaps. No model changes required.
Real-World Impact
- No environment-specific branches - single codebase, single PR workflow
- No manual config edits - all configuration in type-safe Python
- No accidental prod/dev mixing - validated at PR time
- Faster deployments - tag and go, no manual coordination