Prawdy Joela Spolsky’ego o oprogramowaniu dalej się sprawdzają, nawet gdy AI szybko pisze kod. Dowiedz się, jak skupić się na testach, rekrutacji i prostocie, by zapewnić poprawność.

AI potrafi wygenerować kod wyglądający na działający w ciągu kilku minut. To zmienia tempo projektu, ale nie zmienia tego, co sprawia, że oprogramowanie odnosi sukces. Lekcje z „prawd oprogramowania” Joela Spolsky’ego nigdy nie dotyczyły szybkości pisania. Chodziło o osąd, pętle sprzężenia zwrotnego i unikanie samonapędzającej się złożoności.
Zmienił się koszt tworzenia kodu. Możesz poprosić o trzy podejścia, pięć wariantów albo pełny rewrite i dostać coś niemal natychmiast. Nie zmienił się koszt wybrania właściwego podejścia, sprawdzenia go i życia z nim przez miesiące. Czas zaoszczędzony przy pisaniu często przesuwa się na decydowanie, co właściwie miałeś na myśli, walidowanie przypadków brzegowych i upewnianie się, że dzisiejsze szybkie zwycięstwo nie stanie się jutrzejszym podatkiem na utrzymanie.
Poprawność, bezpieczeństwo i utrzymywalność nadal zajmują realny czas, bo opierają się na dowodzie, nie na przekonaniu. Flow logowania nie jest skończony, gdy się zkompiluje. Jest skończony, gdy niezawodnie odrzuca złe dane wejściowe, obsługuje dziwne stany i nie wycieka danych. AI może brzmieć przekonująco, a jednak pominąć jeden kluczowy szczegół, jak sprawdzenie uprawnień na endpointzie czy warunek wyścigu przy aktualizacji płatności.
AI jest najsilniejsze, gdy traktujesz je jak szybkie narzędzie do szkicu. Świetnie radzi sobie z boilerplatem, powtarzalnymi wzorcami, szybkim refaktorem i eksploracją opcji do porównania obok siebie. Użyte dobrze, kompresuje fazę „pustej kartki”.
AI szkodzi najbardziej, gdy dasz mu niejasne cele i zaakceptujesz wynik na twarz. Te same wzorce porażek pojawiają się w kółko: ukryte założenia (niezadeklarowane reguły biznesowe), nietestowane ścieżki (obsługa błędów, retry’e, puste stany), pewne błędy (przekonujący kod, który jest subtelnie niepoprawny) i „sprytne” rozwiązania, których potem trudno wytłumaczyć.
Jeśli kod jest tani, nowym rzadkim zasobem jest zaufanie. Te prawdy mają znaczenie, bo chronią to zaufanie: wobec użytkowników, współpracowników i twojego przyszłego ja.
Gdy AI może wygenerować funkcję w minutach, kusi, żeby traktować testy jako wolną część, której trzeba się pozbyć. Punkt Spolsky’ego dalej obowiązuje: wolna część to miejsce, gdzie jest prawda. Kod jest łatwy do wyprodukowania. Poprawne zachowanie nie jest.
Pożyteczny przesunięcie to traktować testy jak wykonywalne wymagania. Jeśli nie potrafisz opisać oczekiwanego zachowania w sprawdzalny sposób, nie skończyłeś myśleć. W pracy wspomaganej przez AI ma to jeszcze większe znaczenie, bo model może pewnie wygenerować coś, co jest tylko trochę niepoprawne.
Zacznij testować od tego, co najbardziej zaszkodzi, jeśli się zepsuje. Dla większości produktów to kluczowe przepływy (rejestracja, checkout, zapis, eksport), uprawnienia (kto może oglądać, edytować, usuwać) i integralność danych (brak duplikatów, poprawne sumy, bezpieczne migracje). Potem pokryj brzegi, które zwykle powodują nocne incydenty: puste wejścia, długi tekst, strefy czasowe, retry’e i niestabilne zewnętrzne granice jak płatności, e-maile czy przesyłanie plików.
AI świetnie proponuje przypadki testowe, ale nie wie, co faktycznie obiecałeś użytkownikom. Używaj go jak partnera do burzy mózgów: poproś o brakujące przypadki brzegowe, scenariusze nadużycia i kombinacje uprawnień. Potem wykonaj pracę ludzką: dopasuj pokrycie do rzeczywistych reguł i usuń testy, które jedynie „testują implementację”, zamiast zachowania.
Spraw, żeby błędy były możliwe do działania. Padający test powinien mówić, co się zepsuło, a nie wysyłać cię na poszukiwania skarbów. Trzymaj testy małe, nazywaj je jak zdania i rób komunikaty o błędach konkretne.
Powiedzmy, że budujesz prostą aplikację „notatki zespołowe” z pomocą AI. Ekrany CRUD pojawiają się szybko. Ryzyko poprawności to nie UI. To kontrola dostępu i reguły danych: użytkownik nie może zobaczyć notatek innego zespołu, edycje nie mogą nadpisać nowszych zmian, a usunięcie notatki nie powinno zostawić osieroconych załączników. Testy zamykające te reguły będą wydawać się wąskim gardłem, ale są też siatką bezpieczeństwa.
Gdy testowanie jest wąskim gardłem, wymusza jasność. To ta jasność sprawia, że szybki kod nie zamienia się w szybkie błędy.
Jedna z najbardziej trwałych prawd jest taka, że prosty kod wygrywa z wymyślnym. AI kusi, by zaakceptować wyszukane abstrakcje, bo przychodzą wypolerowane i szybko. Koszt pojawia się później: więcej miejsc do ukrycia błędów, więcej plików do przeszukania i więcej momentów „co to w ogóle robi?”.
Gdy kod jest tani, to złożoność jest tym, za co płacisz. Mały, nudny projekt jest łatwiejszy do przetestowania, łatwiejszy do zmiany i łatwiejszy do wytłumaczenia. To ma jeszcze większe znaczenie, gdy pierwszy szkic pochodzi z modelu, który potrafi brzmieć pewnie, a być subtelnie niepoprawny.
Praktyczna zasada: trzymaj funkcje, komponenty i moduły małe, tak by współpracownik mógł je przejrzeć w minutach, a nie godzinach. Jeśli komponent React wymaga wielu custom hooków, lokalnej maszyny stanów i warstwy „smart renderer”, zatrzymaj się i zapytaj, czy naprawdę rozwiązujesz realny problem, czy po prostu akceptujesz architekturę, bo AI ją zaproponowało.
Kilka „testów prostoty” pomoże się odsunąć:
Prompty mają tu znaczenie. Jeśli poprosisz o „najlepszą architekturę”, często otrzymasz nadbudowaną. Poproś o ograniczenia skłaniające do mniejszej liczby elementów ruchomych. Na przykład: użyj najprostszego podejścia z najmniejszą liczbą plików; unikaj nowych abstrakcji, chyba że usuwają duplikację w 3+ miejscach; preferuj jawny kod nad uniwersalne helpery.
Przykład: poprosisz AI o dodanie kontroli ról do strony admina. Sprytna wersja wprowadza framework uprawnień, dekoratory i DSL konfiguracyjny. Prosta wersja sprawdza rolę użytkownika w jednym miejscu, blokuje trasy w jednym miejscu i loguje odmowy dostępu. Prosta wersja jest łatwiejsza do przeglądu, łatwiejsza do przetestowania i trudniejsza do błędnej interpretacji.
Jeśli budujesz w narzędziu czatowym takim jak Koder.ai, prostota także zwiększa wartość migawek i rollbacków. Małe, oczywiste zmiany łatwiej porównać, zachować lub cofnąć.
Gdy kod jest łatwy do wygenerowania, cenna umiejętność to wybieranie, co w ogóle powinno istnieć, i upewnianie się, że to poprawne. Stara rada „zatrudniaj świetnych programistów” dalej obowiązuje, ale rola się przesuwa. Nie zatrudniasz osoby, żeby szybciej pisała kod. Zatrudniasz kogoś, kto potrafi ocenić, dopracować i obronić produkt.
Najbardziej wartościowe osoby w pracy wspomaganej przez AI mają zwykle cztery cechy: osąd (co ma znaczenie), gust (jak wygląda dobrze), umiejętność debugowania (znalezienie prawdziwej przyczyny) i komunikację (jasne przedstawianie kompromisów). Potrafią wziąć funkcję napisaną przez AI, która „w większości działa”, i uczynić z niej coś, czemu można zaufać.
Zamiast prosić o idealne rozwiązanie od zera, daj kandydatowi AI-wygenerowany pull request (lub wklejony diff) z kilkoma realistycznymi problemami: niejasne nazwy, ukryty przypadek brzegowy, brak testów i drobny błąd bezpieczeństwa.
Poproś, by wyjaśnili, co kod ma robić prostym językiem, znaleźli najbardziej ryzykowne części, zaproponowali poprawki i dodali (lub nakreślili) testy, które wykryłyby regresje. Jeśli chcesz silny sygnał, zapytaj też, jak zmieniliby instrukcję, żeby następna próba AI była lepsza.
To ujawnia, jak myślą w rzeczywistych warunkach: niedoskonały kod, ograniczony czas i potrzeba wyboru priorytetów.
AI często brzmi pewnie. Dobrzy zatrudnieni potrafią się temu postawić. Potrafią powiedzieć nie funkcji, która dodaje złożoność, nie zmianie, która osłabia bezpieczeństwo, i nie wypuszczeniu bez dowodu.
Konkretny sygnał to sposób odpowiedzi na pytanie „Zmergowałbyś to?”. Silni kandydaci nie odpowiadają nastrojem. Dają decyzję i krótką listę wymaganych zmian.
Przykład: prosisz o „szybką” zmianę kontroli dostępu, a AI proponuje rozsianie checków po handlerach. Silny kandydat odrzuca to i proponuje jedną jasną warstwę autoryzacji oraz testy dla ścieżek admin i non-admin.
Na koniec: buduj wspólne standardy, żeby zespół edytował wyjścia AI w ten sam sposób. Trzymaj to prosto: jedna definicja ukończenia, spójne oczekiwania przeglądowe i baza testowa.
Gdy AI może wygenerować dużo kodu w minutach, kusi, by pominąć myślenie i po prostu iterować. To działa w demo. Psuje się, gdy potrzebujesz poprawności, przewidywalnego zachowania i mniej niespodzianek.
Dobry prompt to zazwyczaj krótki spec w przebraniu. Zanim poprosisz o kod, zamień niejasny cel w kilka kryteriów akceptacji i jawnych non-goals. To zapobiega temu, by AI (i twój zespół) cicho rozszerzali zakres.
Trzymaj spec mały, ale konkretny. Nie piszesz powieści. Wyznaczasz granice wokół:
Zdefiniuj „skończone” przed generacją, nie po. „Skończone” powinno być czymś więcej niż „kompiluje się” czy „UI wygląda dobrze”. Dołącz oczekiwania testowe, kompatybilność wsteczną i co będzie monitorowane po wydaniu.
Przykład: chcesz „dodać reset hasła”. Jaśniejszy spec może mówić: użytkownicy proszą o reset przez e-mail; linki wygasają po 15 minutach; ta sama wiadomość wyświetla się niezależnie od tego, czy e-mail istnieje; limit żądań na IP; loguj próby resetu bez przechowywania tokenów w otwartym tekście. Non-goal: brak redesignu strony logowania. Teraz twój prompt ma szyny ochronne, a przeglądy stają się prostsze.
Prowadź lekki changelog decyzji. Jeden akapit na decyzję wystarczy. Zapisz, dlaczego wybrałeś podejście i dlaczego odrzuciłeś alternatywy. Gdy ktoś zapyta „dlaczego jest tak, a nie inaczej?” za dwa tygodnie, będziesz miał odpowiedź.
Największa zmiana z AI to fakt, że tworzenie kodu jest łatwe. Trudna część to zdecydowanie, co kod powinien robić i udowodnienie, że to robi.
Zacznij od napisania celu i ograniczeń prostym językiem. Dołącz, czego nigdy nie wolno zrobić, co może być powolne i co jest poza zakresem. Dobre ograniczenie jest testowalne: „Żaden użytkownik nie powinien widzieć danych innego użytkownika” albo „Sumy muszą zgadzać się z eksportem księgowym co do grosza”.
Zanim poprosisz o kod, poproś o prosty projekt i kompromisy. Chcesz, by AI pokazało swoje rozumowanie w formie, którą możesz ocenić: co będzie przechowywać, co będzie walidować i co będzie logować. Jeśli zaproponuje coś sprytnego, naciśnij i poproś o najprostszą wersję, która nadal spełnia ograniczenia.
Powtarzalna pętla wygląda tak:
Mały scenariusz: dodajesz „status zwrotu” do ekranu zamówienia. AI może szybko wygenerować UI, ale poprawność żyje w przypadkach brzegowych. Co jeśli zwrot jest częściowy? Co jeśli provider płatności ponownie wyśle webhook? Najpierw napisz te przypadki, potem zaimplementuj jedną porcję (kolumna w bazie + walidacja) i zweryfikuj testami, zanim przejdziesz dalej.
Jeśli używasz Koder.ai, funkcje takie jak tryb planowania, migawki i rollback naturalnie pasują do tej pętli: zaplanuj najpierw, generuj w kawałkach i zapisuj punkt przywracania dla każdej znaczącej zmiany.
Gdy generowanie kodu jest szybkie, kusi, by traktować kod jako produkt pracy. Nie jest. Produktem pracy jest zachowanie: aplikacja robi właściwą rzecz, nawet gdy coś idzie nie tak.
AI często brzmi pewnie, nawet gdy zgaduje. Błąd to pomijanie nudnej części: uruchamiania testów, sprawdzania przypadków brzegowych i walidacji rzeczywistych wejść.
Prosta praktyka: zanim zaakceptujesz zmianę, zapytaj „Skąd wiemy, że to poprawne?”. Jeśli odpowiedź brzmi „wygląda dobrze”, to grasz w ruletkę.
AI lubi dodawać dodatki: cache, retry’e, więcej ustawień, dodatkowe endpointy, ładniejsze UI. Część z tych pomysłów jest dobra, ale podnoszą ryzyko. Wiele błędów bierze się z „miłych do posiadania” funkcji, których nikt nie zamawiał.
Trzymaj twardą granicę: rozwiązuj problem, który sobie postawiłeś, potem stop. Jeśli sugestia jest wartościowa, zamieść ją jako oddzielne zadanie z własnymi testami.
Duży commit wygenerowany przez AI może ukrywać tuzin niepowiązanych decyzji. Przegląd staje się odklepywaniem, bo nikt nie ogarnia wszystkiego naraz.
Traktuj wyjście z chatu jak szkic. Rozbij je na małe zmiany, które możesz przeczytać, uruchomić i cofnąć. Migawki i rollbacky pomagają tylko wtedy, gdy robisz je w sensownych punktach.
Kilka prostych ograniczeń zapobiega większości bólu: jedna funkcja na zestaw, jedna migracja na zestaw, jedna obszar wysokiego ryzyka naraz (auth, płatności, usuwanie danych), testy zaktualizowane w tym samym commicie i jasna instrukcja „jak zweryfikować”.
AI może odtworzyć wzorce ze źródeł treningowych lub zasugerować zależności, których nie rozumiesz. Nawet jeśli licencja jest ok, większe ryzyko to bezpieczeństwo: zaszyte sekrety, słabe tokeny, niebezpieczne operacje na plikach i zapytaniach.
Jeśli nie potrafisz wytłumaczyć, co fragment robi, nie wysyłaj go. Poproś o prostszą wersję albo napisz samodzielnie.
Wiele błędów „działa u mnie” to tak naprawdę błędy danych i skali. AI może dodać zmianę schematu, nie myśląc o istniejących wierszach, dużych tabelach czy czasie przestoju.
Realistyczny przykład: model dodaje nową kolumnę NOT NULL do tabeli PostgreSQL i backfilluje ją w wolnej pętli. W produkcji może to zablokować tabelę i zepsuć aplikację. Zawsze rozważ, co się stanie przy milionie wierszy, wolnej sieci lub nieudanego deployu w połowie drogi.
Wyobraź sobie mały wewnętrzny tracker zgłoszeń: ludzie wysyłają prośby, menedżerowie zatwierdzają lub odrzucają, a dział finansów oznacza pozycje jako opłacone. Brzmi prosto, a z pomocą AI możesz szybko wygenerować ekrany i endpointy. Część, która Cię spowolni, to te same stare prawdy: reguły, nie pisanie.
Zacznij od spisania minimum, które musi być poprawne. Jeśli nie potrafisz tego wytłumaczyć prostym językiem, nie potrafisz tego przetestować.
Szczupła definicja pierwszej wersji często wygląda tak: pola (title, requester, department, amount, reason, status, timestamps); role (requester, approver, finance, admin); statusy (draft, submitted, approved, rejected, paid). Potem określ przejścia, które się liczą: tylko approver może zmienić submitted na approved lub rejected; tylko finance może zmienić approved na paid.
Używaj AI w kontrolowanej kolejności, żeby łapać błędy wcześnie:
Najcenniejsze testy to nie „czy strona się ładuje”. To sprawdzenia uprawnień i przejść stanów. Udowodnij na przykład, że requester nie może zatwierdzić własnego zgłoszenia, approver nie może oznaczyć jako opłacone, odrzucone zgłoszenia nie mogą być opłacone, a (jeśli taka jest reguła) kwoty nie mogą być edytowane po przesłaniu.
To, co zajmuje najwięcej czasu, to doprecyzowanie przypadków brzegowych. Czy approver może zmienić zdanie po odrzuceniu? Co jeśli dwóch approverów kliknie approve jednocześnie? Co jeśli finance musi częściowo zapłacić? AI potrafi wygenerować kod dla dowolnej wybranej odpowiedzi, ale nie potrafi wybrać odpowiedzi za ciebie. Poprawność pochodzi z podejmowania tych decyzji, a potem wymuszania, by kod ich przestrzegał.
AI może wygenerować dużo kodu szybko, ale ostatnia mila to wciąż praca ludzka: udowodnienie, że robi to, co miałeś na myśli, i bezpieczne zachowanie w przypadku awarii.
Zanim zaczniesz odznaczać pola, wybierz najmniejszą definicję „skończonego”, która ma znaczenie. Dla małej funkcji to może być jedna ścieżka szczęśliwa, dwie ścieżki błędów i szybkie sprawdzenie czytelności. Dla płatności lub auth podnieś poprzeczkę.
Powiedzmy, że AI dodaje „masowe zapraszanie użytkowników” do ekranu admina. Happy path działa, ale prawdziwe ryzyko to przypadki brzegowe: zduplikowane e-maile, częściowe niepowodzenia i limity. Solidna decyzja o wysyłce to jeden automatyczny test na duplikaty, jedno ręczne sprawdzenie komunikatu przy częściowych błędach i plan rollbacku.
Gdy kod jest tani, ryzyko przesuwa się na jakość decyzji: o co prosiłeś, co zaakceptowałeś i co wysłałeś. Najszybszy sposób, by te prawdy zadziałały w pracy wspomaganej przez AI, to dodanie szyn ochronnych, które zapobiegają przeciekaniu „prawie dobrych” zmian.
Zacznij od jednostronicowego specu dla następnej funkcji. Trzymaj to prosto: dla kogo, co ma robić, czego nie ma robić i kilka testów akceptacyjnych zapisanych zwykłym językiem. Te testy akceptacyjne stają się twoim kotwiczaniem, gdy AI zaproponuje kuszący skrót.
Zestaw szyn ochronnych, który skaluje się bez dużego narzutu procesowego:
Prompty są teraz częścią twojego procesu. Uzgodnij styl: jakie biblioteki są dozwolone, jak obsługujemy błędy, co oznacza „done” i jakie testy muszą przejść. Jeśli prompt nie da się ponownie użyć przez innego współpracownika, jest zbyt niejasny.
Jeśli wolisz chat-first sposób budowania web, backend i aplikacji mobilnych, Koder.ai jest jednym przykładem platformy vibe-coding, gdzie tryb planowania, migawki i eksport źródeł mogą wspierać te szyny. Narzędzie może przyspieszać szkice, ale to dyscyplina sprawia, że ludzie kontrolują poprawność.
Traktuj wyjście AI jak szybki szkic, a nie skończoną funkcję. Najpierw zapisz 3–5 kryteriów akceptacji w formie zaliczone/niezaliczone, potem wygeneruj jedną małą część (jeden endpoint, jeden ekran, jedną migrację) i zweryfikuj ją testami oraz próbami scenariuszy błędów, zanim przejdziesz dalej.
Bo to testy odkrywają, co kod naprawdę robi. AI może wygenerować przekonującą logikę, która jednak pomija jedno kluczowe założenie (kontrole uprawnień, retry’e, stany brzegowe). Testy zamieniają oczekiwania w coś, co można uruchomić, powtarzać i ufać temu.
Zacznij od tego, co spowodowałoby największe szkody, gdyby się zepsuło:
Dodawaj dalsze pokrycie po zabezpieczeniu zachowań o wysokiej szkodliwości.
Poproś o najprostsze rozwiązanie z jasnymi ograniczeniami, a potem usuń dodatkowe warstwy, jeśli nie „płacą czynszem”. Dobra zasada: nie wprowadzaj nowej abstrakcji, chyba że usuwa duplikację w 3+ miejscach lub ułatwia dowodzenie poprawności.
Napisz krótki spec: wejścia, wyjścia, błędy, ograniczenia i non-goals. Dołącz konkretne przykłady (przykładowe żądania/odpowiedzi, przypadki brzegowe). Zdefiniuj „done” z góry: wymagane testy, kompatybilność wstecz i szybka instrukcja „jak zweryfikować”.
Rozbij zmiany. Każdy zestaw zmian powinien dać się przejrzeć w kilka minut:
To sprawia, że przegląd jest rzeczywisty, a nie odklepywanie.
Nie ufaj pewności — ufaj dowodom. Uruchamiaj testy, próbuj złych danych i weryfikuj granice uprawnień. Zwróć też uwagę na typowe pułapki AI: brak sprawdzeń auth, niebezpieczne budowanie zapytań, słabe przechowywanie tokenów i ciche przełapywanie błędów.
Wolę jawne endpointy przejść niż uniwersalne „update anything”. Na przykład: submit, approve, reject, pay zamiast ogólnej trasy aktualizacji. Następnie pisz testy, które wymuszają, kto może wykonać każdą przejście i które przejścia są zabronione.
Daj kandydatowi AI-wygenerowany diff z realistycznymi problemami: mylne nazwy, brak testu, przypadek brzegowy i mały błąd bezpieczeństwa. Poproś, żeby wyjaśnił, co kod robi, znalazł największe ryzyko, zaproponował poprawki i nakreślił testy, które doda. Pokazuje to, jak myślą przy niedoskonałym kodzie i ograniczonym czasie.
Używaj funkcji narzędzia do wspierania dyscypliny: planuj najpierw, generuj małymi kawałkami, rób migawki przed ryzykownymi zmianami i wycofuj, jeśli weryfikacja zawiedzie. W narzędziach czatowych takich jak Koder.ai to dobrze pasuje do trybu planowania, migawek i rollbacków — szczególnie przy zmianach dotykających auth, płatności czy migracji.