| ▲ | ruuda 3 days ago |
| From the application point of view, recently I'm converging on this: define data structures for your config. Ensure it can be deserialized from json and toml. (In Rust this is easy to do with Serde; in Python with Pydantic or dataclasses.) Users can start simple and write toml by hand. If you prefer KSON, sure, write KSON and render to json. If config is UI, I think the structure of the data, and names of fields and values, matter much more than the syntax. (E.g. `timeout = 300` is meaningless regardless of syntax; `timeout_ms = 300` or `timeout = "300 ms"` are self-documenting.) When the configuration grows complex, and you feel the need to abstract and generate things, switch to a configuration language like Cue or RCL, and render to json. The application doesn't need to force a format onto the user! |
|
| ▲ | squirrellous 2 days ago | parent | next [-] |
| We use protobuf as schemas for json config. Protobuf has builtin json support and works across languages. It’s great for multi-language projects. |
| |
| ▲ | fireflash38 2 days ago | parent [-] | | This is what I have been debating using for a project at work. You can also nest messages within other messages easily with protobuf so you can aggregate/deaggregate configs as you want. Combined with protobuf validation plugins for your Lang and you get a rather neat package. | | |
| ▲ | squirrellous 2 days ago | parent [-] | | As usual the real struggle is getting everyone to agree on a configuration language :-) |
|
|
|
| ▲ | jdwyah 2 days ago | parent | prev | next [-] |
| the duration one in particular bugs me. I work on a dynamic configuration system and i was super happy when we added proper duration support. we took the approach of storing in iso duration format as a string. so myconfig = `5s` then you get a duration object and can call myconfig.in_millis. so much better imo. |
|
| ▲ | wofo 3 days ago | parent | prev [-] |
| I like this take! Have you used Cue or RCL yourself? How was the experience? |
| |
| ▲ | ruuda 2 days ago | parent | next [-] | | I’ve toyed with Cue, but never in a production setting. I like the ideas behind it, it’s very elegant that the same mechanism enables constraining values and reducing boilerplate. It’s somewhat limited compared to Jsonnet, RCL, Dhall, etc., you don’t get user-defined functions, but the flip side of that is that when you see something being defined, you can be confident that it ends up in the output like that, that it’s not just an input to a series of intractable transformations. I haven’t used it in large enough settings to get a feeling for how much that matters. Also, I find the syntax a bit ugly. We did a prototype at work to try different configuration languages for our main IaC repository, and Cue was the one I got furthest with, but we ended up just using Python to configure things. Python is not that bad for this: the syntax is light, you get types, IDE/language server support, a full language. One downside is that it’s difficult to inspect a single piece of configuration, you run the entry point and it generates everything. As for RCL, I use it almost daily as a jq replacement with easier to remember syntax. I also use it in some repositories to generate GitHub Actions workflows, and to keep the version numbers in sync across Cargo.toml files in a repository. I’m very pleased with it, but of course I am biased :-) | | |
| ▲ | wofo 2 days ago | parent [-] | | Didn't know about RCL! The project readme looks promising. I'll have to take it out for a spin. Thanks for creating it :) |
| |
| ▲ | diarrhea 2 days ago | parent | prev [-] | | Not the OP but I’m a big fan of this pattern as well. At work we generate both k8s manifests as well as application config in YAML from a Cue source. Cue allows both deduplication, being as DRY as one can hope to be, as well as validation (like validating a value is a URL, or greater than 1, whatever). The best part is that we have unit tests that deserialize the application config, so entire classes of problems just disappear. The generated files are committed in VCS, and spell out the entire state verbatim - no hopeless Helm junk full of mystery interpolation whose values are unknown until it’s too late. No. The entire thing becomes part of the PR workflow. A hook in CI validates that the generated files correspond to the Cue source (run make target, check if git repo has changes afterwards). The source of truth are native structs in Go. These native Go types can be imported into Cue and used there. That means config is always up to date with the source of truth. It also means refactoring becomes relatively easy. You rename the thing on the Go side and adjust the Cue side. Very hard to mess up and most of it is automated via tooling. The application takes almost its entire config from the file, and not from CLI arguments or env vars (shudder…). That means most things are covered by this scheme. One downside is that the Cue tooling is rough around the edges and error messages can be useless. Other than that, I fully intend to never build applications differently anymore. |
|