Великие разработчики знают, когда и как рефакторить код Rails

Опубликовано: 2022-03-11

Крупномасштабный рефакторинг: зачем вообще делать что-то подобное?

Если он не сломан, не чините его.

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

Исправление функциональности, улучшение пользовательского интерфейса, повышение скорости и эффективности использования памяти, добавление функций: все это действия, для которых легко увидеть, стоит ли их выполнять, а затем мы, как опытные разработчики Rails, выступаем за или против того, чтобы тратить на них свое время. Однако есть деятельность, которая по большей части попадает в серую зону — стандартный рефакторинг и особенно крупномасштабный рефакторинг кода.

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

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

Дааааа, это устаревший код, который имел смысл в то время, но спецификации изменились, и теперь его исправлять слишком дорого?

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

При планировании рефакторинга кода Rails вам может понадобиться сложная диаграмма для начала работы.

Вопрос: «Зачем нам вообще это делать?» является естественным и может быть столь же важным, как и его выполнение...

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

Улучшения производительности

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

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

Архитектурные улучшения

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

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

Обновление

Иногда код изначально хорошо написан. Вы очень довольны этим. Он быстрый, эффективно использует память, удобен в сопровождении и полностью соответствует спецификациям. Изначально. Но затем спецификации меняются, бизнес-цели меняются или вы узнаете что-то новое о своих конечных пользователях, что опровергает ваши первоначальные предположения. Код по-прежнему работает хорошо, и вы по-прежнему довольны им, но что-то просто неловко, когда вы смотрите на него в контексте конечного продукта. Вещи размещены в немного неправильной подсистеме, или свойства находятся в неправильном классе, или, может быть, некоторые имена больше не имеют смысла. Сейчас они выполняют роль, которая в бизнес-терминах называется совершенно по-другому. Тем не менее, по-прежнему очень сложно оправдать какой-либо крупномасштабный рефакторинг Rails, поскольку требуемая работа будет такой же, как и в любом другом примере, но преимущества гораздо менее ощутимы. Если подумать, поддерживать его не так уж и сложно. Вы просто должны помнить, что некоторые вещи на самом деле являются чем-то другим. Вам просто нужно помнить, что A на самом деле означает B, а свойство Y на A на самом деле относится к C.

И здесь кроется настоящая выгода. В области нейропсихологии есть много экспериментов, предполагающих, что наша кратковременная или рабочая память способна удерживать всего 7+/-2 элемента, один из них — эксперимент Штернберга. Когда мы изучаем предмет, мы начинаем с основных элементов, и первоначально, когда мы думаем о понятиях более высокого уровня, мы должны думать об их определениях. Например, рассмотрим простой термин «соленый пароль SHA256». Первоначально мы должны хранить в нашей рабочей памяти определения для «соленого» и «SHA256» и, возможно, даже определение «хеш-функции». Но как только мы полностью понимаем этот термин, он занимает только один слот памяти, потому что мы понимаем его интуитивно. Это одна из причин, по которой нам необходимо полностью понимать концепции более низкого уровня, чтобы иметь возможность рассуждать о высокоуровневых. То же самое относится и к терминам и определениям, характерным для нашего проекта. Но если нам приходится вспоминать перевод в истинном значении каждый раз, когда мы обсуждаем наш код, этот перевод занимает еще один из этих драгоценных слотов рабочей памяти. Это создает когнитивную нагрузку и затрудняет понимание логики в нашем коде. В свою очередь, если рассуждать сложнее, значит, больше шансов, что мы упустим важный момент и внесем ошибку.

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

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

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

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

Подготовка

Убедитесь, что ваше тестовое покрытие очень хорошее во всех областях кодовой базы, которые вы, вероятно, затронете. Если вы видите, что некоторые части недостаточно хорошо покрыты, сначала потратьте некоторое время на увеличение тестового покрытия. Если у вас вообще нет тестов, вам следует сначала потратить время на их создание. Если вы не можете создать надлежащий набор тестов, сосредоточьтесь на приемочных тестах и ​​напишите столько, сколько сможете, и обязательно напишите модульные тесты во время рефакторинга. Теоретически вы можете провести рефакторинг кода без хорошего покрытия тестами, но это потребует от вас много ручного тестирования и делать это часто. Это займет гораздо больше времени и будет более подвержено ошибкам. В конечном счете, если ваше тестовое покрытие недостаточно хорошее, стоимость выполнения крупномасштабного рефакторинга Rails может быть настолько высока, что вам, к сожалению, следует подумать о том, чтобы не делать этого вообще. На мой взгляд, это преимущество автоматических тестов, которому недостаточно часто уделяется внимание. Автоматизированные тесты позволяют проводить рефакторинг часто и, что более важно, делать это смелее.

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

Вам понадобится помощник для процесса рефакторинга. Вы можете предпочесть что-то другое, но мне нравится простой лист бумаги и ручка. Я начинаю с того, что записываю первоначальное изменение, которое хочу внести, в левом верхнем углу листа. Затем я начинаю искать все места, затронутые изменением, и записываю их под начальным изменением. Здесь важно использовать свое суждение. В конечном счете, заметки и диаграммы на бумаге для вас, поэтому выберите стиль, который лучше всего соответствует вашей памяти. Я пишу короткие фрагменты кода с маркерами под ними и множеством стрелок, ведущих к другим подобным примечаниям, указывающим на то, что зависит от него напрямую (полная стрелка) или косвенно (пунктирные стрелки). Я также помечаю стрелки условными обозначениями в качестве напоминания о какой-то конкретной вещи, которую я заметил в кодовой базе. Помните, что вы будете возвращаться к этим заметкам только в течение следующих нескольких дней, пока будете вносить запланированные в них изменения, и совершенно нормально использовать очень короткие и загадочные напоминания, чтобы они занимали меньше места и их было легче размещать на бумаге. . Несколько раз я убирал свой стол спустя месяцы после рефакторинга Rails и находил одну из этих бумаг. Это была полная тарабарщина, я понятия не имел, что что-либо значило на этой бумаге, кроме того, что это могло быть написано кем-то сумасшедшим. Но я знаю, что эта бумажка была незаменима, пока я работал над задачей. Кроме того, не думайте, что вам нужно записывать каждое изменение. Вы можете группировать их и отслеживать детали по-другому. Например, в своей основной статье вы можете отметить, что вам нужно «переименовать все вхождения Ab в Cd», а затем вы можете отслеживать особенности несколькими различными способами. Вы можете написать их все на отдельном листе бумаги, вы можете запланировать еще раз выполнить глобальный поиск всех его вхождений или вы можете просто оставить все исходные файлы, в которых необходимо внести изменения, открытыми в выбранном вами редакторе. и сделайте мысленную пометку, чтобы вернуться к ним, как только вы закончите наносить изменения.

Когда вы наметите последствия ваших первоначальных изменений, поскольку они носят крупномасштабный характер, вы, скорее всего, определите дополнительные изменения, которые сами по себе будут иметь дальнейшие последствия. Повторите анализ и для них, отмечая все зависимые изменения. В зависимости от размера изменений вы можете записать их на том же листе бумаги или выбрать новый пустой. Очень важная вещь, которую нужно попытаться сделать при планировании изменений, — это попытаться определить границы, в которых вы действительно можете остановить изменения ветвления. Вы хотите ограничить рефакторинг наименьшим разумным, округленным набором изменений. Если вы видите точку, в которой вы можете просто остановиться и оставить все как есть, сделайте это, даже если вы видите, что ее нужно реорганизовать, даже если она концептуально связана с другими вашими изменениями. Завершите этот раунд рефакторинга кода, тщательно протестируйте, разверните и возвращайтесь за дополнительными сведениями. Вы должны активно искать эти точки, чтобы держать размер изменений под контролем. Конечно, как всегда, рассудите. Довольно часто я сталкивался с тем, что мог отрезать процесс рефакторинга, добавляя несколько прокси-классов для небольшого преобразования интерфейса. Я даже начал их реализовывать, когда понял, что это будет столько же работы, сколько и продвижение рефакторинга немного дальше до точки, где будет точка «естественной остановки» (т.е. почти не требуется прокси-код). Затем я отступил, отменил свои последние изменения и провел рефакторинг. Если все это немного похоже на составление карты неизведанной территории, то это потому, что мне так кажется, за исключением того, что карты территорий только двухмерны.

Исполнение

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

После того, как вы на самом деле рефакторите код, обратите внимание на странные фрагменты кода, которые делают что-то очень специфическое и могут показаться плохим кодом. Может быть, это плохой код, но довольно часто они на самом деле обрабатывают странный случай, обнаруженный при исследовании ошибки в рабочей среде. Со временем в большей части кода Rails появляются «волосы» или «бородавки», которые обрабатывают странные угловые ошибки, например, здесь странный код ответа, который может быть необходим для IE6, или условие, которое обрабатывает странную ошибку синхронизации. Они не важны для общей картины, но все же являются важными деталями. В идеале они явно покрываются юнит-тестами, если не пытаться их сначала покрыть. Однажды мне поручили портировать приложение среднего размера с Rails 2 на Rails 3. Я был очень хорошо знаком с кодом, но он был немного запутанным, и нужно было принять во внимание множество изменений, поэтому я выбрал повторную реализацию. На самом деле, это не было реальной перереализацией, так как это вряд ли когда-либо было разумным шагом, но я начал с чистого приложения Rails 3 и преобразовал вертикальные срезы старого приложения в новое, примерно используя описанный процесс. Каждый раз, когда я заканчивал вертикальный срез, я просматривал старый код Rails, просматривая каждую строку и дважды проверяя, есть ли у нее аналог в новом коде. По сути, я собирал все «ворсинки» старого кода и копировал их в новую кодовую базу. В конце концов, в новой кодовой базе были учтены все крайние случаи.

Обязательно достаточно часто проводите ручное тестирование. Это заставит вас искать естественные «пробелы» в процессе рефакторинга, что позволит вам протестировать часть системы, а также даст вам уверенность в том, что вы не сломаете ничего, что вы не ожидали сломать в процессе. .

Заверните

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

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

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

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

Когда бы вы этого не сделали?

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

Ваше тестовое покрытие плохое

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

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

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

Вы поджимаете время

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

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

Заключение

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