Это первая из 4 частей, посвященных передовым методам организации репозиториев кода.

  • Часть 1: Проблема с общим кодом (эта статья)
  • Часть 2: Как лучшие технологические компании организуют кодекс (скоро)
  • Часть 3: Результаты опроса Monorepo и Multirepo (скоро, но заполните опрос здесь)
  • Часть 4: Структура принятия решений о РЕПО (скоро)

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

Ранее в этом году моя компания GetHuman собрала деньги и выросла с 2 инженеров до 8. Хорошо, что мы смогли начать писать намного больше кода. Плохо то, что мы начали писать намного больше кода.

Эта проблема

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

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

Эффективное совместное использование кода в масштабе сложно.

Чтобы быть ясным, поделиться кодом несложно. Есть много разных способов совместного использования кода, и большинство из них относительно легко реализовать. Самое сложное - сделать это эффективно в масштабе, что означает:

  1. Несколько модулей кода (которые имеют общий код)
  2. Несколько членов команды
  3. Высокая скорость изменения
  4. Практически нет потери индивидуальной продуктивности

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

Проблемы масштабируемости

За последний год большинство проблем, с которыми мы столкнулись при масштабировании нашей кодовой базы, попали в одну из следующих категорий:

  • Рефакторинг
  • Управление версиями
  • Тестирование
  • Обзор
  • Последовательность
  • Развертывание
  • Размер

Каждая категория более подробно описана ниже.

Рефакторинг

Допустим, мы хотим изменить поле в общей модели данных с «isSubscriber» на «subscriberFlag». Вы можете просто опубликовать это однострочное изменение кода и позволить любому коду, использующему эту модель данных, сломаться, или вы можете попытаться обновить все ссылки на «isSubscriber» одновременно во всей базе кода. Многие редакторы кода умеют это делать, но весь код должен загружаться в редактор одновременно.

Это может стать серьезным источником боли по мере роста вашей кодовой базы. Например, в какой-то момент у нас было 42 модуля npm, разделенных на 15 различных репозиториев. Было почти невозможно использовать Webstorm со всем этим кодом (он регулярно блокировался для повторной индексации). VS Code лучше, потому что он обычно не блокируется, но intellisense часто недоступен (я подозреваю, что из-за аналогичной необходимости постоянно переиндексировать код в фоновом режиме).

Управление версиями

Каждый раз при обновлении подчиненной зависимости у вас есть выбор:

  1. Позвольте восходящим модулям обновиться в какой-то момент в будущем
  2. Заставить все вышестоящие модули немедленно обновить

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

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

Тестирование (для JavaScript)

В общем, интеграционные и сквозные (e2e) тесты могут быть непростыми. Для JavaScript проблему тестирования нескольких локальных зависимостей можно лучше всего описать этой хорошо известной поговоркой JavaScript:

"Npm связывание - отстой, йоу"

Теоретически вы должны иметь возможность npm связывать все свои локальные зависимости во время внесения изменений. Однако на практике это редко работает хорошо, когда у вас есть большое количество взаимозависимых модулей npm. Связанные с этим накладные расходы нетривиальны (например, посмотрите, сколько времени у вас уйдет на то, чтобы npm связать 3+ модулей вместе), и, казалось бы, регулярно возникает широкий спектр сложных проблем (например, эта неприятная проблема с TypeScript или эта неприятная проблема). раздражающая проблема Angular », или эта проблема Babel , или эта проблема Webpack , или эта другая проблема Webpack … вы поняли).

Обзор

Мы всегда стараемся, чтобы каждый запрос на вытягивание (PR) был как можно более мелким и конкретным. Однако иногда даже небольшие целевые обновления требуют изменений в нескольких разных репозиториях. Каждое задействованное репо требует накладных расходов на другой PR. Вдобавок ко всему, рецензентам трудно понять весь масштаб изменения, так как они должны просмотреть все задействованные ОР.

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

  1. Во-первых, как настроить систему, в которой нужные люди могут просматривать нужные PR в нужное время?
  2. Во-вторых, как гарантировать, что любая система, которую вы настраиваете для ответа на первый вопрос, не создает несостоятельную матрицу привратников, которые снижают производительность труда?

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

Последовательность

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

Развертывание

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

CircleCI

TravisCI

Кодирование

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

Размер

Еще одна серьезная проблема при попытке использовать одно репо для всего - это размер. Принято считать, что с аппаратным обеспечением проще в краткосрочной перспективе масштабировать по вертикали (т. Е. Добавить больше памяти или больше ЦП), но в долгосрочной перспективе лучше масштабировать по горизонтали (т. Е. Добавлять больше машин). Та же логика верна и для репозиториев кода.

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

  • Клонирование занимает несколько минут, а иногда и не удается
  • Слияние занимает слишком много времени
  • Репо не подходит для некоторых ноутбуков разработчиков

В общем, работа с большим репо может быть в лучшем случае проблематичной и со временем будет только ухудшаться, если не будут приняты меры по уменьшению размера (подробнее об этом в Части 2 этой серии).

Итак, что нам делать со всем этим?

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

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

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

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

Я не думаю, что есть одно идеальное решение, которое подойдет всем, но я думаю, что многие разные компании придумали решения, которые им подходят. Ключ в том, чтобы объединить эти знания в единый и легко усваиваемый пакет. Именно этого я и надеюсь достичь в следующей части этой серии сообщений в блоге «Как лучшие технологические компании организуют кодекс».

Я еще не публиковал остальные 3 статьи, но как только я их напишу, я дополню эту статью ссылками:

  • Часть 1: Проблема с общим кодом (эта статья)
  • Часть 2: Как лучшие технологические компании организуют кодекс (скоро)
  • Часть 3: Результаты опроса Monorepo и Multirepo (скоро, но заполните опрос здесь)
  • Часть 4: Структура принятия решений о РЕПО (скоро)

А пока, пожалуйста, оставляйте комментарии ниже и / или напишите мне в Twitter, поделившись своими мыслями и мнениями по этой теме.