Попробую на маленьком примере показать откуда берётся запутанный код.

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

Для упрощения написания маленькая буква будет означать что условие ложно, а большая - что истинно.

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

   aaAA
   bBBb
cd 1101
cD 1101
CD 0000
Cd 1101
  • СДНФ: a&c|b&c|a&d|b&d (14 знаков)

  • Самый неоптимизированный вариант выглядел бы так, далее будем назвать это спагетти-кодом: a&b&c&d|a&B&c&d|A&b&c&d|a&b&c&D|a&B&c&D|A&b&c&D|a&b&C&d|a&B&C&d|A&b&C&d (71 знак)

Естественно, пишем первый вариант.

А потом прибегает менеджер и говорит что нужно изменить самую малость, случай abcd = 0. Отражаем на карте:

   aaAA
   bBBb
cd 0101
cD 1101
CD 0000
Cd 1101

Если бы задание сразу было таким, то мы написали бы другой совершенный код:

  • СДНФ:a&B&c|a&D&c|A&b&c|a&C&d|A&b&C&d (31 знак) больше, чем в начале, но перейти от начального кода к этому сложно! Нужно внести изменения практически везде.

  • Так как переписывать всё не хочется, делаем костыль к изначальному СДНФ: (a&c|b&c|a&d|b&d)&(A|B|C|D) (27 знаков). Немного изменений, меньше тестировать, но потеряна структура. Но не сильно. Я бы так и сделал.

  • А в спагетти-коде было бы изменить проще всего, даже короче станет: a&B&c&d|A&b&c&d|a&b&c&D|a&B&c&D|A&b&c&D|a&b&C&d|a&B&C&d|A&b&C&d (63 знака).

Снова приходит менеджер. Условие C не нужно, его случайно добавили. Убираем половину карты:

  aaAA
  bBBb
d 0101
D 1101

Совершенный код был бы таким, если бы мы знали заранее:

  • СДНФ: a&B|a&D|A&b (11 знаков)

    Но у нас уже есть код, и менять его весь не хочется:
    Нужно идти по всему коду и из каждого условия убирать условия c и C.

  • Второй костыль к изначальной СДНФ: (a|b|a&d|b&d)&(A|B|D) (21 знак), в основном пришлось удалять, структура не вернулась, но изменение не было сложным.

  • Спагетти-код: a&B&d|A&b&d|a&b&D|a&B&D|A&b&D (29 знаков) пришлось выкинуть пол-программы, внести изменения в каждый блок (!)

Снова менеджер. Ещё одно изменение, ABd = 1, и можно отдавать заказчику:

  aaAA
  bBBb
d 0111
D 1101
  • СДНФ: a&B|A&b|A&d|a&D (15 знаков).

  • Третий костыль к изначальной СДНФ: (a|b|a&d|b&d)&(A|B|D)|A&B&d (27 знаков) эх, всё дальше от идеала. По размеру уже между минимальной формой и спагетти-кодом.

  • Спагетти-код: a&B&d|A&b&d|a&b&D|a&B&D|A&b&D|A&B&d (35 знаков) дописали условие, основную часть не трогаем.

Итог

Что хотел клиент: a&B|A&b|A&d|a&D

Что передано клиенту: (a|b|a&d|b&d)&(A|B|D)|A&B&d

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

Выводы

  • Какой бы ни был у вас код, всегда найдётся изменение, которое испортит всю его структуру. И именно с этим изменением к вам и придут.

  • Есть случаи для которых спагетти-код самое подходящее решение.

  • Чем больше оптимизирован код, тем больше придётся менять.

  • Всегда задним числом понятно, какой код надо было написать.

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

  • Костыли приближают вас к спагетти-коду, но достичь его одними костылями сложно.

Комментарии (3)


  1. evgenyk
    02.10.2025 11:07

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

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


    1. Sau Автор
      02.10.2025 11:07

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

      Вариант с таблицей для логических функций поддерживаю.


  1. anshdo
    02.10.2025 11:07

    Ну, собственно, именно по-этому нужен регулярный рефакторинг.