В последние годы, рабство фронтенд разработчиков, использующих современные фреймворки, стало обыденностью. И данная статья рассказывает о том, как Angular буквально заставляет разработчиков удовлетворять свои прихоти, погружая их в enterprise кабалу.
Если пару лет назад шли вопросы о том, какой фреймворк выбрать, то сейчас все задаются вопросом: “Как оттуда выбраться”. Давайте разберемся с тем как это произошло, и что стоит делать, чтобы не попасть в жернова фронтенд фреймворка.
Как всё начиналось
Во времена, когда появление CSS было признано божественным вмешательством, никто не мог представить, что радость будет недолгой. Один парень из Купертино перевернул мир с ног на голову, представив общественности новый девайс.
Совершенный, идеальный, выверенный до миллиметров гаджет, который в дальнейшем изменил мир, стал опорной точкой к изменению подхода взаимодействия пользователя с приложениями. Свайпы, тачи и клики нанесли решающий удар по разрушению старой школы веб-разработки, определив новый стандарт разработки, что веб-приложение должно работать как с PC, так и на различных мобильных устройствах.
Чуть позже группа энтузиастов, сделало приложение, где
пышногрудыедевицы стали размещать свои фотографии. Немного погодя приложение так понравилось людям, что смогло покорить сердце одного парня из Гарварда, который любил следить за всеми и вся. Он не удержался и купил перспективный проект, который в дальнейшем стал крупнейшей торговой площадкой.
Бизнес начал требовать все больше и больше интерактивности и некогда забытый язык становится снова актуальным. Javascript из языка чернокнижников и колдунов, начинает превращается в современный язык, который начинают поддерживать большинство браузеров.
Как Angular захватил сердца разработчиков
Появившееся фреймворки и библиотеки того периода (dojo, Ext JS, Backbone.js, Ember.js, MooTools, jQuery, AngularJs, React) были плохо предназначены для больших, масштабируемых приложений.
Именно тогда, ребятам из Google пришла гениальная идея - сделать фронтенд похожим на бэкенд. Взяв в качестве основы TypeScript, который в свою очередь разработан одним из разработчиков С#, были реализованы современные паттерны и решения, в частности Dependency Injection, style encapsulation, web components, lazy loading, tree shaking, hmr, shadow dom и прочее.
Все это было названо Angular 2, который пришел на замену AngularJS. Подробная документация, реализация material, роутинг, style guides позволило привлечь огромное количество разработчиков в фронтенд.
Миграция с PHP на TypeScript стала обыденностью.
И все больше и больше крупных компаний начали переходить на современный стек. И большинство из них стали использовать Angular в своих проектах.
Почему разработчики выбирали Angular, а не React?
Ответ достаточно прост - он был не похож на большинство новомодных фреймворков.
Angular нес в себе большой пласт договоренностей и решений, которые решали большинство возникающих проблем в то время. Вот часть договоренностей и решений:
Строгая типизация с Typescript. Чем более типизирован язык, тем меньше ошибок может содержать код. Банальным примером может быть строгая проверка на NULL. Если соблюдать это правило с самого начала разработки, то это позволит избежать такого количества ошибок, что позволит сократить весь штат тестировщиков в компании, и переложить тестирование непосредственно на разработчика.
RxJS. Если Promise стали глотком свежего воздуха по созданию асинхронных событий, то RxJS был сравни торнадо. Создание потоков данных, объединение, подмена и сокрытие, выполнение по условию - все это кардинально изменило подход к созданию асинхронных событий.
Реализация внедрения зависимостей (Dependency Injection) стала ключом к началу перехода разработчиков из бэка во фронт. Упрощение управлением и созданием сервисов, позволило создавать разные инфраструктурные слои, что дало возможность структурировать JavaScript код.
Angular Router позволил взглянуть на шаблонизацию в новом ракурсе. Если раньше большинство шаблонизаторов имели простую и понятную, систему вложенности друг в друга, то Angular шагнул дальше и предоставил разработчикам мульти вложенность. Другими словами можно было разделить экран на несколько частей и сделать каждый элемент системы полноценным независимым юнитом со своей навигацией и роутингом, позволяя сохранять состояние каждого из таких компонентов в url.
Angular Form раз и навсегда решили проблему с созданием и управлением формами. Реактивные формы, которые работали быстро, ожидаемо, имели множество состояний и валидаторов, заняли особое место в сердцах разработчиков. Создание кастомных контролов с помощью ControlValueAccessor обладали настолько сильным влиянием, что уже безнадежно приковывали разработчика к фреймворку.
Схематики (Schematics) и Angular CLI дали возможность разработчику созданию шаблонов и генерации кода. Описав свои правила, можно было добиться невероятной скорости разработки.
Так Angular шаг за шагом начал привлекать к себе все больше разработчиков. Однако вместе с этим начали расти и требования к фреймворку. Неизбежный рефакторинг и изменение принципов привело к созданию миграций - инструкций, которые позволяли обновлять исходный код проекта на Angular до актуального состояния. Но как оказалось в дальнейшем, этого оказалось мало.
Трудности работы Angular с SSR
Так как одной из главных зол фронтендера является SEO, ребята из Angular решили это очень просто - они просто не стали ничего делать.
Не путать с CEO (CTO) - к нему нет претензий, хотя он тоже может приносить проблемы.
Если взять и развернуть новый проект на Angular (на данный момент 13 версия), то вы не получите SEO из коробки. Для поддержки SEO вам необходимо настроить SSR, который в Angular представлен пакетом Universal.
Universal стал частью Angular, но все равно до сих пор, поставляется отдельно.
И если сказать, что реализация SSR в Angular имеет определенные сложности, то это значит не сказать ничего. Проще отстрелить себе ногу, чем реализовать правильный, быстрый Server Side Rendering.
По природе своей Angular представляет собой браузерную версию, где есть window, но нет global, и соответственно нет NodeJS.
И все было бы ничего, если бы не было Prerender’а - механизма предподготовленных страниц, которые можно скомпилировать заранее и не рендерить каждый раз при запросе клиента.
Серверная платформа Angular накладывает слишком много “НО” на разработку.
Если используется что-то из Window, необходимо использовать isPlatformBrowser;
Если есть асинхронные гуарды (can-actives), то нужно писать правила для того, чтобы серверный рендер не уходил в бесконечное ожидание;
Если есть таймеры и интервалы как нативные, так и из rxjs, то их необходимо либо игнорировать в серверной платформе, либо переопределять наблюдателей;
Если используется пререндер, и у станицы есть асинхронный гуард, то он должен отдавать true, иначе пререндер уйдет в бесконечное ожидание;
Если для неизвестных страниц нужно отдавать страницу со статус кодом 404, на уровне всего приложения, то тогда на уровне ноды необходимо иметь sitemap или карту роутов, чтобы сервер по url мог понять, есть ли такая страница или нет;
Для всех Http запросов нужно решить - должны ли они выполняться при серверной сборке или нет. И тут много подводных камней. Если используется JWT в качестве сессии, то проблем будет чуть меньше. Однако, если в качестве авторизации используются куки, добро пожаловать в Ад. Будет необходимо выполнять запросы c withCredentials, но из-за того, что server это не browser то нужно будет пробрасывать все куки, что добавляет проблемы с безопасностью. Единственным верным решением будет нанять DevOps чернокнижника, который не проклянет вас и сможет все это разрулить. Иначе вы можете начинать учить латынь, а там уже доки по docker, kubernates, helm-chat’ам, ansible и многим другим интересным словам. Ах, да, еще есть ssl сертификаты.
И если вдруг показалось, что это все, я должен вас расстроить - это еще не все. Иначе было бы слишком просто.
Если вы вдруг решили использовать LocalStorage, то нужно придумать как быть в случае серверной платформы. Если вы придумали, что делать с LocalStorage в серверном рендеринге, то не забудьте придумать, что делать с этим при PWA.
И этот процесс бесконечный. Это становится первым кольцом обременения на шее разработчика.
Подытожить можно тем, что используя любую технологию нужно задумываться о её реализации в SSR, иначе красные не пропустят билд.
Redux в Angular
Но как бы Angular Team ни старалась сохранить баланс между поддержкой и новыми фичами, JavaScript развивался параллельно с Angular.
Ребята из Facebook начали генерировать множество потрясающий идей и библиотек. Так как собственные потребности в интерактивности росли, то facebook ничего не оставалось как придумать Redux - систему управления состояниями. Конечно, React дал разработчику redux из коробки, реализовав его в ядре библиотеки.
Angular же пошел другим путем, где сославшись на RxJS и DI отказался от какой либо имплементации Redux. И тогда на сцену вышел NgRx со своей системой reducer’ов, selector’ов и action’ов. Разработчики NgRx четко понимали, зачем нужен Redux и добавили мегафичу в лице Effect’ов - механизма выполнения асинхронных экшенов.
Со стороны это было примерно также как охотнику вместо обычного ружья дали снайперскую винтовку, а вместо гранат предложили использовать РПГ.
NgRx стало вторым связывающим кольцом на шее разработчика, с одной стороны позволяя упростить асинхронность, с другой стороны заставляло создавать множество кода для поддержки управления состоянием.
- Почему это стало кольцом?
- Шариковое тестирование (marble testing).
Так как с обычным тестированием есть проблемы, а тестирование асинхронных событий это еще больший геморрой. Это не сложно, но больно и долго.
Выпускать код в продакшн без тестов нельзя, а их написание занимает гораздо больше времени, чем реализация самого Redux State’а. Вот разработчики и думают постоянно - а нужен ли мне еще один state или просто оставить переменную в компоненте.
Я наверное пропущу блок про тестирование в Angular, так как сейчас все используют jest, а Angular Team все еще не хочет перейти с karma на jest.
Монорепозиторий в Angular
Время шло и кодовая база проектов расширялась. Настал момент, когда запуск тестов перевалил за 1 час реального времени, так как в проекте существовали тысячи компонентов и сотни сервисов.
Отпочковавшиеся от Angular Team разработчики объединили свои усилия и создали компанию Nrwl, которая начала консультировать компании из Fortune 500 на тему создания enterprise приложений.
Так как в это время все больше и больше разработчиков говорило о моно репозиториях, Nrwl выпускает свой набор инструментов по созданию и управлению монорепозиторием, основываясь на стандартных Angular CLI.
Без греха можно сказать, что это был ошеломляющий успех. Разделение кода на приложение и библиотеки, которые не требовали отдельного билда просто разорвало рынок.
Основная проблема разработки в лице Dependency Hell была решена, что позволило взглянуть на разработку с нового ракурса.
Шаг за шагом, разбиение и разделение позволило ускорить разработку приложений на Angular, начиная возвращать разочаровавшихся разработчиков. Собственная система кеша тестов, инструменты для упрощения создания схематик, валидация и ограничения на используемые библиотеки развернули разработку еще раз.
Тогда же и проявилось еще одно кольцо бремени на шее разработчика, так как все имеет свою цену.
- Решил сделать коммит?
- Сделай линт всех изменений.
- Решил запушить изменения?
- Запусти тесты на все изменения в файлах, а также на все связанные компоненты системы, которые используют измененные файлы.
Маркетинг и аналитика
Маркетологи и бизнес аналитики, следующие враги качественной разработки. Новые маркетинговые стратегии, новые источники трафика, аб тесты, все это вынуждает разработчика из красивого, идеально сложенного и написанного кода создавать лапшеобразного монстра.
Конечно, можно писать качественный, покрытый тестами код, но к сожалению, позволить это могут далеко не все. Для проверки разных гипотез, нужно временами менять до 10% кодовой базы, которая разрабатывалась несколько лет. И конечно, нет ни одного аргумента делать проверку гипотезы по всем принятым канонам.
И тут есть одна ловушка, которая подкашивает большинство начинающих разработчиков:
- Когда гипотеза подтверждается, нужно переписывать проект. Но это невозможно объяснить руководству и менеджменту.
И диалог будет следующий:
- Ну все же работает! Зачем что-то менять, пусть все остается так!
И разработчик, который смотрит в лицо менеджеру думает лишь только о том ...
Так появляется еще одно скрытое кольцо. Это тоже заслуга Angular. Если бы в Angular любили бы спагетти или мафальдине, то проблемы бы не было.
Влияние мобильных устройств на разработку
И если до этого момента была определенная ясность в том, что стоит делать, то с ростом доли мобильных устройств, разработка начинает подстраиваться под мобильные устройства.
Сначала приходиться заигрывать с мобильной разработкой, создавая приложения обертки на базе WebView. В приложение пробрасываются некоторые базовые компоненты для поддержки камеры, гиперссылок и разных вебовских штук.
Потом появляется своя аналитика, так как обычная аналитика web версии уже не удовлетворяет потребностям отдела маркетинга, так как невозможно оценить стоимость рекламных кампаний в Google Play и App Store
Приложение обрастает новыми фичами и приходит момент, когда уже веб платформы становится недостаточно.
И тогда в Angular приходит Ionic и NativeScript, которые обещают разработчику использовать текущую кодовую базу, добавив всего лишь компоненты обертки и немного изменив роутинг.
Quod licet Iovi (Jovi), non licet bovi.
Можно догадаться, что здесь открываются вторые врата в Ад.
Тут мало что можно сказать, так как к этому времени, проекты очень разные и специфичные. В блоге Angular’а можно найти примеры связки Angular и NativeScript, где все работает относительно не плохо.
Из личного опыта - если придерживаться определенной тактики, то можно достичь определенного успеха.
Я собирал приложения в связке Nx + NgRx + Universal + Apollo + NativeScript, и к моему удивлению оно отлично работало. Конечно, всегда есть боль с обновлением Angular, когда выходит новая версия, но обычно за 2-3 месяца, выходит достаточное количество фиксов, что позволяет перейти на новую версию.
Магия найма
Последним кольцом является найм Angular разработчиков. Именно успех HR помогает сохранить жизнь и рассудок других Angular разработчиков в компании.
Чем больше становится проект, тем больше изменений требуется вносить в проект. Но вот в чем соль - Количество необходимых изменений несоизмеримо больше, чем объем появляющихся на рынке новых Angular разработчиков.
Это вызвано двумя ключевыми факторами:
- Порог вхождения в фреймворк,
- Корпоративное рабство.
Откройте любой сайт с вакансиями и посмотрите отношение количества вакансий к количеству резюме с Angular. Разработчиков просто нет. А из тех, кто есть на рынке либо сверх завышенные ожидания по ЗП, либо их знания слишком скупы.
Мидл и миледи просят 350к, а сеньор и сеньорита просят от 500к.
Под корпоративным рабством подразумевается то, что компания в которой есть толковый разработчик, готова пойти на любые меры, чтобы удержать разработчика. В ход идут бонусы, 13 зарплата, фитнес, ДМС, зарубежные корпоративы, подписки на netflix и другое.
Но даже это не позволит решить кадровый вопрос. И это в результате приведет лишь к одному, что разработчик будет работать за 3-4 человек, так как кроме него, будет некому работать.
Забавно, но один из российских IT гигантов заранее знал об этом. Это Yandex. Не для кого не секрет, что Yandex находится на темной (ну ладно, светлой) стороне силы, взяв в качестве инструментария React.
Совпадение? Не думаю.
А вот ребята Олега прогадали. Ну не совсем, конечно, но бизнес и часть сервисов точно задело.
Стоит отдать уважение ребятам из Tinkoff, за taiga-ui, что смогли выложить свои наработки в github. Работа там проделана громадная, но тоже временами видны некоторые проблемы с миграциями. Не знаю, получилось ли у них съехать с LESS на SASS и навести порядок с рядом компонентов.
Хотя, даже если смотреть исходники Angular Material, то некоторые моменты вызывают слезы и тоску.
Enterprise кабала
Если обобщить вышесказанное, то получается следующая картина:
Angular сложный, громоздкий, строго типизированный, многофункциональный и многопрофильный фреймворк, который требует большого погружения для эффективного использования.
Большое количество ограничений и стандартов вынуждают разработчика соблюдать множество правил, которые многократно увеличивают стоимость разработки, но давая взамен хорошую масштабируемость и расширяемость проекта. Большую часть времени разработчик тратит на поддержку стандартов и принципов, но это безальтернативный путь, так как другого пути нет.
Если проект нуждается в SSR, то разработчик будет вынужден соблюдать большое количество правил и ограничений, который просто не позволят сбилдить проект.
Маркетинг высосет последние соки из разработчика, заставляя проверять сотни гипотез, большинство из которых будут неверны, а те немногие идеи, которые подтвердятся, принесут месяцы рефакторинга и новой головной боли.
Мобильные устройства, которые все больше определяют современный рынок, заставят разработчика так или иначе реализовывать фичи только для мобильных устройств, вне зависимости от его желания.
Из-за сложности привлечения новых кадров, компании готовы пойти на любые меры, чтобы удержать сотрудников, что приводит к неоправданным затратам. А из-за малого предложения, каждый Angular разработчик будет работать за 3-4 человек.
И тут можно задать один вопрос - Когда разработчик тратит больше 90% времени на поддержку фреймворка, то тогда чем является фреймворк - универсальным решение или enterprise кабалой.
Решение только за вами.
Заключение
В заключении хочется сказать только одно - Бегите глупцы. Flutter или Svetle уже вас ждут.
P.S. Совсем забыл про NodeJS и Angular для backend’а - NestJS, где можно подключить TypeOrm, Redis, Rabbitmq, MongoDB, Graylog, Elasticsearch и Clickhouse. Но это немного выходит за рамки JavaScript. Ладно, пойду перепишу все на новый JS framework.