Какой лучший способ справиться с рефакторингом большого файла?

42

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

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

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

Так есть ли способ рефакторинга, не требовать, чтобы кто-то еще прекратил работать (надолго) или объединил свои функциональные ветви для разработки?

Hoff
источник
6
Я думаю, что это также зависит от используемого языка программирования.
Роберт Анджейюк
8
Мне нравятся "небольшие пошаговые" проверки. Если кто-то не обновит свою копию репо, эта практика сведет к минимуму конфликты слияния для всех.
Мэтт Раффель
5
Как выглядят ваши тесты? Если вы собираетесь провести рефакторинг большого (и, вероятно, важного!) Фрагмента кода, убедитесь, что ваш тестовый набор находится в действительно хорошем состоянии, прежде чем проводить рефакторинг. Это значительно упростит проверку правильности ваших настроек в файлах меньшего размера.
CorsiKa
1
Существует множество подходов, которые вы можете использовать, и наилучший подход будет зависеть от вашей ситуации.
Стивен
3
Я присоединился к проекту, где самый большой файл длиной 10 тыс. Строк содержит класс, который сам по себе имеет длину 6 тыс. Строк, и все боятся его трогать. Я имею в виду, что ваш вопрос великолепен. Мы даже придумали шутку, что этот класс - хорошая причина разблокировать колесо прокрутки в мышах.
ElmoVanKielmo

Ответы:

42

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

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

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

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

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

  1. Извлеките функциональность в отдельный модуль.
  2. Измените старые функции, чтобы переадресовывать их вызовы на новый API.
  3. Со временем портируйте зависимый код на новый API.
  4. Наконец, вы можете удалить старые функции.
  5. (Повторите для следующего набора функций)

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

Но в конце концов, любое решение потребует от вас координации с вашей командой.

Амон
источник
1
@Laiv К сожалению, это очень общий совет, но некоторые идеи из гибкого пространства, такие как непрерывная интеграция, явно имеют свои достоинства. Группам, которые работают вместе (и часто интегрируют свою работу), будет легче вносить большие сквозные изменения, чем командам, которые работают только вместе. Это не обязательно о SDLC в целом, больше о сотрудничестве внутри команды. Некоторые подходы делают совместную работу более осуществимой (например, принцип Open / Closed, микросервисы), но команда OP еще не готова.
Амон
22
Я бы не пошел так далеко, чтобы сказать, что ветвь функции должна иметь короткое время жизни - просто она не должна отклоняться от своей родительской ветви в течение длительных периодов времени. Регулярное объединение изменений из родительской ветви в ветвь функций работает в тех случаях, когда ветвь функций должна задерживаться дольше. Тем не менее, рекомендуется хранить ветки функций не дольше, чем это необходимо.
Дэн Лайонс
1
@Laiv По моему опыту, имеет смысл заранее обсудить с командой проект пострефакторинга, но обычно проще всего, если один человек вносит изменения в код. В противном случае вы вернетесь к проблеме, что вам нужно объединить вещи. 4k строк звучат как много, но на самом деле это не для целевых рефакторингов, таких как extract-class . (Я бы так усердно отнесся к книге Рефакторинга Мартина Фаулера, если бы прочитал ее.) Но 4k строк - это много только для нецелевого рефакторинга, такого как «давайте посмотрим, как я могу улучшить это».
Амон
1
@DanLyons В принципе, вы правы: это может распространить часть усилий по слиянию. На практике слияние Git во многом зависит от последнего общего предка, объединяющего объединяемые ветви. Функция слияния master → не дает нам нового общего предка на master, а функция слияния → master делает. При повторном слиянии master → feature может случиться так, что нам придется снова и снова разрешать одни и те же конфликты (но см. Git rerere, чтобы автоматизировать это). Перебазирование здесь строго превосходит, потому что наконечник мастера становится новым общим предком, но у переписывания истории есть другие проблемы.
Амон
1
Для меня все в порядке, за исключением разглагольствования о том, что git делает его слишком простым для ветвления, и, следовательно, слишком часто разветвляет devs. Я хорошо помню времена SVN и даже CVS, когда ветвление было трудным (или, по крайней мере, громоздким) настолько, что люди вообще избегали его, если это возможно, со всеми связанными проблемами. В git, будучи распределенной системой, наличие множества веток на самом деле ничем не отличается от наличия множества отдельных репозиториев (т. Е. Для каждого разработчика). Решение лежит в другом месте, проблема не в том, чтобы легко переходить. (И да, я вижу, что это просто в стороне ... но все же).
AnoE
30

Выполните рефакторинг небольшими шагами. Допустим, ваш большой файл имеет имя Foo:

  1. Добавьте новый пустой файл Barи передайте его в "trunk".

  2. Найдите небольшую часть кода, в Fooкоторую можно перейти Bar. Примените перемещение, обновите из транка, соберите и протестируйте код и передайте в «транк».

  3. Повторите шаг 2 до Fooи Barимеют одинаковый размер (или любой другой размер вы предпочитаете)

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

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

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

Док Браун
источник
2
Это особенно полезно при rerereвключенной опции
gits
@ D.BenKnoble: спасибо за это дополнение. Я должен признать, что я не эксперт по git (но описанная проблема не относится конкретно к git, она применима к любой VCS, которая допускает ветвление, и мой ответ должен соответствовать большинству этих систем).
Док Браун
Я рассчитывал исходя из терминологии; на самом деле, с помощью git такое слияние по-прежнему выполняется только один раз (если кто-то просто тянет и сливает). Но всегда можно тянуть и выбирать, или объединять отдельные коммиты, или перебазировать в зависимости от предпочтений разработчика. Это занимает больше времени, но, безусловно, выполнимо, если автоматическое объединение, скорее всего, не удастся.
Д. Бен Кнобл
18

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

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

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

Идея состоит в том, чтобы медленно двигаться к своей цели. Будет казаться, что прогресс идет медленно, но вдруг вы поймете, что ваш код намного лучше. Требуется много времени, чтобы перевернуть океанский лайнер.

Карл Билефельдт
источник
Файл, возможно, начал большой. Файлы такого размера могут быть созданы быстро. Я знаю людей, которые могут написать 1000-е LoC за день или неделю. И ОП не упомянул автоматизированные тесты, что указывает на то, что они отсутствуют.
Чак Коттрилл
9

Я собираюсь предложить другое, чем обычно, решение этой проблемы.

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

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

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

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

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

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

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

Однако вы решили сделать свой рефакторинг, удачи!

computercarguy
источник
Это фантастическое предложение, которое отражает действительно хороший способ достижения координации команды, который будет иметь решающее значение для ее работы. Кроме того, если некоторые ветви не могут быть объединены обратно в masterпервую очередь, у вас есть, по крайней мере, все в комнате, чтобы помочь справиться с объединениями в эти ветви.
Колин Янг
+1 за предложение кода моба
Джон Рейнор
1
Это как раз решает социальный аспект проблемы.
Чак Коттрилл
4

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

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

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

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

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

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

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

Грегори Нисбет
источник
-3

Подожди до дома. Разбить файл, зафиксировать и объединить с мастером.

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

Ewan
источник
3
Тем не менее, это означало бы, что им придется объединить мои рефакторинги с их изменениями ...
Хофф
несколько похожий: предложение о беспорядочной файловой структуре
Ник Алексеев
1
Ну, на самом деле им все равно приходится иметь дело со слияниями, если они все меняют эти файлы.
LAIV
9
Это проблема «Сюрприз, я сломал все твои вещи». ОП должен получить вступительный взнос и одобрение, прежде чем делать это, и выполнение этого в запланированное время, когда никто не имеет файла «в процессе», могло бы помочь.
Computercarguy
6
Ради любви ктулху, не делай этого. Это худший способ работы в команде.
Легкость гонки с Моникой