Обновления Angular. Общие соображения
Так уж повелось, что я работаю с Angular еще со времен AngularJs. Для тех, кто не в курсе: это не просто долго, это очень долго — на вскидку года так с 2013 как минимум. С течением времени Angular остепенился и принялся выкатывать мажорные обновления с частотой два раза в год. Поскольку всю дорогу приходилось с этим как-то жить, за годы потихоньку выработался определенный алгоритм поведения при очередном обновлении. Не скажу, что спасает от боли и страданий, но искренне надеюсь, что слегка их облегчает.
А оно вообще надо?
Вопрос философский, и касается не только Angular. Лично мне доставляет чувство глубокого морального удовлетворения осознание того, что у меня в продакшене крутятся актуальные версии продуктов. По своему опыту могу сказать: стоит это дело упустить — и догонять потом очень больно. Прыжки через мажорные версии, как правило — это реки боли и страданий. Имхо, не обновляемый проект — умирающий проект.
Есть такая вредная иллюзия: а зачем трогать, если и так работает. Разгадка ее появления проста — лень и безблагодатность. Если в проект страшно тыкать палкой, то, скорее всего, это дохлая лошадь.
Чисто гипотетически. Допустим, у вас на проекте Angular 6 версии. Так уж вышло. А в данный момент актуальна, допустим, 12. Так вот, поиск ответа на вопрос по коду превращается в своеобразный квест. На сайте Angular вам предлагается найти версию документации к древней 6 версии, над каждой статьей умного человека или вопроса из stackoverflow придется порядочное время медитировать и примерять актуальность. Так себе задача.
Это я уже не говорю о том, что продукты все же как-то эволюционируют. Как правило, в сторону улучшения.
Кому не нужны обновления
Если Вы не гордый одинокий ковбой, а работаете в коллективе (а так оно зачастую и бывает), то надо понимать, что есть две категории людей, которым на обновления вообще плевать: это заказчики и менеджеры. Под менеджерами я понимаю всех этих product owners, scrum masters, руководителей проектов и прочих. Имя им легион и, в той или иной степени, вы от них зависите — они формируют хотелки и платят деньги за их реализацию. Вы производите код, они производят совещания. Вы хотите обновить продукт, для них эта идея не имеет никакой практической ценности, какой бы понимающий вид при этом они ни делали. Это нормально.
Есть, конечно, обновления, затрагивающие безопасность. Поскольку про безопасность никто ничего конкретного, как правило, не знает, но звучит это офигенно угрожающе, то этот аргумент работает. К сожалению, не все обновления затрагивают безопасность.
Есть обновления, повышающие быстродействие. Откровенно не советую использовать в качестве решающего аргумента. Производители продуктов склонны преувеличивать свои достижения (и это понятно — там тоже программисты сидят) по увеличению быстродействия, и, как правило, реальный результат находится крайне далеко от продекларированного на каких-то там тестов в сферическом вакууме. После проведения обновления вам обязательно предложат продемонстрировать поученный рост производительности на вполне себе конкретных тестах. Будет не очень удобно, если вместо заявленных 30% вы получите 0,3%.
Короче, единственный выход — это тернистый путь воспитания. Необходимо приложить весь свой жалкий набор soft skills, чтобы приучить руководящий состав к мысли, что обновления — это не просто стильно, модно, молодежно, но еще и нужно. Необходимое зло — проще смириться, чем спорить. Если убедить не удается — вам конкретно не повезло с работой, пора рассылать резюме.
Второй шаг на тернистом пути — задачи по обновлению должны попадать в спринт и эстимироваться, как и прочие задачи. Да, они не создают ценности для заказчика, но время то на них тратится. Провести обновление в свободное от работы время за вечерним чаем не получится, а скрытые овертаймы — это такое себе.
Кто, когда, как
В случае, если над проектом работает группа дрессированных павианов, из их числа необходимо назначить самого умелого. Его skills должны быть hard, и, что немаловажно, он должен иметь четкое представление как весь этот продукт коллективного разума работает в целом. Этот несчастный назначается ответственным за обновление и готовится испить чашу. Самое тупое, что можно сделать с обновлением — это пустить все на самотек и предоставить каждому проводить его по своему разумению.
Обновлять Angular (да и не только его) для команды строго рекомендуется в начале спринта. Это позволит принять возможные баги на этапе разработки, а не втыкать на них на продакшене. Для этого ответственный за обновление должен в предыдущем спринте создать ветку с готовым обновлением, после окончания спринта не вливать ее в мастер, а влить мастер в нее и раздать команде в начале следующего спринта. Таким образом для участников команды все сведется к тупому удалению папки node_modules и выполнению команды npm install. Ну, может еще понадобится парочка незначительных телодвижений.
Собственно обновление
Итак, ответственный крайний за обновления назначен. Все, что дальше — исключительно для него.
Контроль пакетов
Нельзя вот просто так взять и обновиться. Это было бы неинтересно и совсем не больно.
Раз уж вас назначили самым умным павианом, придется соответствовать. И не выпускать бразды правления всю дорогу, чтобы потом не было мучительно больно.
Для начала надо бы открыть файлик packege.json и пристально изучить секции dependencies и devDependencies. Все эти зависимости можно условно разделить на три части:
- Angular. Это то, что живет в Angular-овском репозитории — собственно фреймворк, Angular Material и т.п. Как правило, пакет начинается с волшебного слова @angular. Мажорные версии выпускаются одновременно и соответствуют друг другу. Но есть и исключения — так, допустим, пакет @angular/flex-layout обновляется на протяжении некоторого времени после выхода мажорных версий Angular.
- То, от чего зависит Angular: rxjs, tslib, zone.js, typescript и группа зависимостей для тестирования — все, что начинается с karma и jasmine. Самое пристальное внимание следует обращать на версии rxjs и typescript.
- То, что зависит от Angular — пакеты сторонних производителей. Это одно из основных мест, нуждающееся в контроле.
- Всякое прочее, не зависящее от Angular.
На все репозитории, содержащие пакеты, использующиеся в проекте, в особенности на ангуларовские, rxjs, typescript, а также все пакеты 3 группы, необходимо подписаться, чтобы быть в курсе обновления версий. Просто обязательно. Почта не треснет, но вы обязаны быть в курсе процесса обновлений пакетов.
Самая опасная группа — это пакеты сторонних производителей, зависящие от Angular. Чем их меньше, тем лучше для проекта. Они мало того, что могут быть наполнены лютым трэшем чуть более, чем полностью, так они еще могут быть в один прекрасный день просто заброшены. Как правило, обновляются до актуальной версии Angular по мере возможности, желания и наличии смузи и энтузиазма у разработчиков. Будьте бдительны!
Зачастую в команде присутствует один, а то и несколько юных падаванов, которые не в состоянии проверить наличие элемента в массиве без lodash, а для реализации мега задачи на двадцать строк кода им необходима как минимум библиотека килобайт на сто. Со всей ответственностью заявляю: эцих с гвоздями должен помочь. Не помогает — добро пожаловать на мороз, там ходят голодные HR-ы и есть шанс рано или поздно попасть в подходящую команду с суммарным уровнем IQ 40.
Вообще говоря, если проект уже не молод и достаточно упитан, то при внимательном рассмотрении зависимостей можно выявить целую кучу странных артефактов, причем как они сюда попали, где используются и кем внедрены не знает никто — все на уровне преданий, сказок и предположений. Не стоит доводить до этого. Вы, как ведущий павиан, должны это дело пресекать на корню, потому как вся боль за бардак в packege.json будет доставаться исключительно вам.
Выход обновления
Итак, Angular презентовал новую мажорную версию. Ок, пора готовить булки.
Но не сразу. Хороший мажор должен настояться. Хотя бы пару недель, а лучше месячишко. За это время версия сменит нолики в конце на что-нибудь более гарантирующее, что некоторое количество багов уже поправили.
Ну а потом, собственно, можно помониторить те репозитории пакетов, зависящих от Angular, на которые вы подписались. Залетаем и прямо сходу смотрим packege.json и указанную там версию Angular.
Если версия актуальна, идем в Issues и ищем актуальные вайны типа «После обновления не работает». Изучаем, примеряем на себя.
Если версия не актуальна, опять-таки идем в Issues и ищем тему (она там 100% будет) с названием «Angular vXX Update» или типа того. Читаем хронику борьбы за живучесть, жалобы на недостаток времени и обещания светлого будущего прямо вот завтра. Пакет к обновлению не готов, подписываемся на Issue и идем дальше спокойно педалить код на старой версии.
Если совсем, конечно, совсем невтерпеж, то можно попытаться обновиться и с таким пакетом. Но тут уже как повезет. Мне вот, допустим, в этом плане везло крайне редко. Притом рога вылезали в самых неожиданных местах и, что самое обидное, в самое неподходящее время.
Подготовительные работы
Рано или поздно, так или иначе все подтянулись, и, вроде бы, все готово. Пора брать в спринт задачку и приступать.
Открываем сайт обновления и вчитываемся в инструкции. Особое внимание обращаем, не упомянута ли всуе новая минимально допустимая версия Node.js.
Если таки да, то изучаем установленную версию. Если она не соответствует требованиям (а я искренне надеюсь, что вы пользуетесь nvm), нужно это дело исправить. Но тут есть момент. Естественно, устанавливать стоит только LTS, причем отнюдь не самый последний.
На примере. Смотрим доступные LTS: Erbium(v12.22.8), Fermium(v14.18.2), Gallium(v16.13.1). Читаем: «Make sure you are using Node 12.20.0 or later«. Перевожу на русский: «Erbium — точно подходит, Fermium — скорее всего подходит, Gallium — скорее всего не подходит». С особой настороженностью следует отнестись к версиям, включающих в себя npm с мажорным обновлением — там тоже возможны сюрпризы. Так в приведенном примере в Erbium и Fermium входит npm 6 версии, а в Gallium аж 8. Это повод насторожиться. В общем, не стоит гнаться за самым последней версией, лучше поставить минимально допустимую.
В качестве подготовки рекомендуется в укромном месте по-быстрому создать новой angular приложение прямо как описано в документации. Затем открыть его совместно с обновляемым приложением и вдумчиво сравнить несколько файлов:
- package.json — сравниваем версии rxjs и typescript. Если версия typescript поменялась (что практически наверняка), идем, допустим, сюда и изучаем все изменения, дабы представить себе масштабы разрушений. Если поменялась версия RxJs, то это предвестник беды. С душевным трепетом идем сюда, ищем раздел «Deprecations & Breaking Changes» и смотрим, что бравая команда разработчиков успела перепилить за истекший период. Они могут!
- tsconfig.json — смотрим что поменялось и поменялось ли вообще.
- angular.json — смотрим что добавилось, убавилось, и не изменилась ли случайно схематика.
- Опционально можно бросить взгляд на ./src/polyfills.ts.
Затем берем папку node_modules проекта и аккуратно копируем ее куда-нибудь подальше. После успешного обновления ее можно с чистой совестью удалить. Зачем? Во-первых, если что-то пойдет не так утилита обновления angulara имеет обыкновение полностью удалять папку node_modules. Можно, конечно, откатить git и выполнить npm install. И сам так делал, пока не столкнулся с такой ситуацией: откатился — и npm install наглухо завис при попытке установки какого-то старого rxjs. Помогло только откат на предыдущую версию Node.js и npm. Такие дела.
Обновление
Очевидно, что если у вас не одно, а кучка angular приложений, обновляться рекомендуется начиная с самого маленького. Если вы настолько продвинуты, что у вас есть собственные библиотеки, обновляться придется начинать с них.
Следуем инструкциям сайта обновления.
npx @angular/cli@XX update @angular/core@XX @angular/cli@XX
Как правило, не сработает и упадет с ошибкой несоответствия peerDependencies. Чтобы этого не произошло, можно добавить одну из двух опций:
- —legacy-peer-deps — игнорировать ошибки peerDependencies;
- —force — бороться за живучесть до последнего и пытаться впихнуть даже нвпихуемое.
Я использую —force.
В результате выполнения команды будут не только установлены новые версии angular-овских пакетов, но и внесены ряд изменений в ваших файлах. Эти изменения желательно просмотреть — все ли сделано правильно, и не грохнул ли angualar что-то лишнее. К слову, выполняется команда достаточно большое количество времени, в особенности если ваше приложение достаточно жирное.
Далее, если вы используете Angular Material:
ng update @angular/material@XX
Если вы используете Angular Flex-Layout (а скорее всего так оно и есть), то нужно добавить @angular/flex-layout.
Флаги устанавливаются, как и в предыдущей команде (в моем случае —force).
Если не хочется коммитить результаты выполнения предыдущей команды, можно добавить флаг —allow-dirty.
Эта команда также будет посильно пытаться исправить какие-то элементы в вашем коде и привести его в соответствие. Измененные файлы также не грех проинспектировать.
Далее (уже не по инструкциям сайта) нужно попробовать обновить прочие пакеты, зависящие от Angular. При этом стоит использовать команду ng update, в особенности для пакетов так или иначе связанных с Angular.
Например, команда обновления eslint у меня выглядит приблизительно так:
ng update @angular-eslint/schematics @angular-eslint/builder @angular-eslint/eslint-plugin @angular-eslint/eslint-plugin-template @angular-eslint/template-parser @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-plugin-jsdoc eslint-plugin-prefer-arrow —force —allow-dirty
Затем опять возвращаемся к сайту обновления и меланхолично проходим по пунктам в поиске подходящих под ваш код. Так же меланхолично фиксим.
Запускаем линтер, тесты, затем пытаемся запустить само приложение. Как правило, между каждым из этих шагов будет по ведру рефакторинга. От себя отмечу, что крайне желательно, чтобы линтер или ваша IDE могла отыскать и отметить deprecated методы. Эти мины замедленного действия особенно любит подкладывать под ваш код RxJs.
Послесловие
На моей памяти ни одно из мажорных обновлений Angualar не проходило для меня гладко. Может быть, конечно, это все виноваты кривые руки, качество кода или фазы Луны. Но что-то мне подсказывает, что не только в этом дело.
Автор фото: charliegolonkiewicz, CC BY 2.0