If you feel the need to put code in your runtime config, that means your design sucks.
If you feel the need to put code in your runtime config, that means your design sucks.
For instance: I just brought up a new Factorio game server yesterday, just by adding a single line to my jsonnet configuration machinery: https://gerrit.hackerspace.pl/c/hscloud/+/246 . This in turn brought up a bunch of new k8s resources, including some persistent volumes, a deployment, and a loadbalancer service, after running a single `kubecfg update` command.
In your scenario, it's more like provisioning of infrastructure and in that case - configuration is the code, so I can see where you are coming from.
I mostly write financial software so the use case is usually closer to classical single machine applications. And in that case, I prefer simple TOML.
edit: Now I understand that being a plugin it must have proper abstraction, but ad-hoc logic do exist.
> ... but ad-hoc logic do exist.
True. I don't disagree, no tool is right for every job.
Should it be? If an application doesn't have configuration, then the logical thing to do would be hard code all the values it needs in the code itself. This could be done with a number of variables/constants defined in the entrypoint method for each value needed, or defining a data structure that contains all the values needed in the same method.
Reading a config file and serializing it into those variables or data structure shouldn't be a hard problem. Formats like YAML or json allow for deserailizing the string into a data structure (though you have to add your own validation). ini format files can be done the same way in a loop, but still require adding validation.
The validation could be based on data type or allowed values. While that's rather tedious, it's not complex.
I would look at Terraform and beg to differ - they started with a simple stringly typed language and had to build a typed language. Still no support for conditionals, so everyone has to abuse the only bit of control flow on offer - ternary variables and counts.
CircleCI is just as bad - it's a nice yaml format until you want a conditional step in workflow that's conditional on something other than the branch name.
It's very risky to settle on a static config at design time since the users will find a need. How would you cater to that user without offering a programming interface to configure it?
No, but static config files won't give you that proof either. Unless I'm misunderstanding your point?
At least if I write build scripts in e.g. ts-node I can use the type system to provide some modicum of safety, which seems like a plus.
If your tooling requires static configs, but you really need dynamism, you’re stuck with dynamically generated static configs, which are even worse than a dynamic config.
The problem here is of course how not to make your DSL suck, which Jenkins' did not avoid. But if you're lucky, your language of choice already has a few libraries implementing embedded DSLs for configuration, and it's quite possible that at least one of them is actually usable.
See ansible and the YAML config files.
In this case, a real language would have been a better choice.
Also, sometimes you need to generate config files. In that case, templating is not always a good choice.
Well, the choice is Java, or Java and yaml.
> The amount of complexity introduced just for configuration is insane.
Again, it's Java, vs. Java + Spring + env + application.yaml + application-$profile.yaml (in my case usually).
Once config constants enter your language, they're type-checked. Tree-structures suddenly need to be well-formed. No more trying to remember if you force https-only with 'enforceHttps', 'enforceHTTPS', 'enforceTLS', 'enforceSSL'. (= True,= true, :true, :1, :on, = "on")
> no regressions under different configurations.
It's great for that! Instantiate three copies of your service from Main - say, one backed by a hashmap, one connecting to a live db, one connecting to your docker-compose db, etc. Run the the same set of commands (in the same language your tests are already written in) and assert the outputs.
I don't want to do step 2 unless I can do step 3.
It just so happens that type-checking happens during recompilation, and testing happens after.
Well, the same thing can be said about pretty much anything. I've seen good and bad Django setups, so what?. The point is configuration as code works well if you know what you are doing. A config minimal language (toml, yaml, etc) may also work well as long as you don't need something that's not supported by it, and as long as you don't need to debug it.
If you are willing to accept the defaults you aren't required to write any configuration.
Hello from every plugin system ever. True fact: not all configuration is basic.
And usually these things are implemented with way more forethought.
I actually agree with you. Elsewhere in the comments I say that the OP article failed to name this properly: https://news.ycombinator.com/item?id=22788696
But it is what the article is really discussing.
https://www.gnu.org/software/make/manual/html_node/Functions...
Naturally, trying to do anything even slightly complex with it quickly leads to an unreadable cacophony of dollar signs, backslashes, and soft and hard tabs (you need both!). Just like the modern YAML-based systems, Make’s language wasn’t originally intended to be a full scripting language, but had scripting bolted on because people needed it. If only it had chosen to embed a real programming language instead, you could have the functionality without sacrificing readability.
It's ad-hoc because it was built gradually on top of the original Unix make, which supported rules and variables but not any of the more advanced features. This history explains many of its quirks and relatively extreme limitations as a programming language, such as:
- Expressions must be all on one line, except within `define` blocks or if you use backslashes to join lines together (which tends to result in a lot of backslashes).
- Hard tabs are required to introduce commands in rules but are (mostly) banned outside of them, so if you want any kind of indentation for an if block or user-defined function, you need to use spaces instead.
- User-defined functions. Calling them is merely more verbose than calling builtin functions (you need to invoke the `call` builtin function). Defining them is worse: function parameters are numbered rather than named, similar to a shell, but in a shell you can reassign them to named variables, whereas Make's expansion rules make that awkward to achieve.
- And of course, no types other than strings (a limitation partly shared by shells, but most shells do have arrays, even if they're awkward to use).
Admittedly, most of that gradual evolution happened a long time ago, and GNU Make has been relatively stable as a language since then. It's not "ad-hoc" in the sense that it's constantly changing or ill-defined. But that stability also means that it never outgrew the limitations of its original design.
Some research I did for fun:
The oldest version of GNU Make I can find [1], from 1988, already had a handful of functions, including `foreach`, `filter`, `patsubst`, etc., as well as support for multi-line definitions (`define`/`endef`). `call`, on the other hand, didn't appear until 1999. Amusingly, its initial implementation came with a comment [2] bemoaning the lack of "nested lists and quotes", and even semi-seriously proposing that GNU Guile (Lisp implementation) be integrated into Make, in order to let Makefile authors use a 'real' programming language. No fewer than 13 years later, that proposal actually became a reality; unfortunately, it was an optional feature which distributions tended to leave disabled, ergo Makefile authors could not rely on it being present, so the feature has seen approximately zero use.
[1] within https://gcc.gnu.org/pub/binutils/old-releases/binutils-1988-...
[2] https://github.com/mirror/make/blob/c4353af3f9b143b213644c09...
Also, Bash is a scripting environment (sort of), and VimL is a solid part of vim. These are scenarios, where you don't add undue complexity or bugginess to the software. But personally, I wouldn't kid myself and pretend that I can write applications as solid as bash or vim.
Makefiles are just bash scripts with dependency resolutions. So again, scripts and not configuration.