В любой дискуссии о версионировании — самые горячие споры обычно ведутся вокруг надуманной проблемы: «как нам при помощи правильной заверсионированности нивелировать нерадивость и низкую компетенцию наших сотрудников, не способных создавать обратно-совместимый код?».
Смотрите:
Не существует алгоритма, который позволяет определить, совместимые или же нет сделаны доработки по отношению в предыдущей версии. И более того, даже сами разработчики не всегда это могут определить. — https://habr.com/ru/articles/982740/
Это позор.
Да, обратная совместимость — как та самая сволочь с кафедры прикладной казуистики, превращает любую техническую дискуссию в философский трактат о природе времени и неизбежности смерти. Стоит кому-нибудь предложить сломать API, как тут же находится человек с горящими глазами, готовый час рассказывать о том, как это невозможно, как это разрушит экосистему и погубит всех котят в радиусе километра. Но правда в том, что большинство страхов об обратной совместимости — это именно мифы, подпитываемые ленью, невежеством или банальным нежеланием разбираться в проблеме.
Итак.
Миф №1: «Обратная совместимость требует сохранения всего старого кода навечно»
Нет. Обратная совместимость — это не мавзолей, где каждая строчка кода бальзамируется и выставляется на всеобщее обозрение. Это искусство создания переходных периодов, когда старое и новое существуют параллельно достаточно долго, чтобы пользователи успели мигрировать. Никто не требует держать костыли десятилетиями — достаточно дать людям полгода или год, четко объявить о deprecation, предоставить инструменты миграции и затем спокойно удалить устаревший код. Или не удалять — будьте уверены, в Уренгое есть КБ, инженеры которого все еще используют версию 0.1.0 вашего API; просто удалите документацию и вываливайте ворнинги прямо им в дышло^W терминал¹.
Проблема не в обратной совместимости как таковой, а в отсутствии политики версионирования и доверия к пользователям.
Миф №2: «Поддержка старых версий делает код нечитаемым»
Плохой код делает код нечитаемым. Обратная совместимость делает его просто немного более многословным. Если у вас функция превращается в монстра Франкенштейна только потому, что вы добавили опциональный параметр с дефолтным значением, проблема не в обратной совместимости, а в архитектуре. Хорошо спроектированный интерфейс позволяет расширяться без превращения в помойку. Да, придется написать пару adapter-функций, да, придется держать в голове контракт. Но это не магия и не квантовая физика — это обычная инженерная работа, которую делают миллионы программистов каждый день.
Кроме того, абстракции в виде адаптеров — нужны и так, если вы не на скрауливаете интернет на перле, конечно.
Миф №3: «Нельзя исправить старые ошибки, не сломав совместимости»
Да окститесь. Можно, разумеется. Более того, это делается постоянно. Добавьте новую функцию с исправленным поведением, пометьте старую как deprecated, дайте людям время перейти. Или используйте флаги совместимости, которые позволяют явно выбрать новое поведение. Или версионируйте API так, чтобы v2 жил рядом с v1, не мешая ему гнить в покое. Единственное, что действительно нельзя — это исправить ошибку так, чтобы об этом никто не узнал. Но это и не нужно: пользователи не идиоты, они понимают, что ошибки бывают, если только вы не пытаетесь делать вид, что ошибки — это фичи.
Не существует багов, вызванных тем, что вы принимаете в функции не те параметры, которые ей нужны для нормальной работы. Бывает мало параметров, бывают лишние, — но обе эти ситуации не требуют ничего ломать.
Миф №4: «Обратная совместимость душит инновации»
Муа-ха-ха. Инновации душит неумение думать. Обратная совместимость — это просто одно из ограничений, с которым нужно работать, как с производительностью или безопасностью. Да, она накладывает определенные рамки, но эти рамки не запрещают делать что-то новое — они требуют делать это аккуратно. История полна примеров систем, которые развивались десятилетиями, оставаясь обратно совместимыми: от HTTP до Linux kernel. Erlang обратно-совместим до первой публичной версии 80-х годов.
Если Google может добавлять новые фичи в Chrome, не ломая половину интернета, то и вы справитесь со своим API для обработки JSON.
Миф №5: «Пользователи сами виноваты, если используют недокументированное поведение»
Да, но нет. С технической точки зрения — да. С человеческой — нет. Если ваш код делает что-то определенное, люди будут на это полагаться, документировали вы это или нет. Это называется закон Хайрама: все наблюдаемое поведение вашей системы будет использоваться кем-нибудь. Даже внутри одной команды. Можно сколько угодно писать в README большими буквами «ЭТО ВНУТРЕННЯЯ РЕАЛИЗАЦИЯ, НЕ ТРОГАТЬ», но если это работает и решает чью-то проблему, оно будет использоваться². Винить пользователей за это — все равно что оставлять открытым люк посреди дороги и удивляться, что кто-то туда упал. Надо либо четко определять контракты и их границы, либо смириться с тем, что люди будут тащить в свой код всё, что плохо прибито.
¹ Вот как работает deprecation в одной из моих самых эзотерических библиотек на руби:

² Случай из жизни: в одной из моих библиотек мне нужно было парсить простенькие PlantUML/Mermaid диаграммки. Ради такого я не тащу в свои проекты монструозные зависимости, поэтому я быстренько набросал свой парсек в приватном пространстве и забыл. Спустя примерно год, я получил issue с претензией, что мой парсер не справляется с — не помню уже точно — типа, вложенными комментариями в стейтах.
Иными словами, чувак нашел мою библиотеку для правильной работы с конечными автоматами (!) и использовал её, чтобы недокументированными вспомогательными функциями парсить сложный PlantUML.
Так что выбор между всеми этими вашими SemVer, CalVer и прочие ShitVer — это споры о том, сколько чертей может уместиться на кончике ножа. Просто обеспечивайте обратную совместимость. Оставьте возможность вызвать первую версию API, даже, если сейчас мажор перевалил за двадцать.
Я делаю именно так — поэтому ни разу за взрослую карьеру мне не приходилось тратить бесценное время, калории и нервные клетки на раздумья о том, как правильно версионировать код.
