Мир разработки полон соблазнов изобретать собственные решения. Один из самых ярких соблазнов — создать свой DSL (Domain-Specific Language). Это звучит красиво: язык, который идеально отражает задачи конкретной предметной области. Но где заканчивается здравый смысл и начинается велосипедостроение? В статье я попробую на примерах показать, когда DSL — это спасение, а когда лучше взять старый добрый Python, C# или даже Bash.

А зачем вообще нужен DSL?

DSL часто сравнивают с секретным языком внутри команды. Когда задачи повторяются, а бизнес-логика становится громоздкой, хочется выразить всё в более компактной и выразительной форме. Так появляются конфигурационные языки, мини-языки для описания правил, трансформаций, пайплайнов данных.

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

Универсальные языки: палка о двух концах

С одной стороны, Python или JavaScript можно использовать почти везде. Но универсальные языки тянут за собой много «балласта»: синтаксический шум, огромные экосистемы и тысячи способов сделать одно и то же.

Пример: попробуйте описать бизнес-правило для тарификации на Python:

# Python
def calculate_tariff(user_type, minutes, base_rate):
    if user_type == "student":
        discount = 0.5
    elif user_type == "corporate":
        discount = 0.8
    else:
        discount = 1.0

    cost = base_rate * minutes * discount
    return round(cost, 2)

print(calculate_tariff("student", 120, 0.15))

Всё понятно для программиста, но бизнес-аналитик или тестировщик вряд ли захочет вникать в эти elif.

Когда DSL — это действительно хорошо

DSL выигрывает там, где описываются повторяющиеся декларативные конструкции.
Пример — простенький язык для тарифов:

# Mini-DSL
TARIFF student {
    DISCOUNT = 0.5
}

TARIFF corporate {
    DISCOUNT = 0.8
}

CALCULATE minutes * base_rate * DISCOUNT

Теперь аналитик может спокойно редактировать правила, не пугаясь def и скобочек.

Типичные ошибки при создании DSL

  1. Забыли про пользователей. DSL должен быть читаем не только для автора, но и для команды.

  2. Изобретение велосипеда. Иногда DSL — это просто плохо сделанный YAML.

  3. Отсутствие туллинга. Без подсветки синтаксиса, автодополнения и валидатора любой язык быстро превращается в боль.

  4. Непродуманная эволюция. Язык живёт дольше, чем проект, а поддерживать его никто не хочет.

Пример: мини-DSL на Python через парсер

Пусть у нас есть задача описывать рабочие смены сотрудников. Напишем маленький DSL на базе lark-parser:

# Python
from lark import Lark, Transformer

grammar = """
start: shift+
shift: "SHIFT" NAME "{" day+ "}"
day: "DAY" NAME HOURS
NAME: /[a-zA-Z]+/
HOURS: /[0-9]+/
%import common.WS
%ignore WS
"""

class ShiftTransformer(Transformer):
    def shift(self, items):
        return {"employee": items[0], "days": items[1:]}
    def day(self, items):
        return {"day": items[0], "hours": int(items[1])}

parser = Lark(grammar, parser="lalr", transformer=ShiftTransformer())

text = """
SHIFT Alice {
    DAY Monday 8
    DAY Tuesday 6
}
"""

print(parser.parse(text))

На выходе получаем JSON-подобную структуру. И бизнес может легко читать текстовое представление.

Когда лучше отказаться от DSL

  • Если задача решается «из коробки» конфигами (например, Kubernetes YAML).

  • Если в компании нет ресурсов поддерживать язык и тулзы.

  • Если язык слишком узкий и его используют два человека.

В таких случаях лучше взять тот же Python или даже Excel с формулами.

Баланс между декларативностью и универсальностью

Ключевой момент: DSL не должен быть самоцелью. Он должен быть «слоем общения» между кодом и предметной областью. Иногда выгоднее взять универсальный язык, но ограничить стиль кода линтерами и правилами. А иногда — построить лёгкий DSL, но с трансляцией в тот же Python.

Личный опыт и грабли

Когда-то я писал DSL для описания ETL-пайплайнов. Красиво получилось: строки вроде

EXTRACT csv FROM "/data/users.csv"
TRANSFORM normalize_names
LOAD postgres TO "users"

Но через полгода понадобилось добавить сложные фильтры. И тут выяснилось, что DSL слишком узок. В итоге всё равно встроили куски Python. Урок: думать наперёд и не пытаться выразить «всё» собственным языком.

Мой вывод

DSL — это инструмент. Иногда он делает жизнь проще, иногда — только сложнее. Правило простое: создавайте DSL только тогда, когда уверены, что:

  • предметная область устойчива,

  • пользователей много,

  • поддержка языка не ляжет грузом на одного разработчика.

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

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


  1. atues
    18.09.2025 12:39

    Даже есть соответствующий талмуд. Рекомендую


    1. apcs660
      18.09.2025 12:39

      Экая глыба, экий матерый человечище...


  1. ProMix
    18.09.2025 12:39

    плохо сделанный YAML

    А вы видели хорошо сделанный?

    На мой взгляд, спецификация позволяет слишком много вольностей в синтаксисе, чтобы YAML был хорошей вещью


  1. punzik
    18.09.2025 12:39

    Можно сделать dsl на лиспе, и не изобретать новый синтаксис. И не будет проблем с узостью.