Продолжаю выкладывать выдержки из вводного курса нашей компании по промышленному программированию.
Часть вторая: Принцип сохранения функционала
Рассказывается чем вредны конфиги и почему писать много кода — плохо. Другие части можно найти тут.
Многие языки программирования совмещают как описательный (декларативный) подход для структур данных, так и императивный — для описания операций над данными.
Пример — это C/C++, в котором в файлах заголовка (*.h) декларативным способом описываются структуры данных, функции и методы, а в файлах программы (*.c/*.pp/*.cpp) на императивном языке описываются действия над данными. Другой пример — это PHP, который делится на HTML часть (все *ML языки являются декларативными), описывающий структуру данных для визуализации, и PHP часть, предназначенную для описания действий по заполнению структуры данными.
Важно помнить, что, в общем-то, любой функционал по преобразованию данных можно свести к программе на машине Тьюринга. Неважно, на каком языке и в какой парадигме этот функционал был написан. Таким образом по аналогии с законом сохранения материи Ломоносова-Лувуазье можно ввести закон сохранения функционала.
Сумма функционала программы определяется только разностью источника и стока данных, количество преобразований внутри программы на сумму функционала не влияет.
Фактически, этот закон является одним из переложений третьего начала термодинамики о мере возрастания энтропии. Его можно переформулировать так:
При возрастании количества функционала, количество кода увеличивается.
Этот закон — один из основополагающих для понимании теории программирования. Он задаёт понимание того, что большее количество кода не означает большего количества функционала. Зато большее количество функционала никогда не бывает бесплатным — за возрастание функционала необходимо платить описанием правил перехода, к примеру — строчками кода.
Теорема о программировании на конфигах
Важным следствием этого закона является теорема о программировании на конфигах. Дело в том, что конфиг файл обычно выносят из программы, основываясь о предположении, что при изменении конфиг файла не придётся лезть в программу и менять её код.
При этом зачастую забывается, что любой конфиг-файл по сути своей является не только источником информации, но и декларативным описанием структуры данных, в которую эта информация уложена. По сути, любой конфиг файл — это программа на декларативном ЯП.
Соответственно, добавляя конфиг файл, программист как минимум вносит в программу
а) новый источник данных
б) пишет интерпретатор конфиг-файла
То есть программист добавляет новый функционал в программу (т.к. добавляет ещё один внешний источник данных, а значит в программе необходимо поддержать преобразования данных из этого источника), причём часть этого функционала он описывает на другом, отличном от ЯП на котором делается родительская программа ЯП (собственно, язык конфига).
По закону о сумме функционала, программисту никогда не удастся таким образом создать конфиг, чтобы он полностью снял заботу с пользователя о программировании функционала — часть функционала всегда будет писаться на языке конфига.
Чем больше программа требует конфигов, тем больше пользователю этих конфигов приходится на них программировать. Если же конфиги, при этом, ещё и имеют разный формат заполнения (property-файлы, XML, GUI формы, etc), то пользователю, к тому же, придётся изучить ровно столько новых ЯП, сколько этих конфигов. Это, соответственно, не добавит счастливости пользователю.
Пытаясь сделать программу более функциональной, гибкой к различным ситуациям, вынося настройки во вне системы, программист просто наращивает энтропию материнской программы, создавая новые декларативные ЯП и заставляя пользователя учиться программировать на этих декларативных ЯП.
Не реализуя какой-либо функционал, но давая возможность его включить в конфигах, программист переносит обязанность программировать этот функционал с себя на пользователя программы.
Теорема о простом фреймворке
Одним из следствий теоремы о программировании на конфигах является проблема “гибкого и настраиваемого фреймворка”. Дело в том, что суть любого фреймворка — это предоставление API. API — это, по сути, тоже декларативный язык, описывающий правила использования данного фреймворка. Чем больше раскрыто API, чем больше доступно для этого API параметров, тем более богат этот язык и тем сложнее его изучить, а значит и использовать.
Самый логичный и понятный фреймворк — это фреймворк, предоставляющий минимальный API с максимальным функционалом за ним.
Что такое логичный и понятный фреймворк? Если с помощью фреймворка можно делать одну и ту же вещь несколькими разными способами, это означает что декларативный язык API этого фреймворка является избыточным. Простой фреймворк — это фреймворк, избыточность API которого минимальна.
Теорема о Вавилонском смешении языков
Каждый источник информации, а значит и каждый вид информации требует код, преобразующий этот вид информации к своему внутреннему родному (нативному) представлению. К примеру, конфиг диктует структуру представления информации, API диктует правила вызова. Верно и обратное утверждение, что каждый API, каждый конфиг порождает новый источник информации, которая тоже нуждается в преобразовании вызывающей стороной к нативному виду.
Таким образом, чем больше пар язык-язык используется в программе, чем больше используется структур данных, тем больше программа содержит преобразований. Количество преобразований при этом больше, чем если бы информация поступала в программу с использованием структур данных на базовом для программы ЯП.
Фактически, использование одного дополнительного конфига или дополнительного языка программирования добавляет в программу в два раза больше кода, чем добавление ещё одного источника данных или ещё одной структуры данных, описанной на материнском языке программы.
Чем больше разных языков используется в проекте (включая сюда разные форматы конфигов и разные API), тем больше кода возникает в программе, при сохранении того же функционала.
Собственно, из этого следует теорема о Вавилонском смешении языков:
Добавление фреймворка, конфига или ЯП для облегчения разбора данных приводит к усложнению разбора данных.
У нас с этим непосредственно столкнулись ребята, работающие с функционалом новой корзины — там крутой замес описательной и исполнительной парадигм, представленной в нескольких видах конфигов и использующих несколько видов API.
Автор: ymik