I wrote progfiguration in large part out of a frustration with Ansible roles. One of its central theses is that for sufficiently advanced configuration management, writing code in a regular imperative programming language beats writing a declarative configuration document or DSL.
The big caveat in that thesis is sufficiently advanced configuration management. Declarativity is easier to get started with, and easier to read for simple cases. My favorite example where declarativity shines is in simple repetitive resource declaration, like a CloudFormation template for DNS records.
However, as the systems I want to configure become more complex, this declarativity starts to impose painful costs. Some examples:
When I’m working in a declarative system, I always miss a good interactive debugger.
One of my favorite Python tricks is to add a
that starts the interactive debugger on an unhandled exception
idb_excepthook in my Python script template.)
Declarative systems are stuck with printf debugging if they can do even that.
It’s available with
progfiguration apply --debug.
(See the progfigsite command reference documentation.)
progfiguration, I never find myself thinking something like
“I’ll have to generate the config from templates before I can apply it”.
I can still use templating where it makes sense,
like when deploying files to a remote system,
but this came for free because the configuration is just a Python program.
I can use
or install and import something more powerful like jinja2
if I need it.
I also never find myself writing configuration that includes deploying a shell script and immediately running it. Instead, the functionality of the shell script can be part of my configuration, because my configuration is just Python.
Finally, I also get compartmentalization from temporary variables and real functions for free,
progfiguration configurations are just Python code.
Any programmer can cram massive functionality into a single line,
but the only use for this is showing off and obfuscated programming competitions –
or, as it turns out, declarative configuration like Terraform and CloudFormation.
I’ve done some horrible things inside a Terraform resource definition
because I was building a long query string for a monitoring platform
and it has no way to iteratively build a string variable.
By the same token, the cheap function calls that regular imperative languages like Python have let me break off small blocks of larger processes into more understandable named sets of tasks. This makes configurations made up of much smaller steps more readable. Declarative systems may have something like this, but it’s not always flexible enough to be worth it.
So that’s the theory.
What we gained from the heavy declarative paradigms
isn’t always worth it in complex cases.
I’m already enjoying working with
my personal server configurations.
If any of this resonates,
check it out.
Postscript: I would be remiss if I didn’t link to https://noyaml.com/.