Переход от бессерверного проекта к Ruby on Rails

Перевод статьи “Moving my serverless project to Ruby on Rails”
Алекса Котлярски от 14 ноября 2020 года.

У меня есть маленький сторонний проект: подарочные карточки для хакеров hacker gifts. Он использует Shopify для всего что связанно с продажей: внешний вид, оплата, возвраты, отчеты и пр.

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

Следующим шагом была автоматизация. Я подключил услугу бессерверных вычислений AWS Lambda. В то время это была новая технология и мне хотелось узнать про неё побольше. Мне казалось что она очень хорошо ложится в мой кейс: функция с одной ответственностью, которая может запускаться в любое время и не требует поддержания сервера.

Сначала всё было очень просто. Я написал JavaScript функцию, задеплоил её на AWS Lambda, добавил веб-хук в Shopify и всё заработало!

Начальные преимущества от бессерверных вычислений (для домашних проектов):

  • Легко начать
  • Не надо настраивать и поддерживать сервера
  • Бесплатно для небольшой нагрузки

Но в действительности написание простых Lambda-функций обернулось лишь десятью процентами работы.

Время шло и бэк становился сложнее. Понадобилось хранить состояния для каждой головоломки, рассылать письма, показывать информацию по заказам. То, что началось с одной простой функции выросло в целую кучу бессерверных функций, SNS-топики, S3-бакеты и таблицы в DynamoDB. Всё связывалось вместе YAML-клеем, бесструктурными JSON’ами то там то здесь и какими-то зашитыми конфигами в панели управления AWS.

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

Но в этот раз всё было иначе. Я не мог взять и просто отрефакторить всё так же как в традиционном монолитном приложении. Вот почему:

Когда части системы очень простые, то сложность системы сдвигается в область взаимодействия частей.

Но взаимодействие между бессерверными частями происходит вне моего приложения. Lambda-функция публикует сообщения в SNS, другая подхватывает его и пишет что-то в базу, третья берёт эту запись и отправляет email…

Я могу протестировать каждую отдельную часть этого процесса, но у меня нет уверенности в процессе целиком. Что если сообщение не дойдёт до очереди, как мне узнать об этом? Как система будет восстанавливаться? Как мне откатиться и попробовать заново? Где искать логи?

Другой ворох проблем прятался в конфигурации: bad Route 53 (DNS от AWS), опечатки в имени SNS-топика, ошибка в регионе бакета S3. Поиск ошибок был очень сложен, потому что нет единого лога, где можно поискать проблему.

При бессерверных вычислениях вы уже не работаете со своим приложением, вы работаете с распределённой системой.

В этот момент я почувствовал себя обманутым.

Я купился на простой деплой и возможность не думать о сервере, но в конце мне пришлось проектировать систему с учётом всех ограничений платформы.

Недостатки бессерверных вычислений (для домашних проектов):

  • Тяжело следовать за потоком обработки информации
  • Невозможно воспроизвести окружение локально
  • Низкая скорость выпуска новых версий продукта
  • Невозможно осуществить сквозное тестирование
  • Слабая документация (зачастую в устаревших постах на Medium)
  • Отсутствие соглашений (приходится принимать сотни незначащих решений)

Да и просто не было никакого удовольствия от этой бессерверной работы. Поэтому я решил всё переписать — ведь это домашний проект, который я делаю ради своего удовольствия. Выбор пал на Ruby on Rails.

Я не пользовался Rails с 2013 года и в течении восьми лет в Facebook я в основном занимался JavaScript’ом.

Опыт переезда обратно на рельсы был хорош, но…. Без происшествий. Изменилось немного: кое-что добавили, кое что изменили.

Конечно пришлось столкнуться с некоторыми магическими проблемами в Ruby. Но в отличие от аналогичных вещей в JavaScript, решения находились очень легко.

Rails поставляется с огромным количеством встроенных и настроенных фич. В JavaScripte мне приходилось пользоваться случайными библиотеками и согласовывать их чтобы сделать необходимый вещи: роутинг, обертки над файловыми хранилищами, предпросмотр писем, управление ключами, фреймворк с тестированием, миграции базы, логирование, метрики производительности, скрипты деплоя. С Rails я могу вообще не думать об этих деталях и сконцентрироваться на создании ценности продукта.

Это как сесть за Теслу, после нескольких лет ручной сборки машины из запчастей. Всё тоже состоит из запчастей, но отлаженных и хорошо состыкованных для совместной работы.


Преимущества от Rails (для домашнего проекта):

  • Соглашения
  • Инструментарий и библиотеки
  • Документация
  • Монолит который легко понять и протестировать

Недостатки Rails (для домашнего проекта):

  • Слегка тяжеловато для начала
  • Больно, если ты отходишь от соглашений
  • Необходим сервер
  • Звучит не круто в 2020 (уже не круто или возможно пока)

Бессерверные вычисления как чёрная дыра. Обещают быть увлекательным путешествием, но гравитация притягивает и приходится большую часть усилий тратить на преодоление сложности, вместо того чтобы сконцентрироваться на продукте.


Не знаю как вам, а мне эта статья очень зашла. Она как-то ложится сейчас в намечающийся тренд Rails Again. Чуть позже думаю будет более детальное изложение мыслей на этот счёт.

Оригинал: Moving my serverless project to Ruby on Rails от 14 ноября 2020 года.

Автор: Alex Kotlyarskiy