Czego szukamy w Senior .NET developera

Senior .NET developer to dziś coś znacznie więcej niż programista C#. Ekosystem Microsoftu w ostatnich latach przeszedł rewolucję — pełna migracja na cross-platformowy .NET 8 i .NET 9, dominacja ASP.NET Core jako standardu backendowego, dojrzałe Minimal APIs, AOT compilation, Blazor United, a do tego Azure jako de facto chmurowa platforma dla większości projektów enterprise w Polsce. Dobry kandydat na seniora musi sprawnie poruszać się po C# 12 (primary constructors, collection expressions, ref readonly parameters, alias any type), rozumieć cykl życia obiektów i tunowanie Garbage Collectora (Workstation vs Server GC, regiony, LOH), umieć modelować logikę biznesową w duchu Domain-Driven Design oraz dobierać właściwe narzędzia ORM — Entity Framework Core do większości scenariuszy, Dapper tam, gdzie liczy się surowa wydajność i kontrola nad SQL-em.

W ARDURA Consulting weryfikujemy nie tylko deklaratywną wiedzę, ale i to, czy kandydat potrafi uzasadnić wybór wzorca architektonicznego. Senior .NET to ktoś, kto wie, kiedy CQRS daje realną wartość, a kiedy jest overengineeringiem, kto rozumie różnicę między Clean Architecture w stylu Jasona Taylora a klasycznym warstwowym podejściem, i kto potrafi przeprowadzić code review pod kątem alokacji pamięci, asynchroniczności i thread-safety. Szukamy też dojrzałości w obszarze testów — xUnit lub NUnit jako framework, Moq lub NSubstitute jako biblioteka mockująca, FluentValidation do walidacji, AutoMapper świadomie używany (lub świadomie odrzucony na rzecz manualnego mappingu). To profil osoby, której można powierzyć decyzje architektoniczne na projekcie wartym kilkaset tysięcy złotych miesięcznie.

Top 15 pytań rekrutacyjnych z odpowiedziami

1. Czym różni się Task od ValueTask i kiedy warto sięgnąć po ten drugi? ValueTask to struct, więc unika alokacji na stercie, gdy operacja kończy się synchronicznie. Dobry kandydat wskaże, że nie wolno go awaitować dwukrotnie ani używać w scenariuszach z Task.WhenAll. Stosujemy go w hot-pathach (np. cache hit) — wszędzie indziej Task jest bezpieczniejszy.

2. Jak działa Garbage Collector w .NET 8 i czym różni się Workstation GC od Server GC? Generacyjny, kompaktujący, z regionami (zamiast segmentów od .NET 7). Server GC ma osobny heap i wątek per CPU, agresywniej zrównolegla — domyślny dla ASP.NET Core. Workstation jest lepszy dla aplikacji desktopowych i procesów małych mikroserwisów (mniejszy memory footprint).

3. Na czym polega AOT compilation i jakie ma ograniczenia? Native AOT kompiluje aplikację do natywnego kodu bez JIT-a — szybszy startup, mniejsze zużycie pamięci, idealny dla CLI, Azure Functions, mikroserwisów. Ograniczenia: brak runtime code generation, ograniczona refleksja, większość ORM-ów (w tym pełne EF Core) wymaga konfiguracji source generators.

4. Co to jest pattern Mediator i dlaczego MediatR jest popularny? Decouple’uje nadawcę i odbiorcę żądania. MediatR pozwala oddzielić logikę handlera od kontrolera, naturalnie pasuje do CQRS — IRequest<TResponse>, INotification, pipeline behaviors (logging, validation, transactions). Senior powie też o krytyce: dodatkowy poziom indyrekcji i koszt utrzymania w prostych CRUD-ach.

5. Entity Framework Core czy Dapper? EF Core daje change tracking, migracje, LINQ — produktywność na poziomie aplikacji biznesowej. Dapper to lekki mikro-ORM bez change trackingu, świetny do raportów i zapytań krytycznych dla wydajności. W praktyce hybryda: EF do command-side (zapis), Dapper do query-side (odczyt).

6. Czym jest IAsyncEnumerable<T> i kiedy go używać? Strumień asynchroniczny, idealny dla danych zwracanych partiami z bazy lub API. Konsumowany przez await foreach. Eliminuje konieczność trzymania pełnego zbioru w pamięci.

7. Co to są Minimal APIs i czy zastępują kontrolery? Funkcyjny styl definiowania endpointów (app.MapGet(...)). Mniej boilerplate’u, idealne dla małych serwisów i Azure Functions. Nie zastępują kontrolerów w dużych projektach, gdzie potrzeba filtrów, model bindingu i konwencji.

8. Jak działa SignalR i czym różni się od WebSocketów? Abstrakcja nad WebSocketami, Server-Sent Events i long pollingiem — z automatycznym fallbackiem. Daje hubs, grupy, autoryzację. Pod spodem najczęściej WebSockets, ale developer nie musi się tym przejmować.

9. Czym jest gRPC i kiedy warto go wybrać zamiast REST? Binary protocol oparty na HTTP/2 i Protocol Buffers. Lepszy throughput, niższe latencje, streaming. Stosujemy w komunikacji service-to-service. REST zostaje dla publicznych API i klientów przeglądarkowych.

10. Jak zaprojektować architekturę Clean według Jasona Taylora? Warstwy: Domain (entities, value objects), Application (use cases, MediatR handlers), Infrastructure (EF Core, Azure Service Bus), Presentation (ASP.NET Core). Zależności idą zawsze do środka. Domain nie wie nic o EF Core ani o ASP.NET.

11. Co to są Source Generators i do czego się przydają? Kompilatorowe generowanie kodu w czasie buildu. Eliminują refleksję runtime — szybciej, mniej alokacji, kompatybilność z AOT. Używane przez System.Text.Json, Regex, MediatR (od v12).

12. Co to jest Blazor United? Połączenie Blazor Server, Blazor WebAssembly i statycznego renderowania w jednej aplikacji — wybierany per komponent. Pozwala startować z SSR, a interaktywne fragmenty hostować jako Server lub WASM.

13. Jak obsłużyć transakcje rozproszone w mikroserwisach .NET? Saga pattern (choreografia lub orkiestracja). W praktyce: outbox pattern z Azure Service Bus, ewentualnie MassTransit. Senior odradzi 2PC.

14. Co to jest Cosmos DB i kiedy go wybrać? Globalnie rozproszona baza NoSQL, model wieloparadygmatowy (SQL API, MongoDB API, Gremlin). Wybieramy ją do scenariuszy multi-region, niskich latencji odczytu i przewidywalnego throughputu (RU/s).

15. Czym jest FluentValidation i dlaczego nie atrybuty [Required]? Walidacja w osobnej klasie, łatwo testowalna, kompozycyjna, wspiera złożone reguły biznesowe. Atrybuty DataAnnotations są OK dla prostych modeli DTO, ale nie do logiki domenowej.

Zadania techniczne — 3 scenariusze

Scenariusz 1 — Refaktoryzacja kontrolera do CQRS z MediatR. Dajemy kandydatowi kontroler OrdersController z trzema endpointami (Get, Create, Update), w którym cała logika — dostęp do EF Core, walidacja, mapowanie — siedzi w akcjach. Zadanie: wprowadzić MediatR, rozdzielić Commands i Queries, dodać pipeline behavior do logowania i walidacji FluentValidation, zaproponować testy jednostkowe handlerów z NSubstitute. Oceniamy: czy kandydat poprawnie wydzielił warstwę Application, czy nie zostawił logiki w kontrolerze, czy testy są deterministyczne. Dobry senior zauważy, że IRequestHandler powinien być sealed i zaproponuje walidację jako osobny IPipelineBehavior.

Scenariusz 2 — Optymalizacja zapytania EF Core. Pokazujemy endpoint, który ładuje 50 zamówień razem z klientem, pozycjami i produktem każdej pozycji — i generuje 200+ zapytań SQL (klasyczny N+1). Zadanie: zdiagnozować problem, naprawić, dodać projekcję do DTO, porównać Include z AsSplitQuery, zmierzyć różnicę w EF Core Logging. Senior wskaże też, że dla read-heavy endpointu lepiej użyć AsNoTracking, a w skrajnych przypadkach przejść na Dapper z surowym SQL-em. Bonus: zaproponuje cache w Redis (przez IDistributedCache) z polityką invalidacji.

Scenariusz 3 — Implementacja webhooka z idempotencją. Endpoint przyjmuje webhook od zewnętrznej bramki płatności. Wymagania: idempotencja (dwa razy ten sam event = jeden zapis), kolejkowanie asynchroniczne (Azure Service Bus), retry z exponential backoff, obserwowalność (OpenTelemetry traces). Zadanie: zaprojektować flow, napisać Minimal API endpoint, handler komendy, opisać tabelę idempotency key w EF Core. Oceniamy: czy kandydat rozumie różnicę między at-least-once a exactly-once, czy poprawnie wyizoluje transakcję bazodanową od publikacji wiadomości (outbox pattern), czy potrafi argumentować dobór technologii.

Red flags

Pierwszy sygnał ostrzegawczy to nieznajomość różnicy między Task a ValueTask u kogoś, kto twierdzi że ma 5+ lat doświadczenia z asynchronicznym .NET. Drugi — zerowa wiedza o GC i alokacjach, połączona z deklaracją „pisałem aplikacje wysokowydajne”. Trzeci to nieumiejętność napisania testu jednostkowego bez bazy danych — senior musi rozumieć mockowanie i izolację zależności. Dalej: bezrefleksyjne stosowanie AutoMappera wszędzie (i niemożność uzasadnienia kiedy go odpuścić), bezkrytyczne przepisywanie wszystkiego na MediatR „bo czysto”, brak doświadczenia z migracjami EF Core na produkcji, ignorowanie tematów obserwowalności (logi, metryki, traces). Czerwona lampka zapala się też, gdy kandydat nie pyta o domenę biznesową projektu — senior .NET zawsze pyta o constraintsy, użytkowników i kompromisy, zanim zaproponuje architekturę. Inne sygnały: nieumiejętność wyjaśnienia outbox pattern przy okazji rozmowy o mikroserwisach, brak doświadczenia z Azure (kandydat nie odróżnia App Service od Container Apps od AKS), używanie async void poza event handlerami, brak wiedzy o Span i Memory w kontekście wydajności, ignorowanie cancellation tokens w długo działających operacjach. Senior, który nie potrafi przeprowadzić code review pod kątem thread-safety albo nie wie, czym jest ConfigureAwait(false) i kiedy ma znaczenie, prawdopodobnie nie jest seniorem.

Jak ARDURA Consulting weryfikuje kandydatów

W ARDURA Consulting prowadzimy trzyetapową weryfikację Senior .NET developera w 5 dni roboczych. Etap pierwszy — rozmowa techniczna z naszym Tech Leadem (60 minut, 15 pytań kalibrowanych do stacka klienta, weryfikacja głębi C# 12, .NET 8/9, EF Core, architektury). Etap drugi — zadanie praktyczne na żywo (CQRS refactor lub optymalizacja EF Core, ok. 90 minut, oceniamy nie tyle finalny kod, ile sposób myślenia i komunikację). Etap trzeci — rozmowa kulturowo-biznesowa z klientem, gdzie sprawdzamy fit zespołowy i rozumienie domeny.

Każdy z naszych kandydatów ma za sobą weryfikację realnych projektów w technologiach Microsoftu — od migracji legacy .NET Framework 4.8 do .NET 8, przez budowę systemów event-driven na Azure Service Bus i Cosmos DB, po wdrożenia gRPC i Blazor w środowiskach enterprise. Sprawdzamy referencje, zlecamy klientowi krótkie zadanie pilotażowe (1-2 dni), pomagamy w setupie środowiska deweloperskiego i wdrożeniu w procesy zespołu. Po podpisaniu kontraktu nasz Tech Lead pełni rolę punktu eskalacji jakości — jeśli klient ma wątpliwości techniczne, mamy 48 godzin na ich rozwiązanie. To mechanizm, który stoi za naszym wskaźnikiem 99% retencji.

Dzięki bazie ponad 500 sprawdzonych seniorów i 99% retencji prezentujemy klientowi 2-3 kandydatów dopasowanych do projektu w 14 dni. Średnia oszczędność klienta na pełnym procesie rekrutacyjnym vs in-house — 40%. Wdrożenie eksperta na projekt trwa zwykle 2 tygodnie, a kontrakty są elastyczne — od trzymiesięcznych zleceń wspierających konkretną release’ę, po wieloletnie engagementy w ramach dedykowanych zespołów. Sprawdź dostępnych ekspertów →