Skip to content

SemVer vs VerLess

Stefan edited this page Aug 11, 2021 · 4 revisions

Версионирование (semver) vs безверсионность (verless)

Вероятность словить баг при обновлении = Сумма(частота обновления зависимости / частота ломания зависимости)

Версионирование

Публикуются разные версии под одним именем.

  • Ломающие изменения: увеличивается мажоная версия (1.2.3 => 2.0.0)
  • Рефакторинг: увеличивается минорная версия (1.2.3 => 1.3.0)
  • Расширение функциональности: увеличивается минорная версия (1.2.3 => 1.3.0)
  • Баг-фиксы: увеличивается версия билда (1.2.3 => 1.2.4)
  • Конфликт версий: зависимости могут случайно подтянуть разные версии одного модуля, что может внести несовместимость
  • Миграция: в одном модуле нельзя одновременно использовать разные версии другого модуля, модули разных версий в общем случае не могут быть реализованы друг через друга, что утяжеляет бандл в случае конфликта
  • Энтропия: каждый модуль лаконичен, но в зависимостях между модулями бардак с версиями
  • Автокомплит: какую версию подключил - такую версию интерфейса и подсказывает
  • Фиксация ревизий: посредством лок-файлов

Версионирование в разработке нужно для воспроизводимости билдов на разных машинах в разные моменты времени.

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

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

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

Безверсионность (verless)

Оно же принцип Открытия/Закрытия. Совместимые версии публикуются под одним именем, несовместимые - под разными.

  • Ломающие изменения: публикуется под новым именем (module2 => module3)
  • Рефакторинг: публикуется под тем же именем (module2 => module2)
  • Расширение функциональности: публикуется под тем же именем (module2 => module2)
  • Баг-фиксы: публикуется под тем же именем (module2 => module2)
  • Конфликт версий: если зависимостям нужны несовместимые интерфейсы, то грузятся разные модули, которые могут быть реализованы через друг-друга и не иметь проблем с совместимостью
  • Миграция: можно использовать разные версии интерфейса ибо они находятся в разных модулях, реализация старого интерфейса может использовать новую реализацию (или наоборот), что предотвращает дублирование в случае конфликта
  • Энтропия: в одном пакете может быть несколько модулей с похожими именами (module, module2, module3) - либо терпеть, либо периодически создавать пакет с новым именем (pack2/module3 -> pack3/module)
  • Автокомплит: так как можно использовать несколько версий интерфейса, то все варианты и предлагает
  • Фиксация ревизий: посредством системы контроля версий

Безверсионность нужна, что б всегда получать билд с актуальными на момент сборки зависимостями и часто, но по-немногу фиксить наведенные от них баги.

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

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

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

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

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

А нужно ли?

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

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