4 ms·
> If your configs are really complicated enough that they need to be generated at the time your main program runs, to me that means your code needs refactoring.
by danShumway 7y ago
> If your configs are really complicated enough that they need to be generated at the time your main program runs, to me that means your code needs refactoring.
I'm not sure that's true. In fact, I would go so far as to say for some programs, it's the opposite.
In the example I describe above (forgetting about lambda functions or anything like that) I'm shipping a program that consumes a small set of very rigid input options. I consume a single array of strings that need to resolve to file paths.
That's incredibly easy to validate: that's the type of limited input where I can be pretty certain that my program will not have any bugs or vulnerabilities around the input. It's also an incredibly simple API to teach to users, it's literally one option. And importantly, my program is still pretty powerful -- you can do whatever you need to do to get that file list without me needing to extend what input I accept.
I can't think of a good way to convert that to a pure JSON/YAML config without either making my software more error prone or making my software much less useful.
If returning lambda functions from a config file scares you, forcing the config to be a static object literal but allowing that object literal to be generated at runtime will still (often) result in a runtime that is less error prone, easier to validate, and easier to debug when something goes wrong. This is because at the end of the user's dynamic config, they'll still be returning a small, easy-to-understand object literal -- whatever logic was used to build it.
If instead you put that logic in your main program, and you accept globs for files, and you start doing complicated things like allowing arrays of multiple globs and file excludes -- well, suddenly you have a big chunk of parsing logic in your main program that end-users can't debug when something goes wrong. It becomes harder to validate whether or not you have some bug around the order that patterns are passed to you, or whether there's an error if you try to use a globbed filepath as an excluded filepath, or whatever.
In other words, it is usually better to use procedural logic to generate declarative logic than it is to use declarative logic to generate procedural logic. Of course, your program might be simple enough that you can get away with a config file that's turned into a bunch of `if` statements when its parsed. But it probably would be simpler if you got rid of as much parsing code as possible and focused on more rigidly validating a smaller, stricter set of options.
> I can see providing a separate config editor program users can use to generate configs for your main program
If you ignore the stuff about lambda functions, that's exactly what we're doing. We're just piggybacking on a real language to do it instead of wasting time writing our own graphical/command-line utility.
----
Where returning functions is concerned:
> Either that, or what you are calling a "config" is really a plugin/extension/scripting system
I think this is a kind of blurry line. I'm not going to say there is no difference between a plugin system and a config, but I have run into programs that have both configs and plugin systems, and where the plugin system exists mostly to deal with the fact that the config API is not very useful.
As a user, given the choice between learning one API and two, I usually prefer to learn one API. A lot of static site generators come to mind; I don't want to learn two different APIs for a templating engine just so I can change how links render. I don't want to learn both your plugin API and your config API. Just give me one, single API for templates that can be fully described in a single paragraph of text.
I dunno. I've been doing functional programming long enough that the idea of lambdas just doesn't scare me. Functional paradigms are wildly useful when building library APIs that get imported; I don't see why we should throw them away just because our API lives in a dotfile.
- pdonis 7y ago> I consume a single array of strings that need to resolve to file paths. And I'm finding it very hard to imagine why your users would need to write code to give your program those paths, instead of just giving you a text file with a list of file paths in it. Of course I don't know anything about your program or its use cases other than what you've said here. But I do know that if your users are writing code, what you're describing doesn't look to me like a config. See further comments below. > that's exactly what we're doing Making your users write actual code is not the same as providing them with a config editor. > I think this is a kind of blurry line. I think the line is pretty clear: are your users expected to know how to write code, or not? If they're not, then why would they need the ability to write code to generate your config, since they can't do it anyway? If they are, then as I said, you don't have a static program with a config, you have a scripting language and an interpreter. Your program is just a customized version of the standard interpreter for whatever programming language your users are expected to use. I don't have any problem with this if it's appropriate for your users. I just don't think the word "config" is the right one to use to describe it. > I've been doing functional programming long enough that the idea of lambdas just doesn't scare me. Whether it scares you or not is irrelevant unless you are the only one writing configs for your program. If your users are doing it, the relevant question is what scares them, not you.
- danShumway 7y agoSure -- if your users can't write code, then you obviously can't tell them to use code to generate a config. But take a step back and look at a lot of the examples that people are bringing up here -- Docker, Babel, Webpack, Github Actions, code linters, testing libraries, Ansible. These are all environments where it's pretty reasonable to expect users to know how to write code. If you're mostly getting hung up about me calling this a config, let me see if I can rephrase the original article's point in a way that is more palatable to you: A lot of people are building programs explicitly for programmers that use config files, when those programs would be much better suited by instead dropping the configs entirely and using something more like scripted options. These program authors struggle building a config schema that is powerful enough for programmers yet simple enough that programmers won't get irritated learning yet another multi-page API. What the authors have missed is that they shouldn't be trying to control their program with a config at all. Their entire product is geared at programmers, so they should just let programmers generate the options that they pass in. There's very little reason for Babel to be setting options with a JSON config file instead of a JS script, because nobody uses Babel other than Javascript programmers. For these specific programs that are struggling to express themselves with YAML/JSON, a non-config scripted setup would be less error-prone and easier for their users to learn.