Skip to content

Why Not Hydra?#

Hydra (by Meta Research) is a powerful configuration framework built for ML experimentation. Its composition system, CLI overrides, parameter sweeps, and plugin architecture make it the go-to choice for training pipelines with many hyperparameter combinations.

The trade-off is scope: Hydra is a framework that takes over your entry point, working directory, and logging. For application configuration — web services, data pipelines, microservices — this is too much. dature is a library that loads config and returns a dataclass. Nothing else.

Side-by-Side#

Hydra dature
Approach Framework — takes over entry point, working dir, logging Library — loads config, returns a dataclass
Config definition YAML files + @dataclass structured configs stdlib @dataclass — single source of truth
Formats YAML only YAML (1.1/1.2), JSON, JSON5, TOML (1.0/1.1), INI, .env, env vars, Docker secrets
Env variables oc.env resolver; no .env support First-class: env vars, .env files, ${VAR:-default} expansion in all formats + file paths
CLI overrides Built-in: python app.py db.port=3306 + tab completion No CLI
Composition Config groups, defaults list, package overrides Multi-source merge with explicit strategies
Parameter sweeps Built-in multirun + sweeper plugins (Ax, Optuna, etc.) No — not a use case
Object instantiation instantiate() — creates objects from config with DI No — config loading only
Variable interpolation OmegaConf ${path.to.key} + custom resolvers ${VAR:-default} env expansion in all formats + file paths
Validation Basic type checking via OmegaConf Annotated validators, root validators, custom validators
Type support Primitives, enums, basic containers. No Union types Primitives, datetime, IPv4Address, Enum, SecretStr, ByteSize, Union types, and more
Error messages OmegaConf exceptions Human-readable: source file, line number, context snippet
Secret masking No Auto-masks secrets in errors and logs
Debug / audit Output dir with saved config + logs debug=True — which source provided each value
Plugin system Sweepers, launchers, config sources, search path Custom loaders via Source subclass
Dependencies hydra-core + omegaconf + antlr4-runtime adaptix (pure Python)
Config result OmegaConf.DictConfig (dict-like wrapper) Your actual @dataclass instance
Maintenance Last release: Dec 2022. Acknowledged as unmaintained by other Meta teams Active development

YAML-Only vs. Real-World Formats#

Hydra reads YAML exclusively. You can reference env vars via OmegaConf's ${oc.env:VAR} resolver, but there's no native support for:

  • .env files — ubiquitous in local development. Not supported.
  • TOML — the standard for Python packaging (pyproject.toml), increasingly used for app config. Not supported.
  • JSON / JSON5 — common in web services and JavaScript-adjacent tooling. Not supported.
  • INI — legacy format still common in enterprise. Not supported.

dature handles all of these out of the box:

config = dature.load(
    dature.Yaml12Source(file=SOURCES_DIR / "hydra_defaults.yaml"),
    dature.Toml11Source(
        file=SOURCES_DIR / "hydra_config.toml",
        skip_if_broken=True,
    ),
    dature.EnvSource(prefix="APP_"),
    schema=Config,
)

OmegaConf Is Not a Dataclass#

Hydra wraps your config in OmegaConf.DictConfig — a special container that looks like a dataclass but isn't:

# Hydra
@hydra.main(config_path="conf", config_name="config")
def app(cfg: DictConfig) -> None:
    # cfg is OmegaConf DictConfig, not your dataclass
    # isinstance(cfg, YourConfig) → False
    # No IDE autocompletion for fields
    # No type safety at runtime
    pass

dature returns your actual dataclass:

config = dature.load(
    dature.Yaml12Source(file=SOURCES_DIR / "hydra_defaults.yaml"),
    schema=Config,
)
assert isinstance(config, Config)
# Full IDE support, type safety, __post_init__ works

The @hydra.main Problem#

Hydra takes over your application entry point. The @hydra.main decorator:

  • Changes your working directory — every run creates a new output dir. Your relative paths break.
  • Controls logging — replaces your logging configuration.
  • Manages the output directory — creates outputs/YYYY-MM-DD/HH-MM-SS/ structure whether you want it or not.

This is useful for ML experiments. For a web service, it's hostile.

dature is a library, not a framework — it loads config and returns a dataclass. No side effects, no directory changes, no logging hijack.

Weak Validation#

OmegaConf validates types but not values. Need to check that a port is between 1 and 65535? That a URL is valid? That a string matches a pattern?

# Hydra/OmegaConf — no built-in value validation
@dataclass
class Config:
    port: int = 8080  # Any int is accepted, even -1

dature uses Annotated validators:

"""dature vs Hydra — Annotated validators for value constraints."""

from dataclasses import dataclass
from pathlib import Path
from typing import Annotated

import dature
from dature import V

SOURCES_DIR = Path(__file__).parent / "sources"


@dataclass
class Config:
    host: str
    port: Annotated[int, (V > 0) & (V < 65536)] = 8080


dature.load(
    dature.Yaml12Source(file=SOURCES_DIR / "hydra_validators_invalid.yaml"),
    schema=Config,
)
Error
  | dature.errors.exceptions.DatureConfigError: Config loading errors (1)
  +-+---------------- 1 ----------------
    | dature.errors.exceptions.FieldLoadError:   [port]  Value must be greater than 0
    |    ├── port: -1
    |    │         ^^
    |    └── FILE '{SOURCES_DIR}hydra_validators_invalid.yaml', line 2
    +------------------------------------

Plus root validators for cross-field checks, custom validators, and standard __post_init__.

And when validation fails, dature underlines the exact problematic value:

Config loading errors (1)

  [port]  Value must be greater than 0
   ├── port: -1
   │         ^^
   └── FILE 'config.yaml', line 2

Compare with OmegaConf's error for a wrong type:

omegaconf.errors.ValidationError: Value 'abc' of type 'str'
could not be converted to Integer
    full_key: port
    object_type=Config

No file, no line number, no context.

What Hydra Does Better#

To be fair — Hydra has powerful features designed for ML experimentation that dature doesn't try to replicate:

  • Config composition — defaults list, config groups, and package overrides let you assemble complex configs from reusable fragments. This is Hydra's killer feature for ML pipelines with many model/dataset/optimizer combinations.
  • CLI overridespython train.py model.lr=0.001 db=postgresql with tab completion. No code changes needed to override any config value.
  • Parameter sweeps — built-in multirun mode with sweeper plugins (Ax, Optuna, Nevergrad) for hyperparameter optimization.
  • Object instantiationinstantiate() creates Python objects directly from config with recursive dependency injection.
  • Variable interpolation — OmegaConf's ${path.to.key} cross-references within config, plus custom resolvers for computed values.
  • Plugin system — extensible launchers (local, Submitit, Ray), sweepers, and config sources.
  • Output management — automatic output directories with saved config, logs, and overrides for each run. Essential for ML experiment tracking.
  • Community ecosystemhydra-zen (Pythonic config generation), lightning-hydra-template, DVC integration.

Maintenance#

Hydra's last release (1.3.0) was in December 2022. Issues accumulate without maintainer response. Other Meta projects have acknowledged it as unmaintained. The hydra-zen community project fills some gaps, but the core framework is effectively frozen.

dature is under active development with a clear roadmap: remote sources (Vault, AWS SSM), built-in caching, and config watching.

When to Use Hydra#

Hydra is still a reasonable choice if:

  • You're running ML experiments with many hyperparameter sweeps
  • You need config composition via defaults lists and config groups
  • You want CLI overrides with tab completion
  • You use instantiate() to build object graphs from config
  • You need output directory management for experiment tracking
  • You're already deep in the Hydra ecosystem (hydra-zen, lightning-hydra-template)

For application configuration — dature.