Николай Ланец
23 нояб. 2020 г., 9:25

Как переписать 400 000 строк кода и почти ничего не сломать

Всем привет!

Давно я ничего не писал, но это не значит, что я совсем отошел от дел :) Просто никто ничего и не спрашивал. Но сейчас я буду восстанавливать активность ибо есть много полезного материала.

Дело в том, что в этом году я много работал и освоил много интересных и полезных техник. Плюс ко всему поработал в СберТехе, где еще прокачал скилы в командной работе (на нашем проекте работало более 300 специалистов, это совсем не то, что дома фрилансить). Поэтому, уволившись в начале октября, я принялся переписывать свою PrismaCMS, дабы она более соответствовала современным требованиям, а именно, чтобы с ней можно было работать в команде, поддерживала техники CI/CD и т.п. В итоге получился довольно интересный продукт. Собственно, https://prisma-cms.com тоже переписывается на нем, и хотя работа эта еще не закончена, я все же буду выкладывать промежуточные варианты и рассказывать примерно что и как делал.

Итак, первое, что я сделал, это еще раз пробежался по рынку и посмотрел что есть из готового для создания современных веб-проектов. 
Требований было несколько:

  1. SEO-friendly. Предыдущая версия моего движка вполне себе справлялась с созданием каких-нибудь CRM, где SEO вообще никакой роли не играло, но я понимаю, что платформа должна подходить для создания практически любого уровня сайтов, в том числе и eCommerce, где SEO-требования очень высокие. Чаще всего без нормального SEO сайт просто не сможет привлечь покупателей.
    То есть должна быть возможность легко задавать мета-теги страницам, четкие серверные коды а-ля 200, 404, 401, редиректы и т.п., а так же удобный роутинг страниц.
  2. Качественный сборщик конечных скриптов. При этом важно, чтобы он умел делать отдельные бандлы для отдельных страниц, чтобы по мере наращивания функционала не все в кучу собиралось (чтобы не рос бесконечно общий бандл), а был один базовый бандл для стартового функционала сайта и, заходя на отдельные страницы, подгружался нужный для их работы код, если еще не был загружен. Это важно, чтобы гугл-тесты показывали хороший результат производительности, ведь чем хуже показатели, тем хуже позиция сайта в поисковой выдаче.
  3. Минимальный объем функционала "из коробки". Это должен быть именно фреймворк, а не готовая CMS со своим заложенным функционалом какой-то определенной направленности. То есть важно, что бы поддерживался самый важный базовый функционал, но чтобы это легко можно было расширять и дорабатывать, и не запутаться в тоннах кода. Да, если это взять как есть, то вот так сразу сайт не сделаешь. Но на этом вполне можно будет сделать пару-тройку заготовок и их уже использовать для создания конечных сайтов. Забегая вперед, скажу, что первая такая заготовка будет сделана в обозримом будущем и это будет такой аналог shopModx, только на JS.
  4. Это должен быть уже довольно зрелый программный продукт, на котором уже реализованы и работают конечные сайты, чтобы это был не какой-то очередной супер-стартап, а проверенный в бою продукт.
  5. И главное - поддержка TypeScript. Да, я долго писал на чистом JS, но теперь я переходу на TS, потому как даже в одного очень сложно обслуживать свой JS-код в чистом виде, ведь javascript не умеет в строгую типизацию, и легко передать в вызываемый метод не те параметры и ожидать совсем не того, что на самом деле можешь получить. Ну, мне очень сложно здесь объяснить почему все так, если вы не знакомы с TypeScript. Но я планирую провести ряд вебинаров и на примерах рассказать.
В результате, мой выбор пал на next-js, который практически полностью соответствовал моим требованиям. И хотя в процессе я столкнулся с некоторыми серьезными такими подводными камнями, которые отняли не один день на поиск решений, тем не менее, решения были найдены и это только лишний раз подтвердило, что выбор был сделан правильно. Нет программных продуктов без минусов, но если есть возможность самому эти минусы исправлять, то это очень жирный плюс продукту. Гораздо хуже, когда нельзя даже какую-то мелочь поправить самостоятельно.

Ну и как водится, все это надо проверять в боевых условиях, поэтому первым делом я взялся переписать https://prisma-cms.com на новом движке, чтобы было не в теории, а на практике понятно, что и как работает, а что нет. И вот всю самую суть этого маленького эксперимента можно обозначить этой картинкой :)

Честно сказать, я думал все будет проще и быстрее. То есть next-js к текущей версии сайта я прикрутил довольно быстро и в целом все заработало. Но Дьявол кроется в мелочах. Я же хотел, чтобы у меня сайт работал на новых технологиях и быстро. А у меня старая версия была сделана с уклоном больше в сторону функционала. Чего только стоят одни только фильтры. Но для работы многих этих компонентов требуется GraphQL-схема на стороне клиента. То есть прежде чем страница загрузится, сначала должен выполниться запрос на сервер, получить оттуда схему, распарсить ее и т.п., и только после этого сможет отрисоваться страница. А этом даже на не слабом компьютере занимает 0.2-0.4 секунды. А про мобильники я вообще молчу. Вот я и решил выпилить эти схемы, а компоненты сделать более независимые. И, как оказалось, это совсем не простая задача. То есть мне пришлось переписывать многие свои компоненты. Точнее, я планировал чутка подредактировать один-два компонента, но в итоге пришлось практически полностью переписать все основные компоненты. У меня же сайт не монолит, а использует кучу отдельных компонентов. При этом их пришлось переписывать самую их основу, выпиливая все старое вспомогательное, включая dev-сервер, чтобы они полностью соответствовали новым технологиям и основному проекту.

Если вдаваться в детали, то старые компоненты были основаны на create-react-app и react-scripts, а упаковывались с помощью nwb. И вот react-scripts до сих пор не умеет в нормальный Server-Side-Rendering, а nwb не умеет в TypeScript (да и вовсе по ходу проект загнулся). Так что в качестве дев-среды для компонентов тоже пришлось вводить next-js, а упаковку выполнять нативными средствами самого TypeScript. 

И тут большая радость, что практически все компоненты основаны на заготовке @prisma-cms/component-boilerplate, так как всю новую основу по сути я делал только в одном месте, а в компоненты просто подгружал гитом, резолвил конфликты и допиливал конечный функционал. И вот тут имеет смысл перечислить фичи, которые входят @prisma-cms/component-boilerplate "из коробки":

  1. TypeScript
  2. SCSS/SASS
  3. Eslint
  4. Formatter
  5. Jest/TS-Jest
  6. Storybook
  7. Next-JS as dev-server
  8. Git hooks via husky
  9. Npm version scripts

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

Но далее нарисовалась следующая проблема. Код переписывался практически полностью, и надо было проверять функциональность на конечном сайте. А это все-таки разные проекты. В классическом варианте у меня цикл разработки должен был выглядеть так:
  1. Дорабатываю функционал в компоненте.
  2. Упаковываю и публикую его в npm registry.
  3. Обновляю зависимость на конечном сайте.
  4. Проверяю работу и если что-то не так, выполняю все по кругу.
Скажу так, это адище. Когда в компоненте много всего сделал, то еще как-то оправдывается все это. Но когда надо подправить пару строчек кода, то больше времени уходит на упаковку и установку, чем непосредственно на программирование. Хотелось же просто отредактировать код в компоненте и сразу иметь возможность проверить результат на конечном сайте. Ранее я для этого использовал yarn link, который позволяет локальные разрозненные компоненты подключать как зависимости через симлинки. Но здесь есть пара значительных проблем:
  1. Каждый компонент все равно имеет свой самостоятельный node_modules. То есть у вас не будут работать нормально контексты и прочие инглтоны, так как каждый пакет тащит свой собственный инстанс и они никак не связаны.
  2. Вытекает из первого пункта. Я начал использовать функциональные компоненты реакта, и вот они категорически против, если используется более одной версии реакта. То есть все подключенные зависимости должны использовать единый реакт и не иначе. Классические class-based компоненты реакта более лояльны в этом плане были и yarn link еще хоть как-то спасал.
А если учесть то, что некоторые мои компоненты зависят друг от друга, то есть чтобы проверить работу обновленного компонента на сайте, надо отредактировать и опубликовать сразу несколько пакетов, то это реально просто становится неоправдано с точки зрения трудозатрат и эффективности. В итоге я освоил yarn workspaces. Вот это уже тяжелая артиллерия и решает практически всем мои проблемы в работе с несколькими компонентами. Если кто с таким сталкивался, то знает, что это называется monorepo и чаще всего в таких случаях используются инструменты типа lerna. Но я не стал доводить до этого. Мне приходилось уже работать с монорепами, и слышал мнение других опытных специалистов, что монорепа - это ад и путь в никуда, что потом это все обслуживать и разгребать очень сложно. И я с ними согласен. Поэтому моя реализация здесь - это только внутренняя перелинковка для более быстрой разработки в рамках конечного проекта, но все же каждый пакет является самостоятельным, имеет свой собственный репозиторий и т.п. Вы легко можете проследить разницу, изучив исходники многих крупных проектов, как тот же react-scripts. В packages много других других зависимостей, которые по сути своей являются отдельными пакетами (и в npmjs даже по разным адресам находятся), но при этом найти их исходники бывает очень сложно. То есть вы заходите на npmjs страницу конечного компонента, хотите перейти на github, чтобы посмотреть его исходники, а ссылка на главный репозиторий, где ты вынужден копаться в исходниках всего проекта, чтобы найти нужный тебе. И тут еще один очень не приятный момент: так как гитхаб-репозиторий на весь проект общий, то и все релизы, все тикеты и т.п. - тоже общие. И выполнить поиск нужного тебе тикета или исходного кода через поиск становится практически не реально. У активного проекта тысячи тикетов в общей сложности, а пакетом многие десятки, и когда все в одной куче,  то поиск реально сильно усложняется. В общем, я так не хочу, поэтому каждый пакет живет своей жизнью.

Думали проблемы закончились? Конечно же нет)))

Следующая проблема вытекает из предыдущего решения. Когда у нас workspace, то yarn сам следит за всеми зависимостями и все общие зависимости загружает в корневой node_modules. И когда в конечном пакете выполняется import 'some_module', то этот модуль резолвится из корневого node_modules, то он есть совсем не обязан быть установлен в конечном пакете и в рамках всего воркспейса все может отлично работать. Но как я и говорил выше, все-таки мои компоненты обособленные и все необходимые для них зависимости должны устанавливаться для них в обязательном порядке, то есть прописаны в package.json, включая указание нужных версий. То есть для по завершению работы, для чистоты эксперимента, надо собрать пакет, залить в npm registry, потом отдельно его установить со всеми его зависимостями, выполнить билд пакета, запустить тексты, убедиться, что все прошло успешно, и если что-то не так, опять пройти все эти круги ада. 

Решением данной проблемы являются принципы CI (Continues Integration). Реализация может отличаться, но главная функция - автоматизация этой самой рутины. Лучшим инструментом для этого считается Jenkins, но для нас все это сложно:) Поэтому я просто освоил азы github actions и прописал базовые скрипты в самом компоненте. Теперь по завершению работы с компонентом мне достаточно закоммитить изменения в гит и выполнить команду npm version major|minor|patch. Далее npm сам выполнит все прописанные тесты, соберет пакет, зальет в регистри, создаст новый коммит в гит с указанием тега и выльет все это на github.com. А там уже github, увидев папочку .github и найдя там срипты, выполнит их. А в скриптах прописано:
  1. Выполнить установку зависимостей
  2. Выполнить билд компонента
  3. Выполнить тесты

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

И все это делается автоматически! То есть если раньше на это у меня уходило 15-30 минут, то сейчас я просто выполняю npm version и переключаюсь на другой пакет (ну или что другое делать). А если мне надо таким образом вылить все пакеты в воркспейсе, то я могу это сделать всего одной командой: yarn workspaces run npm version major|minor|patch. И все. Все пакеты соберутся, опубликуются, выльются на гитхаб и пройдут все прописанные тесты, и если где-то что-то не так, я получу уведомления только по проблемным пакетам. И не буду сидеть и простоя пялиться в процесс, наблюдая за выполнением, тратя на это зря время.

В итоге, еще на двух зависимостях в проекте, моя эффективность работы выросла раз в пять, в прямом смысле. И это еще были относительно небольшие компоненты. Сейчас у меня в проекте 7 зависимостей, включая такой сложный компонент как @prisma-cms/front-editor, в рамках одного которого только было переписано несколько тысяч строк кода. И я думаю, что без всего этого внедренного инструментария я бы еще очень долго все это переписывал.

Результат же за полтора месяца получился следующий:


И это только по самому проекте, не считая изменений в зависимостях, а там тоже совсем не одна тысяча строк. И главное отличие от того, как работа велась ранее, это то, что в браузер я практически не смотрел. Это раньше было так: на основном мониторе редактор кода, а на другом мониторе браузер, в котором сразу обновляется страница при изменении кода. Вот сидишь, пишешь код и наблюдаешь за результатом. А сейчас практически вся работа делается чисто глядя в редактор. А он подсказывает "Здесь ты не тот тип передаешь. А там у тебя зависимость сломалась. Здесь ты вообще бредишь."... Не редко, мой код выглядел вот так:


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

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

Комментарии с предложениями и вопросами очень приветствуются.

P.S. Предварительную версию сайта выложил здесь: https://new.prisma-cms.com/

Исходники традиционно на гитхабе, сейчас в отдельной ветке: https://github.com/prisma-cms/prisma-cms.com/tree/next-js

Там еще не все сделано, но все же можно вполне оценить разницу по скорости загрузки страницы и работы сайта в целом. Вся информация берется из одного АПИ-сервера, то есть полностью соответствует информации здесь, и когда будет доработан, заработает на основном домене, а эта версия уйдет на поддомен. Все упаковано в контейнеры и управляется из одного docker-compose проекта https://github.com/prisma-cms/docker.

Исходники новой заготовки для проектов, о которой и шла речь, вот здесь: https://github.com/prisma-cms/nextjs

Кто уже умеет во все это, можете поиграться.


UPD: Очень хорошая статья про next-js: https://habr.com/ru/company/ruvds/blog/442654/

Проект жив и это радует. А то был слух, что мы тебя потеряли :)
Да не, просто в работе был весь :)
О класс! Прочитал с воодушевлением, снова Николай весь в работе, собственное детище взращивает! Круто! Весьма и впечатляет. Надеюсь будет развитие уже такого варианта и это принесет всем нам отличные эмоции от управления хорошим и добротным продуктом! Спасибо! Ожидаю новостей!
Сергей, спасибо на добром слове! :)
Будет.
Доброго времени суток. Николай, какова реальность на сегодня в плане воплощения проекта? :)
Сергей, привет!
Ну, спрос не особо велик оказался :)
Но как раз сейчас готовлюсь записать видео на часик-два. Должно быть интересно.
А так обновление prisma-cms.com - это задача не первостепенная. Есть основная работа, она время тоже съедает много.
Приветствую Николай! Смотрю понемногу каждый день видео, труды твои бесценны, дорогого стоит! Как досмотрю, отпишусь про впечатление и в целом! Не обессудь, тоже большая занятость.
Сергей, привет!

Да не по немногу :) В выходные получается нормально поработать. Вчера опять 16 часов проработал. О какой дифф: https://github.com/prisma-cms/prisma-cms.com/compare/5237078bea32354...6fc19fe342e

504 файла измененно, минус 89.164, плюс 77.644 строк :)

Удалил практически весь легаси код и перенес новый код из временной папки в корень. И дописал/переписал кучу страниц сайта. И это все за день, Карл! Раньше такой объем недели две выполнялся :)

А сегодня буду доделывать оооочень интересный и важный функционал. Думаю, успею и выложу, напишу статью. Я этот функционал закладывал почти год назад (по логам вижу, что делал с 22 по 31 декабря прошлого года), но так и не успел доделать (и тогда бы у меня еще месяц бы заняла работа). Но теперь-то я его точно добью :)

Посмотрел, почти все, трудно некоторые моменты даются, но разобраться получается!) Рад такому прогрессу в кодировании! Это очень круто! Ждем статью!)))
Сергей, скоро будет. Я на выходных чуть-чуть не успел. Появились подводные камни. Они решаемые, но не успел. А в будни основная работа. Но на этих выходных должен доделать и выкатить.

Добавить комментарий