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:
.envfiles — 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,
)
| 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 overrides —
python train.py model.lr=0.001 db=postgresqlwith 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 instantiation —
instantiate()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 ecosystem —
hydra-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.