Мир разработки полон соблазнов изобретать собственные решения. Один из самых ярких соблазнов — создать свой 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
Забыли про пользователей. DSL должен быть читаем не только для автора, но и для команды.
Изобретение велосипеда. Иногда DSL — это просто плохо сделанный YAML.
Отсутствие туллинга. Без подсветки синтаксиса, автодополнения и валидатора любой язык быстро превращается в боль.
Непродуманная эволюция. Язык живёт дольше, чем проект, а поддерживать его никто не хочет.
Пример: мини-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)
ProMix
18.09.2025 12:39плохо сделанный YAML
А вы видели хорошо сделанный?
На мой взгляд, спецификация позволяет слишком много вольностей в синтаксисе, чтобы YAML был хорошей вещью
punzik
18.09.2025 12:39Можно сделать dsl на лиспе, и не изобретать новый синтаксис. И не будет проблем с узостью.
atues
Даже есть соответствующий талмуд. Рекомендую
apcs660
Экая глыба, экий матерый человечище...