10 punktów code review kodu AI: testy i bezpieczeństwo
Wyobraź sobie: AI przygotowało funkcję, dopisało testy i wyjaśniło, że rozwiązanie jest gotowe. Build przechodzi, nazwy są czytelne, a pull request wygląda przekonująco. Dopiero sprawdzenie dostępu do cudzej faktury pokazuje, że aplikacja pozwala odczytać dane innego użytkownika.
Jak sprawdzać kod napisany przez AI? Zacznij od wymagań i scenariuszy błędów. Przejrzyj zmiany, uruchom odpowiednie kontrole projektu, zweryfikuj bezpieczeństwo i dodaj testy przypadków, których rozwiązanie mogło nie uwzględnić. Poniżej znajdziesz checklistę 10 punktów, dwa przykłady w JavaScript i pytania do przećwiczenia przed rozmową techniczną.
Temat wrócił we wrześniu 2026: 11 września GitHub rozbudował Copilot code review o szerszą analizę z użyciem narzędzi powłoki oraz automatyczne zamykanie uwag po poprawkach. To aktualny przykład rozwoju narzędzi do przeglądu kodu. Opisana dalej checklista jest autorską propozycją procesu weryfikacji, którą możesz zastosować niezależnie od używanego asystenta.
Ocena kodu AI — pytania
Jak ustalić, czy rozwiązanie spełnia wymagania?
Przed czytaniem implementacji zapisz zachowanie, którego oczekujesz. Uwzględnij użytkownika, dane wejściowe, wynik oraz sytuację, w której operacja powinna się nie udać. Dzięki temu oceniasz rozwiązanie według kontraktu, a nie według przekonującego komentarza wygenerowanego razem z kodem.
Dla endpointu faktur kontrakt może brzmieć: „Zalogowany użytkownik odczytuje wyłącznie własne faktury. Próba dostępu do cudzej faktury kończy się odmową i nie zwraca jej treści”. Z tego zdania od razu wynikają co najmniej dwa scenariusze testowe. Jeśli projekt dopuszcza dostęp administratora, trzeba osobno określić tę rolę i warunki jej użycia.
Dlaczego czytelny kod może zawierać poważny błąd?
Formatowanie, typy i dobre nazwy pomagają zrozumieć implementację, ale nie dowodzą jej poprawności. Funkcja może poprawnie korzystać z API, a jednocześnie używać niewłaściwego identyfikatora lub pomijać regułę biznesową. Szczególnie łatwo przeoczyć warunek, którego w kodzie w ogóle nie ma.
W czasie review śledź przepływ danych: skąd pochodzi wartość, kto może ją zmienić i do jakiej operacji trafia. Porównaj zachowanie z wymaganiem. Komentarz „sprawdź uprawnienia” nie zastępuje sprawdzenia wykonywanego po stronie serwera.
Co sprawdzić w API i zależnościach zaproponowanych przez AI?
Zweryfikuj, czy używana funkcja istnieje w wersji biblioteki z projektu i czy jej kontrakt odpowiada założeniom rozwiązania. Sprawdź dokumentację, plik blokady zależności oraz podobne wywołania w repozytorium. Przykład pasujący do innego wydania może wyglądać wiarygodnie, lecz nie działać w Twojej aplikacji.
Nowa zależność wymaga uzasadnienia: jaki problem rozwiązuje i czy projekt ma już odpowiednie narzędzie? Przejrzyj także zmiany pliku blokady oraz skrypty instalacyjne. Jeśli rozwiązanie zmienia jedną funkcję, a aktualizuje kilkadziesiąt pakietów, wyjaśnij ten zakres przed akceptacją.
Bezpieczeństwo i dane — pytania
Jak odróżnić uwierzytelnienie od autoryzacji?
Uwierzytelnienie ustala tożsamość użytkownika. Autoryzacja sprawdza, czy ta osoba może wykonać konkretną operację na konkretnym zasobie. Sam fakt zalogowania nie daje dostępu do każdej faktury ani do danych wszystkich klientów.
OWASP zaleca domyślną odmowę dostępu i sprawdzanie uprawnień przy każdym żądaniu. W review szukaj miejsca, w którym serwer porównuje użytkownika z właścicielem zasobu lub ocenia właściwą politykę dostępu. Identyfikator właściciela przekazany przez przeglądarkę nie jest wiarygodnym dowodem uprawnienia.
Jak wykryć błąd dostępu do cudzej faktury?
Ustalmy prostą politykę: fakturę może odczytać tylko jej właściciel. Obiekt użytkownika pochodzi z zaufanej sesji serwera, a faktura z bazy danych. Poniższy kod sprawdza istnienie obu obiektów, lecz pomija relację między nimi.
// Błędna implementacja: zalogowany użytkownik
// uzyskuje dostęp do dowolnej istniejącej faktury.
function canReadInvoice(user, invoice) {
return Boolean(user && invoice);
}
Minimalna poprawka musi sprawdzać właściciela. W tym przykładzie kontrakt danych wymaga niepustych identyfikatorów tekstowych; nie dopuszczamy sytuacji, w której dwa brakujące identyfikatory zostaną uznane za równe.
function canReadInvoice(user, invoice) {
return Boolean(
user &&
invoice &&
typeof user.id === "string" &&
user.id.length > 0 &&
typeof invoice.ownerId === "string" &&
user.id === invoice.ownerId
);
}
To przykład samej reguły dostępu, a nie kompletnego endpointu. Kontrolę trzeba wykonać przed zwróceniem danych lub skutkiem ubocznym. W systemie wielu organizacji uwzględnij również granicę organizacji, jeśli wynika z modelu uprawnień, i zweryfikuj rzeczywistą obsługę odmowy testem integracyjnym.
Co sprawdzić w logach i danych przekazywanych do narzędzi AI?
Przejrzyj nowe logi, komunikaty błędów i dane użyte w promptach. Pełny obiekt żądania może zawierać token sesji, nagłówek autoryzacji lub informacje klienta. Log, który pomagał podczas debugowania, może pozostać w rozwiązaniu i trafić do produkcji.
Stosuj zasady projektu dotyczące dostępu i przetwarzania danych. Do odtworzenia błędu przygotuj możliwie mały przykład z danymi syntetycznymi. Sprawdź także, czy wynik błędu nie ujawnia sekretów albo szczegółów infrastruktury użytkownikowi aplikacji.
Testy i JavaScript — pytania
Czy zielone testy wystarczą do zaakceptowania kodu AI?
Nie. Testy mogą sprawdzać tylko poprawny scenariusz albo powielać założenia błędnej implementacji. Potrzebne są również przegląd wymagań, przypadki negatywne i sprawdzenie integracji. Test „zalogowana osoba widzi fakturę” przejdzie dla obu pokazanych wersji funkcji.
Dobierz przypadki na podstawie kontraktu. Dla faktur będą to: dostęp właściciela, odmowa dla innej osoby, brak użytkownika, brak zasobu i niekompletne identyfikatory. Sam wysoki procent pokrycia nie pokaże, czy testujesz właściwą regułę.
Jak napisać test regresji dla błędnej autoryzacji?
Test regresji powinien odtwarzać konkretną sytuację, w której rozwiązanie łamie wymaganie. Najpierw sprawdź, czy wykrywa wadliwą wersję, a dopiero potem uruchom go z poprawką. W przeciwnym razie trudno ocenić, czy chroni przed powrotem błędu.
Poniżej używamy wbudowanego runnera testów Node.js. Zapisz poprawioną funkcję oraz te importy i testy w pliku invoice-access.test.mjs, a następnie uruchom node --test invoice-access.test.mjs. W istniejącym projekcie zastosuj jego aktualny framework testowy.
import test from "node:test";
import assert from "node:assert/strict";
test("właściciel może odczytać fakturę", () => {
assert.equal(
canReadInvoice({ id: "anna" }, { ownerId: "anna" }),
true
);
});
test("inna osoba nie może odczytać faktury", () => {
assert.equal(
canReadInvoice({ id: "anna" }, { ownerId: "piotr" }),
false
);
});
test("brak sesji lub identyfikatorów oznacza odmowę", () => {
assert.equal(canReadInvoice(null, { ownerId: "anna" }), false);
assert.equal(canReadInvoice({ id: "anna" }, null), false);
assert.equal(canReadInvoice({}, {}), false);
assert.equal(canReadInvoice({ id: "" }, { ownerId: "" }), false);
});
Test jednostkowy nie potwierdza jeszcze poprawności całej ścieżki HTTP. Osobny test powinien sprawdzić, że endpoint rzeczywiście wywołuje kontrolę uprawnień i że odpowiedź odmowna nie zawiera danych faktury. To właśnie na styku poprawnej funkcji i jej użycia może pozostać luka.
Dlaczego async w forEach może oszukać autora review?
forEach nie czeka na obietnice zwracane przez callback. Funkcja oznaczona jako async może więc zakończyć się przed zakończeniem powiadomień, chociaż każde wywołanie callbacka zawiera await. W review śledź, na którą obietnicę czeka funkcja nadrzędna.
Załóżmy, że kontrakt wymaga wysłania powiadomień kolejno, zakończenia funkcji dopiero po ich wysłaniu i przekazania błędu do wywołującego. Ta implementacja nie spełnia kontraktu:
async function notifyUsers(users, send) {
users.forEach(async (user) => {
await send(user);
});
}
Przy wymaganej kolejności prostym rozwiązaniem jest pętla for...of. Każde wywołanie zostanie zakończone przed rozpoczęciem następnego, a błąd przerwie funkcję i odrzuci zwracaną obietnicę.
async function notifyUsers(users, send) {
for (const user of users) {
await send(user);
}
}
Jeśli kolejność nie ma znaczenia, projekt może potrzebować współbieżności. Wtedy trzeba określić limit równoległych operacji, zachowanie przy błędach i sposób ponawiania. Samo zastąpienie pętli przez Promise.all nie rozstrzyga tych wymagań i nie anuluje automatycznie operacji już rozpoczętych.
Proces przeglądu — pytania
Jak przejrzeć zmianę bez zgubienia jej kontekstu?
Zacznij od opisu zadania, zakresu zmian i wywołań zmienionych funkcji. Przeczytaj diff, ale zajrzyj także do otoczenia kodu: reguł domenowych, obsługi błędów i istniejących testów. Poprawny fragment może zostać użyty w sposób, który zmienia zachowanie całej aplikacji.
Duże zmiany podziel na części, które można osobno wyjaśnić i zweryfikować. Dla każdej części ustal, co zmienia się dla użytkownika i jakie dowody potwierdzają działanie. Usunięty test, wyłączony lint lub szersze uprawnienia wymagają tak samo uważnego przeglądu jak nowa funkcja.
Czy drugi model AI może zastąpić code review człowieka?
Drugi model może wskazać dodatkowe problemy, ale także powtórzyć błędne założenie. Jego uwagi trzeba zweryfikować w kodzie i testach, a decyzja o przyjęciu zmiany należy do zespołu. Brak uwag od dwóch modeli nie dowodzi, że rozwiązanie spełnia wymagania.
GitHub opisuje Copilot code review jako wsparcie przeglądu wykonywanego przez człowieka i wskazuje ograniczenia narzędzi agentowych. Przydatna uwaga review zawiera warunek wywołania, miejsce w kodzie, skutek i sposób odtworzenia. Ogólne stwierdzenie „warto poprawić bezpieczeństwo” nie wystarcza do oceny zmiany.
Jak sformułować prompt, który pomaga weryfikować kod?
Podaj reviewerowi wymaganie, zakres zmiany i ograniczenia projektu. Poproś o scenariusze błędów oraz dowody, które da się sprawdzić. Dzięki temu wynik review łatwiej zamienić w test lub konkretną poprawkę.
Poniższy prompt jest punktem wyjścia. Zastąp opis uprawnień kontraktem własnej aplikacji i udostępnij wyłącznie kontekst dopuszczony przez zasady zespołu.
Przejrzyj zmianę według tego wymagania:
zalogowany użytkownik odczytuje wyłącznie własne faktury.
Sprawdź kontrolę dostępu, przepływ danych i testy.
Dla każdego problemu podaj:
- plik i miejsce w kodzie,
- warunek, który uruchamia błąd,
- skutek dla użytkownika,
- przykład lub test odtwarzający problem.
Oddziel potwierdzone problemy od hipotez.
Nie zmieniaj kodu. Wymień brakujący kontekst i kontrole,
których nie udało się wykonać.
Po otrzymaniu uwag zweryfikuj je samodzielnie. Sprawdź rzeczywisty wynik uruchomionej komendy, zamiast polegać wyłącznie na zapewnieniu, że „testy przechodzą”. Jeśli model wskazał problem, który nie występuje, zapisz wyjaśnienie wynikające z kodu lub kontraktu.
Checklista 10 punktów przed akceptacją
Checklistę stosuj do konkretnej zmiany. Przy każdym punkcie możesz zapisać „sprawdzone”, „nie dotyczy — dlaczego” albo „blokuje przyjęcie”. Dzięki temu widzisz, które decyzje mają dowody, a które pozostają założeniami.
- Wymagania: czy umiesz opisać oczekiwane zachowanie i co najmniej jeden scenariusz odmowy lub błędu?
- Zakres: czy diff dotyczy zadania, a zmiany konfiguracji i pliku blokady są uzasadnione?
- API: czy używane metody istnieją w wersjach zależności projektu i mają właściwy kontrakt?
- Dane wejściowe: czy obsłużono brak danych, wartości graniczne i dane niezgodne z kontraktem?
- Uprawnienia: czy serwer sprawdza dostęp do właściwego zasobu przed zwróceniem danych lub skutkiem ubocznym?
- Sekrety i logi: czy zmiana nie ujawnia tokenów, danych klientów ani nadmiarowych szczegółów błędu?
- Asynchroniczność: czy funkcja czeka na wymagane operacje, przekazuje błędy i uwzględnia limity współbieżności?
- Testy: czy przypadki wynikają z wymagań i czy test regresji wykrywał wadliwą wersję?
- Integracja i wydajność: czy sprawdzono rzeczywiste użycie zmiany, liczbę zapytań, rozmiar danych i istotne skutki uboczne?
- Dowody i odpowiedzialność: czy odpowiednie kontrole projektu zakończyły się sukcesem, a osoba przyjmująca zmianę potrafi ją wyjaśnić?
Brak uprawnień, utrata danych lub nieznane zachowanie operacji płatniczej powinny zatrzymać przyjęcie zmiany do wyjaśnienia. Drobna uwaga o nazwie zmiennej ma inny ciężar. Oceniaj priorytet na podstawie skutku i prawdopodobnego scenariusza, a nie długości komentarza review.
Rozmowa techniczna — pytania
Jak przygotować się do rozmowy o kodzie wygenerowanym przez AI?
Przećwicz wyjaśnienie wymagań, wskazanie ryzyk i dobranie testów do konkretnego fragmentu kodu. Przygotuj przykład, w którym wykrywasz błąd, odtwarzasz go i potwierdzasz poprawkę testem regresji. Taka odpowiedź pozwala pokazać sposób myślenia na konkretnym zadaniu.
Pytania w tej sekcji są autorskimi ćwiczeniami, a nie statystyką pytań zadawanych przez firmy. Spróbuj w kilka minut omówić funkcję canReadInvoice: jakie ma założenia, gdzie musi być wywołana i czego nie potwierdzają jej testy jednostkowe?
Co odpowiedzieć na pytanie „AI napisało kod i testy — co sprawdzasz dalej?”?
Zacznij od rozdzielenia wygenerowanego rozwiązania od kryteriów jego oceny. Wyjaśnij, jak porównujesz kod z wymaganiem i jakie przypadki dobierasz niezależnie od implementacji. Następnie wskaż integrację, uprawnienia i wynik uruchomionych kontroli.
Przykładowa odpowiedź: „Czytam diff i kontrakt funkcji. Dopisuję scenariusz, którego autor testów mógł nie uwzględnić, np. dostęp innego użytkownika. Sprawdzam endpoint oraz faktyczne wyniki testów. Akceptuję zmianę, gdy rozumiem jej działanie i mam dowody dla istotnych ryzyk”.
Jak przećwiczyć wykrywanie błędu z async i forEach?
Napisz test, w którym send czeka na obietnicę kontrolowaną przez test. Przed jej rozwiązaniem funkcja notifyUsers nie powinna zgłosić zakończenia. Potem dodaj przypadek odrzucenia send i sprawdź, czy błąd dociera do wywołującego.
Nie opieraj ćwiczenia wyłącznie na opóźnieniu typu „poczekaj 100 ms”. Sterowanie obietnicą pozwala sprawdzić kolejność zdarzeń bez zgadywania czasu wykonania. Dla pokazanej wadliwej wersji wybierz najpierw przypadek bez odrzucenia: nieobsłużony błąd callbacka może przerwać runner, zamiast dać czytelny wynik asercji.
Powiązane artykuły
- Backend Testing — testy jednostkowe, integracyjne i TDD
- Frontend Testing — Jest, React Testing Library i Cypress
- Frontend Security — CSP, CORS i bezpieczeństwo aplikacji
Źródła
Stan informacji o narzędziach: 2 października 2026. Przykłady kodu i checklista zostały przygotowane na potrzeby tego artykułu.
Przygotuj się do rozmowy technicznej
Wybierz plan i zyskaj dostęp do 2300+ pytań rekrutacyjnych z 30+ technologii.