Pull to refresh

Хранение кода в бд или собираем код по кирпичикам

Reading time 7 min
Views 10K
Данная статья написана Napolsky. По известным причина он не смог ее опубликовать. Если статья вам понравилась — поощрите автора известным способом.

В этом топике я расскажу об одном разрабатываемым мною подходе в веб программировании, сердцем которого является хранение кода в базе данных. Несколько замечаний по дальнейшему тексту:
  • Под словосочетанием «код страницы» имеется ввиду исполняемый (php) код
  • Во всех вопросах, касательно производительности, имеется ввиду чистое время генерации страницы, без использования акселлераторов, систем кеширования и т д

Как все начиналось


Для того чтобы понять, а «зачем оно собственно надо» быстренько пройдем тот путь, который и привел меня к хранению кода в бд. Так сложилось, что свой путь в веб программировании я начинал не с написания каких-либо скриптов или модулей для существующих систем, а сразу с написания собственного движка сайта с абсолютного нуля. К этому моменту я имел двухлетний опыт программирования на C++ и, конечно же, по накатанной пытался строить свой веб движок на ООП (правда в то время в PHP от ООП было одно название :) ). В пределах разумного, я очень люблю свои «велосипеды». Особенно большие. И прежде чем воспользоваться готовым решением, всегда задаюсь вопросом «а нельзя ли написать получше?».


Вообще написание своих велосипедов очень полезно, особенно для начинающих разработчиков (когда на первом месте стоит поднятие профессионализма, а не написание кода в отведенный срок и бюджет). Только написание собственных решений дает понимание того, как что-то устроено изнутри на самом низком уровне. А это в свою очередь дает понимание сложности, ресурсоемкости, скорости тех или иных подходов, что, в конечном счете, выливается в выбор правильного инструментария для решения задачи. Например, в университете нас заставляли писать свои pushback'и для массивов, чтобы мы не забывали, что за казалось бы простыми и тривиальными вещами может скрываться что-то гораздо большее.

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

Не задумывались ли вы над количеством «мертвого» кода на странице? На самом деле его количество как правило в 7-15 раз превышает количество кода, который действительно будет выполнен при обращении к странице. Возьмите к примеру класс комментариев. В нем будут методы render(), delete(), edit(), add(), compress(), answer() и т д. При этом за 1 выполнение скрипта как правило будет вызван всего 1 из этих методов (delete — при удалении, edit — при редактировании и т д), а остальные заведомо не будут вызываться. Вот и считайте, сколько такого лишнего кода набежит на странице.

По началу я пытался проводить оптимизацию, «разрезая» и «склеивая» большие библиотеки или классы под нужды различных страниц, уменьшая таким образом количество инклудов и «мертвого» кода. Но это, конечно же, тупиковый путь. Шло время. Проекты, написанные на этом движке (царство им небесное :) ) становились все больше. Вместе с этим росло количество и размеры подключаемого кода, а вместе с ними и время генерации страниц. Я начал все чаще думать о том, как избавиться от «мертвого» кода. И тут меня посетила смелая, показавшайся даже бредовой мысль. А что если…

Рождение идеи


А что если разделить код на максимально мелкие независимые части, чтобы иметь возможность собирать на странице только то, что действительно нужно? То есть разделить все функции, классы (в идеале и методы классов) и прочее. Таким образом, мы получим много много маленьких «кирпичиков», из которых потом будем складывать страницу. Тем самым появится возможность полностью избавиться от «мертвого» кода и инклудов. Меня по-настоящему взбудоражила эта идея, но вопросов было больше чем, ответов: как это сделать, будет ли это работать, какие подводные камни ожидают в реализации, насколько быстра такая система? Короче пока я не имел ни малейшего представления о том, как это реализовать и как оно будет работать. Но попробовать, конечно же, стоило.

Путь воина


Идеология заключается в том, что разбив всё на максимально малые кусочки кода, мы сможем собрать из них что угодно.Вопроса о том, как хранить «кирпичики» кода не возникало — так как они уже были не кодом, а являлись по сути данными с набором атрибутов, то единственным возможным вариантом было использование бд. Постараюсь показать принцип работы подобной системы максимально просто и абстрактно, только передав суть.

1 Хранение кирпичиков

Тут все просто и понятно: каждая отдельная функция, класс (а лучше даже метод класса), контроллер модуля, представление модуля и т д — это отдельная строка в бд. Например в простейшем случае таблица может иметь вид id|code|name|componentType (где componentType — тип кирпичика(функция, класс, модуль..))

2 Хранение зависимостей

Так как код одного кирпичика может вызывать другой кирпичик (например зависимости типа функция-функция, модуль-функция или даже страница-модуль), то нужно хранить репликации. Сделать это можно с помощью таблицы репликаций, которая, в простейшем случае, имеет вид id|parentId|childId. Таким образом мы решаем проблему правильного сбора «кирпичиков» для вложенных конструкций:

function A()
{
B();
}



В этом случае в таблице репликаций будет запись, что А «нуждается» в B. Следовательно при подключении А автоматом будет подключена B.
3 генерация кода страниц

Хорошо, у нас есть все кирпичики, но как из них собрать код страницы? Для этого, конечно же, нужен отдельный скрипт, который будет собирать из наших бесполезных самих по себе «кирпичиков» работоспособный код страницы. Назовем этот скрипт Codegen. Каким он будет зависит от того, что и как вы хотите собрать из своих «кирпичиков». В этом заключается одна из сильных сторон подхода: из одних и тех же кирпичиков вы можете собирать принципиально разные коды страниц. Можете даже собрать «классическую» архитектуру. Во избежание недопониманий: генерация кода страницы годегеном происходит 1 раз, а не при каждом обращении к странице.

На выходе получаем монолитный сгенерированный код для каждой страницы. При этом, в зависимости от Codegen, возможно как сразу получать весь необходимый код для страницы, так и подгружать некоторые части во время выполнения страницы (посредством eval из базы).

Пожинаем плоды


Таким образом мы можем достичь следущих главных результатов:
— полное отсутствие инклудов на странице
— сведение «мертвого» кода к нулю


Вот что это дало в моем конкретном случае:
  • количество кода сократилось с 12000-14000 до 1500-2000 строк на странице
  • количество инклудов на странице сократилось с 16-22 до 0
  • Время генерации страницы сократилось с 0.25-0.3 до 0.04-0.05 секунды (~600%. Напоминаю, что это без кеша в классике. с кешом цифра будет поменьше)


За и против


Рассмотрим подробно плюсы и минусы идеологии хранения кода в бд.

Минусы
-Невозможность полноценно использовать IDE.Как следствие.Так как код хранится в бд, то для его редактирование/написание должен быть свой интерфейс(я например использую веб интерфейс). Как это примерно выглядит, можно посмотреть здесь. Вообще для меня особых неудобств это никогда не представляло. Все необходимые мне инструменты (подсветка кода, горячие клавиши..) могу быть легко реализованы на веб интерфейсе. Для тех, кому нужно большее, полноценной замены IDE все же нет.

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

-Поддержка. Как и у всего, что не распространено поддержки вашего проекта другими разработчиками не будет никакой. Действительно проблема, которая решается только популяризацией.

В этом топике так же были указаны еще минусы с которыми я попробую поспорить:

исходники это файлы, в итоге с ними можно делать любые файловые операции

Честно сказать, я не представляю что можно такого сделать с файлом, чего нельзя будет сделать со строкой в бд. Наоборот, строка в бд — куда более гибкая вещь чем файл.
Распостранение/backup/update

… делается с дампом sql(один файл) намного проще и быстрее, чем с большим количеством файлов при классике.
Безопасность, прямой код инжекшн в случае проблем

Проблема Тоже кажется надуманной. Сделайте разных пользователи для баз движка и баз сайта.
Бекап, представляете, бывает так, что их не делают, и тогда любые ваши «кастомизации» on site коту под хвост если сломаеться база

Для работы движка (после того как сработает кодеген) бд уже не нужна. То есть сайт может работать и при выключенной бд.

Плюсы
Скорость. Для меня это стало решающим фактором. Впервые, когда я сравнил скорость на старом «классическом» движке и на новом, я был потрясен результатом.
Гибкость на макроуровне. Чем из наиболее мелких и простых частей состоит конструктор, тем более сложные вещи можно из него собрать.
Атрибуты у частей кода. Так как наши кирпичики хранятся в таблице, то каждому из них мы можем задавать какие либо атрибуты, посредством добавления соответствующего поля. Это действительно очень важная особенность, открывающая новые просторы в разработке.
Возможность проводить любую обработку исполняемого кода перед его выполнением. Как вы помните, весь код у нас проходит через codegen, а следовательно в нем мы можем его модифицировать произвольным образом. Например, применять языковые пакеты на стадии генерации кода страниц. Или еще таким образом: если в коде часто встречается какая-нибудь строка, например
if(!$user->isAdmin()) {ErrorLog('нехватает прав'); return;}


Вы можете вместо нее писать везде коротко
_CHECKADMIN


А на стадии генерации просто заменять его на нужный вам код. Так что предварительная обработка кода тоже дает простор для фантазии программиста.

Заключение


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

P.S. Если возникнет интерес, я могу продолжить эту тему описанием своей реализации предложенного подхода.
Tags:
Hubs:
+18
Comments 270
Comments Comments 270

Articles