Na rozmowach z Reacta najczęściej pada kilkanaście pytań: czym jest komponent, JSX, różnica między props a state, działanieuseStateiuseEffect, po co jestkeyw listach oraz kiedy komponent się renderuje. Rekruter nie sprawdza definicji z pamięci, tylko to, czy rozumiesz, dlaczego komponent zachowuje się tak, a nie inaczej. Dobra odpowiedź to jedno zdanie definicji i krótki przykład.
Rozmowa techniczna z Reacta rzadko jest egzaminem z całej dokumentacji. Najczęściej to te same kilkanaście tematów, w których rekruter sprawdza, czy rozumiesz, jak React decyduje o odświeżeniu widoku, a nie tylko czy potrafisz skopiować komponent z tutorialu. Poniżej znajdziesz pytania, które wracają na rozmowach na stanowisko juniora, razem z tym, jak na nie odpowiadać. Jeśli dopiero zaczynasz, najpierw przeczytaj, czym jest React i dlaczego warto zacząć od niego we frontendzie, a ten tekst potraktuj jako listę kontrolną. Pytania czysto językowe (closures, this, Promise) są osobno w artykule o pytaniach rekrutacyjnych z JavaScript, bo na rozmowie z Reacta prawie zawsze padają obok tych z Reacta.
Przykłady z tego artykułu uruchomiliśmy w React 19.3.1, dlatego wyniki podane w komentarzach to faktyczne wyniki, a nie zgadywanie.
Jak odpowiadać na pytania rekrutacyjne z Reacta?
Najlepiej w trzech krokach: jedno zdanie definicji, krótki przykład (najlepiej kilka linijek kodu lub opis z życia komponentu) i informacja, kiedy to ma znaczenie w praktyce. Taka odpowiedź zajmuje 30–60 sekund i pokazuje, że rozumiesz mechanizm, a nie wkułeś formułkę. Jeśli czegoś nie wiesz, powiedz, co wiesz, i rozumuj na głos. Rekruterzy oceniają sposób myślenia, a nie tylko wynik.
Pytania są ułożone od podstaw do tematów, które częściej pojawiają się przy ofertach na mid-poziom.
Podstawy Reacta
Czym jest React i czym różni się od frameworka?
React to biblioteka JavaScript do budowania interfejsów z komponentów. Odpowiada za warstwę widoku: opisujesz, jak interfejs ma wyglądać dla danego stanu, a React zajmuje się jego aktualizacją. Nie narzuca routingu, pobierania danych ani struktury projektu, dlatego do realnej aplikacji dokłada się zwykle router, bibliotekę do danych i narzędzie budujące (np. Vite). Frameworki w rodzaju Next.js biorą te decyzje na siebie, co opisuje artykuł o Next.js, App Routerze i pierwszym projekcie.
Co to jest komponent i czym różni się funkcyjny od klasowego?
Komponent to funkcja, która przyjmuje dane (props) i zwraca opis fragmentu interfejsu. Nazwa komponentu zaczyna się wielką literą, bo inaczej JSX potraktuje ją jak zwykły znacznik HTML. Komponenty klasowe (class Foo extends Component) są starszym zapisem z metodami cyklu życia, takimi jak componentDidMount. Dziś nowy kod pisze się jako funkcje z hookami, a klasy poznasz głównie w starszych projektach. Przykład komponentu klasowego i jego użycia znajdziesz w tekście o dynamicznym renderowaniu komponentów w React.
Co to jest JSX?
JSX to składnia, która pozwala pisać znaczniki podobne do HTML wewnątrz kodu JavaScript. To nie jest HTML ani szablon: narzędzie budujące zamienia JSX na zwykłe wywołania funkcji, które tworzą opis interfejsu. Dlatego w JSX piszesz className zamiast class, a wartości JavaScript wstawiasz w klamrach {}.
function Greeting({ name }) {
return <h1 className="title">Cześć, {name}!</h1>;
}
Zwykłe pytanie uzupełniające: czemu komponent nie może zwrócić dwóch sąsiednich elementów bez opakowania? Bo funkcja zwraca jedną wartość. Rozwiązaniem jest element nadrzędny albo fragment <>...</>, który nie dodaje znacznika do DOM.
Czym różnią się props od state?
Props to dane przekazane do komponentu przez rodzica, których komponent nie powinien zmieniać (są tylko do odczytu). State to dane należące do komponentu, które mogą się zmieniać w czasie, a ich zmiana powoduje ponowne wyrenderowanie. Najprościej: props wchodzą z zewnątrz, state żyje w środku. Gdy dziecko ma coś zmienić u rodzica, rodzic przekazuje mu funkcję w propsach.
| props | state | |
|---|---|---|
| Kto jest właścicielem | rodzic | sam komponent |
| Czy komponent może go zmienić | nie | tak, przez funkcję ustawiającą |
| Czy zmiana odświeża widok | tak, gdy rodzic przekaże nową wartość | tak, zawsze |
Co to jest virtual DOM i po co React go używa?
Virtual DOM to opis interfejsu w postaci zwykłych obiektów JavaScript. Przy zmianie stanu React buduje nowy opis, porównuje go z poprzednim (ten proces nazywa się uzgadnianiem, reconciliation) i zmienia w prawdziwym DOM tylko to, co się różni. Wniosek, który rekruter chce usłyszeć: nie chodzi o to, że virtual DOM jest „szybszy od DOM”, tylko że pozwala pisać kod deklaratywnie (opisujesz wynik), a React minimalizuje ręczne operacje na drzewie.
Hooki
Jak działa useState i dlaczego stan nie zmienia się od razu?
useState zwraca bieżącą wartość i funkcję, która ją zmienia. Wywołanie tej funkcji nie zmienia zmiennej w trwającym renderze, tylko zleca nowy render z nową wartością. Dlatego zaraz po setValue(5) odczyt nadal pokazuje starą wartość. W teście tego zachowania zmienna odczytana w obsłudze zdarzenia zaraz po setV(5) miała nadal wartość 0, a po renderze na ekranie było 5.
Z tego wynika klasyczne pytanie: co się stanie, gdy trzy razy w jednej obsłudze zdarzenia wywołasz setCount(count + 1)? Każde wywołanie widzi ten sam, stary count, więc licznik wzrośnie o 1, nie o 3. Rozwiązaniem jest zapis funkcyjny, w którym React podaje najnowszą wartość:
function Counter() {
const [count, setCount] = useState(0);
const addThree = () => {
// setCount(count + 1) wywołane 3 razy daje wynik 1
setCount((current) => current + 1);
setCount((current) => current + 1);
setCount((current) => current + 1); // wynik: 3
};
return <button onClick={addThree}>{count}</button>;
}
W naszym uruchomieniu wersja z count + 1 dała 1, a z funkcją aktualizującą 3.
Dlaczego nie wolno mutować stanu?
React uznaje, że stan się zmienił, gdy nowa wartość jest innym obiektem (porównanie referencji). Jeśli dodasz element przez items.push('b') i wywołasz setItems(items), referencja się nie zmienia, więc React nie odświeży widoku. W teście lista po kliknięciu nadal pokazywała a, a po zapisie setItems([...items, 'b']) pokazała a,b. Zasada: tablice i obiekty w stanie zastępujesz nowymi (spread, map, filter), nie modyfikujesz. Powtórkę tych metod znajdziesz w artykule o metodach tablicowych w JavaScript.
Do czego służy useEffect i jak działa tablica zależności?
useEffect uruchamia kod po wyrenderowaniu komponentu i służy do efektów ubocznych: pobierania danych, subskrypcji, timerów, ręcznych zmian w DOM. Drugi argument, tablica zależności, mówi Reactowi, kiedy efekt ma się uruchomić ponownie:
| Zapis | Kiedy efekt się uruchamia |
|---|---|
useEffect(fn) | po każdym renderze |
useEffect(fn, []) | tylko po pierwszym renderze (zamontowaniu) |
useEffect(fn, [id]) | po pierwszym renderze i po każdej zmianie id |
Funkcja zwrócona z efektu to cleanup: React woła ją przed kolejnym uruchomieniem efektu i przy usunięciu komponentu. Tu sprząta się timery, subskrypcje i nasłuchiwanie zdarzeń.
Dlaczego efekt uruchamia się dwa razy w trybie deweloperskim?
Bo komponent jest owinięty w <StrictMode>, który w trybie deweloperskim celowo montuje komponent, sprząta i montuje ponownie, żeby wykryć efekty bez poprawnego cleanupu. W naszym teście kolejność to mount > cleanup > mount, a bez Strict Mode samo mount. W produkcji tego podwójnego uruchomienia nie ma. Odpowiedź „wyłączę Strict Mode” jest słaba. Dobra: poprawię efekt tak, żeby cleanup odwracał to, co zrobił efekt.
Co to jest wyścig (race condition) w pobieraniu danych w useEffect?
Gdy id zmieni się, zanim przyjdzie odpowiedź na poprzednie zapytanie, starsza odpowiedź może dotrzeć później niż nowsza i nadpisać ją w stanie. W teście zmieniliśmy id z 1 na 2, najpierw rozwiązało się zapytanie 2, potem 1, i bez zabezpieczenia na ekranie został user-1, czyli dane starego id. Zabezpieczenie to flaga w cleanupie:
useEffect(() => {
let ignore = false;
fetchUser(id).then((user) => {
if (!ignore) setUser(user); // ignorujemy spóźnioną odpowiedź
});
return () => {
ignore = true;
};
}, [id]);
Z flagą na ekranie zostało user-2. W większych projektach takie rzeczy zwykle zlecasz bibliotece do danych albo frameworkowi, ale pytanie o wyścig sprawdza, czy rozumiesz mechanizm. Podobna pułapka z sieci: fetch nie odrzuca Promise przy statusie 404 czy 500, więc trzeba sprawdzać response.ok.
Dlaczego efekt uruchamia się przy każdym renderze, mimo tablicy zależności?
Najczęściej dlatego, że w tablicy jest obiekt, tablica lub funkcja tworzona w ciele komponentu. Każdy render tworzy nową referencję, więc React widzi „zmianę”. W teście efekt z zależnością { a: 1 } stworzoną w komponencie uruchomił się 3 razy po dwóch dodatkowych renderach, a ten sam obiekt zdefiniowany poza komponentem tylko raz. Rozwiązania: przenieść stałą poza komponent, zależeć od wartości prostych (options.a) albo zapamiętać obiekt przez useMemo. To także powód, dla którego reguła exhaustive-deps z ESLint nie jest „upierdliwa”, tylko wskazuje realne błędy.
Do czego służy useRef i czym różni się od useState?
useRef zwraca obiekt { current }, który przetrwa kolejne rendery, a zmiana current nie powoduje renderowania. W teście trzy kliknięcia zwiększające ref.current nie wywołały żadnego dodatkowego renderu, a przycisk dalej pokazywał 0. Dwa typowe zastosowania: dostęp do elementu DOM (np. focus() na polu) i przechowywanie wartości, która nie wpływa na wygląd (id timera, poprzednia wartość). Jeśli wartość ma zmieniać to, co widzi użytkownik, potrzebny jest state.
Jakie są reguły hooków?
Hooki wywołuje się tylko na najwyższym poziomie komponentu lub własnego hooka (nie w pętli, warunku ani zagnieżdżonej funkcji) i tylko w komponentach funkcyjnych lub innych hookach. React identyfikuje hooki po kolejności wywołań, więc ta kolejność musi być taka sama w każdym renderze. Własny hook (custom hook) to funkcja o nazwie zaczynającej się od use, która łączy inne hooki, np. useLocalStorage albo useDebounce. Pozwala wyciągnąć logikę z komponentu bez zmiany jego wyglądu.
Listy, formularze i dane między komponentami
Po co jest key w listach i czemu nie używać indeksu?
key pozwala Reactowi rozpoznać, który element listy jest którym między renderami. Powinien to być stabilny, unikalny identyfikator (np. id z bazy), a nie indeks, jeśli lista może się zmieniać (usuwanie, sortowanie, wstawianie). Przy kluczu-indeksie React przypisuje stan do pozycji, nie do danych.
W naszym teście lista dwóch osób, Ali i Oli, miała pod każdą pozycją pole tekstowe. Wpisaliśmy tekst przy Ali i usunęliśmy ją z listy. Z kluczem index na ekranie została Ola, ale z polem zawierającym tekst wpisany dla Ali. Z kluczem id pole było puste, jak powinno.
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
Czym różni się komponent kontrolowany od niekontrolowanego?
W komponencie kontrolowanym wartość pola jest w stanie Reacta (value i onChange), więc React jest jedynym źródłem prawdy i możesz na bieżąco walidować dane. W niekontrolowanym wartość trzyma sam DOM, a odczytujesz ją przez ref albo przy wysłaniu formularza. Kontrolowane są wygodne w typowych formularzach, niekontrolowane wystarczą tam, gdzie wartość jest potrzebna tylko raz. Walidację formularzy po stronie JavaScript opisuje artykuł o tworzeniu i walidacji formularza.
Co to jest „lifting state up” i prop drilling?
Gdy dwa komponenty potrzebują tych samych danych, przenosisz stan do ich najbliższego wspólnego rodzica (lifting state up) i przekazujesz go w dół propsami. Gdy dane trzeba przepychać przez wiele warstw komponentów, które same ich nie używają, mówimy o prop drilling. Jest to drobny problem, dopóki warstw jest 2–3; przy większej liczbie sięga się po Context albo bibliotekę do stanu.
Kiedy użyć Context, a kiedy Reduxa?
Context służy do udostępniania wartości całemu poddrzewu bez przekazywania propsów (motyw, zalogowany użytkownik, język). Ma jedną cechę, o którą rekruterzy dopytują: gdy wartość w Context się zmienia, renderują się wszystkie komponenty, które z niego korzystają. Redux (i podobne biblioteki) sprawdza się przy dużym, współdzielonym stanie ze skomplikowanymi zmianami, bo daje przewidywalny przepływ akcji i narzędzia do debugowania. Dobra odpowiedź nie wybiera „zawsze Redux”, tylko zaczyna od lokalnego stanu i podnosi go dopiero, gdy potrzeba.
Renderowanie i wydajność
Kiedy komponent się renderuje?
Komponent renderuje się, gdy zmieni się jego stan, gdy rodzic przekaże nowe propsy albo gdy zmieni się wartość w używanym Context. Ważny szczegół: gdy renderuje się rodzic, domyślnie renderują się też wszystkie jego dzieci, nawet jeśli ich propsy się nie zmieniły. Render to jeszcze nie zmiana w DOM: React najpierw wywołuje funkcję komponentu, a do DOM trafia tylko różnica.
Do czego służą React.memo, useMemo i useCallback?
React.memo pomija ponowne renderowanie komponentu, gdy jego propsy są takie same jak poprzednio (porównanie płytkie). useMemo zapamiętuje wynik kosztownego obliczenia, a useCallback zapamiętuje funkcję, żeby przy kolejnych renderach miała tę samą referencję. Najczęściej łączy się je razem, bo funkcja przekazana jako prop zmienia referencję w każdym renderze i unieważnia memo.
W teście komponent opakowany w memo, który dostawał funkcję zdefiniowaną w rodzicu, wyrenderował się 3 razy po dwóch kliknięciach w rodzica. Ta sama funkcja owinięta w useCallback dała 1 render.
const Child = memo(function Child({ onSave }) {
return <button onClick={onSave}>Zapisz</button>;
});
function Parent() {
const [count, setCount] = useState(0);
const onSave = useCallback(() => saveForm(), []); // ta sama referencja
return (
<>
<button onClick={() => setCount(count + 1)}>{count}</button>
<Child onSave={onSave} />
</>
);
}
Pytanie uzupełniające: czy owijać wszystko w memo? Nie. Memoizacja kosztuje pamięć i porównania, więc stosuje się ją po zmierzeniu problemu (np. w React DevTools Profiler), a nie z góry. W nowszych projektach część tej pracy może przejąć kompilator React (React Compiler), ale pytanie o mechanizm nadal się pojawia.
Co zrobić, gdy lista jest bardzo długa i interfejs zwalnia?
Najpierw zmierz, co zwalnia (Profiler). Potem: stabilne klucze, wydzielenie wierszy do osobnych komponentów z memo, paginacja lub wirtualizacja listy (renderowanie tylko widocznych wierszy), przeniesienie ciężkiego obliczenia do useMemo i zbijanie zbędnych aktualizacji stanu (np. debounce przy wyszukiwarce). Kolejność jest ważna: rekruter chce usłyszeć „mierzę, potem optymalizuję”, a nie listę technik na ślepo.
Co powiesz, zanim zaczniesz optymalizować?
„Najpierw zmierzę.” To zdanie jest ważniejsze niż lista technik. Otwieram React DevTools Profiler, sprawdzam, które komponenty renderują się najczęściej i co je wywołuje, i dopiero wtedy decyduję, czy pomaga memo, podział komponentu, czy przeniesienie stanu niżej. Kandydat, który zaczyna od owijania wszystkiego w useMemo, pokazuje, że zna narzędzia, ale nie wie, kiedy ich użyć.
Ekosystem i praktyka
Jak testuje się komponenty Reacta?
Najczęściej przez React Testing Library razem z Jest lub Vitest: renderujesz komponent, szukasz elementów tak, jak zrobiłby to użytkownik (po roli, etykiecie, tekście), wywołujesz kliknięcia przez userEvent i sprawdzasz, co widać na ekranie. Dobra odpowiedź zawiera zasadę „testuj zachowanie, nie implementację”. Szczegóły i przykłady znajdziesz w artykule o testowaniu komponentów w React Testing Library.
Po co TypeScript w projekcie z Reactem?
TypeScript pozwala opisać typy propsów i stanu, więc edytor i kompilator wychwytują literówki w nazwach propsów, brakujące pola czy złe typy zanim kod trafi do przeglądarki. W zespołach pracujących w Reaccie jest dziś powszechny, więc rekruterzy często pytają o podstawy: typowanie propsów, useState<string | null> i zdarzeń. Wprowadzenie znajdziesz w tekście o TypeScript jako kolejnym kroku po JavaScript.
Jak działa routing w Reaccie?
React sam w sobie nie ma routera, więc używa się biblioteki (najczęściej React Router) albo frameworka z routingiem opartym na plikach, jak Next.js. Router dopasowuje adres URL do komponentu i pozwala przechodzić między widokami bez przeładowania strony (aplikacja typu SPA). Warto umieć opisać parametry w adresie (/users/:id) i ochronę widoków dla niezalogowanych.
Jakie zadania z kodem pojawiają się na rozmowach z Reacta?
Najczęściej: lista z wyszukiwarką i filtrowaniem, licznik lub lista zadań (todo), formularz z walidacją, pobranie danych z API i wyświetlenie ich ze stanem ładowania i błędu, prosty komponent „akordeon” lub zakładki oraz własny hook, np. useDebounce albo useFetch. Rekruter patrzy na sposób rozwiązywania: czy dzielisz interfejs na sensowne komponenty, gdzie trzymasz stan, czy pamiętasz o key i stanie błędu. Umiejętności wymagane od juniora frontendowego zbiera artykuł o tym, co musi umieć junior frontend developer.
Co pokaże ekran? Mini-test na rozgrzewkę
Rekruterzy lubią pytanie „co zobaczy użytkownik po kliknięciu?”. Spróbuj odpowiedzieć na trzy poniższe, zanim zajrzysz do odpowiedzi. Wszystkie uruchomiliśmy w React 19.3.1.
Zadanie 1. Co pokaże przycisk po jednym kliknięciu?
function Counter() {
const [count, setCount] = useState(0);
const addThree = () => {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
};
return <button onClick={addThree}>{count}</button>;
}
Odpowiedź: 1. Każde wywołanie widzi ten sam count z bieżącego renderu. Aby dostać 3, trzeba użyć zapisu funkcyjnego setCount((current) => current + 1).
Zadanie 2. Co pokaże lista po kliknięciu „Dodaj”?
function List() {
const [items, setItems] = useState(['a']);
const add = () => {
items.push('b');
setItems(items);
};
return (
<>
<button onClick={add}>Dodaj</button>
<span>{items.join(',')}</span>
</>
);
}
Odpowiedź: nadal a. Tablica została zmieniona, ale referencja jest ta sama, więc React nie widzi zmiany i nie renderuje komponentu. Poprawnie: setItems([...items, 'b']).
Zadanie 3. Ile razy w konsoli pojawi się mount w trybie deweloperskim z <StrictMode>?
function Panel() {
useEffect(() => {
console.log('mount');
return () => console.log('cleanup');
}, []);
return <p>Panel</p>;
}
Odpowiedź: dwa razy, w kolejności mount, cleanup, mount. To celowe sprawdzenie, czy cleanup poprawnie odwraca efekt. W produkcji efekt uruchomi się raz.
Jak przygotować się do rozmowy z Reacta?
Samo czytanie odpowiedzi nie wystarczy, bo pytania uzupełniające szybko pokazują, czy rozumiesz temat. Skuteczniej działa napisanie małej aplikacji bez kopiowania gotowca i głośne tłumaczenie decyzji komuś innemu, na przykład mentorowi. Jeśli nie masz pewności, czy jesteś na to gotowy, sprawdź test gotowości na Reacta. Dla porządku:
| Obszar | Co umieć wyjaśnić własnymi słowami |
|---|---|
| Podstawy | komponent, JSX, props vs state, virtual DOM |
| Hooki | useState (zapis funkcyjny), useEffect (zależności, cleanup), useRef, reguły hooków |
| Listy i formularze | key, komponenty kontrolowane, podnoszenie stanu |
| Wydajność | kiedy komponent się renderuje, memo, useMemo, useCallback |
| Praktyka | pobieranie danych, wyścigi, testowanie, TypeScript, routing |
Na czym juniorzy najczęściej tracą punkty?
Nie na trudnych tematach, tylko na trzech drobiazgach, które wychodzą dopiero przy pytaniach uzupełniających:
- Mutowanie stanu.
items.push(x); setItems(items)nie odświeży widoku, bo referencja jest ta sama. Nie wystarczy napisać „trzeba skopiować”. Wyjaśnij, że React porównuje referencje, dlatego potrzebuje nowej tablicy. key={index}w listach, które się zmieniają. Działa „na oko”, a psuje się dopiero przy usuwaniu lub sortowaniu, bo stan (np. wpisany tekst) zostaje przy pozycji, nie przy danych.useEffectbez cleanupu i z błędnymi zależnościami. Pobranie danych bez obsługi wyścigu, timer bezclearIntervalalbo obiekt w tablicy zależności, który uruchamia efekt w każdym renderze.
Jak brzmi dobra odpowiedź, a jak słaba?
Weźmy pytanie o useEffect:
| Odpowiedź | Dlaczego | |
|---|---|---|
| Słaba | „useEffect odpala się po załadowaniu strony.” | To opis jednego przypadku ([]), nie mechanizmu, a pytanie o tablicę zależności od razu to pokaże. |
| Dobra | „useEffect uruchamia się po renderze i służy do efektów ubocznych, np. pobrania danych. Tablica zależności mówi, kiedy ma się uruchomić ponownie, a funkcja zwrócona z efektu sprząta po poprzednim uruchomieniu.” | Jest definicja, mechanizm i zastosowanie. |
Ten sam schemat działa przy każdym pytaniu z tego artykułu: definicja, mechanizm, przykład użycia.
Co zrobić, gdy nie znasz odpowiedzi?
Nie zgaduj i nie milcz. Powiedz, co wiesz na pewno, a potem rozumuj na głos, np.: „Nie pamiętam dokładnej nazwy, ale wiem, że to dotyczy tego, kiedy React renderuje komponent ponownie, więc sprawdziłbym to w dokumentacji react.dev.” Taka odpowiedź pokazuje, że potrafisz sobie poradzić w pracy, a umiejętność znalezienia odpowiedzi jest w codziennej pracy ważniejsza niż pamięć do szczegółów.
Dobrze też wiedzieć, jak wygląda sama rozmowa. W podcaście Radek Wojtysiak opowiada o rekrutacji technicznej: jak się do niej przygotować i co naprawdę jest sprawdzane.
Znajomość odpowiedzi to połowa sukcesu, drugą jest znalezienie rozmów, na które warto się umówić. Jeśli szukasz ofert, sięgnij po darmowy poradnik o tym, jak szukać pracy w 2026 roku, w którym zebraliśmy 3 sposoby poszukiwania pracy, które u naszych podopiecznych dają najlepsze efekty. Całość procesu, od CV po rozmowę, opisuje artykuł o tym, jak przejść cały proces rekrutacyjny na juniora, a o zadaniach i portfolio piszemy w poradniku jak przygotować się do rekrutacji na juniora developera.
Jeśli braki wyjdą przy hookach albo renderowaniu, to sygnał, że warto uporządkować wiedzę z mentorem. W kursie Frontend z Reactem jest 10 modułów o Reaccie, a każdy kończy się zadaniami i projektem, do których mentor robi code review.


