Teoria, Która Już Istniała
Każdy defekt MOAD miał znane rozwiązanie na dekady przed systematyczną detekcją w 2026. Defekty nie przetrwały, ponieważ nikt nie wiedział lepiej. Przetrwały, ponieważ wiedza nie jest równoznaczna z detekcją.
MOAD-0001: O(N²) list.contains
Donald Knuth, 1973. Sztuka programowania komputerowego, Tom 3: Sortowanie i Wyszukiwanie. Tablice haszujące dla wyszukiwania O(1) w pełni określone, z analizą, w 1973. Różnica między wyszukiwaniem liniowym O(N) a wyszukiwaniem haszującym O(1) — udokumentowana, sformalizowana, szeroko cytowana. Java dostarczyła HashSet w wersji 1.0 (1996). Python dostarczył set jako typ pierwszej klasy w wersji 2.4 (2004). Poprawka istniała przez 30 lat, zanim stała się domyślnym idiomem w każdym ekosystemie.
Richard Hamming, 1986. Wykłady w Bell Labs (później opublikowane jako The Art of Doing Science and Engineering, 1997). Hamming explicite uczył o złożoności algorytmicznej, różnicy między poprawnością a efektywnością oraz niebezpieczeństwie budowania systemów, które działają na małą skalę, ale zawodzą na dużą. Nazwał to „projektowaniem pod problem, który widzisz dzisiaj, a nie pod problem, z którym zmierzysz się jutro”.
MOAD-0002: Sprzężenie przez współdzielony stan globalny
David Parnas, 1972. „On the Criteria To Be Used in Decomposing Systems into Modules.” CACM, grudzień 1972. Parnas argumentował, że moduły powinny być dekomponowane przez ukrywanie informacji — każdy moduł posiada swój stan, bez współdzielonych mutowalnych zmiennych globalnych. Jest to bezpośredni teoretyczny poprzednik poprawki Intertangle. Parnas był jednoznaczny: globalny współdzielony stan tworzy niewidoczne sprzężenie, którego testowanie nie ujawnia.
MOAD-0003: Wyciek tożsamości ThreadLocal
Java 1.2, 1998. ThreadLocal został dostarczony jako klasa biblioteki standardowej Javy. W momencie współistnienia puli wątków i ThreadLocal istniał już mechanizm wycieku. Defekt ma charakter strukturalny: nośnik, którego żywotność jest związana z wątkiem, a nie z jednostką pracy. Dokumentacja ostrzegała przed tym od wczesnego okresu cyklu życia Java EE.
MOAD-0004: Logowanie poświadczeń
RFC 1945, 1996. HTTP/1.0 zdefiniował nagłówek Authorization. Defekt logowania poświadczeń stał się możliwy w dniu powstania nagłówka Authorization. OWASP powstało w 2001 roku i udokumentowało logowanie poświadczeń jako klasę podatności w swoich pierwszych poradnikach. Wzorzec: nagłówek Authorization → middleware logujący → poświadczenia w postaci jawnej na dysku. Znany od czasu pierwszej specyfikacji uwierzytelniania HTTP.
MOAD-0005: Thundering Herd / Cache Stampede
Jądro Uniksa, 1993. Problem „thundering herd” — N procesów budzonych jednocześnie przez wspólne zdarzenie — pojawił się w dyskusjach nad rozwojem jądra Uniksa na początku lat 90. Prace Douga Schmidta nad wzorcem Reactor (1994) i Half-Sync/Half-Async (1995) dotyczyły synchronizacji na poziomie infrastruktury. Wariant „cache stampede” (N wątków obliczających tę samą wartość przy chybieniu w pamięci podręcznej) został opisany w literaturze systemów rozproszonych do 2001 roku.
---
Teoria: kompletna. Narzędzia detekcji: brak. Luka między „knowable” a „detected” wynosi od 28 do 54 lat w zależności od defektu.
Luka poznawalności
Oś czasu pokazuje, że dla każdego defektu MOAD znane było rozwiązanie co najmniej 28 lat przed systematyczną detekcją. Najkrótsza luka (MOAD-0003) wynosi 28 lat. Najdłuższa (MOAD-0002) wynosi 54 lata.
To nie jest opowieść o ignorancji. Knuth, Parnas, Hamming — to najczęściej cytowani autorzy w informatyce. Ich prace były przydzielane na uczelniach. Ich słownictwo (Big O, ukrywanie informacji, złożoność algorytmiczna) stało się standardem programów nauczania.
Dlaczego kod gnije: pięć warunków
Defekt nie utrzymuje się przypadkowo. Pięć warunków strukturalnych, obecnych jednocześnie, tworzy środowisko sprzyjające gniciu. Usunięcie któregokolwiek z nich umożliwia wykrycie.
Warunek 1: Poprawne wyjście
Lista i zbiór odpowiadają na pytanie o przynależność identycznie. list.contains(x) i set.contains(x) zwracają ten sam boolean. ThreadLocal niosący nieaktualną tożsamość nadal niesie jakąś tożsamość — po prostu należy do niewłaściwego żądania. Zalogowane poświadczenia są zalogowane poprawnie — poświadczenie trafia do pliku dziennika bez błędu. Defekt nie jest awarią. Jest awarią jedynie pod względem kosztu lub konsekwencji bezpieczeństwa. Testy sprawdzające wyjście przechodzą. Testy sprawdzające koszt lub konsekwencje bezpieczeństwa: w większości niepisane.
Warunek 2: Brak testów złożoności w CI
Dijkstra powiedział: „testowanie pokazuje obecność defektów, a nie ich brak”. Hamming dodał: defekty, które testujemy, to defekty, które znajdujemy. Potoki CI w 2026 testują: poprawność, bezpieczeństwo typów, kontrakty API, zachowanie funkcjonalne. Nie testują: złożoności algorytmicznej na operację, przyrostu pamięci na wywołanie, czyszczenia nagłówka autoryzacji, cyklu życia tożsamości wątku.
Żaden test nie jest uruchamiany. Żaden test nie kończy się niepowodzeniem. Potok jest zielony. Defekt jest niewidoczny.
Warunek 3: Pochodzenie z małego N
Kod jest pisany i przeglądany w środowiskach deweloperskich. Grafy deweloperskie mają 50 węzłów. Obciążenia żądań deweloperskich mają 10 wątków współbieżnych. Wskaźniki chybień pamięci podręcznej w dewelopmencie są niskie (ciepła pamięć podręczna, mało kluczy). Przy N=50 koszt O(N²) wynosi 2500 operacji. Niewidoczny. Przy N=50 000 koszt wynosi 2 500 000 000 operacji. Budowanie trwa 17 minut zamiast 1 sekundy.
Autor, który napisał kod, nigdy nie widział N=50 000. Recenzent, który go zatwierdził, nigdy nie widział N=50 000. Defekt nie był widoczny w skali, w której go napisano.
Warunek 4: Kopiowanie propaguje się bez kontekstu
Poprawny algorytm jest pouczający. Samouczki uczą na przykładach poprawnych. Dokumentacja pokazuje działający kod. Ten sam szkielet Tarjan SCC — visited = [], wewnętrzny if n not in visited — pojawia się w GHC, Maven, Python pip, Cargo, kompilatorze TypeScript, kompilatorze Kotlin, kompilatorze Scala i javac. Różne zespoły, różne języki, różne dekady. Ten sam skamielina. Oryginalne N=50 autora nie rozprzestrzenia się wraz z kodem. Co się rozprzestrzenia: poprawny wynik. Co zostaje: założenie o wydajności.
Warunek 5: Skala rośnie wokół zamrożonego kodu
Kod się nie degraduje. Infrastruktura skaluje. Resolver zależności napisany w 2003 roku dla 200 pakietów działa na 50 000 pakietów w 2024 roku. Nikt go nie przepisuje — działa. Nikt go nie profiluje — CI jest zielone. N, które sprawia, że koszt O(N²) staje się katastrofalny, pojawia się stopniowo, niewidocznie, na skali produkcyjnej. Do tego czasu oryginalny autor już odszedł. Kod jest zależnością. Nikt nie dotyka działających zależności.
Diagnoza: Pięć warunków
Pięć warunków: poprawny wynik, brak testów złożoności, pochodzenie z małego N, kopiowanie bez kontekstu, skala rośnie wokół zamrożonego kodu.
Wszystkie pięć warunków były obecne przy każdym MOAD jednocześnie. To nie przypadek — to strukturalny podpis klasy osadowych defektów.
Co powiedział Hamming
Wykłady Richarda Hamminga z 1986 roku w Bell Labs — opublikowane w 1997 jako The Art of Doing Science and Engineering — zawierają ostrzeżenia, które brzmią jak bezpośredni opis wzorca defektu MOAD. Nie opisywał MOAD. Opisywał strukturalną tendencję systemów inżynieryjnych do krzepnięcia wokół lokalnie poprawnych decyzji, które stają się globalnie kosztowne.
Hamming o złożoności: „Celem obliczeń jest zrozumienie, a nie liczby. Ale trzeba mieć odpowiednią złożoność algorytmu, bo inaczej liczby nigdy nie przyjdą. Algorytm O(N²) działający dla N=100 nie zadziała dla N=1 000 000 przed emeryturą.”
Hamming o kopiowaniu: „Wielcy inżynierowie nie kopiują rozwiązań. Rozumieją, dlaczego rozwiązanie działa, w jakich warunkach jest ważne i co mogłoby je złamać. Skopiowane rozwiązanie bez jego warunków to bomba zegarowa.”
Hamming o testowaniu: „Testowanie tego, co zmierzono, nie jest tym samym co mierzenie tego, co się liczy. Budujemy rozbudowane zestawy testów dla właściwości, które wybraliśmy do sprawdzenia. Pozostawiamy nietestowane właściwości, których nie wybraliśmy. To, czego nie przetestujemy, zaskoczy nas na produkcji.”
Hamming na skali: „Błąd, który popełnisz w pierwszym roku projektu, będziesz poprawiać jeszcze w dziesiątym roku. Wczesne założenia kostnieją. Projekt skaluje się wokół nich. Nikt nie przepisuje fundamentów.”
Luka między ostrzeżeniem a operacjonalizacją
Ostrzeżenia Hamminga były trafne. Były nauczane. Były cytowane. Były zadawane w programach nauczania. Ale ostrzeżenie to nie detektor. Hamming opisał kształt defektu. Nie zbudował narzędzia, które działa w CI i oznacza gorące ścieżki O(N²), wycieki tożsamości ThreadLocal ani logowanie poświadczeń. Luka między „możliwym do poznania” a „możliwym do wykrycia” to luka między teorią a jej operacjonalizacją jako zautomatyzowanej infrastruktury.
MOAD istnieje, ponieważ branża zbudowała infrastrukturę poprawności, ale nie infrastrukturę wydajności ani bezpieczeństwa na tym samym poziomie. Testy jednostkowe: standard od lat 70. Testy oparte na właściwościach: standard od lat 90. Benchmarki złożoności algorytmicznej w CI: wciąż eksperymentalne w 2026 roku.
Operacjonalizacja ostrzeżenia
Hamming ostrzegał przed kostnieniem złożoności, nieprzestawianymi właściwościami, kopiowaniem rozwiązań bez ich warunków oraz skalą miażdżącą wczesne założenia. Dał nam słownictwo. Nie dał nam detektora.
Potok MOAD wypełnia tę lukę: skanowanie → zgłoszenie → poprawka → test jednostkowy → ujawnienie → PR → scalenie upstream. To zoperacjonalizowany Hamming: nie tylko ostrzeżenie, ale automatyczne wykrywanie i potok napraw.
Sygnatura Fester
Defekt klasy MOAD ma rozpoznawalną sygnaturę. Wszystkie pięć warunków występuje jednocześnie. Wszystkie pięć można sprawdzić, zanim napisze się choćby jeden skan:
1. Poprawne wyjście? Uruchom standardowy zestaw testów. Jeśli przejdzie, defekt dotyczy właściwości wydajności lub bezpieczeństwa, a nie poprawności. Oznacza to, że standardowe CI go nie wykryje.
2. Brak testu złożoności? Sprawdź konfigurację CI. Czy istnieje etap benchmarku? Czy porównuje zachowanie algorytmiczne (nie tylko czas rzeczywisty) z poprzednim commitem? Jeśli nie: warunek obecny.
3. Pochodzenie z małego N? Sprawdź git blame i oryginalny commit. Jaki był rozmiar zbioru danych w pierwszej implementacji? Czy jest on mniejszy od obecnego obciążenia produkcyjnego o więcej niż 100×? Jeśli tak: warunek obecny.
4. Kopia się propaguje? Wyszukaj wzorzec w całej bazie kodu i ekosystemach. Czy ten sam wzorzec strukturalny pojawia się w N≥3 niezależnych bazach kodu bez wspólnego pochodzenia? Jeśli tak: skamielina się rozprzestrzeniła. Każda kopia: nowe ognisko zakażenia.
5. Skala rośnie? Sprawdź metryki produkcyjne. Jakie jest N dziś w porównaniu z N przy pierwszym wdrożeniu? Czy tempo wzrostu jest utrzymane? Przy jakim N defekt staje się krytyczny operacyjnie?
Jeśli wszystkie pięć warunków zostały sprawdzone i potwierdzone: masz defekt klasy MOAD. Poprawka to zawsze zamiana jednej linii lub jednej metody. Najtrudniejsza jest detekcja. Poprawka jest łatwa.
O to właśnie chodziło Hammingowi: inżynieria nie polega na poprawce. Poprawka jest prosta, gdy już się ją dostrzeże. Inżynieria polega na budowaniu systemów, które pozwalają ją dostrzec.
Zastosuj sygnaturę
Sygnatura MOAD: poprawny wynik + brak testu złożoności + pochodzenie z małego N + kopia się propaguje + skala rośnie.
Ten sygnatur nie ogranicza się do pięciu defektów MOAD. Opisuje klasę defektów, która występuje w każdym systemie, w którym testy poprawności są jedyną zautomatyzowaną bramą jakości.