Pull to refresh

Как рефакторинг помогает не потратить кучу денег на продукт

Level of difficultyEasy
Reading time7 min
Views5.5K

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

А бывало ли у вас такое?...
А бывало ли у вас такое?...

Приветствую! Меня зовут Андрей Степанов, я CTO во fuse8. Мне интересно знакомиться с опытом коллег по цеху и делиться своим. В сфере я уже больше 20 лет. В этой статье – размышления и советы относительно практики рефакторинга больших продуктов.

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

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

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

Когда нужно проводить рефакторинг кода продукта

Через год-два после запуска. Это если коротко. Но давайте поясним. Если продукт претендует на развитие, а бизнес – на увеличение прибыли от его использования, кодовая база продукта неизменно будет расширяться.

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

*ходит по краю*
*ходит по краю*

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

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

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

Либо своевременный регулярный рефакторинг, либо монструозный техдолг

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

Зачастую поток бизнесовых задач непрерывный, все эти задачи с высоким приоритетами, и на рефакторинг как будто нет времени.

*звуки недовольства*
*звуки недовольства*

Когда начинаем копаться в коде, чтобы прикрутить новые фичи, понимаем: «Ага, вот тут не вписывается, вот тут устарело, а вот здесь вообще можно внедрить только костыльное решение».

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

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

Большой техдолг: почему отрабатывать его дороже, чем делать своевременный рефакторинг

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

Не надо так
Не надо так

Чем больше в проекте неотрефакторенного кода, тем ближе он к неподдерживаемому состоянию.

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

Так может и нечего беспокоиться, если продукт можно сделать заново? Если возникает такая мысль, то давайте расскажем, почему так не работает.

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

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

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

Легко ли переписать продукт с нуля

Нет. Как минимум потому, что на это потребуется вдвое-втрое больше времени (и денег), чем ушло на первоначальную версию. В нашей практике был подобный случай.

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

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

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

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

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

А еще придется полностью переписать весь процесс деплоя и заново настроить метрики, по которым можно будет отследить «живость» проекта, потому что на старте неминуемо будут возникать проблемы.

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

Что нужно, чтобы рефакторинг был проще и быстрее

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

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

Введение тестов. Рефакторинг подразумевает изменение кода, и удобно сразу иметь возможность прогнать его через тесты. Это уверенность в том, что все работает правильно. Рефакторинг будет более надежным при наличии тестов.

Комментирование кода. Особенно комментарии важны в местах со сложной логикой. Иногда в код приходится вводить странности, вызванные не вредностью разработчика, а внешними обстоятельствами. Например, сервис может использовать какое-нибудь нестандартное API или найдутся какие-то нелогичные на первый взгляд бизнес-требования. Такие места в коде лучше отмечать комментариями.

Ну, и последнее – своевременность.

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

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

Тезисный итог

К чему приводит несвоевременный рефакторинг:

  • На переработку существующей функциональности целиком тратится намного больше времени, чем если бы она была своевременно переработана по кусочкам.

  • Становится страшно вносить правки в существующую функциональность: сложно оценить размер возможных поломок. Это типичная проблема legacy-проектов.

  • При доработке функциональности баги постоянно доходят до продакшена, нет возможности оценить все риски.

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

  • Тяжелый онбординг новичков: они не смогут самостоятельно разобраться с кодовой базой. Придется постоянно советоваться с коллегами, но и это не гарантирует отсутствие дефектов в работе.

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

  • У системы постоянно падает производительность.

  • Если все совсем плохо, придется выбросить текущее решение и написать все сначала. А это боль.

Причины, по которым возникает необходимость рефакторинга:

  • Задача на этапе аналитики была проработана неверно.

  • Назначение функциональности со временем сильно поменялось.

  • Готовая функциональность постоянно дорабатывалась/расширялась, что привело к разрастанию и усложнению кода, появлению костылей.

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

  • Компетенции команды со временем улучшаются, поэтому становятся видны огрехи в ранее написанном коде.

  • Нагрузка на систему и (или) размер хранимых данных превосходят критическую отметку. Из-за этого падает производительность сервиса.

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

Tags:
Hubs:
Total votes 7: ↑6 and ↓1+6
Comments10

Articles