Backward-compatible config with validation_alias

Renaming a configuration variable is a breaking change — unless the model accepts both the old and new name during the transition. validation_alias with AliasChoices is the pydantic v2 mechanism. This page applies it, extending Schema Evolution & Versioning.

Problem 1: a hard rename

# ANTI-PATTERN: old name stops working the instant you rename the field
class Settings(BaseSettings):
    database_url: str        # was DB_DSN; old deploys inject DB_DSN -> error

Every process still injecting DB_DSN fails to construct the model.

Problem 2: the v1 env= idiom that moved

# ANTI-PATTERN: v1 style, removed in v2
database_url: str = Field(env="DB_DSN")   # 'env' is not how v2 aliases work

Field(env=...) is a v1 idiom; v2 uses validation_alias.

Secure implementation

# config/aliases.py
from pydantic import AliasChoices, Field
from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(populate_by_name=True, extra="forbid")

    # Accept the new name first, then the legacy name, during the window.
    database_url: str = Field(
        validation_alias=AliasChoices("DATABASE_URL", "DB_DSN"),
    )

settings = Settings()
# Both `DATABASE_URL=...` and `DB_DSN=...` populate database_url during migration.

AliasChoices("DATABASE_URL", "DB_DSN") accepts either variable, so old and new deployments both validate. After every environment switches to DATABASE_URL, drop DB_DSN from the alias in a later release.

Gotchas & version-specific behaviour

  • List the new name first in AliasChoices — it is preferred when both are set.
  • populate_by_name=True lets the field be set by its Python name as well as the aliases.
  • extra="forbid" means once you remove the old alias, the old variable becomes an error — stop injecting it first.
  • This replaces v1’s Field(env=...); there is no env= argument in v2.

Production parity checklist

  • The rename ships with both names accepted via AliasChoices.
  • The new name is listed first.
  • Telemetry confirms the old name is unused before the alias is removed.
  • CI validates the model with both the old and new variable set.
  • Removal of the old name is a separate, later release.

Conclusion

validation_alias with AliasChoices turns a breaking rename into a no-downtime, two-name window. For sequencing renames, promotions, and removals together, see Handling Breaking Changes in Production Config Schemas.