Things I Don't Like in Configuration Languages

Spoiler: I created my own configuration language maml.dev

Here will be a list of all markup languages/configuration languages I found on the internet, and things I don't like about them.

YAML

- - 1
  - 2
- - 3
  - 4

YAML 1.2 is better than YAML 1.0. But still, the YAML specification is monstrous, and I don't get how people trying to implement it are not going insane. YAML contains too many features.

XML

<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns="http://ex.com/catalog"
         xmlns:cat="http://ex.com/catalog"
         xmlns:old="http://ex.com/oldcatalog">
  <item id="0042">
    <name>R&amp;D Handbook</name>
    <notes priority="1 2 3">n/a</notes>
  </item>
</catalog>

The era of XML is in the past. I remember the hype around XML, how it was thought to be a universal format, and people created a lot of stuff around it. But now it's dead.

JSON

{"name": "John", "age": 30, "car": null}

JSON is nice and JSON won. It's a universal, data-interchange format for the Web and applications. This is why I based my MAML on top of JSON. I fixed things that were a little bit annoying for me inside JSON.

TOML

[[servers.web]]
name = "frontend"
ip = "192.168.1.10"

[[servers]]
region = "eu-central"

[[servers.web]]
name = "backend"
ip = "192.168.1.11"

I don't get why the [table] is more readable, and I don't understand [[array of tables]] out of order. And the lack of null.

JSON5

{
  lineBreaks: 'Look, Mom! \
No \\n\'s!',
  hexadecimal: 0xdecaf,
  a: +.8675309, b: +8675309.,
}

Too many unnecessary features. Object key-value pairs are still unordered. No distinction between integers and floats.

HJSON

{
  Hjson: a string
  RegEx: \s+
  md:
    '''
    First line.
    Second line.
      This line is indented by two spaces.
    '''  
}

Unquoted strings and three different types of comments. Multi-line strings with significant indentation.

JSONH

{
    // use #, // or /**/ comments
    keys: without quotes,
    isn\'t: {
        that: cool? # yes
    }
}

JSONH is same as HJSON but different.

RJSON

{ 
  shopping-list: [ milk butter /* don't forget the beer */ beer ]
  a\ space : t\:em item\ with\ spaces
}

Strings without quotes, but only if they don't contain spaces or commas. No specification.

JSONC

{
    /*
      This is a multi-line comment
      that spans multiple lines
    */
    "name": "Jane Doe",
    "age": 25,
}

There are so many implementations and differences in implementations of JSON with comments. For example, this one has trailing commas. Still allows duplicate keys, still no integers, unordered key-value objects.

HCL

service "aws_ami" "ubuntu" {
  most_recent = true
  instance_type = var.instance_type != "" ? var.instance_type : "t3.micro"
}

I don't like nesting with service "http" "web_proxy" or ability to specify service multiple times in different parts of the file.

Pkl

class Bird {
  name: String
  hidden taxonomy: Taxonomy
}

pigeon: Bird = new {
  name = "Common wood pigeon"
  taxonomy {
    species = "Columba palumbus"
  }
}

This is not a markup language. This is a full-blown programming language. I honestly would just use TypeScript instead of this one.

RON

GameConfig( // optional struct name
    window_size: (800, 600),
    key_bindings: {
        "up": Up,
        "down": Down,
        "left": Left,
        "right": Right,
    },
)

Very Rust-centric.

EDN

#:demo{:id #uuid "de305d54-75b4-431b-adb2-eb6b9e546014"
 :ts #inst "2025-11-04T09:00:00Z"
 :nums [1 2 3 4/2 5.5M]
 :set #{:a :b :c}
 :rx #"[A-Z]+"
 :q #queue ["x" "y"]
 :tag #user{:name "Zed"}}

I guess Clojure developers love it. I'm not one of them.

HOCON

include required("foo.conf")
a : [ 1, 2 ] [ 3, 4 ]
data-center-east = ${data-center-generic} { name = "east" }
{ foo include : 42 }

Too many features. Is it some sort of programming language? And it looks like it's very focused on just one project.

NestedText

default repository: home
report style: tree
compact format: {repo}: {size:{fmt}}.  Last back up: {last_create:ddd, MMM DD}.
date format: D MMMM YYYY
size format: .2b

YAML variant with only strings. But I need booleans!

KDL

author "Alex Monad" email=alex@example.com active=#true
scripts {
  message """
    hello
    world
    """
}

KDL is much closer to XML with properties on nodes. But I like JSON-like data structures. They are easier to understand. Also, I don't like significant indentation on multi-line strings.

SDLang

// Trailing semicolons are optional
prop true;

anotherprod on; author "Peter Parker"

this-is.1_valid$Tag-Name

renderer:options "invisible"
physics:options "nocollide"

title \
    "Some title"

They do have true and false. Why adding on and off? By default, integers are 32 bits long. Suffix 10L is needed for 64-bits.

CUE

import "math"

piPlusOne: math.Pi + 1

"quoted field names": {
    "four^four":         math.Pow(4, 4)
}

I would just use TypeScript instead of this programming language. Too many features; the specification is too big.

Dhall

let makeUser = \(user : Text) ->
      let home       = "/home/${user}"
      let privateKey = "${home}/.ssh/id_ed25519"
      let publicKey  = "${privateKey}.pub"
      in  { home, privateKey, publicKey }
    {- Add another user to this list -}
in  [ makeUser "bill"
    , makeUser "jane"
    ]

Again, it is a programming language. So I guess only one implementation of Dhall exists?

Jsonnet

local my_function(x, y=10) = x + y;
{
  person1: {
    name: "Alice",
    welcome: "Hello " + self.name + "!",
  },
  person2: self.person1 { name: "Bob" },
  len: [
    std.length('hello'),
    std.length([1, 2, 3]),
  ]
}

Well, this is some sort of programming language with dynamic types. But there are so many good programming languages, so I don't know why this one needs to be used.

Nickel

let conf = {
  name = "NiCl",
  version  = "0.0.1$",
  description = "My cool app!"
} in

let SemanticVersion = fun label value =>
  let pattern = "^\\d{1,2}\\.\\d{1,2}(\\.\\d{1,2})?$" in
  if std.string.is_match pattern value then
    value
  else
    let msg = "invalid version number" in
    std.contract.blame_with_message msg label
  in

let AppSchema = {
  name | String,
  version | SemanticVersion,
  description | String,
} in

conf | AppSchema

Nice programming language. Not a markup language.

Starlark

def fizz_buzz(n):
    """Print Fizz Buzz numbers from 1 to n."""
    for i in range(1, n + 1):
        s = ""
        if i % 3 == 0:
            s += "Fizz"
        if i % 5 == 0:
            s += "Buzz"
        print(s if s else i)

fizz_buzz(20)

Starlark is a programming language. Good lack porting/reimplementing Starlark in another programming language.

UCG

let tuple = {
    inner = {
        field = "value",
    },
    list = [1, 2] + [3],
    "quoted field" = "quoted value",
};

This is just an implementation of a language. Not portable to other languages.

UCL

.include "${CURDIR}/path.conf"
.macro_name(param={key=value}) "something";
section "blah" "foo" {
    key = value;
}

Okay. An implementation of a configuration language. No specification. No other implementations.

Confetti

probe-device eth0 eth1

user * {
    login anonymous
    password "${ENV:ANONPASS}"
    machine 167.89.14.1
    proxy {
        try-ports 582 583 584
    }
}

Nice logo! But other than that, I found it difficult to understand what's going on in this example. What about escaping in strings?

Ziggy

{
  .name = "ziggy",
  .version = @v("1.0.0"),
  .license = @spdx("MIT"),
  .dependencies = {
    "react": "next",
    "leftpad": "^1.0.0",
  },
  .repository = Git {
    // "type" is now the struct name
    .url = "https://github.com",
  },
  .description = 
    \\# Ziggy
    \\
    \\A Data Serialization Language.
  ,
}

I actually liked the idea of differentiating structs and maps. But this is just an implementation of a tool. No specification. And I don't like Zig-style multiline strings.

HUML

website::
  ports:: 80, 443
  enabled: true
  factor: 3.14
  props:: mime_type: "text/html", encoding: "gzip"
  tags::
    - "markup"
    - "webpage"
    - "schema"

Nice idea to make YAML less worse. But it's still a YAML, with the same problems and significant indentation.

MAML

{
  project: "MAML"
  tags: [
    "minimal"
    "readable"
  ]

  # A simple nested object
  spec: {
    version: 1
    author: "Anton Medvedev"
  }

  # Array of objects
  examples: [
    {
      name: "JSON", born: 2001
    }
    {
      name: "MAML", born: 2025
    }
  ]

  notes: """
This is a multiline raw strings.
Keeps formatting as-is.
"""
}

So, I decided to create a specification for my own language. And I wanted a distinctive name, not JSON something. I wanted a nice name and a strict specification. All languages have trade-offs. MAML as well.

7
11488

Comments

Xavier Noria
2025-11-07 08:38:37
To me, a config language is primarily key: value. And I like MAML supported value types and syntax. If the rare case in which you want an array, you assign it to a key and all is uniform. When designing a new _config_language, I believe you need to get rid of JSON inertia.
jsw
2025-11-07 08:07:36
Some very asture insights! Makes me want to read more of your posts. Does your blog have an RSS feed?
Avatar
antonmedv
Nope =) I do not post often enough to justify RSS.
Anonymous
2025-11-06 15:27:59
Multiline strings can use the position of the closing """ to signify the left edge.Swift does this for example and works very nicely. https://docs.swift.org/swift-book/documentation/the-swift-programming-language/stringsandcharacters/ "The whitespace before the closing quotation marks (""") tells Swift what whitespace to ignore before all of the other lines."
Avatar
antonmedv
Yes, I fought about it, but decided agains. Right now I'm thinking about using Zig-style strings https://github.com/maml-dev/maml/discussions/13
Jerald M Sheets
2025-11-06 11:57:31
Some kind of way, with the exception of YAML(Ansible), you managed to skip every major configuration platform in existence in favor of artifacts that I, configuration management maven for 15 years now, have never heard of. I think the appropriate approach to this topic is for those who write their own configuration raw, and use no platform (again, tailor-made for configuration work) to execute on configuration which, in my opinion. Is myopic at best.
Mark
2025-11-06 06:39:49
Removing the top level brackets mean that it’s possible to have a very simple and easy to understand config file that’s just Key: value Which is frequently all we need. Adding the brackets just adds clutter, i may as well just use JSON. I know it’s a small detail but it’s a config file, not a formal object.
Avatar
antonmedv
What about arrays on top? Or concatinating two files? Or in for some reason you got only half of the file?
heke
2025-11-06 03:56:24
If there will be a Python library to import/export dictionaries from/to MAML, that would be great.
Avatar
antonmedv
Like this? https://pypi.org/project/maml-py/
Ezequiel
2025-11-06 01:54:46
Missed the downsides of INI format :-(
John
2025-11-05 22:41:39
Allow indentation of multiline strings. (i.e. use a delimiter for the beginning of a line). https://matklad.github.io/2025/08/09/zigs-lovely-syntax.html
Janko
2025-11-05 12:57:55
A good list and good reasons for each format. I like your choice, it just makes sense, it's almost odd that those above have so much problems. Where JSON with comments and multiline strings is basically all you really need and want. :) MAML with few changes (comments, multiline-string, unnecessary outside block, no commas needed) also loads as normal Rye (Rebol language relative). Those changes would make it more compatible to Lisps for example, but less to JSON. So I'm not sure if it's desirable in general :) I played with this a little and created a gist: https://gist.github.com/refaktor/7183a9d943780f074b1eee8e1333c475 I hope to see more MAML in future and less YAML :)
Avatar
antonmedv
Thanks!
Xavier Noria
2025-11-05 08:24:58
If it is a new config language, why keep the surrounding braces? I don't thing they have any sense as part of config.
Avatar
antonmedv
True! A was thinking about this, and I actually think adding brackets simplifies parsing and make it clear: it is an object or and array on top level. Also makes lanuage more uniform. But MAML spec is just v0.1 now. Maybe I will add somethinkg in the future.