Rozmowa techniczna zaczyna się po przejściu testów
Po poprawnym rozwiązaniu poproś o jedną zmianę, testy regresji i opis dla kolejnej osoby. Sprawdź, jak kandydat pracuje z istniejącym kodem i jego kontraktem.
Ernest Bursa
Gdy rozwiązanie przejdzie testy, poproś kandydata o jedną jasno określoną zmianę. Niech sprawdzi nowe zachowanie i dotychczasowy kontrakt, a potem opisze pracę dla kolejnej osoby. Pytania zadane na tym etapie rozmowy technicznej pokazują, jak kandydat pracuje z istniejącą implementacją. Wybierz zmianę na podstawie obowiązków na danym stanowisku i oceniaj wszystkich według tych samych kryteriów, opierając się na konkretnym kodzie i testach.
Dyskusja na Hacker News o Competitive Programmer’s Handbook przypomina o rozwiązywaniu zadań algorytmicznych. Książka Anttiego Laaksonena to wersja robocza z 3 lipca 2018 roku. We wstępie autor opisuje programowanie konkursowe jako projektowanie i implementację algorytmów, których rozwiązania są sprawdzane pod kątem poprawności. Wyjaśnia też, że programy konkursowe są krótkie i nie wymagają późniejszego utrzymania.
Z tego ostatniego rozróżnienia wynika przydatne pytanie rekrutacyjne: co się stanie, gdy poprawne rozwiązanie trzeba będzie zmienić? Jeśli zatrudniasz do utrzymania produktu, warto to sprawdzić. Zachowaj ocenę rozumowania algorytmicznego tam, gdzie wymaga go stanowisko. Zbierz też materiał do oceny pracy, która zaczyna się wtedy, gdy inny programista musi polegać na tym kodzie.
Co pokazuje rozwiązanie, które przechodzi testy?
Przejście testów pokazuje, że implementacja działa poprawnie w sprawdzonych przypadkach. Daje też podstawę do rozmowy o poprawności i złożoności. To konkretne obserwacje, choć część pytań o utrzymanie kodu pozostaje bez odpowiedzi.
Nie wiesz jeszcze, jak kandydat odczytuje kontrakt, którego sam nie ustalił. Być może nie potrafisz też ocenić, czy odróżnia nowe wymaganie od przypadkowej zmiany zachowania. Gotowe rozwiązanie niewiele mówi o tym, jaką notatkę zostawiłby kolejnemu programiście.
Podręcznik jasno określa własny kontekst: w zadaniu konkursowym liczy się poprawna implementacja spełniająca ograniczenia. Zadanie rekrutacyjne powinno odpowiadać obowiązkom, które nowa osoba ma wykonywać. Można uznać oba te cele bez podważania żadnego z nich.
Jeśli programista ma pracować nad serwisem rezerwacyjnym, zachowanie znaczenia istniejącego API może zasługiwać na osobne kryterium oceny. Jeśli stanowisko dotyczy tworzenia algorytmów, ich wyprowadzanie i analiza mogą nadal stanowić główny punkt rozmowy. Określ te cele przed wyborem ćwiczenia.
W artykule korzystamy z fikcyjnego przykładu rezerwacji i proponujemy praktyczny zestaw materiałów do rozmowy. Nie jest to zweryfikowane narzędzie rekrutacyjne. Nie przedstawiamy dowodów, że to konkretne ćwiczenie przewiduje produktywność, usuwa uprzedzenia lub prowadzi do lepszych decyzji o zatrudnieniu.
Szersze omówienie form oceny znajdziesz w przewodniku po rozmowach technicznych w dobie AI. Tutaj skupiamy się na mniejszym zadaniu: istniejącej funkcji, jednej zmianie, teście i opisie dla kolejnej osoby.
Wybierz pracę nad utrzymaniem kodu, której wymaga stanowisko
Zacznij od obowiązku, który nowy programista będzie wykonywać już po dołączeniu. Zamień go w małe zadanie, którego wykonanie da się zaobserwować. Usuń z ćwiczenia konfigurację niezwiązaną z jego celem.
Wytyczne amerykańskiego Office of Personnel Management dotyczące analizy stanowisk łączą zadania zawodowe z kompetencjami potrzebnymi do ich wykonania. W pracy nad oprogramowaniem może to oznaczać zachowanie kontraktu API, rozszerzenie modelu danych lub opisanie skutków zmiany. To nasze przykłady zastosowania tych wytycznych.
Celem tego zestawu materiałów jest zmiana zachowania z zachowaniem udokumentowanego kontraktu. Sprawdź, czy kandydat potrafi wskazać rekordy objęte zmianą, wdrożyć ją i zabezpieczyć dotychczasowe zachowanie testami. Ważny jest też zrozumiały opis tego, co się zmieniło.
Do tego nie trzeba konfigurować twojego środowiska produkcyjnego ani odkrywać nieudokumentowanej reguły biznesowej. Udostępnij działającą komendę uruchamiającą testy, małe repozytorium i potrzebne zasady. Problemy z instalacją rozwiąż przed oceną zmiany w kodzie.
Wytyczne OPM dotyczące próbek pracy opisują zadania zbliżone do pracy wykonywanej na danym stanowisku. Ostrzegają też przed ocenianiem kompetencji, których zamierzasz nauczyć dopiero po zatrudnieniu. Jeśli nowa osoba ma poznać twój framework po dołączeniu, nie rób z jego znajomości niewypowiedzianego kryterium rozstrzygającego.
Kandydat powinien znać zakres zadania: które pliki są istotne, co ma oddać i jak będzie to oceniane. Powiedz też, czy wolno korzystać z dokumentacji, wyszukiwarki i pomocy AI. Te szersze decyzje omawiamy w przewodniku po projektowaniu zadań próbnych.
Daj wszystkim tę samą implementację, która przechodzi testy
Udostępnij każdemu tę samą implementację i testy. Zadanie dotyczące utrzymania kodu powinno zaczynać się od wspólnego punktu wyjścia. Dzięki temu jego ocena nie zależy od wcześniejszego rozwiązania zagadki algorytmicznej.
W naszym fikcyjnym przykładzie serwis rezerwacyjny zapisuje przedziały czasu jako wartości całkowite. Funkcja sprawdza, czy wszystkie rezerwacje mogą dotyczyć jednej sali bez konfliktów. Kod poniżej działa z biblioteką standardową Pythona. Język służy tu jako przykład, a nie wymaganie oceny.
Dotychczasowy kontrakt
Umieść te zasady w README obok funkcji:
- Każdy rekord ma wartości
startiend, przy czymstart < end. - Przedziały są półotwarte: rezerwacja kończąca się o
20nie koliduje z rezerwacją zaczynającą się o20. - Rekordy mogą przyjść w dowolnej kolejności.
- Funkcja zwraca wartość logiczną i zachowuje pierwotną kolejność listy wejściowej.
- Nieprawidłowe przedziały są poza zakresem ćwiczenia. Można założyć, że podany warunek wstępny jest spełniony.
Te zasady rozstrzygają kwestie, które bez nich kandydat musiałby odgadywać. Rezerwacje mogą następować bezpośrednio po sobie, a sortowanie nie może zmieniać listy przekazanej do funkcji.
Rozwiązanie i testy na początek
# bookings.py
def can_share_room(bookings):
ordered = sorted(bookings, key=lambda booking: booking["start"])
return all(
previous["end"] <= current["start"]
for previous, current in zip(ordered, ordered[1:])
)
# test_bookings.py
import unittest
from bookings import can_share_room
class BookingTests(unittest.TestCase):
def test_empty_and_single_booking(self):
self.assertTrue(can_share_room([]))
self.assertTrue(can_share_room([{"start": 10, "end": 20}]))
def test_adjacent_bookings_can_share(self):
self.assertTrue(can_share_room([
{"start": 10, "end": 20},
{"start": 20, "end": 30},
]))
def test_overlapping_bookings_cannot_share(self):
self.assertFalse(can_share_room([
{"start": 10, "end": 20},
{"start": 15, "end": 25},
]))
def test_unsorted_input_keeps_its_order(self):
bookings = [
{"start": 20, "end": 30},
{"start": 10, "end": 20},
]
original = [booking.copy() for booking in bookings]
self.assertTrue(can_share_room(bookings))
self.assertEqual(original, bookings)
if __name__ == "__main__":
unittest.main()
README podaje jedną komendę: python -m unittest. Uruchom ją samodzielnie przed przekazaniem repozytorium. Kandydat powinien dostać kod, który przechodzi testy bez pobierania zależności i ukrytych danych dostępowych.
Poproś o wyjaśnienie, dlaczego po sortowaniu wystarczy sprawdzać nakładanie się sąsiednich par. Jeśli późniejszy przedział nakłada się na wcześniejszy, w posortowanej sekwencji musi wystąpić konflikt między sąsiadami. Możesz też omówić koszt sortowania i alokację dodatkowej listy, jeśli te zagadnienia są istotne na danym stanowisku.
Rozmowę o algorytmie zapisuj osobno od obserwacji dotyczących utrzymania kodu. Kandydat może jasno wyjaśnić algorytm, a mimo to przeoczyć zgodność ze starszymi danymi. Ktoś inny może starannie zachować kontrakt, ale potrzebować pomocy przy wyjaśnianiu złożoności. Osobne kryteria pozwalają dokładniej opisać ocenę.
Poproś o jedną zmianę i testy, które ją zabezpieczą
Sformułuj precyzyjną prośbę: anulowane rezerwacje mają przestać zajmować salę, a starsze rekordy zachować dotychczasowe znaczenie. Poproś o zmianę ograniczoną do tego wymagania i testy, które odróżnią nowe zachowanie od tego, co musi pozostać bez zmian.
W fikcyjnym README użyj dokładnie tej instrukcji:
Rekordy mogą teraz zawierać pole logiczne
cancelled. Rekord zcancelled: Truenie zajmuje sali. Brak pola lubcancelled: Falseoznacza, że rezerwacja nadal ją zajmuje. Zachowaj dotychczasowe zasady dotyczące przedziałów i kolejność danych wejściowych. Załóż, że podane wartości anulowania są logiczne. Dodaj testy i krótką notatkę dla kolejnej osoby pracującej nad kodem.
Zakres zadania obejmuje jedną zmianę zachowania. Kandydat nie musi wymyślać uprawnień do anulowania, parsowania dat, zapisu danych ani nowego formatu odpowiedzi. Jeśli zauważy te kwestie, może wspomnieć o nich w notatce.
Implementacja obejmująca jedną zmianę
Jednym z poprawnych rozwiązań jest filtrowanie przed sortowaniem:
def can_share_room(bookings):
active = [
booking for booking in bookings
if not booking.get("cancelled", False)
]
ordered = sorted(active, key=lambda booking: booking["start"])
return all(
previous["end"] <= current["start"]
for previous, current in zip(ordered, ordered[1:])
)
Domyślna wartość dla brakującego pola zachowuje znaczenie starszych rekordów. Filtrowanie nie zmienia listy przekazanej do funkcji. Sprawdzanie konfliktów działa tak jak wcześniej, bo otrzymuje już tylko rekordy zajmujące salę.
To przykładowa odpowiedź pomocna przy przygotowaniu ćwiczenia. Nie umieszczaj jej w materiałach dla kandydata. Akceptuj też inne implementacje spełniające kontrakt. Preferencje dotyczące nazw i układu kodu nie powinny stawać się niewypowiedzianymi wymaganiami.
Testy potwierdzające zmianę
Dodaj te metody do udostępnionej klasy testowej:
def test_cancelled_overlap_does_not_block_room(self):
self.assertTrue(can_share_room([
{"start": 10, "end": 20},
{"start": 15, "end": 25, "cancelled": True},
]))
def test_explicit_false_still_blocks_room(self):
self.assertFalse(can_share_room([
{"start": 10, "end": 20},
{"start": 15, "end": 25, "cancelled": False},
]))
def test_all_cancelled_bookings_leave_room_available(self):
self.assertTrue(can_share_room([
{"start": 10, "end": 20, "cancelled": True},
{"start": 15, "end": 25, "cancelled": True},
]))
Pierwszy nowy test nie przechodzi na pierwotnej implementacji. To potwierdza, że test sprawdza żądaną zmianę. Drugi zabezpiecza rekordy jawnie oznaczone jako aktywne, a pierwotny test konfliktu chroni rekordy bez pola anulowania.
Dotychczasowe testy sąsiadujących rezerwacji i kolejności nadal są ważne. Sprawdzają elementy kontraktu, których wymaganie nie zmieniło. Poproś kandydata o wskazanie asercji potwierdzających nową regułę oraz tych, które chronią stare zasady.
Możecie też omówić dodanie testu sprawdzającego, czy anulowane rekordy pozostają na liście przekazanej do funkcji. Pokazałby on wprost, że reguła zachowania danych wejściowych obejmuje również nową strukturę danych. Brak takiego testu odnotuj jako konkretną lukę w pokryciu. Następnie sprawdź, czy implementacja rzeczywiście narusza tę regułę.
Sama liczba testów nie zasługuje na dodatkowe punkty. Kilka testów powtarzających ten sam scenariusz może utrudnić zauważenie brakującego przypadku granicznego. Sprawdzaj związek każdej asercji z opisanym kontraktem.
Omów przekazanie kodu według wspólnych pytań
Poproś każdego kandydata o wyjaśnienie zmiany na podstawie tych samych głównych pytań. Zanim recenzenci omówią końcową decyzję, powinni zapisać konkretne obserwacje z kodu i testów.
Wytyczne OPM dotyczące ustrukturyzowanych rozmów opisują ustalone z góry pytania zadawane w tej samej kolejności oraz wspólne standardy oceny. Zastosowanie tych zasad do rozmowy o kodzie to praktyczna rekomendacja. Nie oznacza, że ćwiczenie ma certyfikację OPM, ani nie potwierdza, że pozwala przewidywać wyniki w pracy.
Notatka przydatna kolejnej osobie
Przykładowa notatka może brzmieć tak:
Anulowane rezerwacje są pomijane przed sortowaniem. Przy braku pola anulowania rezerwacja jest domyślnie aktywna, więc starsze rekordy zachowują dotychczasowe znaczenie. Istniejące testy sąsiadujących rezerwacji i kolejności danych wejściowych nadal przechodzą. Nowy test nakładających się anulowanych rezerwacji nie przechodzi na pierwotnej funkcji. Zakładamy, że wartości anulowania w danych wejściowych są logiczne; walidacja importowanych danych pozostaje poza zakresem tej zmiany.
Taka notatka wyjaśnia kolejnemu programiście, co się zmieniło, co pozostało bez zmian i gdzie kończą się założenia. Nie wymaga długiego eseju ani relacji z każdej edycji.
Zadaj wszystkim te same trzy główne pytania:
- Jakie dotychczasowe zachowanie zostało zabezpieczone i co to potwierdza?
- Który test nie przeszedłby bez tej zmiany?
- Co kolejny programista powinien wiedzieć, zanim ponownie zmieni tę funkcję?
Recenzent może dopytać o oddaną pracę. Zapisuj również te pytania, zwłaszcza jeśli zawierały informacje, które inny kandydat dostał już w początkowej instrukcji. Powtarzające się wątpliwości to powód, żeby doprecyzować materiały.
Wspólna karta oceny
Przed rozpoczęciem rozmów uzgodnij, jakie konkretne obserwacje wpisywać do karty:
| Kryterium | Co zapisać | Co sprawdzić przy wątpliwościach |
|---|---|---|
| Odczytanie kontraktu | Brak pola anulowania i wartość false są rozpoznawane jako aktywna rezerwacja | Pomijanie starszych rekordów lub zmiana zasad dotyczących sąsiadujących rezerwacji |
| Poprawność zmiany | Anulowane rekordy nie wpływają na dostępność sali | Uwzględnianie anulowanych rezerwacji przy porównywaniu konfliktów |
| Ochrona przed regresją | Asercje są powiązane z nowym i dotychczasowym zachowaniem | Pokazanie przechodzących testów bez wyjaśnienia, co sprawdzają |
| Przekazanie kodu | Opis zmiany, zachowanego zachowania i założenia dotyczącego danych wejściowych | Pozostawienie kolejnej osobie konieczności odgadywania kontraktu |
To propozycje dla tego ćwiczenia. Dostosuj je do wcześniej określonych obowiązków i ustal ich wpływ na decyzję o zatrudnieniu, zanim zobaczysz rozwiązania. Nie przedstawiaj ich jako progów punktowych potwierdzonych badaniami.
Sprawdź materiały z osobami z zespołu, które znają dane stanowisko. Zapytaj, gdzie potrzebowały wyjaśnień i czy zadanie odpowiada umiejętnościom wymaganym już przy dołączeniu. OPM zwraca uwagę, że opracowanie i przeprowadzenie próbek pracy wymaga wysiłku. Zaplanuj czas na przygotowanie i ocenę.
Jeśli głównym zadaniem na stanowisku seniora jest ocena propozycji architektury, wybierz inne ćwiczenie. Tę odpowiedzialność omawiamy w przewodniku po rozmowie z doświadczonym programistą o przeglądzie projektu. Tak mała zmiana w kodzie nie sprawdzi każdego rodzaju decyzji inżynierskiej.
Zorganizuj zadanie programistyczne w Kit
Samodzielnie przygotuj ćwiczenie i kryteria oceny, a następnie użyj Kit do zorganizowania zadania programistycznego w procesie rekrutacji. Implementacja, testy i review kodu pozostają w GitHub. Ich oceną zajmują się ludzie.
Zadania programistyczne w Kit korzystają z prywatnych repozytoriów tworzonych z repozytorium szablonowego pracodawcy. Kandydaci łączą konta GitHub, żeby wykonać zadanie. Po oddaniu pracy recenzenci z połączonymi kontami GitHub mogą otrzymać zaproszenia do repozytorium i sprawdzić tam kod oraz historię zmian.
Umieść README, początkowe testy i wymagane rezultaty w szablonie. Jeśli oczekujesz pull requestu, napisz to w instrukcji. Kit nie tworzy go automatycznie ani nie ocenia implementacji. Przygotuj kryteria, według których zespół oceni zadanie.
Ustal termin oddania pasujący do procesu, a przewidywany nakład pracy wyjaśnij osobno. Termin w kalendarzu nie mierzy godzin faktycznie spędzonych na programowaniu. Nie traktuj jego upływu jako dowodu, że repozytorium zostało zablokowane. Szerszy proces, w tym terminy i ocenę, opisuje przewodnik po konfiguracji zadań programistycznych.
Poprawne rozwiązanie to przydatny punkt wyjścia. Jeśli zatrudniasz do utrzymania kodu, dodaj jedną jasną zmianę, testy chroniące kontrakt i notatkę przydatną kolejnej osobie. Zacznij od sprawdzenia tego zestawu na rzeczywistym obowiązku w twoim zespole. Potem dopracuj instrukcje przed przekazaniem ich kandydatom.
Powiązane artykuły
Wypróbuj Kit przez 30 dni.
Rekrutacja, zgłoszenia podatności i szkolenia w jednym koncie, dla zespołów, w których nikt nie robi tego na pełny etat. Kartę podajesz na starcie; zrezygnuj przed końcem okresu próbnego, a nic nie zapłacisz.
Zacznij za darmo