Pull to refresh

Старый код: почему он такой

Perfect code *Designing and refactoring *
Большинство из разработчиков рано или поздно сталкиваются с необходимостью что-нибудь поменять в коде, которому уже много лет. К тому моменту над этим кодом успело поработать, сменяя друг друга, множество программистов, и каждый из них что-то менял или добавлял новые кусочки.

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

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

Работоспособность и экономика


Обычно старый код на 100% работоспособен. Ну или, если быть честным, на 95%. Боль начинается, когда необходимо внести в систему изменения, особенно, когда изменения приводят к появлению плавающих багов, на борьбу с которыми, обычно, уходит большая часть времени.

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

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

Любые изменения затрагивают большое количество пользователей и могут негативно повлиять на стабильность продукта. Если продукт – флагманский и приносит компании кучу денег, то нарушение стабильности может обойтись компании в кругленькую сумму. В данном случае как нельзя более точно описывает ситуацию анекдот про сына сисадмина: «тогда, ради Бога, ничего не трогай, ничего не меняй!».

Очевидно, что в такой консервативной атмосфере изменения как минимум крайне нежелательны, а скорее всего – невозможны без тщательного согласования с множеством заинтересованных лиц. И, если изменения продиктованы потребностями бизнеса (обычно – это новые фичи или попытки улучшить текущие), то обосновать их гораздо проще, чем «рефакторинг» или, тем более, «всё переписать заново».

Как обосновать рефакторинг бизнесу – тема отдельной статьи. Вкратце, для этого должны сойтись три фактора:
  • у бизнеса должна появиться потребность внести большие изменения в продукт;
  • бизнес должен осознать, что вносить изменения в существующую систему долго и дорого или даже невозможно;
  • должен найтись грамотный руководитель разработки, который должен правильно «продать» бизнесу необходимость рефакторинга / рерайтинга).

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

Гуру-носитель знаний


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

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

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

Не стоит и говорить о том, что попытки кого-то ещё работать с этим кодом приводят к огромным трудозатратам. Автор-гуру держит знания о коде «в оперативной памяти» и помнит (если повезёт) историю забивания того или иного костыля, тогда как новый программист вынужден продираться через хитросплетения костылей, обросших вековым мхом и разбираться в устройстве легаси с нуля.

Ну и нельзя забывать про bus-factor. Если «тот самый» программист уходит из компании, код медленно начинает превращаться в тыкву. Кстати, время работы такого кода после ухода автора прямо пропорционально его мастерству и может достигать нескольких лет. Но, увы, «работает» не значит «развивается» и внесение изменений в такой код либо просто невозможно, либо разрушает всю систему.

Запутанная логика


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

Неактуальная документация и юнит-тесты


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

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

В результате рано или поздно, документация замирает на уровне «так было в прошлом году, а сейчас всё поменялось». Увы, парадокс документации в том, что там, где она действительно нужна – в самых сложных, запутанных и даже алогичных на первый взгляд местах, её написание и поддержка занимает неоправданное количество времени и сил и обычно откладывается.

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

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

Что дальше?


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

В продолжение темы: Техобслуживание кода. Как продать рефакторинг бизнесу
Tags:
Hubs:
Total votes 25: ↑20 and ↓5 +15
Views 21K
Comments Comments 17