Хабрахабр

Общие компоненты силами разных команд. Доклад Яндекса

Создание и сопровождение общих компонентов — процесс, в котором должны быть заняты множество команд. Руководитель службы общих компонентов Яндекса Владимир Гриненко tadatuta объяснил, как их разработка переросла выделенную команду «Лего», как мы сделали монорепозиторий на базе GitHub с помощью Lerna и настроили Canary-релизы с внедрением в сервисы прямо в CI, что для этого понадобилось, а что ещё предстоит.

Меня зовут Владимир, я занимаюсь общими штуками в интерфейсах Яндекса. — Рад вас всех приветствовать. Наверное, если вы не очень глубоко пользуетесь нашими сервисами, у вас может возникнуть вопрос: что мы все верстаем? Про них и хочу поговорить. Что там верстать?

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

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

И их даже чуть поболее, чем на этом слайде. На самом деле сервисов в Яндексе, действительно, поболее. Они есть для разных устройств, на разных языках. За каждой такой ссылкой стоит куча разных интерфейсов с огромной вариативностью. Они работают иногда даже в автомобилях и прочих разных странных вещах.

Машинки желтые ездят. Яндекс сегодняшний — это не только веб, не только разные товары со складами, доставкой и всем таким. И не только всякие автоматические интеллекты. И даже не только то, что можно съесть, и не только железяки. Зачастую — очень богатые. Но все перечисленное объединяет то, что для каждого пункта нужны интерфейсы. Мы постоянно создаем что-то новое, каждый день. Яндекс — это сотни разных огромных сервисов. Эти люди работают в разных офисах, живут в разных часовых поясах, на работу постоянно приходят новые ребята. У нас трудятся тысячи сотрудников, среди которых сотни именно фронтендеров, разработчиков интерфейсов.

При этом мы, насколько нам хватает сил, стараемся, чтобы он был однообразен, един для пользователей.

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

Мы стараемся, чтобы этого не происходило. Чего стоит, переключаясь вот так по слайдам, убедиться, что ничего никуда на пиксель не съехало?

Тут десятки тысяч коммитов и почти 400 контрибьюторов. Чтобы еще чуть-чуть показать масштаб, я взял скриншот репозитория, который хранит только код фронтенда для новых браузеров — только выдачи по документам, без картинок и видео. Вот список синих ссылок, который вы привыкли видеть. Это только в верстке, только один проект.

Сергей Бережной, мой руководитель, очень любит эту историю, что раз уж нас так много собралось в компании — хочется, чтобы от нашего взаимодействия суммарно получалось как в JavaScript: один плюс один больше, чем два.

Первое, что приходит в голову в таких условиях, — повторное использование. И мы пытаемся получить все что можно от взаимодействия. Это какая-то картинка с подписью и еще какие-то разные элементы. Вот, например, сниппет видео на сервисе в поисковой выдаче по видео.

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

Или, вот, допустим, есть отдельный сервис Яндекс.Эфир, который чуть менее чем полностью состоит из похожих сниппетов.

Или, допустим, сниппет видео в нотификаторе, который есть на разных страницах портала.

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

Очевидно, похоже. Похоже? Если мы действительно позволим сервисам свои готовые компоненты легко внедрить на другие сервисы портала, то, очевидно, этот сервис, за счет того, что пользователи смогут взаимодействовать с их данными на разных площадках, получит больше пользователей. И что? Пользователи от этого тоже выиграют. Это здорово. Они будут вести себя привычно. Они будут видеть одинаковые штучки одинаково. То есть не придется каждый раз заново угадывать, что тут имел в виду дизайнер, и как с этим взаимодействовать.

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

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

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

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

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

Вы его копипастите к себе. Начинали мы в незапамятные времена, еще в SVN, и было лампово и удобно: папочка с HTML, прямо как в Bootstrap. И все. Рядом папочка со стилями, какой-то там JS, который тогда умел что-то просто показать/скрыть.

Здесь подсвечен b-domeg, который отвечал за авторизацию. Как-то так выглядел список компонентов. Мы называли «домик», хотя она намекала на почтовый конверт, потому что входили, обычно, в почту. Возможно, вы еще помните, на Яндексе, действительно, была такая форма для логина и пароля, с крышей.

Потом мы придумали целую методологию, чтобы иметь возможность поддерживать общие интерфейсы.

Сама библиотека внутри компании обзавелась собственным сайтом с поиском и всякой таксономией.

Видите, тоже почти 10 тысяч коммитов и более 100 контрибьюторов. Репозиторий сейчас выглядит вот так.

Сейчас она выглядит вот так. А вот это папка того самого b-домика в новой реинкарнации. Там внутри уже собственных папок больше, чем на пол-экрана.

А так выглядит сайт на сегодняшний день.

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

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

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

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

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

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

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

Мы ее с этой стороны решаем тем, что у нас появился Буткемп, который при появлении разработчика в Яндексе первым делом позволяет ему в течение восьми недель походить в разные команды, посмотреть, как там что устроено, и потом сделать выбор, где он будет работать окончательно. А один из способов решить проблему донесения информации — позволить разработчикам познакомиться с другими командами, включая ту, которая занимается общими компонентами интерфейсов. Он будет ориентироваться, где что есть. Но за это время у него заметно расширится кругозор.

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

Скорее всего, это поправить какую-то чиселку в package.json, перезапустить сборку. Эту версию нам нужно опубликовать в npm, а дальше пойти в репозиторий какого-то проекта, где используется библиотека, и внедрить эту версию. И что мы увидим? Возможно, еще перегенерировать package-lock, создать pull request, посмотреть, как пройдут тесты.

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

Гораздо печальнее, когда мы все это проделали, внедрили в десять разных сервисов. Причем это еще хорошо, когда мы внедряем в один сервис, и прямо там сразу все сломалось. Мы уже пошли заваривать смузи, или что там нужно. Там ничего не сломалось. И там находится баг. В это время версия внедряется в 11-й проект, или в 25-й. Причем этот patch может взорваться в каком-то из предыдущих. Мы возвращаемся по всей цепочке, делаем патч и внедряем его во все предыдущие 20 сервисов. Весело. Ну и так далее.

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

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

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

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

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

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

Чудо в том, что теперь выпустилась canary-версия с этими изменениями и она автоматически была внедрена во все репозитории, где используется этот компонент. Приходит робот и говорит — окей, я создал задачу на то, чтобы случилось следующее чудо. Здесь видно, что запустилась целая куча проверок. Там запустились автотесты, как и в этом репозитории.

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

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

Общий монорепозиторий, в котором отстроены линтеры. Что мы получили? Любая команда может прийти, положить свой компонент и проверить его JS unit-тестами, покрыть его скриншотами и т. То есть все пишут код одинаково, в нем есть все виды тестов. Все уже будет из коробки. д. Оно у нас благодаря богатым внутренним инструментам и правда умное. Умное код-ревью, которое я уже упоминал.

Звать его в пулл-реквест бессмысленно, система это учтет. Разработчик сейчас в отпуске? Система это тоже учтет. Разработчик заболел? И он такой: нет, сейчас я занят чем-то срочным или на встрече. Если оба условия не выполнились и разработчик, кажется, свободен — ему прилетит уведомление в какой-то из его мессенджеров, по его выбору. Система автоматически поймет, что нужно назначить следующего из списка. Он может прийти туда и просто написать команду /busy.

То есть нам с любым изменением кода нужно выпустить служебный пакет, который мы сможем проверить на разных сервисах. Следующий шаг — публикация той самой canary-версии. А когда все сошлось — запустить релизы. Дальше нам нужно запустить тесты при внедрении на все эти сервисы.

Это тоже работает из коробки. Если в изменении затронута какая-то статика, которая должна загружаться с CDN, нужно ее автоматически опубликовать отдельно. Но так как мы хотим, чтобы все это было автоматически, нужно, чтобы changelog был сформирован автоматом и где-то публиковался. При выпуске релиза нам, конечно, нужно знать, что происходило, что менялось.

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

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

Теги
Показать больше

Похожие статьи

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

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть