Как Я Технический Стек Пересмотрел

В 2021 году, когда приложение по учету финансов из AppStore потеряло наши данные за 2+ года, я собрал MVP econumo за 2 недели на PHP + Symfony + PostgreSQL + VueJS, но спустя 3 года поддержки я пересмотрел технический стек для бекенда, и вот почему.

Давайте разбираться

Мне нравится современный PHP. Этот язык здорово развился за последние несколько лет: разработчики прокачали как сам язык, так и его перформанс. PHP оброс инфраструктурой и в сообществе разработки все чаще вспоминают Laravel, как хороший пример простого фреймворка с низким порогом входа и кучей возможности. Появился rector, который существенно упрощает миграцию, как для популярных библиотек, так и для версий языка(rector сам меняет ваш код, применяя возможности из новых версий и заменяет то, что стало недоступно). Минусы, в общем-то, никуда не делись, но если признавать их и знать, как с ними работать, то проблем особо и нет.

С 2014 года я использую Symfony. Это серьезный фреймворк, который из коробки дает отличную базу для разработки приложений, следуя лучшим практикам (зачастую заимствованных из Java), и обладая из-за этого более высоким порогом входа, по сравнению с Laravel, например. Это хороший фреймворк для работы в команде, но стоит признать, что часто мы тратили больше времени на конфигурацию, нежели чем на кодинг.

PostgreSQL - мощная, бесплатная и открытая СуБД с возможностью гибкого скейлинга и различных конфигураций для разных кейсов, от хайлоада до биг-даты. Является выбором по-умолчанию для многих разработчиков с разных ЯП.

Вы можете сказать, что у представленных инструментов есть куча проблем и сложностей (и будете правы), но речь будет не об этом.

В мире разработки курсируют два основных мнения:

  • Для быстрого старта начинай использовать то, что хорошо знаешь;
  • Под каждую задачу нужно использовать тот инструмент, который подходит для решения этой задачи.

В 2021 году я считал, что мой стек соответствует обоим пунктам, но спустя 3 года могу рассказать в чем я заблуждался (и это не про перформанс).

Инфраструктура и ее эксплуатация

Я понимаю, что я не DevOps и поэтому предпочитаю держать свою инфраструктуру максимально простой - так меньше потенциальных проблем, плюс легко написать Ansible-скрипты. Для личных проектов скейлинг и прочие радости хайлоада не нужны, а вот наличие бэкапов и Infrastructure as Code - считаю must have.

Если не устанавливать PHP в систему, а использовать Docker-compose, то для проектов с низким трафиком проблем точно не будет. Единство окружений - это удобно для разработки. С rector обновление версий не выглядит сложным, но все равно значительное время придется потратить, чтобы отревьювить изменения и провести регресс.

PostgreSQL тоже можно использовать в Docker-compose, но придется повозиться с налаживанием бекапов, а так же с процессом обновления (особенно мажорных версий). Это незаметно, пока работаешь в команде с выделенными ролями, но когда начинаешь заниматься этим сам в свободное от работы время (пет-проект же), то понимаешь, что это довольно ресурсоёмко.

Чтобы убрать с себя часть нагрузки, я решил попробовать AppPlatform от DigitalOcean. Просто предоставил docker-образ с приложением, заказал Managed PostgreSQL и довольный начал платить $50 в месяц за эту красоту. И все это на минимально возможных мощностях.

Поддержка и обновление пакетов

Symfony динамично развивается, и за 3 года успешно представил 2 новых мажорных версии фреймворка. Я изначально приземлился на LTS версию с желанием не обновляться часто, однако мой deprecation.log выглядит уже не очень. Использовать rector для обновления версий пакетов мне не удалось, так как есть разные зависимости. В какой-то момент Dependabot принес мне критическое обновление версии одного из core-пакетов, что потянуло за собой обновления других пакетов, и в итоге поломав мне авторизацию. Изменения пришлось откатить и от обновления временно отказаться.

Неочевидно на первый взгляд

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

Благодаря принципу

Для быстрого старта начинай использовать то, что хорошо знаешь

я быстро стартовал и мы начали использовать econumo уже спустя пару недель, что дало мне мотивацию продолжать работать над проектом, улучшая и адаптируя его под наши нужды.

А вот когда я думал про

Под каждую задачу нужно использовать инструмент, который подходит для решения этой задачи.

я оценивал сам проект с технической точки зрения. Это личный проект, нагрузка отсутствует, потенциально open source. Реляционная СуБД подходит отлично. PHP проблем не вызовет. Если очень захочется - можно масштабировать горизонтально.

Однако, на более продолжительной дистанции, вектор оценки оказался не полным, и я увяз с инфраструктурой и пакетами, увеличивая риск демотивации и остановки развития и поддержки проекта. Добавилось еще то, что спустя год хостинга я понял, что потратил $600 чисто на инфраструктуру, что для хобби не так уж и много, но в целом можно было бы потратить на что-то другое.

Солопренерство

Я почти смирился с тем, что застрял на старых версиях в моменте. А потом купил VPS за $5 на Hetzner и развернул всю инфраструктуру в докере, снизив регулярные расходы в 10 раз. К слову, за год ничего не произошло и все работает стабильно.

Но мысль о том, что что-то не так, не давала мне покоя. А потом я наткнулся на любопытное видео - https://youtu.be/lASLZ9TgXyc?si=S8iUgjFWxhiC-gma (htmx тут не при чем) и меня осенило, как можно делать иначе.

Стало понятно, что если бы это был коммерческий проект и работа была в команде, то проблем бы не было. Однако, основное ограничение, которое было не очевидно на старте - это я сам. А точнее то, что я работаю один над проектом, имея в запасе нерегулярные часы, которые я могу уделить разработке. Я понял, что я могу тратить минимальные ресурсы на поддержку инфраструктуры и обновление зависимостей, если скорректирую стек.

Замена

Я решил поменять PostgreSQL на SQLite. Первичная реакция у многих разработчиков была такой: “ты дебил?”. Но если отбросить привычные доводы, то остается набор сухих фактов:

  • SQLite - это либа, очень стабильная. SQLite3 уже порядка 20 лет. Мигрировать базу не надо, обновления приходят с операционной системой. Обратная совместимость 100%.
  • Делать бэкапы ну очень просто, не нужно никаких дополнительных тулзов, особенно, пока база небольшая.
  • Запускать новые окружения, гонять тесты в CI, дебажить проблемы очень просто - достаточно скопировать/создать файл базы.
  • Для econumo не требуется интенсивная запись, но требуется много чтения. И в этом SQLite хорош!

В общем, я экспортировал SQL из PostgreSQL и создал SQLite базу на его основе - ни единой проблемы! Во время адаптации приложения я столкнулся с некоторыми проблемами (об этом ниже), но в целом миграция из одной СуБД в другую прошла довольно успешно. И как бонус, по сравнению со средним response time в облаке DO за $50 в месяц в 400мс, response time для $5 VPS с SQLite упал до 50ms.

Осознание

Во время миграции я столкнулся с некоторыми особенностями реализации адаптера SQLite для Doctrine (например, работа с date-полями в SQLite), из-за которых вместо использования ORM методов я просто перешел на обычные SQL-запросы в репозиториях. И это стриггерило меня на следующие мысли:

  1. Если бы я просто использовал PDO с обычными SQL-запросами, то достаточно было бы просто заменить конфигурацию проекта.
  2. Мнение, что Doctrine предоставляет универсальный интерфейс и позволяет легко мигрировать с одной СуБД на другую - ошибочно. Doctrine предоставляет универсальный интерфейс, что усложняет миграцию, так как ты не знаешь, реализованы ли все необходимые абстракции и функции.
  3. Doctrine предоставила несколько классных инструментов: репозитории, меппинг данных на сущности, фикстуры и миграции. Но их довольно просто можно реализовать самому в минимально необходимом виде.
  4. Я себя поймал на мысли, что я забыл синтаксис SQL для создания/обновления таблиц, индексов, внешних ключей, так как привык генерировать их через Doctrine. А когда начал генерировать миграции для SQLite нашел пару неявных багов, которые я бы не заметил, если бы не разбирался с работой внешних ключей в SQLite.

Доктриной мысли не ограничились и привели меня к тому, что для единственного разработчика вся эта абстракция, которая изначально воспринималась как благо, на самом деле является проблемой. Если ты опытный разработчик, то тебе не нужны все эти академически правильные библиотеки и абстракции. Ты прекрасно можешь спроектировать приложение, которое будет выполнять свою функцию и без фреймворка. Когда приложение большое, и над ним работает команда - тогда готовая архитектура и фреймворки помогают синхронизироваться команде, упрощают онбординг младших разработчиков и передачу знаний и кода. А когда ты один - тебе это не нужно. Я осознал, что не нужны мне все эти внешние библиотеки и зависимости. Я и сам прекрасно могу реализовать очень простой Dependency Injection, контроллеры, репозитории, авторизацию и т.д. И это будет гораздо проще и гибче, так как я решаю свои конкретные задачи. Обновлять такой проект будет очень просто и быстро.

Переписывать, конечно, я проект сейчас уже не возьмусь, но выводы на будущее сделал.

Go-go-go

После еще некоторого размышления, я подумал, а какая технология сделала бы поддержку проекта максимально простой, сопоставимой с SQLite? В приведенном выше youtube-видео, докладчик использует node, что несильно отличается от PHP, но мысль такая, что node - простой и стабильный фреймворк, который почти не меняется.

Я решил абстрагироваться, и начал перебирать в голове языки программирования, которые

  • компилируемые (проект не соберется пока есть ошибки);
  • кросс-платформенный (тут как минимум linux/amd64, linux/arm64 или mac os);
  • с хорошей стандартной библиотекой, которая не меняется часто (не нужно тянуть много зависимостей);
  • простые. И осознал, что это ж Go!

В дополнение к списку выше стало понятно, что для него даже Docker не нужен, а сервис запускается прекрасно через systemctl. Про проблемы производительности вообще можно забыть. А значит можно хостить много приложений на VPS за $5!

Выводы

После трех лет работы над своим пет-проектом, я пересмотрел выбранный технический стек (для личных проектов) в пользу:

  • CloudFlare - dns-хостинг + выпуск и обновление SSL-сертификатов (бесплатно).
  • VPS на любом хостинге (я использовал DigitalOcean, Hetzner и сейчас сижу на бесплатном OracleCloud).
  • NGINX - в качестве реверс-прокси. Легко настраивается, обеспечивает безопасный коннект между cloudflare и сервером.
  • GO - в качестве языка разработки. Компилируемый, кросс-платформенный, простой язык с хорошей стандартной библиотекой.
  • SQLite - простейшая СуБД, которую не нужно обновлять, и которую легко бекапить. В случае чего - легко мигрировать данные в другую СуБД, либо использовать Turso.

А так же включил в оценку проекта фактор КТО, КОГДА, и КАК ДОЛГО будет заниматься его поддержкой. А вообще сейчас размышляю, а нужны ли все эти многослойности в коммерческой разработке, но это отдельный вопрос.


Что касается фронтенда - там все немного сложнее, но проскальзывают рекомендации использовать HTMX и Alpine.js. Сам пока не пробовал, но как разберусь, напишу отдельный пост.