CUE: Your next configuration language?
There are reasons why JSON or YAML are popular: they have a rather simple syntax, they cover almost any configuration need, and they are popular. (Yes, I know, this is recursive argumentation, but popularity is a positive feedback loop.)
A typical configuration for a server with a host and a port might look like this in YAML:
exampleServer:
host: appliedgo.net
port: 3000
Looks clear and precise, but is this really the best way to configure your apps?
The problems of traditional configuration langauges
Here is what YAML, JSON, and TOML configuration files don't tell you:
- The type of a field. Sure,
host
is probably a string andport
looks like an integer, but can you really tell? - Allowed values or ranges. Can I specify a port below 1024? What's the maximum value allowed?
- Whether a field is mandatory. What if I omit the host value?
- Default values. If a field isn't mandatory, what's the default value?
And don't forget other downsides:
- YAML is whitespace-sensitive. Omit a space, or accidentally insert a tab when your editor has tabwidth=2 set, and the file renders invalid.
- YAML has the famous Norway problem. In YAML,
no
is a valid way of sayingfalse
, butno
could also be the country code of Norway. - JSON does not support comments (unless you use the “JSON With Comments” flavor that all JSON interpreters do not understand)
- TOML makes it easy to accidentally add a parameter meant to be at the top level inside a table instead, if the table header is above the editor's viewport.
And the list goes on.
Try CUE for a change
The majority of problems with traditional configuration languages can be traced back to a lack of a schema specification.
The CUE language allows defining schemas for your data. Even better, you can define the schema in CUE itself, no special schema syntax needed. And CUE is a validation language at heart, and so it can validate configuration against a given schema.
Here is an example: The CUE schema of the above YAML configuration:
#Server: {
host!: string
port: int & >=1024 & <=65535 | *8080
}
In a very compact and concise way, these lines describe the requirements for a valid Server configuration:
- “#Server” is a schema definition (as it is prefixed by a hash)
- The host field must be a string
- The host field is mandatory (note the exclamation mark)
- The port must be an integer between 1024 and 65535
- The port field defaults to 8080 if omitted
The corresponding configuration is straightforward:
exampleServer: #Server & {
host: "appliedgo.net"
port: 3000
}
This snippet says, “I adhere to the #Server schema, and I provide these host and port values.”
No ambiguities left.
CUE comes with a cue
command that can validate configurations and export them to YAML or JSON.
But using CUE with Go is where the fun starts.
- Use the
cue
package to read in and validate CUE, YAML, or JSON configuration - Generate CUE schemas from Go
- Validate Go types against CUE schemas
CUE schemas can become a single source of truth for your data types and configurations. If this sounds too hyperbolic, have a look at
hof
. This project uses CUE as a single source of truth to generate and validate about everything you need for building an application.
Want to give CUE a try in your next project?