Don’t Repeat Yourself: Keeping DRY with Dynamically-Generated Configs 🌞#

DRY – Don’t Repeat Yourself – is a principle of software development that cautions against repetitive software patterns and workflows. One of the major benefits of using hydra-zen’s config-creation functions is that they help us abide by the DRY principle. On the other hand, manually writing configs for your code will often leave you soaking WET (Writing Everything Twice).

To develop an intuition for these claims, let’s suppose that we want to configure the following class.

Contents of vision/model.py#
from typing import Tuple

# This is a class that we want to configure
class DNN:
    def __init__(
        self,
        input_size: int,
        output_size: int,
        layer_widths: Tuple[int, ...] = (5, 10, 5),
        device: str = "cpu",
    ):
        pass

We’ll compare the processes of configuring DNN using a YAML-file, using a hand-written dataclass, and using hydra_zen.builds().

Statically-Defined Configs are WET#

Manually writing a structured config for DNN entails hard-coding its current import-path as a string as well as explicitly mirroring its signature:

Manually configuring DNN with a dataclass#
from dataclasses import dataclass

@dataclass
class Builds_DNN:
    input_size: int
    output_size: int
    layer_widths: tuple[int, ...] = (5, 10, 5)
    device: str = "cpu"
    _target_: str = "vision.model.DNN"
Manually configuring DNN with a YAML file#
_target_: vision.model.DNN
input_size: ???
output_size: ???
layer_widths:
- 5
- 10
- 5
device: cpu

Doing this once isn’t so cumbersome, but consider that any time we modify DNN by:

  • changing its location (e.g. move it to vision.classifiers.DNN)

  • updating any of its parameters’ names, default values, or annotations

  • adding or removing a parameter to/from its signature

then we will need to mirror this change in our configuration as well. I hope you brought your towel, because things are getting WET 🌊.

Having to manually sync our configs with our code is not only tedious but it also creates a hot-spot for mistakes and bugs.

Generating Configs with builds Keeps Us DRY#

We can stay nice and DRY by dynamically generating our configurations with builds().

Configuring DNN using builds()#
from hydra_zen import builds
from vision.model import DNN

ZenBuilds_DNN = builds(DNN, populate_full_signature=True)

Here we don’t need to worry about repeating ourselves in order to keep our config in sync with our code: the config (complete with type annotations and default values) is automatically and dynamically generated for us at runtime! 🌞

Additionally, any configured parameters that we do manually specify via builds() will be validated against the signature of DNN as the config is being created. Thus typos and mistakes will be caught fast and early - before we even launch our app.