ousterhout-quality-program
Co robi
Używaj za każdym razem, gdy pisany lub przeglądany kod tworzy lub zmienia granicę — nowy moduł, klasę, komponent, pomocnika, hook, usługę lub wrapper; każdą ekstrakcję lub centralizację współdzielonego kodu; każdy moment „zróbmy to wielokrotnego użytku” — oraz podczas jawnego przeglądu, refaktoryzacji lub projektowania modułu. Ocena, czy abstrakcja jest warta swojej ceny: głębokość modułu, czy ukryć decyzję projektową, czy powielony kod chroni wspólną inwariantę, czy tylko rymuje, czy interfejs jest stabilny. Chroni przed mechanicznym stosowaniem SOLID/Clean Code, które tworzy wiele płytkich klas. Definiuje także test kosztu czytelnika (kod tani do czytania i zmiany dla ludzi i agentów) oraz procedurę refaktoryzacji istniejącej bazy kodu do tego standardu.
Instalacja otwiera tę pozycję w Twojej aplikacji AgentsRoom na komputerze. Jeśli aplikacji jeszcze nie ma, trafisz na stronę pobierania.
SKILL.md
--- name: ousterhout-quality-program description: Używaj za każdym razem, gdy pisany lub przeglądany kod tworzy lub zmienia granicę — nowy moduł, klasę, komponent, pomocnika, hook, usługę lub wrapper; każdą ekstrakcję lub centralizację współdzielonego kodu; każdy moment „zróbmy to wielokrotnego użytku” — oraz podczas jawnego przeglądu, refaktoryzacji lub projektowania modułu. Ocena, czy abstrakcja jest warta swojej ceny: głębokość modułu, czy ukryć decyzję projektową, czy powielony kod chroni wspólną inwariantę, czy tylko rymuje, czy interfejs jest stabilny. Chroni przed mechanicznym stosowaniem SOLID/Clean Code, które tworzy wiele płytkich klas. Definiuje także test kosztu czytelnika (kod tani do czytania i zmiany dla ludzi i agentów) oraz procedurę refaktoryzacji istniejącej bazy kodu do tego standardu. --- # Program Jakości Ousterhouta ## Przegląd Zadaniem modułu jest ukrycie złożoności za małym interfejsem. Kluczową miarą jest **głębokość**: głęboki moduł oferuje prosty interfejs nad znaczącą funkcjonalnością; płytki moduł ma interfejs niemal tak samo złożony jak jego implementacja, więc nie zwraca kosztów. Złożoność to to, co odczuwasz, gdy zmiana zmusza cię do zrozumienia lub dotknięcia kodu, którego się nie spodziewałeś — Ousterhout wskazuje dwa źródła: **zależności** (nie możesz zmienić A bez zmiany B) oraz **niejasność** (ważna informacja nie jest oczywista). Ousterhout sam mówi ci, jak *czuje się* dobry moduł. Najlepiej działa w połączeniu z kilkoma innymi perspektywami, które mówią, gdzie powinny być granice i jak bezpiecznie do nich dążyć. Ta umiejętność to właśnie takie połączone spojrzenie. ## Gdzie Przeglądy Naprawdę Zawodzą Dwa błędy, które ta umiejętność ma naprawić — obserwowane wielokrotnie w kodzie pisanym przez agentów — dotyczą **rozwiązania**, a nie samej decyzji o podziale/niepodziale: 1. **Płytka poprawka.** Mając sześć rzutowań `as unknown as`, recenzent bez wsparcia centralizuje je w jeden generyczny helper `castRows<T>()` — czyściej, ale niejasność pozostaje. Głęboka poprawka to typowane mapery wiersz→domena z testami ustalonymi najpierw (stosując Parnasa: rzutowanie to zapach brakującej granicy; Beck: udowodnij mapowanie zanim je przeniesiesz). Uporządkowanie zapachu to nie jego usunięcie. 2. **Refleksyjne wyodrębnienie.** Mając tę samą logikę aktualizacji powtórzoną w trzech rodzeńczych komponentach, każdy recenzent bez wsparcia mówił „wyodrębnij wspólny helper” — odruch DRY. Reguła tego programu, rozszerzająca Metza: czekaj na inwariant, nie na trzeci podobny fragment — centralizuj, gdy kod chroni wspólną regułę, nie gdy się rymuje. Gdy zalecasz poprawkę, sprawdź ją pod kątem obu: czy usuwa niejasność, czy ją tylko przesuwa, oraz czy wyodrębnienie chroni inwariant, czy tylko usuwa duplikację kształtu? ## Brama Proporcjonalności Pomiń tę perspektywę, gdy zmiana nie dodaje nowej eksportowanej/importowalnej nazwy, nie tworzy nowego modułu/klasy/komponentu/helpera/hooka/serwisu/opakowania i nic nie centralizuje. Czyste zmiany nazw, mechaniczne codemody, edycje konfiguracji/danych i poprawki jednolinijkowe są wyłączone. W razie wątpliwości wykonaj tylko dwa podstawowe testy (głębokość, inwariant) i na tym zakończ. ## Pełna Reguła Każdy fragment generowanego lub recenzowanego kodu przechodzi przez soczewkę Ousterhouta, zanim zadanie zostanie uznane za zakończone — nie tylko podczas jawnych przeglądów projektowych — z wyjątkiem zmian poniżej bramy proporcjonalności (brak nowej granicy, brak centralizacji: zmiany nazw, codemody, edycje konfiguracji). Dwa testy: (1) **Głębokość** — nowy interfejs musi ukrywać znacznie więcej niż ujawnia; interfejs tak samo złożony jak to, co opakowuje, nic nie zwraca. (2) **Inwariant** — wyodrębniaj wspólny kod tylko wtedy, gdy chroni wspólną regułę, nigdy dlatego, że trzy miejsca się rymują; poprawka musi usuwać niejasność, nie ją przesuwać (centralizacja sześciu rzutowań w jeden helper to nadal sześć rzutowań). Gdy zmiana tworzy lub przekształca granicę, najpierw znajdź, jak ustalony produkt rozwiązuje problem o takim kształcie i skali i przyjmij jego konwencje, chyba że jest wyraźny powód, by tego nie robić (wzorzec przypomniany z treningu to twierdzenie, nie źródło), potem wykonaj poniższe kontrole. ## Kiedy Używać - Decydowanie, czy nowa klasa/funkcja/hook jest warta swojego interfejsu, czy jest tylko płytkim przejściem. - Plik przekracza próg rozmiaru i decydujesz *jak* go podzielić, nie tylko czy powinieneś. - Powtarzający się kod kusi do wyodrębnienia wspólnego helpera. - Projektowanie lub przegląd granicy wokół reguły biznesowej (sprawdzenie zakresu autoryzacji, reguła pieniędzy/zaokrągleń, strażnik przejścia maszyny stanów, reguła retencji danych). - Interfejs ma otrzymać nowy parametr lub specjalny przypadek. - Doprowadzanie istniejącej bazy kodu do tego standardu — patrz „Refaktoryzacja istniejącej bazy kodu do tego standardu” poniżej. **Nie do:** trywialnych mechanicznych poprawek lub gdy konwencja projektu już narzuca strukturę — patrz Brama Proporcjonalności powyżej. Odsyłaj do `karpathy-guidelines` dla dyscypliny zmian chirurgicznych i umiejętności test-driven-development jako siatki bezpieczeństwa refaktoryzacji, gdy są dostępne. ## Soczewki Każda soczewka dodaje dokładnie jedno pytanie. Ousterhout jest kręgosłupem; pozostałe korygują jego ślepe punkty. | Soczewka | Jedno pytanie, które dodaje | Kiedy ma pierwszeństwo | |---|---|---| | **Ousterhout** — głębokie moduły | Czy ten interfejs ukrywa więcej, niż ujawnia? | Domyślny kręgosłup. | | **Parnas** — ukrywanie informacji | Jaką decyzję projektową (prawdopodobnie zmienną) ten moduł ukrywa? | *Powód*, dla którego moduł powinien być głęboki. Jeśli niczego zmiennego nie ukrywa, głębokość jest kosmetyczna. | | **Brooks** — istotne vs przypadkowe | Czy to usuwa przypadkową złożoność, czy tylko przenosi istotną złożoność domeny? | Eliminacja „refaktoryzacji”, które przesuwają bałagan bez jego zmniejszania. | | **Evans** — Domain-Driven Design | Czy ta granica jest nazwana językiem domeny, a nie ogólnym językiem narzędziowym? | Zmień nazwę `utils`/`helpers` — nazwij granicę po inwariancie, który faktycznie ma to repozytorium. | | **Fowler** — refaktoryzacja / zapachy | Jaki jest najmniejszy bezpieczny krok w kierunku głębszego projektu? | Przekształca „powinno być głębiej” w konkretne kroki za testami przechodzącymi. | | **Beck** — prosty projekt, test-first | Czy udowodniłem obecne zachowanie przed pogłębieniem szwu? | Hamulec przed przedwczesną architekturą. Najpierw działa i jest przetestowane, potem pogłęb właściwy szew. | | **Hickey** — proste vs łatwe | Czy miesza niepowiązane koncepcje, czy jest to naprawdę jedna koncepcja? | Płytki helper jest zwykle *łatwy* (blisko, szybko), nie *prosty* (mało przeplatających się koncepcji). Wol preferować proste. | | **Metz** — duplikacja zamiast złej abstrakcji | Czy ten powtarzający się kod chroni wspólny inwariant, czy tylko wygląda podobnie (zasada tego programu, rozszerzająca Metz)? | Metz: duplikacja jest tańsza niż zła abstrakcja — wstaw z powrotem złą abstrakcję zamiast ją wyginać. Ten program rozszerza ją: **nie** centralizuj, bo się powtarza; centralizuj tylko, gdy chroni prawdziwy inwariant. Toleruj duplikację, aż inwariant się ujawni. | | **Prawo Hyruma** — obserwowalne zachowanie | Czy wywołujący będą polegać na zachowaniu wykraczającym poza kontrakt tego interfejsu? | Argumentuje za małymi, stabilnymi interfejsami: każde obserwowalne zachowanie ostatecznie staje się nośne. | ## Przepis na łączenie Stosuj w tej kolejności — późniejsze soczewki mają znaczenie dopiero, gdy wcześniejsze przejdą: 1. **Metz — brama przyjęć.** Czy ta granica/abstrakcja w ogóle zasługuje na istnienie? Zasada tego programu, rozszerzająca Metz: wyodrębniaj tylko, gdy kod chroni wspólną regułę — trzy podobieństwa to nie ujawniony inwariant. Jeśli nie, zatrzymaj się tutaj. 2. **Parnas / Ousterhout** — Ukryj zmienną decyzję (zakres autoryzacji, reguła zaokrąglania, strażnik przejścia, reguła retencji) za głębokim modułem. 3. **Evans** — Nazwij ten moduł językiem domeny, nie `utils`. 4. **Beck / Fowler** — Dla istniejącego kodu przypnij obecne zachowanie testami, potem refaktoryzuj w małych bezpiecznych krokach. Dla świeżo wygenerowanego kodu nie ma obecnego zachowania do przypięcia — napisz test definiujący zamierzone zachowanie. 5. **Hickey** — Odrzuć interfejsy mieszające niepowiązane koncepcje tylko dlatego, że workflowy wyglądają podobnie. ## Strukturalny antywzorzec **Mechaniczne SOLID / Clean Code produkuje płytkie moduły.** Dogmatyczne czytanie — po jednej klasie na odpowiedzialność, wyodrębnianie każdej funkcji, utrzymywanie wszystkiego malutkim — prowadzi do roju klas, których interfejsy są tak samo złożone jak ich ciała. Gdy zasada mówi „podziel to”, zapytaj, jaką *decyzję* ten podział ukrywa (Parnas) i czy ukrywa więcej niż ujawnia (Ousterhout). Jeśli niczego zmiennego nie ukrywa, nie dziel. Ta zasada jest najważniejsza pod presją refaktoryzacji („posprzątaj to”, „ten plik jest za duży”) — w spokojnej analizie recenzenci już się temu opierają; w trakcie refaktoryzacji, z mandatem na widoczną zmianę, powstaje rój płytkich plików. ## Częste błędy - **Dzieląc tylko ze względu na rozmiar.** Moduł zapytania o 400 linijkach, który ukrywa jedną spójną decyzję, może być głębszy niż cztery moduły po 100 linijek, które każdy przeciekają te same połączenia. - **Nazywając podział `helpers`/`utils`.** Jeśli nie potrafisz nazwać go językiem domeny (Evans), granica jest prawdopodobnie błędna. - **Wyodrębniając przy drugim wystąpieniu.** Zasada tego programu, rozszerzająca Metz: czekaj na inwariant, nie na trzecie podobieństwo. - **Pogłębiając przed przypięciem zachowania.** Beck: bez testu potwierdzającego obecne zachowanie, refaktoryzacja „pogłębiająca” to przepisywanie. - **Licząc przekazywanie dalej jako moduł.** Wrapper przekazujący argumenty dodaje interfejs i nic nie ukrywa — jest płytki z definicji. - **Mylenie rymu z inwariantem.** Najlepszym dowodem wspólnego inwariantu jest współzmiana: kopie były poprawiane lub zmieniane razem w historii (ten sam błąd naprawiony w dwóch miejscach). Podobieństwa zmieniające się niezależnie to rymy; zostaw je zdublowane. - **Porządkując zapach zamiast go usuwać.** Centralizacja sześciu rzutowań w jeden generyczny helper to uporządkowana wersja tej samej niejasności. Głęboką poprawką jest nazwanie granicy, którą rzutowanie maskowało. ## Koszt czytelnika: trzeci test Głębokość i inwariant decydują, czy granica powinna istnieć. Koszt czytelnika decyduje, czy kod wokół niej jest tani w zmianie. Następny czytelnik, człowiek lub agent, płaci za każdą linię, którą musi załadować, by coś bezpiecznie zmienić. Agenci płacą tokenami i nawigują przez wyszukiwanie tekstu, częściowe odczyty oraz pętle typowania/testowania, więc te same wady kosztują ich więcej. Zapytaj: - **Łatwe do znalezienia?** Jedna nazwa na pojęcie, zapisana tak samo wszędzie, dostępna przez zwykłe wyszukiwanie tekstowe. Wady: nazwy składane ze stringów, powiązania przez efekt uboczny importu, łańcuchy re-eksportów ukrywające definicję, dwie nazwy dla jednego pojęcia. - **Czy czytelnik może przerwać wcześniej?** Kontrakt znajduje się na początku pliku lub nad eksportem: co obiecuje, co ukrywa, czego nigdy nie robi. Wada: kontrakt można wywnioskować tylko czytając ciało. - **Czy można to sprawdzić maszynowo?** Precyzyjne typy na wejściu i wyjściu każdej granicy, tak aby sprawdzenie typów zastąpiło czytanie wywołujących. Wady: `any`, gołe słowniki, flagi boolean, których znaczenie jest w ciele. - **Czy sprzężenie jest widoczne?** Miejsca, które muszą się zmieniać razem, są wymuszane (wspólny typ, test, pojedyncze źródło) lub, jeśli to niemożliwe, oznaczone w obu miejscach. Dowodem ukrytego sprzężenia jest współzmiana w historii, o której nic nie mówi kod. - **Bez szumu?** Brak komentarzy powtarzających kod, brak zakomentowanego kodu, brak martwych gałęzi, brak komentarzy historii zmian, brak przestarzałych ścieżek obok ich zamienników. - **Przewidywalne?** Układ podąża za istniejącym wzorcem repozytorium; test jest tam, gdzie czytelnik go szuka i działa samodzielnie. Rozmiar pliku jest celowo pominięty. Bardzo duży plik to powód, by szukać drugiej ukrytej decyzji, nigdy powód do cięcia: czytelnicy mogą wyszukiwać i czytać zakres, a podział, który nic nie ukrywa, dodaje interfejsy bez usuwania obciążenia. Do znaczników w kodzie i mapy repozytorium używaj `context-audit` tam, gdzie jest dostępny: jego kotwica `AIDEV-NOTE:` (jeden fakt nie do odzyskania plus odniesienie do pochodzenia, maksymalnie dwie linie, w miejscu) jest konwencją dla sprzężenia, którego nie da się wymusić. ## Refaktoryzacja istniejącej bazy kodu do tego standardu Retrofity ocenia się tak samo jak nowy kod; różni się kolejność i powściągliwość. Większość bazy kodu powinna pozostać bez zmian. 1. **Spis, tylko do odczytu.** Wypisz granice (moduły, usługi, współdzielone helpery). Dla każdego rekordu: decyzja, którą ukrywa, lub „brak”; rozmiar interfejsu względem ciała; partnerzy współzmian z historii; wady podnoszące koszt czytania. Jeszcze nic nie zmieniaj. 2. **Ranguj według zmienności, nie brzydoty.** Priorytet to jak często kod się zmienia razy koszt jego czytania. Zimny kod, który działa, zostaje taki, jaki jest, nawet jeśli jest płytki. Istotna złożoność domeny pozostaje tam, gdzie jest (Brooks). 3. **Przypisz jedno rozwiązanie na znaleziony problem:** - warstwa przejściowa lub wrapper, który nic nie ukrywa: usuń go, wywołujący używają tego, co opakowywał; - zła abstrakcja zgięta przez flagi i przypadki specjalne: wstaw ją z powrotem (Metz), potem szukaj prawdziwej inwarianty; - płytkie rodzeństwo dzielące jedną decyzję: połącz je za jednym interfejsem; - wyciekła decyzja (wywołujący znają format, regułę, schemat): ściągnij ją do modułu, który ją posiada; - ogólna nazwa (`utils`, `helpers`, `manager`): zmień nazwę na decyzję, którą ukrywa, lub rozpuść ją w wywołujących; - nieopisany typowo boundary: opisz typami i zamień rzutowania na mapper, który je maskował; - ukryte sprzężenie: wymuś je lub oznacz w obu miejscach; - szum: usuń. Rymy, które zmieniają się niezależnie, nie dostają rozwiązania. 4. **Najpierw ustal zachowanie.** Żadne rozwiązanie nie zaczyna się, dopóki test nie potwierdzi obecnego zachowania kodu, którego dotyczy (Beck). Refaktoryzacje zachowują zachowanie; zmiana zachowania to osobny commit. 5. **Podziel pracę na jednostki, które jeden agent może wykonać samodzielnie.** Jedna granica na jednostkę. Każda jednostka nazywa pliki, które posiada, kontrakt, który musi zachować, oraz polecenie, które samodzielnie to potwierdza. Żadne dwie równoczesne jednostki nie piszą do tego samego pliku; pliki współdzielone (barrel, rejestry, tabele tras) mają jednego właściciela lub czekają na integrację. Zmiany interfejsów, od których zależy kilka jednostek, lądują najpierw, jako osobna jednostka. 6. **Zmierz efekt.** Wybierz reprezentatywną zmianę przed rozpoczęciem i policz pliki oraz linie, które czytelnik musi załadować, by ją wykonać; policz ponownie po. Eksportowane nazwy i całkowita liczba linii powinny spaść lub pozostać na miejscu. Refaktoryzacja dodająca interfejsy wymaga podania powodu. 7. **Zatrzymaj się** gdy to, co pozostaje, jest zimne, istotne lub rymujące się. Powiązane umiejętności, tam gdzie dostępne: `repo-review` (typ projektu) tworzy spis jako artefakt tylko do porady; `design-cleanup` wykonuje pętlę naprawy i ponownego skanowania dla przypadkowej złożoności; `context-audit` dodaje kotwice i mapę kodu; `ousterhout-build-deep` to lista kontrolna autora dla agentów wykonujących jednostki. ## Gdzie to się znajduje Ta umiejętność to warstwa przeglądu i oceny: używaj jej, by zdecydować, czy abstrakcja jest głęboka, nazwana dla właściwej decyzji i warta wyodrębnienia. `find-shared-code` używa jej jako testu przy przyjmowaniu, gdy przeszukuje niedawną historię w poszukiwaniu kodu wartego współdzielenia. Poniższy dodatek przedstawia rozumowanie każdego autora. --- ## Dodatek: Soczewki w Głębi Tryb awarii, który łapie każdy autor, i jeden ruch, który daje. Tabela powyżej to szybkie odniesienie; to jest rozumowanie za nią. ### Ousterhout — Głębokie Moduły (kręgosłup) *Filozofia projektowania oprogramowania.* - **Głębokość** = korzyść (ukryta funkcjonalność) ÷ koszt (złożoność interfejsu). Głęboki moduł ukrywa dużo za małym interfejsem. Płytki moduł ma interfejs niemal tak złożony jak ciało, więc nic nie zyskuje. - **Złożoność** to wszystko, co utrudnia zrozumienie lub modyfikację systemu. Dwa źródła: - **Zależności** — nie możesz zmienić jednego elementu bez dotknięcia innego. - **Niejasność** — ważne informacje nie są oczywiste z kodu. - **Objawy:** wzrost zmian (jedna decyzja, wiele edycji), obciążenie poznawcze (ile musisz trzymać w głowie), nieznane nieznane (nie wiesz, jaki kod zmiana dotknie). - **Kluczowy ruch:** ściągnij złożoność *w dół* — moduł absorbuje trudny przypadek, by wywołujący nie musieli. Parametry konfiguracyjne i warstwy przejściowe przesuwają złożoność *w górę* do wywołującego; to płytkość. Łapie: interfejsy, które wyciekają implementację; helpery, które nie pomagają. ### Parnas — Ukrywanie informacji (dlaczego głębokość ma znaczenie) *On the Criteria To Be Used in Decomposing Systems into Modules (1972).* - Rozkładaj na części wokół **decyzji projektowych, które prawdopodobnie się zmienią**, a nie wokół kroków obliczeń. Każdy moduł ukrywa jedną taką decyzję. - To jest bezpośredni przodek modułu głębokiego. Moduł jest głęboki *ponieważ* ukrywa decyzję, która w przeciwnym razie rozlałaby się na wywołujących. Pułapki: „moduł”, który nic zmiennego nie ukrywa — jego głębokość jest kosmetyczna. Zapytaj: co się zmienia za tym interfejsem, czego wywołujący nigdy nie widzą? Jeśli odpowiedź brzmi „nic”, granica jest ozdobą. ### Brooks — Złożoność istotna vs przypadkowa *Nie ma srebrnej kuli.* - **Złożoność istotna** jest nieodłączna dla domeny (wycena naprawdę jest tak złożona). **Złożoność przypadkowa** to ta, którą narzucają nasze narzędzia i struktura. - Tylko złożoność przypadkową można usunąć. Refaktoryzacja, która „sprząta” przez przeniesienie istotnej złożoności domeny z jednego pliku do drugiego, nic nie zrobiła. Pułapki: przetasowania podszyte pod uproszczenie. Zapytaj: czy całkowita złożoność spadła, czy tylko się przesunęła? ### Evans — Domain-Driven Design *Domain-Driven Design.* - Granice powinny być nazywane w **wszechobecnym języku** domeny, a nie w ogólnych, użytkowych terminach. Moduł o nazwie `helpers` nic nie nazywa; moduł `AccessScope` lub `PricingPolicy` nazywa inwariant. - Ograniczone konteksty zapobiegają wyciekaniu biznesowych inwariantów przez szwy. Pułapki: poprawny podział z bezsensownymi nazwami. Jeśli nie potrafisz nazwać modułu w języku domeny, prawdopodobnie przeciąłeś granicę w złym miejscu. ### Fowler — Refaktoryzacja i zapachy kodu *Refaktoryzacja.* - Daje konkretne, bezpieczne, nazwane ruchy (Extract Function, Move Field, Replace Conditional with Polymorphism), by przejść od obecnego projektu do głębszego. - Każdy ruch zachowuje zachowanie i jest mały, więc pozostaje odwracalny. Pułapki: przepaść między „to powinno być głębsze” a wiedzą, jaki będzie następny commit. Ousterhout wyznacza cel; Fowler jest drogą. ### Beck — Prosty projekt, testowanie najpierw *Test-Driven Development; XP.* - Cztery zasady prostego projektu, w kolejności opublikowanej przez Becka: przechodzi testy, brak duplikacji, ujawnia intencję, najmniej elementów. Ten program stosuje późniejszą kolejność Fowler/Haines — intencja przed duplikacją — ponieważ służy to rozszerzającej regule inwariantu Metz (patrz Metz, poniżej): nie działaj na duplikacji, dopóki nie potrafisz nazwać intencji, którą chroni. - Testowanie najpierw to hamulec przed przedwczesną architekturą. Najpierw spraw, by działało i udowodnij zachowanie, potem pogłębiaj szew, który teraz chronią testy. Pułapki: architektura budowana zanim zachowanie jest ustalone. Bez testu potwierdzającego obecne zachowanie, refaktoryzacja „pogłębiająca” to niezweryfikowany przepis. ### Hickey — Proste vs łatwe *Simple Made Easy.* - **Proste** = nie splątane: jedna koncepcja, nie przeplatana z innymi (obiektywne). - **Łatwe** = pod ręką, znane, szybkie do sięgnięcia (względem ciebie). - Te dwie cechy są niezależne. Płytki helper jest zwykle *łatwy* — szybki do napisania, blisko — ale nie *prosty*, jeśli splata niepowiązane kwestie. Pułapki: wygoda podszywająca się pod projekt. Wol preferować konstrukty, które utrzymują koncepcje niesplątane, nawet jeśli splątany jest szybszy do napisania. ### Metz — Wol preferować duplikację niż złą abstrakcję *„The Wrong Abstraction” (2016).* - Duplikacja jest znacznie tańsza niż zła abstrakcja. Abstrakcja wyciągnięta za wcześnie zmusza każdego przyszłego wywołującego do dostosowywania się do założeń, które nigdy nie były prawdziwe dla wszystkich. - Gdy abstrakcja okazuje się zła, lekarstwem Metz jest wstawienie jej z powrotem i pozwolenie na powrót duplikacji, zamiast wyginać ją, by pasowała do przypadku, do którego nie była stworzona. - **Reguła tego programu, rozszerzająca Metz: nie centralizuj tylko dlatego, że kod się powtarza. Centralizuj, gdy chroni prawdziwy, wspólny inwariant.** Dopóki inwariant się nie ujawni, toleruj duplikację. Pułapki: nadmierna centralizacja — płytki wspólny helper, wokół którego teraz wszyscy muszą się dostosowywać. To przeciwwaga dla mechanicznego „DRY za wszelką cenę”. ### Prawo Hyruma — Zachowanie obserwowalne staje się kontraktem *„Przy wystarczającej liczbie użytkowników każde obserwowalne zachowanie twojego systemu będzie na czymś zależało.”* - Cokolwiek interfejs *przypadkowo* robi — kolejność, czas, tekst błędu — ktoś w końcu będzie na tym polegał. Więc powierzchnia, którą eksponujesz, jest większa niż ta, którą udokumentowałeś. - To wspiera preferencję Ousterhouta do **małych, stabilnych interfejsów**: im mniej eksponujesz, tym mniej może przypadkowo stać się nośne. Pułapki: szerokie interfejsy, które się skamieniają. Każde dodatkowe obserwowalne staje się przyszłym ograniczeniem. ### Jak to się łączy - **Parnas → Ousterhout:** ukryj zmienną decyzję → moduł jest głęboki. - **Brooks:** potwierdź, że głębokość usunęła złożoność, a nie ją przeniosła. - **Evans:** nazwij granicę w języku domeny. - **Beck → Fowler:** ustal zachowanie, potem refaktoryzuj małymi, bezpiecznymi ruchami. - **Metz:** opieraj się centralizacji, dopóki inwariant nie jest prawdziwy. - **Hickey:** utrzymuj interfejs jednej koncepcji. - **Hyrum:** utrzymuj ten interfejs mały, by mógł pozostać stabilny. Niebezpieczeństwem jest mieszanie Ousterhouta z mechanicznym odczytem SOLID lub Clean Code: to daje wiele malutkich klas i funkcji z płytkimi interfejsami — dokładne przeciwieństwo głębokich modułów. Ousterhout, z Metz jako przeciwwagą, jest antidotum.
Tagi
Warto przeczytać
Claude Ads: skill Claude Code, który audytuje konta reklamowe
Claude Ads to open source'owy skill dla Claude Code: ponad 250 sprawdzeń na Google, Meta, LinkedIn, TikTok czy Amazon Ads, wynik na 100 i priorytetowy plan działania, w kilkanaście minut. Instalacja, komendy, ograniczenia i jak go orkiestrować w AgentsRoom.
AGENTS.md: jeden plik kontekstu dla każdego agenta kodującego (Codex, Antigravity, Claude)
AGENTS.md to przenośny plik z instrukcjami, który twoje agenty kodujące czytają, zanim dotkną kodu. Co w nim umieścić, czym różni się od CLAUDE.md i jak utrzymać jeden kontekst dla Codex, Antigravity i Claude.
Pobierz AgentsRoom
Uruchamiaj wszystkich swoich agentów AI, we wszystkich projektach, z jednego okna.
Aplikacja towarzysząca: monitoruj agentów w podróży
Użyj Claude, Codex, Antigravity CLI lub innego dostawcy AI.
Wysyłaj bugi i prośby bezpośrednio do swojego publicznego backlogu.