Последние пару лет на работе занимаюсь внедрением и поддержкой человекочитаемости кода. Стараюсь следить за его чистотой и ясностью. Беспокоюсь о том, что бы всем всё было понятно. Пытаюсь говорить с бизнесом на одном языке. Переживаю, когда нахожу код с bus factor = 1. Верю, что у каждого есть свои сильные стороны, которые можно и нужно использовать. Люблю решать захватывающие задачи, а могу — даже не интересные.
Наткнулся на данную статью случайно, когда находился в очередном поиске ответа на вопрос «Как же так?!» в интернете. Автор данной статьи занимается мобильной разработкой, но тем не менее его наблюдения и выводы разительно перекликаются с моими.
Владеет кодом тот, кто его регулярно поддерживает.
Однажды я работал в одной компании, где девизом команды разработки была фраза «Ты владеешь тем, что пишешь». Это отлично мотивирует нести ответственность за любой написанный код. Но сказать проще чем сделать.
Вопрос владения кодом звучит просто и поэтому может с легкостью оставаться без внимания долгое время. Но последствия этого могут быть плачевными. Код превращается в спагетти, становится неподдерживаемым и вскоре никто не понимает как работает этот устаревший кусок г*вн@.
Любой код был кем-то написан, а значит он и есть его владелец, так?
Этот упрощенный взгляд применим только для личных проектов или маленьких команд с маленькими проектами.
В действительности самые важные и успешные проекты проходят различные этапы, которые превращают гордо сияющий код в жалкие таинственные обрывки, которых все боятся и ненавидят.
Чтобы узнать, как это происходит, ниже приведены семь этапов эволюции кода проекта, который в итоге становится безобразным.
1. Рождение
Разработчик начинает совершенно новый проект с продуманной архитектурой и чистым, структурированным кодом. Этот проект достаточно небольшой, поэтому один разработчик понимает весь процесс и общую структуру в целом.
По сути один человек владеет кодом.
2. Рост
В проекте появляется больше разработчиков, которые вносят свой вклад в код. Возможно, собирается команда из 3-5 человек. Есть экспертные оценки кода, парное программирование. Все понимают все части кода. Появляется ведущий разработчик, который вырабатывает соглашения по работе с кодом и следит за их соблюдением. Всё чисто и согласовано.
По сути команда владеет кодом.
3. Переиспользование
Чем больше функций (features - Прим. пер.) внедряется в проект, тем больше появляется возможностей переиспользовать написанное ранее, вместо того чтобы писать заново. Используются такие подходы как объектно-ориентированное наследование, выделение общих частей кода в общие классы т. д.
Каждый разработчик владеет своей частью функций, но общий слой принадлежит всем. Он используется на ура, но люди стараются избегать его модификации, так как это может потребовать изменения кода у всех остальных.
4. Объединение
Количество необходимых функций (features - Прим. пер.) растёт и команда из 3-5 разработчиков уже не справляется. Для работы над растущим проектом и удовлетворения его неотложных нужд компания решает нанять временных подрядчиков или одолжить членов команды из других проектов.
Технически код по-прежнему принадлежит команде, но она уже пишет не весь код. Члены команды заняты своими функциями и не могут полностью овладеть получающимся совместным (federated - Прим. пер.) кодом. Позже, когда подрядчики или члены других команд уйдут, код проекта останется в состоянии неполного владения командой.
5. Автоматизация
У одного умного разработчика появляется идея как ускорить работу без привлечения дополнительных людей. В коде видны некоторые шаблоны и общие паттерны, которые могут быть сгенерированы автоматически. Для упрощения процесса такой автоматизации разрабатывается сложный генератор шаблонного кода.
Никому нет необходимости владеть сгенерированным кодом, потому что этот код «сгенерирован хорошо». Через некоторое время разработчик использует его, но уже не знает как это работает внутри, потому что в этом «нет необходимости». Умный разработчик владеет кодом генератора… но ненадолго (см пункт 6 ниже).
6. Переходный период
Умный разработчик умён: он нашёл работу получше и ушёл. Некоторые члены команды тоже ушли. Возможно, организация была реорганизована. Команда сократилась. Руководство решило нанять новых людей для закрытия открывшихся вакансий. Произошло несколько таких итераций и первоначальную команду полностью заменили новыми разработчиками.
Команда по-прежнему «владеет» всем кодом, но теперь ни у кого нет полноценного чувства собственности. Половина из них знает часть кода, но подавляющее большинство изучает только «необходимый» минимум. Любые изменения в коде – это лишь патчи, так как все стараются избегать изменений в основном коде, потому что никто не знает как это работает.
7. Миграция
Через какое-то время все начинают ненавидеть работу с основной кодовой базой. Некоторые начинают рекомендовать переписать приложение полностью. Кроме того появляется новый стек технологий на который мы тоже собираемся переходить.
Однако бизнес понимает, что полное переписывание — это большой риск, поскольку приложение уже активно используется в продакшене. Его нельзя полностью переписать, так как это потребует времени и не гарантирует, что оно будет работать так же, как и существующее приложение.
Принимается решение постепенно оценить, как можно медленно перейти на более новую систему, сохраняя при этом «устаревший код». Старый «собственный» код теперь становится «горячей картошкой», к которому стараются не прикасаться.
Как вы можете заметить, теперь разработчикам не нравится работать с оригинальным хорошо спроектированым для роста и масштабирования кодом.
Может показаться, что проблема была вызвана «переиспользованием», «объединением», «автоматизацией» и «переходным периодом», но на самом деле не это является основной причиной проблем.
Основанием для возникновения всех этих проблем послужило «угасание чувства владения кодом с течением времени».
Как угасает чувство владения кодом?
Как видим, все начиналось хорошо. Но с течением времени дела шли все хуже и хуже, и так по спирали. Ниже приведены несколько причин:
1. Постоянно расширяющиеся функции
Программное обеспечение, которое только добавляет и добавляет функции, но не удаляет их, является бомбой замедленного действия. Нам кажется, что разработчики, которые постоянно создают все новые и новые функции, при этом продолжают до конца своей жизни поддерживать старые, которые они написали раньше.
Старые функции вскоре становятся не актуальными, поскольку авторы могут очень долго не прикасаться к этому коду, так как заняты работой над новыми функциями. И несмотря на то, что автору нравится код и у него сохраняется чувством владения этим кодом, существует предел тому, сколько он может в действительности контролировать.
2. Владеет каждый = никто не владеет
Такое происходит с общим кодом. Если нет человека или команды, которая следит за таким кодом, то вскоре это станет одним из нижеперечисленного:
Город-призрак — никто не хочет вносить туда изменения, так как есть шанс случайно зацепить другие функции. Вскоре никто не представляет как оно работает внутри.
Дикий-дикий запад — код постепенно изменяется и становится не универсальным, где каждая команда добавляет свои собственные уникальные условия обработки и API. Вскоре это превращается в беспорядок.
3. Кодируй, но не владей
Некоторым организациям нравится иметь группу разработчиков, которые перемещаются между проектами для их поддержки. Идея состоит в том, чтобы совместно использовать ресурсы между несколькими проектами.
С точки зрения бизнеса это звучит логично, так как действительно позволяет сэкономить ресурс разработки, но проблема заключается в том, что не ясно кто в конечном итоге будет владеть кодом, который будет написан такой командой.
В отличие от железного продукта (hardware - Прим. пер.), программный живет вечно и его необходимо время от времени изменять, чтобы поддерживать его в актуальном состоянии. Но при таком «временном» владении кодом он довольно скоро перестанет быть актуальным (become stale - Прим. пер.).
4. Упрощение через усложнение
Для ускорения написания кода иногда разработчики создают:
Сложные кодогенераторы для шаблонного кода
Сложные базовые классы или функции, которые должны упростить использование такого кода
Это отличные инициативы, которые действительно могут повысить производительность. Но стоимость тут заложена не столько в создании, сколько в поддержке. Поскольку это сложно, то велика вероятность, что стоимость поддержки также высока, а передача знаний непроста и это зачастую игнорируется.
Очень скоро никто не будет понимать как это работает и никто не захочет к этому прикасаться.
5. Мы здесь не навсегда
Почти наверняка первоначальные разработчики кода не останутся с проектом. Они или перейдут в другую компанию, или на другую позицию или уйдут в связи с реорганизацией.
Обычно, через несколько лет полностью сменится вся группа разработки. Без четкого плана по передаче знаний, скорее всего новая группа:
не будет хорошо знать код
не будет чувствовать привязанности к коду
будет иметь разные предпочтения относительно того каким код должен быть
Со всеми вышеперечисленными причинами скорее всего чувство владения кодом постепенно угаснет.
Как сохранить хорошее чувство владения кодом?
Для обеспечения надлежащего управления кодом как одному разработчику, так и команде ниже приведены несколько рекомендаций:
1. Скажи нет общему владению кодом
Никакой код не должен оставаться “в общем владении”. Если мы хотим сделать часть распространенного кода общедоступным, то у него должен быть разработчик или команда, которые полностью им владеют.
Основным принципом участия для владельца должно являться благополучие кода (the code wellness - Прим. пер.). При этом такой код по-прежнему позволяет другим вносить свой вклад, как и любой другой открытый код.
2. Четкая граница и автономность
Владение кодом должно обладать четкими границами: пакет, модуль или репозиторий. Это позволяет избежать двусмысленности, а также упрощает создание системы по управления этим самым владением. Это также гарантирует, что размер поддерживаемого кода всегда находится в разумных пределах для разработчика или команды.
Хотя в идеале мы и хотим большей согласованности в используемом коде (особенно, если это один и тот же язык), тем не менее мы должны предоставить каждому владельцу кода автономию в выборе его организации. Такая гибкость позволяет владельцу кода чувствовать себя более вовлеченным и не терять мотивацию для дальнейшего развития кода по мере необходимости.
3. Выгруженный код не значит окончательный
Код должен меняться… постоянно. Он требует регулярной поддержки. Код, который остаётся без присмотра на некоторое время (1-2 года) имеет тенденцию становиться устаревшим и неактуальным. Всегда найдётся что изменить или улучшить (обновить библиотеки, провести рефакторинг т.д.).
Чтобы код всегда оставался актуальным необходимо выделять время на его улучшение в то время пока разработчики работают над другими функциями (features - Прим. пер.). Это может быть связано с проработкой тестов, рефакторингом или внедрением новых практик разработки.
4. Удаление кода полезно
Учитывая, что для каждого кода периодически требуется выделение времени для его поддержки, каждый разработчик может владеть кодом только определенного размера.
Чтобы код оставался легко поддерживаемым, кроме добавление новых функций, бизнес должен думать о функциях, которые перестали быть актуальными или больше не используются. Такое очищение поможет сократить код, который необходимо поддерживать. (**Кстати это еще одна причина почему четкое разделение кода реализуемой функции способствует более легкому её удалению).
5. Автоматизация не бесплатна
Автоматизация — одно из самых интересных направлений разработки на начальном этапе. Работа над ней может быть сложной, но она приносит большое удовлетворение.
Реальная цена автоматизации выясняется позже – при её поддержке. Мы должны быть убеждены, что кто-то не только владеет этим, но и полностью разбирается как это работает. Это необходимо для того, что бы можно было решать любые возникающие проблемы или внедрять улучшения в будущем. Такая работа обычно становится чем-то устрашающим для каждого следующего владельца, который не разобрался в контексте кода такой автоматизации.
Однажды сделанная автоматизация хотя и может волшебным образом повысить продуктивность, но время от времени требует развития, чтобы сохранять свою актуальность. И стоимость каждого следующего шага иногда превышает стоимость шага первоначального. Поэтому необходимо иметь это ввиду прежде чем делать каждый следующий шаг.
Владеет кодом тот, кто его регулярно поддерживает
В разработке есть множество практик для создания масштабируемого и поддерживаемого кода, такие как написание чистого кода, хорошо прокомментированного и т. д, которых должен придерживаться каждый разработчик.
Однако в реальности, во многих организациях код по-прежнему превращается в устаревший (legacy - Прим. пер.) и, как следствие, ложится тяжелым бременем на разработчиков при его поддержке.
В большинстве случаев проблема заключается в том, что у такого кода больше нет владельца. Как только это происходит, качество кода резко падает и в конце концов становится неуправляемым.
Код нуждается в постоянной «любви», как в любых отношениях. Если мы будем постоянно «лелеять» его, то он будет продолжать цвести. Если нет, то быстро состарится и вскоре вернется и будет нас преследовать.
А вот дополнительные взгляды на владение кодом в больших компаниях:
https://gromov.com/ru/code-ownership про Facebook
https://habr.com/ru/post/482708/ про Microsoft
Для себя интересно будет узнать на сколько озвученное в статье перекликается с вашей реальностью разработчика.
Комментарии (13)
OlegAxenow
12.01.2023 13:51+5Разработчик начинает совершенно новый проект с продуманной архитектурой и чистым, структурированным кодом.
Всегда же так? Да? Ведь правда?
NekoiNemo
13.01.2023 13:22+2В домашних проектах, где ты и вся команда разработки, и devops, и product owner, и менеджер, и CTO & CEO, и даже клиент, все в одном лице - конечно всегда. А вот в коммерческих проектах такой идеализм как правило выживает не больше пары недель со старта проекта...
aldmarinka
12.01.2023 16:10+2Статья в самое сердечко. Часто встречаю огромные куски кода или целые модули и сервисы, которые писались при царе горохе. И дальше этот код продолжает подпираться костылями, вместо нормального рефакторинга, потому что его или страшно трогать или задача горящая и нужно вклинить ее здесь и сейчас, а не переписывать старое.
Прочесть такой код - та ещё задача(
perfect_genius
12.01.2023 17:24Почему бы не покрыть код тестами, чтобы не бояться трогать его?
DollyPapper
12.01.2023 18:04+5Потому что тестам тоже нужно доверять. Это не панацея. Вы уверены, что понимаете все кейсы которые вы покрыли? Вы уверены, что покрыли все кейсы? Была у меня ситуация. Нужно поменять некоторую логику в одном модуле. Тесты есть. Меняю. Тесты проходят. Через неделю оказывается, что в базе были хранимые процедуры (для тригеров) которые перестали работать как надо. В итоге модуль который я правил работал как надо, а данные которые обновлялись тригерами были некорректными, в следствии чего другой модуль перестал корректно работать. Да тут можно сколько угодно говорить про архитектуру и связанность кода, но факт есть факт - тесты были, но не помогли.
tdimdimich
12.01.2023 21:59+1Это проблема тестирования, а не владения кодом
funca
13.01.2023 22:14+1Это проблема владения проблемой: кто ей владеет тот и крайний. ;)
Тестирование это не панацея, а их процессы retirement тестов - когда давно не падавшие тесты на стабильный код исключаются из тестранов для сокращения времени выполнения, - играют тут злую шутку.
brutto Автор
14.01.2023 11:58Так причина боязни "трогания" не в отсутствие тестов, а в отсутствие понимания как это все работает.
Грубо говоря тесты даже если и есть, но ты не понимаешь как оно работает внутри, то и решения будут приниматься соответствующие: тяп-ляп сбоку или дублирующая функциональность или "нам срочно только тут". В итоге и тесты есть и код работает, а все вместе начинает пованивать (code smell) со временем все больше и больше.
Владение в данном случае призвано блюсти чистоту и своевременную передачу знаний касательно кода, что бы с ним было удобно и приятно работать.
funca
Знания можно передать, мудрость только нажить. Бесхозный код это жалкое зрелище. Но назначение хозяина - ещё не решение. Номинальное владение кодом даётся гораздо проще, чем овладение навыками его понимать и сопровождать.
Код эволюционирует вместе с его авторами. Вы меняете код, код меняет вас. Нужно прямо физически отрастить себе что-то в могзах, чтобы в решении появился паттерн, кодогкнератор, фреймворк, принцип и т.п. Комбинаций тьма и они довольно уникальны. У новых хозяев этого не будет (но за то будет что-то своё и они непременно воспользуются шансом для самореализации). Что-то успеют поменять, что-то нет. Через несколько итераций смены собственников код превращается в такое же лоскутное одеяло, как если бы у него ни каких хозяев вовсе не было.
Решение как мне кажется находится не в плоскости ownership, а в выращивании людей в той же среде. Прямо вместе с этим же кодом, годами. Junior, middle, senior и т.п. (а не 2 недели на KT и оно всё твое).
aldmarinka
Согласна с мыслью, про выращивание людей в той же среде. Только ещё и выращивать должен кто-то, кто давно в проекте и сам его хорошо знает. А то назначают ментором человека, который сам ещё меньше года в компании - он сам-то не всё знает и джунам всего не покажет. Ну и дальше повторится содержание статьи.
brutto Автор
Аха, всё верно на счёт формального владения. В статье как раз говорится о том, как владение в полном смысле этого слова вырождается в формальное и приводит к печальным последствиям.
Для меня интересным и близким к моей реальности тут было то, что показан процесс деградации ответственности и озвучены причины. Как говорится "предупреждён, значит вооружён". )
Моя боль сейчас сосредоточена именно на максимальном возможном упрощении передачи знаний и устранения единоличного владения внутренними проектами компании.