The Configuration Complexity Clock (2012)
mikehadlow.blogspot.com
mikehadlow.blogspot.com
Any sufficiently complicated C or Fortran program contains an ad hoc,
informally-specified, bug-ridden, slow implementation of half of Common Lisp.
I find it specifically applicable to evolving configuration systems.> It can also be interpreted as a satiric critique of systems that include complex, highly configurable sub-systems.
Hard code -> configurable/scriptable/DSL -> Hard code.
The nuance I have discovered is you can do the "hard code" in a wide variety of ways, some much more amenable to non-developers than others.
Our latest stack goes back to hardcoding because someone can edit the source directly in GitHub and have an updated app deployed automatically for evaluation within 2-3 minutes. Another simple compare/merge operation in the web UI to move between environments. No visual studio, no git CLI, nothing. Simply make desired edits in browser, click the green button & check the URL again when the bell dings.
Devops wasnt quite this way in 2012. GitHub+Azure have replaced an entire section of my org chart in 2023. The key for us this time around the dial is to separate "nerd" code from "business" code. Not going to be perfect ever but we can get it very close with intent and all of the experience we built up so far.
The configurable/scriptable/DSL stuff definitely does work, but once you get to a certain magic # of configuration points, hard code begins to emerge as superior again. Configurable wins in the middle of complexity. Hard code wins at the extremes.
There's different kinds of factories stacked atop one another. There's Guide dependency injection so that the factories are able to reach into config classes and copy custom properties into the actual Kafka constructors right before instantiation.
It's just an extra Rube Goldberg machine in front of Kafka instantiation which doesn't prevent the caller from needing to know the implementation details.
The issue is - which language?
Ideally you want something embeddable, sandboxed, with good tool support, static types/hints and "normal" (not aggressively functional, 0 based indices etc.).
The only language that remotely ticks those boxes is Starlark. That's probably what I would use in future if I had this issue.
I've looked into many other options (Jsonnet, Dhall, Cue, Python, Deno, Lua, Rhai, etc.) but they all fall down in some way.
It's definitely not the worst. It's just a little too weird to be able to write it without actually learning it, which is what you want in a config language. And 1-based indices just shows poor taste.
It also isn't a "full" programming language - it's not Turing complete, which has no real benefit as far as I can tell.
Tbh it's probably one of the better options, but I can predict how my co-workers would react if I tried to get them to use it. I think they would be more accepting of Starlark.
It's inherited from OCaml/Haskell/etc.. I think staying consistent with that works better than trying to convert it to Algol-style, although I guess the jury is still out on new languages that do that.
> It also isn't a "full" programming language - it's not Turing complete, which has no real benefit as far as I can tell.
The most immediate practical benefit is that your configurations can't go into an infinite loop. Depending on your deployment model that can help avoid outages. In theory you could still create a configuration that would take a very long time to evaluate, but in practice that's a lot rarer than creating one that takes forever.
Yeah I'm not sure I buy it. There are much easier ways to prevent accidental infinite loops than artificially making your language Turing incomplete.
Probably the most obvious is a compute budget (i.e. a timeout but deterministic).
Then how come none of them are popular in config languages? Starlark follows the same approach of being non-Turing-complete; all the other config languages I know of are either non-Turing-complete or accept the risk of running forever.
> Probably the most obvious is a compute budget (i.e. a timeout but deterministic).
That tends to have worse problems e.g. seemingly "safe" refactors can now break your config.