Теория, которая уже существовала
Каждый дефект MOAD имел известное решение за десятилетия до систематического обнаружения в 2026 году. Дефекты сохранялись не потому, что никто не знал лучшего решения. Они сохранялись потому, что знание — это не то же самое, что обнаружение.
MOAD-0001: O(N²) list.contains
Donald Knuth, 1973. The Art of Computer Programming, Volume 3: Sorting and Searching. Хеш-таблицы для O(1) поиска полностью описаны с анализом в 1973 году. Разница между O(N) линейным поиском и O(1) хеш-поиском — задокументирована, формализована, широко цитируется. Java поставляла HashSet с версии 1.0 (1996). Python поставляла set как встроенный тип с версии 2.4 (2004). Исправление существовало за 30 лет до того, как стало идиомой по умолчанию в каждой экосистеме.
Ричард Хэмминг, 1986. Лекции Bell Labs (позже опубликованы как The Art of Doing Science and Engineering, 1997). Хэмминг явно преподавал алгоритмическую сложность, разницу между правильным и эффективным, а также опасность создания систем, которые работают в малом масштабе, но терпят неудачу в большом. Он называл это «проектированием под проблему, которую вы видите сегодня, а не под проблему, с которой столкнётесь завтра».
MOAD-0002: Связывание через общее глобальное состояние
Дэвид Парнас, 1972. «О критериях, используемых при декомпозиции систем на модули». CACM, декабрь 1972. Парнас утверждал, что модули должны декомпозироваться по принципу сокрытия информации — каждый модуль владеет своим состоянием, без общих изменяемых глобальных переменных. Это прямой теоретический предшественник исправления Intertangle. Парнас прямо указывал: общее глобальное состояние создаёт невидимое связывание, которое не выявляется при тестировании.
MOAD-0003: Утечка идентичности ThreadLocal
Java 1.2, 1998. ThreadLocal был включён в стандартную библиотеку Java. С момента сосуществования пула потоков и ThreadLocal существовал механизм утечки. Дефект структурный: носитель, время жизни которого — поток, а не единица работы. Документация предупреждала об этом с ранних этапов жизненного цикла Java EE.
MOAD-0004: Логирование учётных данных
RFC 1945, 1996. HTTP/1.0 определил заголовок Authorization. Дефект логирования учётных данных стал возможен в день появления заголовка Authorization. OWASP была основана в 2001 году и задокументировала логирование учётных данных как класс уязвимостей в своих первых руководствах. Паттерн: заголовок Authorization → middleware логирования → учётные данные в открытом виде на диске. Известно с момента первой спецификации HTTP-аутентификации.
MOAD-0005: Эффект «громового стада» / шторм кэша
Ядро Unix, 1993. Проблема «громового стада» — одновременное пробуждение N процессов при общем событии — обсуждалась в разработке ядра Unix уже в начале 1990-х. Работы Дуга Шмидта по паттерну Reactor (1994) и Half-Sync/Half-Async (1995) решали вопросы синхронизации на уровне инфраструктуры. Вариант «cache stampede» (N потоков вычисляют одно и то же значение при промахе кэша) был описан в литературе по распределённым системам к 2001 году. [BLOCK_TYPE knowable/theory_exists]
--- [BLOCK_TYPE knowable/theory_exists]
Теория: известна. Инструменты обнаружения: отсутствуют. Разрыв между «знаемым» и «обнаруженным» составляет от 28 до 54 лет в зависимости от дефекта. [BLOCK_TYPE knowable/knowable_question]
The Knowability Gap [BLOCK_TYPE knowable/knowable_question]
Таймлайн показывает, что для каждого дефекта MOAD известное решение существовало минимум за 28 лет до систематического обнаружения. Самый короткий разрыв (MOAD-0003) — 28 лет. Самый длинный (MOAD-0002) — 54 года. [BLOCK_TYPE knowable/knowable_question]
Это не история о невежестве. Кнут, Парнас, Хэмминг — наиболее цитируемые авторы в информатике. Их работы использовались в университетах. Их терминология (Big O, information hiding, algorithmic complexity) стала стандартной частью учебных программ. [BLOCK_TYPE knowable/knowable_question]
Почему дефект «застаивается»: пять условий
Дефект не сохраняется случайно. Пять структурных условий, присутствующих одновременно, создают среду застаивания. Уберите любое из них — и обнаружение становится возможным.
Условие 1: Корректный вывод
Список и множество отвечают на вопрос о принадлежности одинаково. list.contains(x) и set.contains(x) возвращают одно и то же булево значение. ThreadLocal, хранящий устаревшую идентичность, всё равно хранит какую-то идентичность — просто она принадлежит другому запросу. Учётные данные в логе записываются корректно — учётные данные попадают в файл лога без ошибки. Дефект — не в сбое работы. Он проявляется только в стоимости или последствиях для безопасности. Тесты, проверяющие вывод, проходят. Тесты, проверяющие стоимость или последствия для безопасности: в основном не написаны.
Условие 2: Отсутствие тестов сложности в CI
Дейкстра говорил: «тестирование показывает наличие дефектов, а не их отсутствие». Хэмминг дополнил: дефекты, которые мы тестируем, — это дефекты, которые мы находим. CI-пайплайны в 2026 году проверяют: корректность, типобезопасность, контракты API, функциональное поведение. Они не проверяют: алгоритмическую сложность на операцию, рост памяти на вызов, очистку заголовка авторизации, жизненный цикл идентичности потока.
Ни один тест не запускается. Ни один тест не падает. Пайплайн зелёный. Дефект невидим.
Условие 3: Происхождение из Small-N
Код пишется и рецензируется в средах разработки. Графы разработки содержат 50 узлов. Нагрузка запросов в разработке — 10 параллельных потоков. Частота промахов кэша в разработке низкая (тёплый кэш, мало ключей). При N=50 стоимость O(N²) составляет 2 500 операций. Незаметно. При N=50 000 стоимость составляет 2 500 000 000 операций. Сборка за 17 минут вместо 1 секунды.
Автор, написавший код, никогда не видел N=50 000. Ревьюер, одобривший его, никогда не видел N=50 000. Дефект не был виден в том масштабе, в котором писался код.
Условие 4: Копирование распространяется без контекста
Правильный алгоритм поучителен. Учебники учат на правильных примерах. Документация показывает работающий код. Один и тот же скелет Tarjan SCC — visited = [], вложенный if n not in visited — встречается в GHC, Maven, Python pip, Cargo, компиляторе TypeScript, компиляторе Kotlin, компиляторе Scala и javac. Разные команды, разные языки, разные десятилетия. Один и тот же «фоссилий». Исходное N=50 автора не переносится вместе с кодом. Переносится: правильный результат. Остаётся позади: предположение о производительности.
Условие 5: Масштаб растёт вокруг замороженного кода
Код не деградирует. Инфраструктура масштабируется. Разрешитель зависимостей, написанный в 2003 году для 200 пакетов, работает с 50 000 пакетами в 2024 году. Его никто не переписывает — он работает. Его никто не профилирует — CI зелёный. Значение N, при котором стоимость O(N²) становится катастрофической, приходит постепенно, незаметно, на уровне production. К тому времени автор уже ушёл. Код стал зависимостью. Никто не трогает работающие зависимости.
Диагноз: Пять условий
Пять условий: правильный результат, отсутствие тестов на сложность, происхождение с малым N, копирование без контекста, масштаб растёт вокруг замороженного кода.
Все пять присутствовали одновременно в каждом MOAD. Это не совпадение — это структурная сигнатура класса осадочных дефектов.
Что сказал Хэмминг
Лекции Ричарда Хэмминга 1986 года в Bell Labs — опубликованные в 1997 году как The Art of Doing Science and Engineering — содержат предупреждения, которые читаются как прямое описание паттерна дефекта MOAD. Он не описывал MOAD. Он описывал структурную тенденцию инженерных систем закостеневать вокруг локально правильных решений, которые становятся глобально дорогими.
Хэмминг о сложности: «Цель вычислений — это понимание, а не числа. Но у вас должен быть алгоритм правильной сложности, иначе числа никогда не появятся. Алгоритм O(N²), который работает при N=100, не запустится при N=1 000 000 до того, как вы уйдёте на пенсию».
Хэмминг о копировании: «Великие инженеры не просто копируют решения. Они понимают, почему решение работает, при каких условиях оно справедливо и что может его сломать. Скопированное решение без его условий — это бомба замедленного действия».
Хэмминг о тестировании: «Тестировать то, что вы измерили, — это не то же самое, что измерять то, что важно. Мы строим сложные тестовые наборы для свойств, которые решили проверить. Мы оставляем непроверенными свойства, которые не выбрали. То, что мы оставили непроверенным, и удивляет нас в продакшене.»
Хэмминг о масштабе: «Ошибка, которую вы допускаете в первый год проекта, — это ошибка, которую вы всё ещё исправляете на десятый год. Ранние предположения кальцифицируются. Проект масштабируется вокруг них. Никто не переписывает фундамент.»
Разрыв между предупреждением и операционализацией
Предупреждения Хэмминга были верны. Их преподавали. Их цитировали. Их включали в учебные программы. Но предупреждение — это не детектор. Хэмминг описал форму дефекта. Он не создал инструмент, который запускается в CI и отмечает горячие пути O(N²), утечки идентичности ThreadLocal или логирование учётных данных. Разрыв между «знаемым» и «обнаруживаемым» — это разрыв между теорией и её операционализацией в виде автоматизированной инфраструктуры.
MOAD существует потому, что отрасль построила инфраструктуру корректности, но не построила инфраструктуру производительности или безопасности на том же уровне. Модульные тесты: стандарт с 1970-х. Тестирование на основе свойств: стандарт с 1990-х. Бенчмарки алгоритмической сложности в CI: всё ещё эксперимент в 2026 году.
Операционализация предупреждения
Хэмминг предупреждал о кальцификации сложности, непроверенных свойствах, копировании решений без их условий и о том, как масштаб разрушает ранние предположения. Он дал нам словарь. Он не дал нам детектор.
Пайплайн MOAD заполняет этот разрыв: сканирование → тикет → патч → модульный тест → раскрытие → PR → слияние в upstream. Это операционализированный Хэмминг: не только предупреждение, но и автоматическое обнаружение и пайплайн исправлений.
Fester-подпись
Дефект класса MOAD имеет узнаваемую подпись. Все пять условий присутствуют одновременно. Все пять можно проверить до написания единого скана:
1. Корректный вывод? Запустите стандартный набор тестов. Если он проходит, значит дефект относится к свойству производительности или безопасности, а не корректности. Это означает, что стандартный CI его не поймает.
2. Нет теста на сложность? Проверьте конфигурацию CI. Есть ли этап бенчмарка? Сравнивает ли он алгоритмическое поведение (а не только время выполнения) с предыдущим коммитом? Если нет: условие присутствует.
3. Происхождение от Small-N? Проверьте git blame и исходный коммит. Каков был размер датасета в первой реализации? Этот размер меньше текущей производственной нагрузки более чем в 100×? Если да: условие присутствует.
4. Копия распространяется? Найдите паттерн по всей кодовой базе и по экосистемам. Появляется ли та же структурная схема в N≥3 независимых кодовых базах без общего происхождения? Если да: окаменелость распространилась. Каждая копия — новый очаг гниения.
5. Масштаб растёт? Проверьте производственные метрики. Каково N сегодня по сравнению с N при первом развёртывании? Устойчив ли темп роста? При каком N дефект становится критическим в эксплуатации?
Если все пять пунктов проверены и подтверждены: у вас дефект класса MOAD. Исправление всегда представляет собой замену одной строки или одного метода. Сложность — в обнаружении. Исправление — простая часть.
Именно это имел в виду Хэмминг: инженерия — не про исправление. Исправление очевидно, как только вы его увидите. Инженерия — про построение систем, которые позволяют это увидеть.
Примените сигнатуру
Сигнатура гниения MOAD: корректный вывод + отсутствие теста на сложность + происхождение от Small-N + копия распространяется + масштаб растёт.
Эта сигнатура не ограничивается пятью дефектами MOAD. Она описывает класс дефектов, который сохраняется в любой системе, где тесты корректности — единственный автоматизированный барьер качества.