Типы рефакторинга
Feb. 2nd, 2024 04:49 pmЕсть два осмысленных типа рефакторинга. (Я говорю про настоящий рефакторинг, когда функциональность не меняется от слова "совсем", не "лучше алгоритм", не "большее покрытие вариантов", а когда идёт чистое переименование и реструктуризация кода без какого-либо влияния на поведение).
Первый тип: мы готовимся к новому изменению, и нам надо подвинуть терминологию. Допустим, раньше это называлось "preparation", теперь нам надо добавить в preparation несколько подэтапов, первый шаг, переименовать существующий preparation в seeding, с той мыслью, что seeding будет одним из этапов нового preparation.
Мы берём понятную абстракцию с понятным именем и меняем это имя. Это рефакторинг вида S₁ -> S₂ (semantic -> semantic). В принципе, перемещение кода между модулями можно считать S -> S.
Второй тип интереснее, и я бы сказал, это то, что обычно подразумевают под "расчисткой".
Мы берём непонятное "нечто", у которого нет имени, и начинаем эти имена придумывать. Может быть мы меняем несколько непонятных имён, которые остались от предыдущих итераций, но больше не имеют смысла (допустим, у нас были whitelabels, но самого понятия в продукте больше нет, а есть whitelabel[0], который считается дефолтным хранилищем настроек, и whitelabel[2], который содержит в себе настройки для bootstrapping'а (я специально придумываю имена, чтобы дать почувствовать тут боль). Но whitelabel[2] часто лазит в whitelabel[0], хотя по архитектуре не положено, и даже пишет туда. По архитектуре не положено, но так как у нас осталось всего два, то это было быстрое решение заткнуть проблему. Теперь пришло время это исправлять.
Итак, мы берём нечто, что является самопротиворечивым, набор терминов, которые не отражают текущее видение, и ... И у нас нет того, во что мы хотим превратить. У нас нет слов. Слова из кода нам мешают, потому что являются thick concept, которые направляют нас по смыслу не туда, куда нам надо.
Это и есть самый настоящий жирный тяжёлый рефакторинг, ради которого стоит думать днями. Обычно я пытаюсь выкинуть слова, спуститься на слой ниже (или даже два) и описать какие фактические эффекты мы имеем и из чего (не используя слова вышестоящих уровней).
После этого для преобразований состояний можно пытаться придумывать новые слова. Появляется некий набросок, который в целом, правильный, но не учитывает все нюансы.
В этом месте, казалось бы, надо переименовать и подвигать весь код, но... Но это будет ошибкой, потому что преобразование ¬S -> S (из бессмыслицы в осмысленное) не может делаться в высокоуровневых терминах (мы пытаемся их создать!), то есть на практике такой рефакторинг представляет собой новую бессмыслицу (¬S ∨ S, то есть "всё что попало"), и единственный метод сохранить контекст в мозгу, это оперировать малыми участками кода и малыми шагами.
Для этого приходится придумывать временные костыли (абстракции), которые сохранятся только на время рефакторинга, но которые отдалённо подходят по смыслу для ¬S и ближе к S. "ближе" понятие субъективное, так что периодически приходится откатываться назад, иногда на несколько таких "мостиков".
Финалом успешного рефакторинга становится код, который не выразим в старых терминах (для него он "¬S"), но который более соответствует текущему использованию кода и свежим requirement.
... Во всей этой идилли есть только один смертельный момент: в момент такого рефакторинга невозможно ребейзиться с конфликтами. Невозможно. Если возникает ситуация конфликта, то надо выкидывать весь рефакторинг и начинать его заново. Невозможно разрешить конфликт между изменениями из множества несовместимых абстракций, неописуемых в старом ¬S, и всё ещё не в S.
... Симметрии ради можно рассмотреть рефакторинги вида ¬S->¬S, но смысл? Была фигня, фигня осталась.
Ещё есть S -> ¬S, но это либо git revert, либо откровенный саботаж. Более того, я искренне верю, что ни один органический мозг не может написать работающий ¬S по заданному S так, чтобы оно напоминало ¬S до рефакторинга вида ¬S->S. Это невозможно, поскольку не является мыслительной деятельностью.
Первый тип: мы готовимся к новому изменению, и нам надо подвинуть терминологию. Допустим, раньше это называлось "preparation", теперь нам надо добавить в preparation несколько подэтапов, первый шаг, переименовать существующий preparation в seeding, с той мыслью, что seeding будет одним из этапов нового preparation.
Мы берём понятную абстракцию с понятным именем и меняем это имя. Это рефакторинг вида S₁ -> S₂ (semantic -> semantic). В принципе, перемещение кода между модулями можно считать S -> S.
Второй тип интереснее, и я бы сказал, это то, что обычно подразумевают под "расчисткой".
Мы берём непонятное "нечто", у которого нет имени, и начинаем эти имена придумывать. Может быть мы меняем несколько непонятных имён, которые остались от предыдущих итераций, но больше не имеют смысла (допустим, у нас были whitelabels, но самого понятия в продукте больше нет, а есть whitelabel[0], который считается дефолтным хранилищем настроек, и whitelabel[2], который содержит в себе настройки для bootstrapping'а (я специально придумываю имена, чтобы дать почувствовать тут боль). Но whitelabel[2] часто лазит в whitelabel[0], хотя по архитектуре не положено, и даже пишет туда. По архитектуре не положено, но так как у нас осталось всего два, то это было быстрое решение заткнуть проблему. Теперь пришло время это исправлять.
Итак, мы берём нечто, что является самопротиворечивым, набор терминов, которые не отражают текущее видение, и ... И у нас нет того, во что мы хотим превратить. У нас нет слов. Слова из кода нам мешают, потому что являются thick concept, которые направляют нас по смыслу не туда, куда нам надо.
Это и есть самый настоящий жирный тяжёлый рефакторинг, ради которого стоит думать днями. Обычно я пытаюсь выкинуть слова, спуститься на слой ниже (или даже два) и описать какие фактические эффекты мы имеем и из чего (не используя слова вышестоящих уровней).
После этого для преобразований состояний можно пытаться придумывать новые слова. Появляется некий набросок, который в целом, правильный, но не учитывает все нюансы.
В этом месте, казалось бы, надо переименовать и подвигать весь код, но... Но это будет ошибкой, потому что преобразование ¬S -> S (из бессмыслицы в осмысленное) не может делаться в высокоуровневых терминах (мы пытаемся их создать!), то есть на практике такой рефакторинг представляет собой новую бессмыслицу (¬S ∨ S, то есть "всё что попало"), и единственный метод сохранить контекст в мозгу, это оперировать малыми участками кода и малыми шагами.
Для этого приходится придумывать временные костыли (абстракции), которые сохранятся только на время рефакторинга, но которые отдалённо подходят по смыслу для ¬S и ближе к S. "ближе" понятие субъективное, так что периодически приходится откатываться назад, иногда на несколько таких "мостиков".
Финалом успешного рефакторинга становится код, который не выразим в старых терминах (для него он "¬S"), но который более соответствует текущему использованию кода и свежим requirement.
... Во всей этой идилли есть только один смертельный момент: в момент такого рефакторинга невозможно ребейзиться с конфликтами. Невозможно. Если возникает ситуация конфликта, то надо выкидывать весь рефакторинг и начинать его заново. Невозможно разрешить конфликт между изменениями из множества несовместимых абстракций, неописуемых в старом ¬S, и всё ещё не в S.
... Симметрии ради можно рассмотреть рефакторинги вида ¬S->¬S, но смысл? Была фигня, фигня осталась.
Ещё есть S -> ¬S, но это либо git revert, либо откровенный саботаж. Более того, я искренне верю, что ни один органический мозг не может написать работающий ¬S по заданному S так, чтобы оно напоминало ¬S до рефакторинга вида ¬S->S. Это невозможно, поскольку не является мыслительной деятельностью.
no subject
Date: 2024-02-02 04:44 pm (UTC)Рефакторинг, имхо, следует начинать не раньше, чем появится цельная модель сущностей, которая будет реализовіваться легко, просто и очевидно. А в совсем хороших случаях модель диктует чистку/расширение функционала единственно возможнім образом. У меня біл в практике случай, когда сущности в БД удалось из графа перепаковать в дерево. Не совсем очевидная таксономия досталась в наследство и портила жизнь вообще всем. А как все свелось к дереву - так сразу решилось два десятка зависших задач и, попутно, сразу стали видні очевидніе дірки в функционале.
no subject
Date: 2024-02-05 11:21 am (UTC)Тогда 90% кода никогда не будет написана. Многие вещи развиваются в трушном agile'е (то есть делается едва ощутимый прототип, потом подстраивается под обнаружившиеся требования, снова подстраивается и т.д.).
Иногда это приводит к ошибочным рефакторингам, которые приходится переделывать. Но лучше делать их в моменте, чем ждать 100500 нового кода в старых unsound терминах, чтобы потом всё исправить.
В целом, рефакторинг под новый код - самый приятный, так что лучше стараться делать его когда приятно, а не когда это невыносимая каторга (как альтернатива невыносимому продолжению "как есть").
no subject
Date: 2024-02-05 12:26 pm (UTC)Єто больше философский вопрос - как описать/закодировать то, чего не понимаешь? Можно ли извлечь знание из лапутянского
ажиляторапермутатора ака демон второго рода им. Трурля и Клапауция? Что вообще первично - понимание или описание ака код? Несомненно, польза от описания непонятного есть. Каждая попітка дает понимание границ применимости либо вовсе ошибочности данного описания. Как в случае теплорода. Жаль только, что понимание ошибочности теплорода никак не говорит, что двигаться нужно в сторону кинетической модели.И єто. Я не верю в agile. Єтой методологией можно от гармонии небесніх сфер добраться до єпициклов, но дальше уже все. Тупик. Не существует фазовіх траекторий, непрерівно ведущих от єпициклов к Кеплеру. В какой-то момент нужно дропать весь легаси и рефакторить с нуля.
no subject
Date: 2024-02-06 03:01 pm (UTC)no subject
Date: 2024-02-02 09:17 pm (UTC)О как. А я уже некоторое время смотрю на малые рефакторинги как чисто на альфа-бета-эта эквивалентности, с точки зрения лямбды.
no subject
Date: 2024-02-05 11:24 am (UTC)Альфа-эквивалетность - это, по-русски, переименование переменных. Да, но всех вариантов рефакторинга не покрывает.
no subject
Date: 2024-02-05 11:30 am (UTC)β - это замена аппликации на результат подстановки (а обратно - extract method). (λx E) E' ⇔ E[x := E']
η - вставить параметр/удалить параметр: λx (F x) ⇔ F - where F does not contain x
(могу послать главу про лямбду)
no subject
Date: 2024-02-05 12:12 pm (UTC)А у меня весь пост про ситуацию, когда текущий код стал не осмысленный (нет модели, которая бы его описывала непротиворечиво, а его работоспособность тут-и-сейчас - очень узкий случай, и все инварианты обсыпаются при любом чихе), а из него надо сделать осмысленный, то есть переопределить терминологию, в которой задача описывается.
... У меня, конечно, всё сильно присыпано ансиблами, хотя суть проблемы это не меняет, абстракции, которые реализуют требования.
no subject
Date: 2024-02-05 12:21 pm (UTC)Да понятно, что реальность умеет опровергать теории.