JSON, YAML, TOML, HCL - за последние годы человечество успело изобрести десяток языков для конфигурации.

Каждый обещал быть "простым", "удобным" и "читаемым человеком".

Но по факту - все они страдают от одних и тех же проблем: шумный синтаксис, хрупкие отступы, бесконечные кавычки и отсутствие элементарных возможностей вроде модульности и слияния конфигов.

Пора перестать с этим мириться и сделать конфигурации наконец человеческими.

? Перестаньте

  • утомлять глаза, пытаясь разобраться в тонне бесполезных кавычек

  • утомлять глаза, пытаясь распознать в каком месте есть проблема с отступом

  • писать велосипед, когда в очередной раз нужно мержить конфиги для разных окружений

  • писать велосипед, когда нужно парсить env переменные в структуру конфига

  • забивать мозг, пытаясь разобраться в огромной конфигурации из-за отсутствия модульности

⁉️ Как перестать?

Использовать адекватный язык для конфигурации ATMC
ATMC - минималистичный, но мощный язык конфигурации, созданный для удобного описания системных настроек.
Он сочетает простоту синтаксиса, поддержку импортов, переопределений, гибкого слияния конфигов и переменные окружения.

? Почему ATMC?

  • топовый синтаксис

    • супер минималистичный

    • интуитивно понятный и простой

    • не перегружен конструкциями

    • не зависит от отступов, переносов, кавычек, запятых и прочей чепухи

    • очень простой и в то же время достаточно мощный

  • модульность

    • можно импортировать разные кусочки (модули) конфига

    • очень минималистичный синтаксис импорта

    • можно обращаться к вложенным полям импортированного конфига

  • слияние конфигов (киллер фича)

    • супер легко мерджить несколько конфигов (например, common + stg или prod)

    • есть возможность переопределять поля

    • умное слияние - рекурсивное - не перетирает поле полностью

  • встраивание

    • можно встраивать объекты и массивы с помощью spread (...) оператора

    • с помощью него же и происходит слияние

  • доступ к env переменным

    • $YOUR_ENV_VARIABLE

  • поддержка всех необходимых типов

    • int

    • float

    • string

    • bool

    • object

    • array

  • компиляция в структуру и мапу из коробки

  • поддержка комментариев

  • расширяемость:

    • можно получить итоговый AST со всеми резовленными значениями

    • можно сделать на этой основе компилятор во что угодно

    • в перспективе можно поддержать LSP

? Примеры использования

Простой пример

?File: config.atmc

{
    logging: {
        level: ["warn", "error"]
    }
    database: {
        clickhouse: {
            username: "username"
            password: "password"
            port: 9000
        }
    }
}

Пример с импортом

?File: config.atmc

db ./database.atmc // Супер минималистичный импорт

{
    logging: {
        level: ["warn", "error"]
    }
    database: db // Тут появится объект из импортированного файла
}

?File: database.atmc

{
    clickhouse: {
        username: "username"
        password: "password"
        port: 9000
    }
}

Пример с spread + env

?File: config.atmc

db ./database.atmc

{
    logging: {
        level: ["warn", "error"]
    }
    database: {
        postgres: {
            username: $POSTGRES_USERNAME // Будет подставленно значение из env
            password: $POSTGRES_PASSWORD
        }
        db... // Сюда встроится импортированный конфиг
    }
}

?File: database.atmc

{
    clickhouse: {
        username: "username"
        password: "password"
        port: 9000
    }
}

Пример с доступом к вложенному полю

?File: config.atmc

db ./database.atmc

{
    logging: {
        level: ["warn", "error"]
    }
    database: {
        postgres: {
            username: $POSTGRES_USERNAME // Будет подставленно значение из env
            password: $POSTGRES_PASSWORD
        }
        clickhouse: db.clickhouse // Сюда будет встроен вложенный в db объект
    }
}

?File: database.atmc

{
    clickhouse: {
      username: "username"
      password: "password"
      port: 9000
    }
}

Пример со слиянием

?File: common.atmc

{
    logging: {
        level: ["error"]
    }
    database: {
        postgres: {
            username: $POSTGRES_USERNAME
            password: $POSTGRES_PASSWORD
            host: "localhost"
            port: 5432
        }
    }
}

?File: prod.atmc - конфиг для prod окружения

common ./common.atmc

{
    common... // Встраивается общий конфиг
    // Далее добавляются новые параметры и переопределяются параметры из общего конфига
    logging: {
        level: ["warn", "error"] // Переопределяется
        enable_tracing: true // Добавляется новое поле
    }
    database: {
        postgres: {
            port: 6432 // Переопределяется только порт, остальные поля не будут затронуты      
        }
    }
}

?File: stage.atmc - конфиг для stage окружения

common ./common.atmc

{
    common... // Встраивается общий конфиг
    // Далее добавляются новые параметры и переопределяются параметры из общего конфига
    logging: {
        // Переопредел��ется - можно без запятых
        level: [
            "info"
            "warn"
            "error"
        ]
    }
}

⚙️ Что под капотом?

Язык конфигурации работает благодаря множеству компонентов:

  • lexer

    • преобразует код в токены

  • parser

    • преобразует токены в AST

  • analyzer

    • проверяет семантику в рамках одного AST

    • проверяет наличие неиспользованных переменных

    • проверяет использование неопределенных переменных

  • linker

    • резолвит значения переменных из всех связанных AST

    • резолвит значения переменных среды

    • выдает один итоговый AST

  • processor

    • получает на вход путь до файла с конфигом

    • запускает все необходимые компоненты для обработки всех связанных файлов

    • отдает полученный итоговый AST после линковки

  • compiler

    • компилирует итоговый AST

    • компиляторы могут быть разными (в map, в struct и т.д.)

? Поддержка и интеграция

Язык написан пока только на Go, соответственно работает только для этого языка программирования.
В дальнейшем планируется поддержка других платформ

? В планах

  • компиляция в самого себя

    • будет удобно смотреть итоговый конфиг

    • можно будет выводить итоговый конфиг, как это делает, например, nginx -T

  • кодогенерация структур на базе конфига

    • тогда описывать конфиг придется только в одном месте - в atmc файле

  • автоматическая загрузка env переменных из .env файла

    • сейчас такого нет - нужно будет самим загружать, чтобы они стали доступны в os.Getenv

    • но будет удобно, если добавить

  • подсветка синтаксиса

  • имплементация LSP

    • умная подсветка синтаксиса

    • подсказки

    • поиск определений и помощь в импортах

P.S. Знаю, что уже есть похожие языки, но у всех есть свои недостатки. Где-то приятный синтаксис, но нет модульности; где-то есть модульность, но нет слияния; а где-то есть всё, но язык излишне усложнён. В ATMC есть всё нужное - и ничего лишнего.

Моя цель - сделать конфигурации такими же простыми, как Go-код: предсказуемыми, читаемыми и расширяемыми.


? Репозиторий и примеры: https://github.com/atmxlab/atmc

Комментарии (8)


  1. Landgraph
    10.11.2025 14:30

    Шутка про n-стандартов…

    ИМХО, когда конфиг становится сложнее, чем можно описать в ini файле - что-то пошло не так. И этот конфиг уже перестает быть человекочитаемым и лучше уже иметь какой-то гуй.


    1. paramtamtam
      10.11.2025 14:30

      В дополнение, крайние несколько лет являюсь адептом секты "config-less", когда все что нужно сконфигурировать - должно иметь cli/env ручки для этого, и знаете - этот подход более чем жизнеспособен! И строгие правила, и валидация, и генерация документации из этого кода - прям хорошо. Да что уж говорить - великий и могучий Traefik и тот умеет себя конфигурировать флагами без сильной боли ниже поясницы (если понимаешь что делаешь), а он тот еще комбаин.


  1. Autochthon
    10.11.2025 14:30

    Лучше S-выражений еще ничто человечество не придумало.


    1. nin-jin
      10.11.2025 14:30

  1. Astrowalk
    10.11.2025 14:30

    Кругом убийцы форматов... https://habr.com/ru/articles/248147/


  1. pilot114
    10.11.2025 14:30

    Модульность и слияние нужно реализовать не как фичу самих конфигов, а в том месте где они подключаются, после декодирования - и при этом вообще не зависеть от формата - вот куда усилия приложить надо, а не в изобретение json диалекта


  1. kez
    10.11.2025 14:30

    Основная проблема всех "статичных" языков не в синтаксисе (хотя насчёт ямла можно поспорить), а в том, что в этих языках нет синтаксических конструкций для избежания копипасты.

    HCL пытается с этим что-то делать, вводя модули, но не забывая вводить магические переменные для описания циклов — что, по мне, отвратительно, и начинает напоминать Ansible с его декларативными конструкциями, которые на практике оказываются просто нетипизируемым ямлом. Ямл тоже пытается избежать копипасты со своими anchors, и <<, но, опять же, на практике это превращается в просто месиво.

    Итого, потребность в хорошем языке для конфигов действительно есть.

    В статье отсутствует какое-либо сравнение с существующими языками, включая плюсы и минусы каждого из них. Понятно, что ATMC яыляется более "выразительным", и более абстрактным, нежели JSON, YAML etc. Поэтому также стоит сравнить с Pkl, dhall, CUE, jsonnet и другими.

    По статье, не доконца понял

    • можно ли отрендерить/экспоритировать .atmc конфиг в другие форматы?

    • как сделать ключ с пробелом?

    • можно ли сделать циклы?

    • пофейлится ли конфиг с одинаковыми ключами или есть какое-то правило?

    • при рекурсивном мерже, мержатся ли списки или просто перезаписываются?

    Ещё много вопросов можно задать, но думать лень


    1. user-book
      10.11.2025 14:30

      YAML как раз позволяет "копипастить" причем довольно гибко, можно указывать шаблон, а потом докидывать и перезаписывать конкретные параметры, это не Ansible-приколы а часть стандарта

      вообще из опыта конфиг нужно строить универсальным:

      • хочешь пихай json

      • хочешь yml или что то вообще странное

      • хочешь запускай без конфига вообще

      • хочешь укажи всего пару через cli параметры

      решать за клиента как ему лучше самая порочная практика. Автор статьи вообще создал уже существующее неоднократно, есть куча похожих json-подобных языков конфигурации