The post has been translated automatically. Original language: Russian
One of the strong engineering ideas of Unix is to separate mechanism and policy. The wording may sound a bit abstract, but the meaning is quite simple: one part of the system should be responsible for what it can do at all, and the other for when and how to use these features.
This separation reduces the system's connectivity and makes it more durable. The mechanism can be left the same, and the rules of behavior can be changed independently. Or vice versa: replace the internal implementation without changing the external rules.
The mechanism is responsible for the possibilities
Let's imagine a system that can run tasks on a schedule. The mechanism here is responsible for the startup itself: it stores a queue, tracks time, creates processes, and reports errors.
The policy is responsible for other things: which task to launch, when exactly, how many times to repeat, and what to do if it fails.
If you mix these two parts, every rule change will require intervention in the mechanism itself. If separated, the mechanism remains universal, and the behavior is set separately.
This is what makes the system flexible.
The rules change faster than the mechanisms
A technical mechanism often lives a long time. For example, the way you read a file, send a message, create a process, or write to a log may not change much for years.
The rules of behavior change much more often. One limit today, another tomorrow. There is one approval procedure today, and a new one tomorrow. Today, the event is processed automatically; tomorrow, human confirmation is required.
If such rules are embedded deep in the code, the system begins to constantly require alterations.
If they are made separately, the changes become local.
A good mechanism knows nothing about the causes.
There is a useful mental test.
Let's imagine a component that sends a notification. A good mechanism knows how to deliver a message: where to connect, how to form a request, how to handle an error and try again.
But he doesn't need to know why this message is being sent.
The reason can be any one: the deadline has arrived, the order status has changed, an error has been detected, the person has pressed the button.
The less the mechanism knows about the causes, the more widely it can be used.
Politics is responsible for meaning
The rules of behavior, on the contrary, are responsible for the meaning.
Who should I send the notification to? In what situation? Is it possible to send it at night? Do I need to wait for confirmation? What is considered a critical event?
These issues relate not to the technical delivery of the message, but to the logic of the system.
When such solutions are mixed with the sending mechanism, the code quickly becomes difficult to change.
It is much better when the mechanism remains neutral, and the policy sets the conditions for its use.
Why is this especially important in large systems?
In a small program, mixing mechanism and rules may not bother for a long time. The code is clear, there are few developers, and everyone knows why this or that condition appeared.
But over time, there are more rules. There are exceptions, different modes, user rights, regional differences, and customer characteristics.
Then the mechanism begins to acquire a large number of conditions.
At some point, it is no longer possible to understand where the technical part ends and the subject logic begins.
This is one of the characteristic signs of architectural degradation.
Individual rules are easier to discuss.
There's another reason to separate these levels: rules are often discussed by people who don't write code.
A manager, engineer, accountant, lawyer, or operator may have a good understanding of how the system should work, but is not required to understand the internal implementation of the program.
If the rules exist separately in a clear form, they are easier to check and discuss.
A person can see what conditions are in effect now, where there are exceptions, and what exactly has changed.
When everything is hidden inside the program, the conversation quickly turns into a retelling of the code.
Configuration is one of the ways
The easiest way to separate policy from mechanism is configuration.
The program can perform certain actions, and the settings determine which of them are enabled and under what conditions.
This works well as long as the rules remain relatively simple.
But as the system grows, one configuration may not be enough. Then there are tables of rules, scenarios, states, individual services, or special description languages.
It is not the specific means that is important, but the principle of separation itself.
No need to turn everything into settings.
It's easy to go to the opposite extreme here.
If you take out all the behavior altogether, you can get a huge system of settings that will be more difficult to understand than the usual code.
Therefore, it is not necessary to divide mechanically.
The technical logic must remain in the program. And separately, it is worth taking out what reflects the changing decisions, rules and restrictions.
The boundary runs where the meaning changes, not the device.
One mechanism — many ways to use
A well-separated mechanism can be used in different situations.
For example, the same queue system can handle sending emails, processing files, building reports, and performing background tasks.
She doesn't need to know the purpose of each operation.
It solves only its technical problem.
This allows for reuse without creating a huge universal application.
The policy can change without rewriting the system.
This is especially useful where the rules change frequently.
Let's imagine a system for checking applications. Two conditions are enough today. A month later, another one appears. Then there is a separate rule for a certain category of users.
If each such change requires a rewrite of the basic mechanism, development slows down quickly.
If the rules are presented separately, the system adapts much more easily.
This idea works and is safe.
Even access control clearly shows the difference between a mechanism and a policy.
The mechanism is able to check the user and apply the restriction. The policy determines who exactly has the right to act.
The technical verification method can remain the same as long as the access rules change.
Conversely, you can change the internal authorization mechanism while maintaining the previous rights model.
This independence of the parts greatly simplifies the development of the system.
Artificial intelligence makes separation even more important
Modern artificial intelligence systems are particularly easy to mix mechanism and politics.
The model simultaneously receives the task, rules, context, and data, and then outputs the result. At first glance, this is convenient: you can simply write a long instruction and get the desired behavior.
But gradually a problem arises. Where exactly are the rules? Which ones are required? Which ones relate only to a specific situation? What has changed between the two versions of the manual?
If everything is stored in one big query, the mechanism and policy are mixed up again.
The model can be a mechanism
It is useful to look at artificial intelligence as a mechanism.
He can read text, extract meaning, transform data, form an answer, classify or propose a solution.
But specific rules of behavior can be stored separately.
Then the model becomes a replaceable part of the system.
You can change one model to another without losing the rules themselves.
This is much more stable than a system where all knowledge exists only within a specific request or a specific supplier.
A person can also be a part of politics
Not all decisions need to be given to the program.
Sometimes the rule should sound like this: if confidence is below a certain level, pass the decision on to the person. Or: changes of this type require confirmation.
This is also politics.
The mechanism can perform an action, but the rule determines when automation ends and a human decision begins.
This separation is especially important in systems where the cost of error is high.
The clearer the boundary, the easier it is to change the system.
A good architecture doesn't necessarily have to have a separate file labeled "policy".
Another important thing is that the developer must understand which parts of the system are responsible for the technical capabilities and which are responsible for the rules being changed.
When this boundary is clear, the system is easier to develop.
You can change the rules without changing the mechanism.
You can replace the mechanism by keeping the rules.
You can check these parts independently.
The system lives longer than its solutions
Technology is changing fast. One way to store data today, another tomorrow. There is one library today, and a new one in a few years.
Domain rules often live much longer.
If they are closely related to a specific technical mechanism, each technological change becomes expensive.
If they are separated, the system can change the internals, preserving the meaning.
This is the value of the principle.
A good system doesn't just know how to do something.
She also understands the difference between being able to perform an action and a rule that says when that action is really needed.
Одна из сильных инженерных идей Unix состоит в разделении механизма и политики. Формулировка может звучать немного абстрактно, но смысл довольно простой: одна часть системы должна отвечать за то, что она вообще умеет делать, а другая — за то, когда и как эти возможности использовать.
Такое разделение уменьшает связанность системы и делает её долговечнее. Механизм можно оставить прежним, а правила поведения менять независимо. Или наоборот: заменить внутреннюю реализацию, не меняя внешние правила.
Механизм отвечает за возможности
Представим систему, которая умеет запускать задачи по расписанию. Механизм здесь отвечает за сам запуск: хранит очередь, отслеживает время, создаёт процессы, сообщает об ошибках.
Политика отвечает за другое: какую задачу запускать, когда именно, сколько раз повторять, что делать при неудаче.
Если смешать эти две части, каждое изменение правил потребует вмешательства в сам механизм. Если разделить, механизм остаётся универсальным, а поведение задаётся отдельно.
Именно это делает систему гибкой.
Правила меняются быстрее механизмов
Технический механизм часто живёт долго. Например, способ чтения файла, отправки сообщения, создания процесса или записи в журнал может годами почти не меняться.
Правила поведения меняются намного чаще. Сегодня один лимит, завтра другой. Сегодня один порядок согласования, завтра новый. Сегодня событие обрабатывается автоматически, завтра требуется подтверждение человека.
Если такие правила зашиты глубоко в код, система начинает постоянно требовать переделок.
Если же они вынесены отдельно, изменения становятся локальными.
Хороший механизм ничего не знает о причинах
Есть полезный мысленный тест.
Представим компонент, который отправляет уведомление. Хороший механизм знает, как доставить сообщение: куда подключиться, как сформировать запрос, как обработать ошибку и повторить попытку.
Но ему не обязательно знать, почему это сообщение отправляется.
Причина может быть любой: наступил срок, изменилось состояние заказа, обнаружена ошибка, человек нажал кнопку.
Чем меньше механизм знает о причинах, тем шире его можно использовать.
Политика отвечает за смысл
Правила поведения, наоборот, отвечают за смысл.
Кому отправлять уведомление? В какой ситуации? Можно ли отправить его ночью? Нужно ли ждать подтверждения? Что считать критическим событием?
Эти вопросы относятся не к технической доставке сообщения, а к логике системы.
Когда такие решения смешиваются с механизмом отправки, код быстро становится трудно менять.
Гораздо лучше, когда механизм остаётся нейтральным, а политика задаёт условия его использования.
Почему это особенно важно в больших системах
В небольшой программе смешение механизма и правил может долго не мешать. Код понятен, разработчиков мало, все знают, почему то или иное условие появилось.
Но со временем правил становится больше. Появляются исключения, разные режимы, права пользователей, региональные различия, особенности клиентов.
Тогда механизм начинает обрастать большим количеством условий.
В какой-то момент уже невозможно понять, где заканчивается техническая часть и начинается предметная логика.
Это один из характерных признаков архитектурной деградации.
Отдельные правила легче обсуждать
Есть ещё одна причина разделять эти уровни: правила часто обсуждают люди, которые не пишут код.
Менеджер, инженер, бухгалтер, юрист или оператор может хорошо понимать, как должна работать система, но не обязан разбираться во внутренней реализации программы.
Если правила существуют отдельно в понятной форме, их легче проверять и обсуждать.
Человек может увидеть, какие условия действуют сейчас, где есть исключения и что именно изменилось.
Когда всё скрыто внутри программы, разговор быстро превращается в пересказ кода.
Конфигурация — один из способов
Самый простой способ отделить политику от механизма — конфигурация.
Программа умеет выполнять определённые действия, а настройки определяют, какие из них включены и при каких условиях.
Это хорошо работает, пока правила остаются сравнительно простыми.
Но по мере роста системы одной конфигурации может быть недостаточно. Тогда появляются таблицы правил, сценарии, состояния, отдельные службы или специальные языки описания.
Важно не конкретное средство, а сам принцип разделения.
Не нужно превращать всё в настройки
Здесь легко уйти в противоположную крайность.
Если вынести наружу вообще всё поведение, можно получить огромную систему настроек, которую будет сложнее понимать, чем обычный код.
Поэтому разделять нужно не механически.
Техническая логика должна оставаться в программе. А отдельно стоит выносить то, что отражает изменяемые решения, правила и ограничения.
Граница проходит там, где меняется смысл, а не устройство.
Один механизм — много способов использования
Хорошо разделённый механизм можно использовать в разных ситуациях.
Например, одна и та же система очередей может обслуживать отправку писем, обработку файлов, построение отчётов и выполнение фоновых задач.
Ей не нужно знать назначение каждой операции.
Она решает только свою техническую задачу.
Так появляется повторное использование без создания огромного универсального приложения.
Политика может меняться без переписывания системы
Это особенно полезно там, где правила меняются часто.
Представим систему проверки заявок. Сегодня достаточно двух условий. Через месяц появляется ещё одно. Потом отдельное правило для определённой категории пользователей.
Если каждое такое изменение требует переписывать основной механизм, развитие быстро замедляется.
Если правила представлены отдельно, система адаптируется намного легче.
Эта идея работает и в безопасности
Даже управление доступом хорошо показывает различие между механизмом и политикой.
Механизм умеет проверить пользователя и применить ограничение. Политика определяет, кто именно имеет право на действие.
Технический способ проверки может оставаться тем же, пока правила доступа меняются.
И наоборот, можно изменить внутренний механизм авторизации, сохранив прежнюю модель прав.
Такая независимость частей значительно упрощает развитие системы.
Искусственный интеллект делает разделение ещё важнее
Современные системы с искусственным интеллектом особенно легко смешивают механизм и политику.
Модель одновременно получает задачу, правила, контекст и данные, а затем выдаёт результат. На первый взгляд это удобно: можно просто написать длинную инструкцию и получить нужное поведение.
Но постепенно возникает проблема. Где именно находятся правила? Какие из них обязательны? Какие относятся только к конкретной ситуации? Что изменилось между двумя версиями инструкции?
Если всё хранится в одном большом запросе, механизм и политика снова оказываются смешаны.
Модель может быть механизмом
Полезно посмотреть на искусственный интеллект как на механизм.
Он умеет читать текст, извлекать смысл, преобразовывать данные, формировать ответ, классифицировать или предлагать решение.
Но конкретные правила поведения можно хранить отдельно.
Тогда модель становится заменяемой частью системы.
Можно поменять одну модель на другую, не теряя сами правила.
Это намного устойчивее, чем система, где всё знание существует только внутри конкретного запроса или конкретного поставщика.
Человек тоже может быть частью политики
Не все решения нужно отдавать программе.
Иногда правило должно звучать так: если уверенность ниже определённого уровня, передать решение человеку. Или: изменения такого типа требуют подтверждения.
Это тоже политика.
Механизм умеет выполнить действие, но правило определяет, когда автоматизация заканчивается и начинается человеческое решение.
Такое разделение особенно важно в системах, где цена ошибки высока.
Чем яснее граница, тем легче менять систему
Хорошая архитектура не обязательно должна иметь отдельный файл с надписью «политика».
Важно другое: разработчик должен понимать, какие части системы отвечают за технические возможности, а какие — за изменяемые правила.
Когда эта граница ясна, систему проще развивать.
Можно менять правила без переделки механизма.
Можно заменить механизм, сохранив правила.
Можно проверять эти части независимо.
Система живёт дольше своих решений
Технологии меняются быстро. Сегодня один способ хранения данных, завтра другой. Сегодня одна библиотека, через несколько лет новая.
Правила предметной области часто живут гораздо дольше.
Если они тесно связаны с конкретным техническим механизмом, каждое технологическое изменение становится дорогим.
Если они отделены, система может менять внутренности, сохраняя смысл.
В этом и состоит ценность принципа.
Хорошая система не просто умеет что-то делать.
Она ещё и понимает разницу между возможностью выполнить действие и правилом, которое говорит, когда это действие действительно нужно.