История одного CRUD'а

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

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

В 2015 году, когда я пришёл на своё текущее место работы, мне было непривычно от необыкновенной свободы действий. Буквально, на новом месте можно было проявить весь творческий потенциал как DevOps-евангелиста. Мне нравилось выстраивать процессы, автоматизировать рутину, делать разработку удобной. Больше всего я люблю оптимизации, а больше всего ненавижу - рутину.

Эта история одной боли и попытке не просто "принять обезболивающее", а реально излечить её. Поэтому готовьтесь переварить лонгрид.

Предистория

Когда Роман ещё работал со мной, то я предложил ему написать статью на эту тему. В каком-то смысле, она раскрывает суть и видение на тот момент времени. Но всё идёт своим чередом и много воды утекло с тех пор. Если Роман описывал ситуацию с большим уклоном в сторону технических деталей, то я хочу сделать акцент больше на архитектурных и управленческих. Проще говоря, я хочу изложить взгляд как руководителя на проблему автоматизации разработки.

Итак, чтобы прийти к тому, где мы сейчас находимся и получить качественный инструмент и опыт, нам пришлось пройти через 6 итераций, чтобы понять как выстраивать наши процессы.


Итерация первая: сгружаем общий код

Первой попыткой было просто избавиться от шаблонного кода, выделив наиболее популярные части в общей библиотеке. На тот момент, было несколько проектов, в которых раз за разом приходилось копировать код из одного в другой. Это порядком надоедало. В конце концов, для чего придумали наследование?

Так появилась нулевая версия проекта vstutils. По большому счёту это был и не проект вовсе. Там хранились какие-то наработки по созданию новых проектов, подключалась директория со статикой, которую так же можно было включать в шаблонах по мере необходимости. Базовые классы моделей, наработки для создания REST API вьюх, базовый класс для тестов... Конечно у искушённого читателя это вызывает улыбку (или скорее ухмылку), но это был первый шаг, а проект был больше файлопомойкой.

О сборке статики при помощи каких-то инструментов типа webpack или gulp даже не было и речи на тот момент. Это просто код, который мы не хотели подключать со сторонних ресуров, чтобы приложения работали даже без интернета в закрытом контуре. Тут надо понимать, что большинство наших проектов были как раз таки для подобных условий работы и были простыми админками. Admin LTE (тогда ещё версии 2) был выбран случайно и лишь из-за того, что предыдущие проекты были с его участием. Не сказать, что это шедевр дизайна или там есть какие-то сверхкрутые инструменты, но для наших целей было достаточно, а что-то новое изучать и поддерживать (мигрировать) старые проекты совсем не хотелось.

В общем и целом, процесс разработки был следующий:

  1. Бэкендер пишет код, покрывает его тестами.

  2. Фронтендер пытается из этого что-то осмысленное нарисовать.

  3. Бэкендер дописывает логику, которой нехватает Фронтендеру для реализации чувства прекрасного.

  4. Фронтендер доделывает админку и выкатывает это в общую ветку.

Как вы уже поняли п.2 и 3 повторялись несколько раз и зацикливались. Т.к. к тому времени я уже руководил командой, меня больше всего напрягали ситуации, когда backend уже готов, а фронтенд чем-то другим занят и не может сделать маленькое исправление, которое на поверке оказывалось порой очень большим.

Одним ужасным утром...

...я получил на почту заявление на отпуск от своего коллеги на один месяц. По большому счёту это был единственный, кто знал весь фронт и меня охватил ужас, потому что я больше всего ненавижу писать код на JS. Не поймите меня неправильно, я ничего против языка не имею против (на самом деле имею, но не до ненависти), просто я вырос в разработчики из админов, а потом долго писал на C# и Python, а для JS'а я совсем не заточен.

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

От задачи к задачи, на фронте решались тривиальные задачи в виде рисования формочек и полей, которые были друг на друга похожи и отличались лишь некоторыми условиями. Запросы ходили такими окольными путями, что понять где и в какой момент происходит запрос можно было только лютым дебагом. Тогда я задумался, а почему мы должны это делать снова и снова? Неужели этот процесс никак не автоматизировать? В конце концов, почему мы пишем один и тот же код просто на двух языках? Это же контрпродуктивно!

Итерация вторая: связываем backend с frontend

Первый мажорный релиз был далёк от идеала, но это был первый большой шаг к нему. Меня как автоматизатора и руководителя больше всего раздражала проблема дублирующего кода. Раз за разом мы что-то меняли в коде на backend'е, что практически всегда приводило к поломкам фронта. При этом, если код на сервере был оттестирован и хотя бы покрыт на 100%, то клиентская часть тестировалась методом научно-ненаучного тыка.

Как только проблема была обозначена и выделена, я отправился в глубокое поисковое исследование, ища наиболее оптимальный способ сообщить клиенту о том, как работать с API. На самом деле, вы удивитесь как много различных способов это сделать. Перейдя от поискового исследования к описательному, я остановился на следующих технологиях:

  • Protocol Buffers - по своей сути это бинарный протокол сериализации структурированных данных. Может не совсем точно выразился, но суть в том, что он говорит о том, какие данные летают у нас между клиентом и сервером, собирают это в некоторые классы и реализуют сериализацию/десериализацию. Опустим детали реализации протокола HTTP (ключевое слово "текст") и понятия бинарности данных. Самое главное, почему решили отказаться от данного решения, это громоздкость, необходимость что-то компилировать, прежде чем начать обрабатывать запросы (т.е. все недостатки статической типизации). Но самая главная причина - сложность для последующего взаимодействия. Если REST API можно использовать даже curl'ом, то с protobuf взаимодействовать сильно сложнее.

  • SOAP (Simple Object Access Protocol), который ни разу не simple, да ещё и XML. Одна из причин, почему мы отказались от этого варианта описана в Википедии: "Использование SOAP для передачи сообщений увеличивает их объём и снижает скорость обработки. В системах, где скорость важна, чаще используется пересылка XML-документов через HTTP напрямую, где параметры запроса передаются как обычные HTTP-параметры."

  • HATEOAS - перспективный протокол (хотя это больше архитектура построения REST API), где сам ответ с сервера говорит о том, что можно дальше сделать. В каждом ответе от сервера содержатся глаголы и ссылки на взаимосвязанные данные. Протокол ближе всего к идее HTTP и того как рисуются страницы веб-приложений, но в машиночитаемом виде. В общем-то этот вариант был долго моим фаворитом, потому что позволял довольно динамично менять интерфейс в зависимости от данных и клиента, который запрашивает или отправляет эти данные. Более того, не пришлось бы менять архитектуру API, кроме схемы отправляемых данных, а формат "летающих" данных мог быть разнообразным. Однако самой большой проблемой был объём передаваемых данных. Там, где прилетало всего два коротких поля, был огромный оверхед ссылок на действия с этой сущностью. При всём при этом, набор ссылок мог быть идентичным от запроса к запросу, а как-то закешировать данные было невозможно или потенциально ошибочно.

  • Swagger 2.0 или OpenAPI - спецификация описывающая REST API с помощью формализованной схему. Сама спецификация построена так, чтобы её мог легко прочитать как человек, так и машина. Такой формат схемы позволяет динамически синхронизировать интерфейсы со структурой API, сохранив объём передаваемых данных на прежнем уровне. Как и у HATEOAS формат передаваемых данных не зависит от структуры API и может быть любой. Схема расширяема и позволяет передавать нестандартные (неформализованные в community) настройки, что нам в последствии очень пригодилось, хоть и делали мы это не правильно. Особенность этой спецификации в том, что из неё можно сгенерировать практически любое клиентское или даже серверное приложение на огромном списке ЯП, хотя мы этим ни разу не пользовались. Это была любовь практически с первого взгляда. Почему практически? Потому что сперва мы напоролись на CoreAPI, что вообще никак нам не помогло решить проблему взаимодействия. Возможно мы неправильно её (спецификацию) приготовили, но история забвения coreapi говорит о том, что некоторый смысл в нашем решении был.

Собственно на Swagger 2.0 мы и остановились. Так как наш стек базируется на Django Rest Framework, то мы решили воспользоваться библиотекой drf-yasg, даже не смотря на то, что она не поддерживает OpenAPI 3ей версии. Возможно это было ошибкой, потому что сейчас мы частенько облизываемся на функции в новом стандарте, но всё делалось практически "на лету", поэтому выбирали из того, что легче всего изучить и применить.

Где-то на этом же этапе родился так называемый bulk endpoint, который должен был решить проблему агрегации запросов в один. Хотелось организовать набор запросов так, чтобы можно было выполнить их последовательно и откатить транзакцию в случае ошибки в одном из запросов. Идея родилась в момент, когда нам нужно было наполнить одну сущность набором других сущностей, которые в этом же запросе и создавались. При этом мы не хотели делать это на backend'е в каком-то конкретном action, а именно использовать имеющиеся endpoint'ы. В скоре, нам понравилось так агрегировать запросы и мы сделали так же нетранзакционную точку входа. Оказалось, что некоторые действия по обработке данных запроса можно оптимизировать. Например, можно было один раз авторизовать пользователя, а не делать это на каждый запрос в наборе. Потом появились зависмые запросы (когда данные предыдущего запроса использовались в следующем) и куча полезного "сахара". Наверное мы изобрели свой велосипед, но он нам очень понравился.

Не буду вдаваться в подробности остальных ошибочных решений, но в идеале с релизом наш процесс разработки должен был выглядеть так:

  1. Backend'ер создаёт модель, сериализаторы и вьюхи, которые наружу торчат некоторым набором endpoint'ов. Это всё появляется в схеме, которую можно использовать как документацию к API, делать оттуда запросы и т.д.

  2. Клиент при загрузке web-приложения при первом запросе скачивает схему и на её основе рисует интерфейс.

  3. Если стандартных средств автогенерации нехватает, то подключается frontend'ер и дописывает через сигналы нужную функциональность.

Звучит как план, правда? На деле же мы столкнулись с большими проблемами, которые были связаны с ошибками проектирования. Самой главной ошибкой было использование шаблонизатора, который был форкнут и дописан нашим коллегой, а не популярного решения. Количество костылей в этом шаблонизаторе превзошло все наши ожидания. Верите или нет, но vDOM может быть медленнее, чем DOM. Естественно с увольнением сотрудника, чьё это было решение ушла и компетенция. Наверное это был мой самый большой fail как руководителя.

Однако, решение как никак, но работало. Большинство стандартных CRUD'ов делались автоматически и даже работали. Это была маленькая, но победа над рутиной.

Итерация третья: избавляемся от костылей

Версия 2.0 принесла освобождение от костыля в виде старого самописного шаблонизатора и первую попытку сделать всё с Vue.js. Хотя Роман довольно неплохо описал, почему мы выбрали именно Vue, я попробую всё же не сильно повторяясь резюмировать.

Основным критерием была сложность. Хотелось подобрать такую экосистему, которая позволит как находить новых сотрудников, так и обучать, если определённых компетенций не было. У Vue просто потрясающая документация, которую довольно легко читать. Сам проект довольно простой, а community довольно доброжелательное, не пропитанное "кровавым enterprise".

Простота, так же залог скорости. Если сравнивать кодовую базу Vue с тем же React, то разница в gzipped версии в 5-6 раз. И так со всей экосистемой. Более того, оглядываясь назад и смотря на производительность Vue 3, я убедился в правильности своего решения при выборе фреймворка.

На этой итерации, я принял решение, что нужно сделать код Frontend'а более понятный для разработчика на DRF. Это позволило бы backend'еру быстро сориентроваться в коде и понять какие изменения ему потребуется внести без участия frontend'ера. Звучит как утопия, но, отчасти, нам удалось это реализовать привнеся некоторый шарм в архитектуру js кода. Единый шаблон проектирования (по сути MVT - Model+View+Template) позволил убрать разделение на Frontend и Backend разработчиков и перейти к управлению акцентами в компетенции.

Кропотливый труд и несколько бессоных ночей сильная мотивация дали свои плоды. Интерфейс стал заметно озывчивее, разработчика ускорилась, а поиск новых сотрудников стал заметно проще. На этом этапе статья Романа и обрывается. Не вдаваясь в подробности, мы расстались с ним на добром, а я остался с SRE, backend'ером и стажёром с горящими глазами. Ну и тремя проектами, которые одновременно требовали внимания...

Итерация четвёртая: делаем фронт лучше

Версия 3.0 была новой попыткой сделать разработку удобнее. Отсутствие инструментов сборки фронта превращало в ад создание новых и редактирование старых компонентов. Более того, мы столкнулись с тем, что наш код переставал работать на некоторых версиях браузера, а при разработке приходилось из раза в раз лазись в caniuse для того, чтобы убедиться, что этот синтаксический сахар можно использовать. Ещё большей болью были обновления зависимостей на фронте. Так же, механизм bulk-запросов нуждался в обновлении и переосмыслении. Мы увидели, что сделали в принципе хорошо, но ещё рок-н-ролл.

Webpack и однофайловые компоненты

Собственно сперва нужно было решить проблему сборки. Я с большим скрипом добавил в CI'ные базовые образы node и yarn. Оказалось не всё так плохо, особенно после большого рефакторинга самих образов.

Самой чувствительной частью была сборка пакета. Так как мы используем setuptools и рассчитываем, что установка нашего пакета должна происходить на системе без node.js, то необходимо было реализовать сложный механизм компиляции статики в момент сборки образа и учёта того, что статика может быть уже собрана. Т.к. тут много разных "но", пришлось действовать практически на ощупь. Постановили так: если есть конфиг webpack'а, то это значит надо собирать статику. Проблема в том, что собралась статика или нет, чаще всего узнаёшь на проде, а это всегда больно. Более того, мы практически лишились возможности собирать проекты с зависимостью от git-репозитория, потому что это требовало огромного набора пакетов для сборки.

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

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

Единый endpoint

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

Мы решили не ломать старый механизм, а написать новый костыль, где мы задействуем всю мощь DRF в виде сериализаторов и полей. На выходе нам удалось получить лучшее решение, а конкретно:

  • Единая точка входа для всех приложений.

  • Поддержка разных версий API внутри каждого запроса, а не всей пачки.

  • Добавили тайминги по работе каждой операции, чтобы можно было отследить проблемные.

  • Поддержку выполнения запросов асинхронно.

  • Добились идентичного разультата работы как напрямую, так и через endpoint.

С этого момента мы начали уже не работать на инструмент, а использовать его со всей мощью и усердием. Теперь, прежде чем реализиовать что-либо в проекте, мы задумывались о том, чтобы принести этот код во все проекты. Релизы выкатывались как вода из крана: от 5 до 10 в день. Можно сказать, что в каком-то виде я достиг своей цели, если бы не одно но... Код работал как-то не так, как мы этого ожидали и было много исправляющих релизов, оттуда и много релизов для скромной команды и трёх проектов. Оказалось, что очень многие вещи, которые были реализованы просто не работали и были "для красоты". Такого фейла я не ожидал, но нужно было действовать.

Итерация пятая: Авен-Езер

Можно сказать, что версия 4.0 это некоторое подведение итогов автоматизации процесса разработки и этап оптимизации. Мы полностью переработали JS-код, чтобы он был похож на MVT и работал предсказуемо. Тут же стояла задача покрыть новый код тестами настолько, насколько возможно. На этом же этапе, мы переосмыслили процесс разработки на backend'е. Но обо всём по порядку.

Frontend

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

Мы постарались написать код так, чтобы задействовать возможности bulk'а на максимум. Там где это было возможно, мы ускорили работу с перерисовкой списков, добавили возможность как можно меньше писать кода на js и как можно больше решать проблемы с помощью модификации схемы или полей сериализаторов на backend'е.

В какой-то момент, мы обнаружили что механизм работы с полями, который мы хотели использовать для сериализации/десериализации просто не работал, поэтому мы переписали код с поддержкой этих возможностей. Теперь были конкретные точки входа и выхода, с помощью которых можно было 100% получить ожидаемый результат.

Backend

Тут произошло больше всего работы. Когда взаимодействие уже автоматизировано, то становится скучно и хочется писать кода ещё меньше. Итак, как выглядит типичная задача для CRUD endpoint?

  1. Пишем тест.

  2. Рисуем модель (тест не ходит).

  3. Пишем сериализаторы (тест не ходит).

  4. Пишем вьюху и подключаем сериализаторы и модель (тест ходит частично).

  5. Рисуем фильтры и пермишены (тесты проходят полностью).

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

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

Портянка кода
from django.db import models
from rest_framework.fields import ChoiceField
from vstutils.models import BModel


class Stage(BModel):
    name = models.CharField(max_length=256)
    order = models.IntegerField(default=0)

    class Meta:
        default_related_name = "stage"
        ordering = ('order', 'id',)
        # fields which would be showed on list.
        _list_fields = [
            'id',
            'name',
        ]
        # fields which would be showed on detail view and creation.
        _detail_fields = [
            'id',
            'name',
            'order'
        ]
        # make order as choices from 0 to 9
        _override_detail_fields = {
            'order': ChoiceField((str(i) for i in range(10)))
        }


class Task(BModel):
    name = models.CharField(max_length=256)
    stages = models.ManyToManyField(Stage)
    _translate_model = 'Task'

    class Meta:
        # fields which would be showed.
        _list_fields = [
            'id',
            'name',
        ]
        # create nested views from models
        _nested = {
            'stage': {
                'allow_append': False,
                'model': Stage
            }
        }

На выходе, если подключить модель Task в API, то получится 4 endpoint'а (два списка и две детальных записи), где Stage вложен в Task. При этом будут фильтры по полям из списка автоматически. Согласитесь круто, за 47 строк кода?

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

Итерация шестая: доводим до абсурда

Фактически это текущий этап (правда в beta-обёртке). Ещё никогда я не выпускал бета-релиз под трёхзначным номером. Но нам постоянно кажется, что ещё вот чуть-чуть и наступит дзен.

На этом этапе мы сконцентрировались на трёх вещах.

Интерфейс

Оказалось, что для многих проектов необходимо перерабатывать интерфейс по одному лекалу. Мы проанализировали эти наработки и оказалось легче перенести это в базовый проект и избавиться от кучи шаблонного кода в рабочих проектах.

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

Чтобы легче было перерабатывать весь интерфейс, мы разделили код настолько, что можно заменить практически весь интерфейс так и не притронувшись к основному шаблону.

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

Сервисы

Каждую боль, мы решили по возможности решать внешними сервисами или системой плагинов. Мы проработали этот механизм так, чтобы можно было основное приложение превратить в один пакет с одним модулем settings.py.

Например, решить боль автообновления интерфейса мы собираемся через Centrifugo. Мы проработали механизм подключения внешних хранилищ с помощью django-storages. Доработав механизм подключения БД, теперь можно с лёгкостью реализовывать любые задумки по шардированию данных.

Оптимизация

Оказалось, что если поотключать кучу неоптимизированного кода, то агрегация запросов может быть быстрее, чем непосредственно сами запросы напрямую. Множество различных мест в коде, удалось оптимизировать и получить максимальную производительность "из коробки". С учётом размера команды, это позволяет малыми усилиями делать максимальный результат.

Конечно, это не всё, что мы сделали на данный момент. Нам пришлось переработать весь код, чтобы он стал ещё более понятным и простым для разработчиков, потому что мы делаем это в первую очередь для себя и команды. Поэтому мы поработали и над документацией, чтобы она стала более понятной и актуальной в свете наших задач. Но самое главное, мы стали спать спокойнее, потому что покрыли 85% всего кода тестами и не занимаемся постоянными фиксами базовой функциональности.


Подводя итоги

Всё началось с одного CRUD'а, который заставил меня задуматься над эффективностью работы, а закончилось созданием целой платформы. За последние годы количество кода стало такое, что если бы мне сказали, что мы такой скромной командой сможем написать такой объём, то я бы ни за что не поверил. У всего этого решения есть свои плюсы и минусы.

Плюсы:

  • Избавились от шаблонного кода (не полностью, но до разумного предела). Теперь большая часть рутины это написание тестов. Разве не здорово? Можно заниматься самой интересной частью проекта и не тратить время на ерунду.

  • В следствие предыдущего пункта, скорость разработки увеличилась, а количество релизов в день умножилось вдвое, при этом размер команды остался тот же. Всего один бекэндер может создавать довольно сложное веб-приложение. А при минимальных знаниях JS можно творить по-настоящему крутые штуки.

  • Вся базовая логика и оптимизации сконцентрированы в одном проекте, что автоматически подхватывается на оконечных проектах. Тут же получается и упрощение контроля безопасности (хотя тут конечно палка о двух концах).

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

  • Проще подбирать и обучать персонал. Найти людей, которые бы знали Django или Vue довольно легко, поэтому можно сделать упор именно на важном - на людях (коллектив и коммуникации это основа DevOps Way же!). В конце концов простые вещи обычный бэкендер может сделать даже без участия фронтендера, что исключает проблемы взаимодействия. В тех же случаях, когда нужно исправить front, есть чёткий контракт в виде схемы API, с описаниями и документацией.

  • Поддерживать проекты стало сильно проще, особенно те, где вообще не задействован или не реализован js-код сверх базового.

  • Делая проект получается автоматически хорошее REST API в довесок к довольно не плохому (имхо) интерфейсу. Т.е. это не уродливая нефункциональная джанго-админка, а качественный backoffice и хорошее API.

Минусы:

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

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

  • Поддерживать проекты переходящих на новую мажорную версию платформы просто до тех пор, пока фронтовая часть там или простая, или отсутствует вообще. Но как только есть что-то довольно сложное и сильно кастомное, то переход становится очень болезненным.

  • Холодная загрузка приложения это пока что наибольшая проблема. Генерация схемы занимает довольно приличное время (на одном из проектов с огромным количеством endpoint'ов генерация схемы занимает 800мс). Но справедливости ради, схема генерируется только в момент обновления или первого входа в приложение. В дальнейшем схема приходит из кеша.

В планах так же перевести всё оставшееся на SPA (всё что связанно с учёткой, паролями, авторизацией, регистрацией и т.д.), вынести фронтовые наработки в NPM-пакет (чтобы можно было прикручивать любой backend написанный на чём угодно, хоть на Go или, не дай Бог, PHP), добавить возможность собирать мобильные приложения из проектов (сейчас можно, но пока сложно), избавиться от кучи legacy-кода, перейти на OpenAPIv3 и многое другое.

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

Хотелось бы так же добавить, что как DevOps объединяет разработчиков и администраторов, так и настоящий FullStack не обязательно должен пытаться захватить не свою нишу. Напротив, тесное сотрудничество front и back позволяет использовать сильные стороны друг друга и делать что-то хорошее.


Полезные ссылки

  • Документация vstutils

  • Исходники на Github и Gitlab

  • Спецификация OpenAPI

  • Предыдущая статья о проекте

Источник: https://habr.com/ru/post/660847/


Интересные статьи

Интересные статьи

Просидев на одном предприятии несколько лет, я решил поискать альтернативы. Специально не привожу детали по моей должности, квалификации и стажу, чтобы не создавать предвзятое впечатление и не влиять ...
Никогда не думал, что буду писать на тему криптовалюты. Ну и Хабр сообщество относится к этим темам достаточно негативно. Ну тут дело даже не в самой пирамиде очередной говномонеты DagCoi...
25 лет назад, 4 декабря 1995 года JavaScript был впервые представлен миру. Мы хотим отметить день рождения языка и поблагодарить вас за его поддержку все эти годы, поэтому мы подготовили ...
Пренеприятнейшая история случилась с одним моим знакомым. Но насколько она оказалась неприятной для Михаила настолько же занимательной для меня. Надо сказать, что приятель мой вполне себе UNIX-п...
Как обновить ядро 1С-Битрикс без единой секунды простоя и с гарантией работоспособности платформы? Если вы не можете закрыть сайт на техобслуживание, и не хотите экстренно разворачивать сайт из бэкапа...