Mam dla Ciebie niewygodną tezę: w 2035 roku senior developerem nie zostanie ten, kto pisze najlepszy kod. Bo najlepszy kod będzie pisać AI - szybciej, taniej i bez narzekania na spotkania.
I zanim zamkniesz kartę: to nie jest kolejny tekst w stylu "AI zabierze Ci pracę". Jest dokładnie odwrotnie. Część programistów będzie zarabiać więcej niż dziś - tylko że to będą inni programiści, niż myślisz. A najłatwiej pokazać to nie na wykresach, tylko na prawdziwym kodzie C#.
W tym artykule poznasz 3 typy programistów w erze AI, zrobimy razem code review endpointu ASP.NET Core wygenerowanego przez AI (5 problemów, w tym 1, który w prawdziwej firmie kończy się karą i wizytą u prawnika), przejdziemy przez 5 umiejętności seniora 2035, a na koniec dostaniesz konkretny plan: co zrobić w tym tygodniu, w tym miesiącu i w tym roku, żeby w 2035 być po właściwej stronie tej zmiany.
Wersje: przykłady to ASP.NET Core (kontrolery) + EF Core. Opisane problemy i poprawki wyglądają tak samo w .NET 8, 9 i 10 - bo to nie jest kwestia wersji frameworka, tylko wiedzy o tym, co dzieje się pod maską.
Ta sama praca, 3 różne światy
Spójrz, jak w ciągu 20 lat zmienia się praca programisty:
• 2015: Stack Overflow + dokumentacja. Każdą linijkę piszesz sam.
• 2025: AI pisze szkielet, Ty poprawiasz. Asystent jest w każdym IDE.
• 2035: AI pisze większość kodu. Pytanie brzmi: co robi człowiek?
10 lat temu programista przepisywał odpowiedzi ze Stack Overflow. Dziś AI generuje całe klasy w kilka sekund. A wiesz, co się nie zmieniło? To, że ktoś musi odpowiadać za to, że system działa. I to "ktoś" jest kluczem do całego tematu.
W czym AI jest świetne, a w czym regularnie leży
Gdzie AI już dziś błyszczy. Najpierw lista rzeczy, które AI robi naprawdę dobrze:
• boilerplate i CRUD-y,
• testy jednostkowe do prostego kodu,
• tłumaczenie kodu między językami,
• dokumentacja i komentarze,
• zadania typu "napisz mi funkcję, która...".
Przyjrzyj się tej liście jeszcze raz. To szacunkowo 60-70% codziennej pracy juniora. I to nie jest teoria ani prognoza - to się dzieje teraz.
Gdzie AI regularnie leży. Jest jednak i druga lista:
• Bezpieczeństwo. AI nie zna Twojego modelu uprawnień.
• Wydajność na produkcji. Demo działa, Black Friday nie.
• Edge case'y biznesowe. AI nie wie, że faktura korygująca to w Twojej firmie zupełnie inny proces.
• Architektura całości. AI optymalizuje funkcję, a nie system.
• Odpowiedzialność. AI nie pójdzie na dywanik, gdy padnie produkcja.
I dokładnie w tej luce - między "kod się kompiluje" a "system działa i zarabia pieniądze" - będzie mieszkał senior developer 2035. Za chwilę zobaczysz to na konkretnym kodzie. Najpierw jednak poznaj 3 typy programistów, bo od tego, którym jesteś dziś, zależy Twoja przyszłość.
3 typy programistów w erze AI: Pasażer, Dinozaur i Dyrygent
Typ 1: Pasażer. Wkleja zadanie do AI, kopiuje wynik i nie czyta kodu, który commituje. Działa? Działa. Merge.
Pasażer siedzi w samochodzie, ale nie prowadzi. Dziś jeszcze jakoś jedzie, bo ktoś musi kliknąć "accept". Problem w tym, że jest wolniejszy niż AI i droższy niż AI. Gdy AI stanieje jeszcze bardziej - a stanieje - firma zada sobie proste pytanie: po co nam płatny pośrednik między Jirą a chatem?
Typ 2: Dinozaur. "Prawdziwy programista pisze wszystko sam." Ignoruje AI z zasady. I uwaga: szanuję rzemiosło, a Dinozaur często pisze świetny kod. Tyle że godzinę pisze to, co AI zrobi w 30 sekund, a jego koledzy dowożą 3 razy szybciej. Nie przegrywa jakością. Przegrywa czasem.
W 2035 ręczne klepanie CRUD-ów będzie jak ręczne przepisywanie ksiąg po wynalezieniu druku. Piękne hobby. Słaby model biznesowy.
Typ 3: Dyrygent. Deleguje AI 100% tego, co AI robi dobrze, ale weryfikuje 100% tego, co AI oddaje. Wyłapuje błąd w 3 minuty, bo rozumie, co jest pod maską. I bierze odpowiedzialność za całość.
Dyrygent na koncercie nie gra na żadnym instrumencie. Ale bez niego orkiestra to hałas. Senior 2035 to właśnie dyrygent: AI gra, a człowiek odpowiada za to, żeby wyszła z tego muzyka, a nie kakofonia.
Zadaj sobie szczerze pytanie: którym typem jesteś dziś? A teraz koniec metafor - sprawdźmy to na kodzie.
Code review kodu od AI: endpoint, który działa w demo
Poprosiłem AI o prosty endpoint. Prompt brzmiał tak:
"Napisz endpoint w ASP.NET Core, który zwraca listę zamówień użytkownika razem z pozycjami. Używam EF Core."
8 sekund później miałem gotowy kod:
[HttpGet("api/orders/{userId}")]
public async Task<IActionResult> GetUserOrders(int userId)
{
var orders = await _context.Orders
.Where(o => o.UserId == userId)
.ToListAsync();
foreach (var order in orders)
{
order.Items = await _context.OrderItems
.Where(i => i.OrderId == order.Id)
.ToListAsync();
}
return Ok(orders);
}Kod wygląda porządnie, kompiluje się i działa w demo. A jednak ma w sobie 5 problemów - w tym 1, który w prawdziwej firmie kończy się karą i wizytą u prawnika.
Zanim przeczytasz dalej, zatrzymaj się na chwilę. Serio. Daj sobie 30 sekund i policz, ile problemów widzisz w tym kodzie. To najprostszy test, czy już dziś myślisz jak Dyrygent.
Policzone? Jedziemy.
Problem 1/5: IDOR, czyli cudze zamówienia po zmianie 1 cyfry
Zaczynam od najgroźniejszego. userId przychodzi z URL-a, a na endpoincie nie ma żadnej autoryzacji. Co to oznacza w praktyce? Że każdy może podstawić dowolne ID.
Wywołujesz GET /api/orders/1 - widzisz swoje zamówienia. Zmieniasz 1 na 2 - i widzisz cudze: adres, telefon, historię zakupów. Bez logowania. Wystarczy zmienić 1 cyfrę w adresie.
To się nazywa IDOR (Insecure Direct Object Reference) - jedna z najczęstszych dziur w aplikacjach webowych. Należy do kategorii Broken Access Control, która zajmuje 1. miejsce w OWASP Top 10. Kod działa? Działa. Testy przechodzą? Przechodzą. A RODO mówi "dzień dobry" i firma płaci karę liczoną w procentach rocznego obrotu.
Dlaczego AI tego nie wyłapało? Bo nie zna Twojego modelu uprawnień. To, kto może oglądać czyje zamówienia, jest kontekstem Twojego systemu, a nie składnią C#. Z jednozdaniowego promptu nie da się go wyczytać.
Poprawka. Fix jest banalny: atrybut [Authorize] i ID użytkownika wzięte z tokena (claims), a nie z URL-a. Przy okazji zmieniamy adres na api/me/orders. Endpoint mówi wtedy prawdę ("zwracam TWOJE zamówienia"), a w URL-u nie ma już czego podmieniać:
[Authorize]
[HttpGet("api/me/orders")]
public async Task<IActionResult> GetMyOrders()
{
var userId = User.GetUserId(); /* ID z tokena (claims), nie z URL-a */
/* ... */
}GetUserId() to krótka metoda rozszerzająca, która wyciąga identyfikator z claimów zalogowanego użytkownika. Nazwa claimu zależy od konfiguracji uwierzytelniania (np. ClaimTypes.NameIdentifier albo "sub" z JWT):
public static class ClaimsPrincipalExtensions
{
public static int GetUserId(this ClaimsPrincipal user)
{
var value = user.FindFirst(ClaimTypes.NameIdentifier)?.Value
?? throw new InvalidOperationException("Brak ID użytkownika w tokenie.");
return int.Parse(value);
}
}A jeśli administrator ma widzieć zamówienia innych osób? To osobny endpoint z własną polityką uprawnień (np. [Authorize(Roles = "Admin")]), a nie furtka w endpoincie dla klientów.
Problem 2/5: N+1 zapytań do bazy
Widzisz pętlę foreach? Każdy jej obrót to osobne zapytanie do bazy: najpierw 1 zapytanie po listę zamówień, a potem po 1 zapytaniu na pozycje każdego zamówienia. 30 zamówień? 31 zapytań. Stąd nazwa: N+1.
W demo z kilkoma zamówieniami jest to niezauważalne. Na produkcji w Black Friday baza klęka, a Ty tłumaczysz zarządowi, czemu sklep nie sprzedaje.
Jak to zobaczyć. Najprościej włączyć w środowisku developerskim logowanie zapytań SQL w EF Core:
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("Default"))
.LogTo(Console.WriteLine, LogLevel.Information)); /* logowanie SQL do konsoli */Po wywołaniu endpointu konsola zalewa się zapytaniami - na każde zamówienie przypada osobny SELECT po pozycje. Taki widok zostaje w głowie na długo.
Poprawka. Zamiast pętli - 1 projekcja Select (rekord OrderDto zdefiniujemy za chwilę). EF Core tłumaczy ją na 1 zapytanie SQL, w którym sumę i liczbę pozycji policzy już baza:
var orders = await _context.Orders
.Where(o => o.UserId == userId)
.Select(o => new OrderDto(
o.Id,
o.CreatedAt,
o.Items.Sum(i => i.UnitPrice * i.Quantity),
o.Items.Count))
.ToListAsync();Z 31 zapytań robi się 1 - niezależnie od tego, czy zamówień jest 30, czy 3000. Problem N+1 rozwiązałoby też Include(o => o.Items), ale wtedy wciąż zwracamy całe encje, a to, jak zaraz zobaczysz, osobny problem. Projekcja załatwia oba naraz.
Problemy 3-5: paginacja, wyciek pól i CancellationToken
Problem 3/5: brak paginacji. Ten kod zwraca wszystko, zawsze. Klient z 10 000 zamówień dostaje 10 000 rekordów naraz (w wersji od AI - razem ze wszystkimi pozycjami). Timeout, pamięć, przeglądarka umiera. AI zwróciło "wszystko", bo prompt nie mówił inaczej. Dyrygent wie, że lista bez paginacji to bomba z opóźnionym zapłonem: dziś działa tylko dlatego, że testowy użytkownik ma 5 zamówień.
Poprawka to Skip i Take, ale z 2 ważnymi szczegółami. Po pierwsze, twardy limit rozmiaru strony - bez niego ktoś wyśle pageSize=1000000 i wracamy do punktu wyjścia. Po drugie, stała kolejność (OrderBy). Paginacja bez sortowania to loteria, a EF Core sam ostrzeże Cię w logach przed Skip/Take bez OrderBy:
page = Math.Max(page, 1);
pageSize = Math.Clamp(pageSize, 1, 100); /* twardy limit: max 100 na stronę */
var orders = await _context.Orders
.Where(o => o.UserId == userId)
.OrderByDescending(o => o.CreatedAt)
.Skip((page - 1) * pageSize)
.Take(pageSize)
/* ...projekcja Select do DTO, jak wyżej */
.ToListAsync();Problem 4/5: encje prosto z bazy do JSON-a. Wersja od AI serializuje encje EF bez żadnej warstwy pośredniej. Zobacz, co może trafić do przeglądarki klienta (przykładowa odpowiedź):
[
{
"id": 2,
"userId": 2,
"createdAt": "2026-09-14T10:21:00",
"items": [
{
"id": 7,
"orderId": 2,
"productName": "Klawiatura mechaniczna",
"quantity": 1,
"unitPrice": 399.00,
"costPrice": 210.00
}
]
}
]Pole costPrice to cena zakupu - wewnętrzna informacja, z której każdy policzy marżę firmy. Klient nigdy nie powinien jej zobaczyć. A to tylko 1 z 3 skutków zwracania encji:
• Wyciek pól. Do klienta idzie wszystko, co jest w encji. Co gorsza, każda kolumna, którą ktoś kiedyś doda do tabeli, automatycznie trafi do API.
• Cykle referencji. Przy relacjach dwustronnych (zamówienie zna swoje pozycje, a pozycja zna swoje zamówienie) serializer wpada w pętlę - System.Text.Json domyślnie kończy to wyjątkiem.
• API przyspawane do bazy. Zmienisz nazwę kolumny - psujesz klientów API.
Poprawka: DTO. Do klienta idzie dokładnie to, co ma iść - ani pola więcej. Rekordy w C# robią z tego 2 linijki:
public record OrderDto(int Id, DateTime CreatedAt, decimal Total, int ItemCount);
public record PagedResult<T>(IReadOnlyList<T> Items, int Total, int Page, int PageSize);Problem 5/5: brak CancellationToken. Drobiazg z gatunku tych, które kosztują. Użytkownik zamyka kartę, a Twoje zapytanie dalej miele bazę. 1 request - nieważne. Tysiące dziennie - realny koszt.
Poprawka to 1 parametr. Wystarczy dodać CancellationToken do akcji kontrolera, a ASP.NET Core sam powiąże go z przerwaniem żądania (HttpContext.RequestAborted). Trzeba go tylko przekazać przez cały łańcuch: do CountAsync, ToListAsync i dalej, do serwisów i repozytoriów. Token, który utknie w kontrolerze, nic nie daje.
Wersja Dyrygenta: ten sam endpoint po code review
A tak wygląda endpoint po wszystkich poprawkach:
public record OrderDto(int Id, DateTime CreatedAt, decimal Total, int ItemCount);
public record PagedResult<T>(IReadOnlyList<T> Items, int Total, int Page, int PageSize);
[Authorize]
[HttpGet("api/me/orders")]
public async Task<ActionResult<PagedResult<OrderDto>>> GetMyOrders(
[FromQuery] int page = 1,
[FromQuery] int pageSize = 20,
CancellationToken ct = default)
{
page = Math.Max(page, 1);
pageSize = Math.Clamp(pageSize, 1, 100);
var userId = User.GetUserId(); /* ID z tokena (claims), nie z URL-a */
var baseQuery = _context.Orders.Where(o => o.UserId == userId);
var total = await baseQuery.CountAsync(ct);
var orders = await baseQuery
.OrderByDescending(o => o.CreatedAt)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(o => new OrderDto(
o.Id,
o.CreatedAt,
o.Items.Sum(i => i.UnitPrice * i.Quantity),
o.Items.Count))
.ToListAsync(ct);
return Ok(new PagedResult<OrderDto>(orders, total, page, pageSize));
}Przejdźmy przez to w 60 sekund:
• [Authorize] + ID z tokena - koniec z IDOR-em. Endpoint nazywa się api/me/orders, bo mówi prawdę: zwracam TWOJE zamówienia.
• Projekcja Select do DTO - zero pętli i zero wycieków. Niezależnie od liczby zamówień mamy stałe 2 zapytania: COUNT na potrzeby paginacji i 1 zapytanie po stronę danych.
• Skip/Take z limitem - paginacja, której nikt nie położy.
• CancellationToken - grzeczność wobec bazy.
Jeśli front potrzebuje pełnych pozycji zamówienia, a nie tylko sumy i ich liczby, dokładasz zagnieżdżone DTO pozycji w tej samej projekcji - nadal bez pętli i bez osobnego zapytania na każde zamówienie.
Bilans: 5 problemów, 5 poprawek. Zbierzmy to w jednym miejscu:
| Problem | Skutek | Poprawka |
| IDOR: userId z URL-a, brak autoryzacji | cudze dane po zmianie 1 cyfry, ryzyko kary z RODO | [Authorize] + ID z tokena, endpoint api/me/orders |
| N+1: zapytanie w pętli | 31 zapytań przy 30 zamówieniach, baza klęka pod obciążeniem | projekcja Select - 1 zapytanie |
| Brak paginacji | 10 000 rekordów naraz: timeouty, pamięć | OrderBy + Skip/Take + limit pageSize |
| Encje prosto do JSON-a | wyciek costPrice, cykle referencji, API przyspawane do bazy | DTO (rekordy) |
| Brak CancellationToken | zapytania mielą bazę po zamknięciu karty | CancellationToken przez cały łańcuch |
Zwróć uwagę: żadna z tych poprawek nie wymagała pisania sprytnego kodu. Wymagały wiedzy, że problem istnieje. To jest dokładnie różnica między Pasażerem a Dyrygentem.
8 sekund vs 3 minuty: za co naprawdę płaci firma
Policzmy, za co firma naprawdę płaci. AI napisało pierwszą wersję w 8 sekund. Znalezienie 5 błędów zajęło mi 3 minuty.
I to jest sedno całego tematu: w 2035 nikt nie zapłaci Ci za te 8 sekund. Wszyscy zapłacą za te 3 minuty.
Możesz zapytać: a czy AI nie mogłoby samo zrobić tego code review? Częściowo tak - pod warunkiem, że wiesz, o co zapytać. Prompt "sprawdź ten endpoint pod kątem IDOR i N+1" napisze tylko ktoś, kto wie, że IDOR i N+1 w ogóle istnieją. A ocenić, czy poprawka zaproponowana przez AI jest dobra, i tak musi człowiek, który za ten kod odpowiada. Wiedza nie traci więc na wartości - zmienia się tylko to, gdzie jej używasz.
Pytanie brzmi: skąd wziąć wiedzę, żeby te 3 minuty umieć? Odpowiedź to 5 umiejętności.
5 umiejętności seniora 2035
Moim zdaniem o tym, kto w 2035 będzie seniorem, zdecyduje 5 umiejętności: fundamenty pod maską, bezpieczeństwo jako odruch, myślenie systemowe, rozumienie biznesu oraz komunikacja i odpowiedzialność. Zauważ, że na tej liście nie ma żadnego frameworka.
1. Fundamenty pod maską. Chodzi o rzeczy, które nie zmieniają się co roku:
• jak EF Core tłumaczy LINQ na SQL,
• jak naprawdę działa async/await,
• jak baza wybiera indeks,
• co się dzieje między requestem a response.
N+1 z naszego przykładu wyłapiesz tylko wtedy, gdy wiesz, jak LINQ zamienia się w SQL. AI zna składnię lepiej od Ciebie. Ale błędy robi tam, gdzie kończy się składnia, a zaczyna zrozumienie. Frameworki do 2035 zmienią się 5 razy. To, co pod maską - prawie wcale.
2. Bezpieczeństwo jako odruch. Bezpieczeństwo nie jest u Ciebie osobnym etapem projektu, tylko nawykiem:
• OWASP Top 10 znasz jak tabliczkę mnożenia,
• każde wejście do systemu traktujesz jak potencjalny atak,
• przy każdym endpoincie odruchowo pytasz: "kto może to wywołać i z jakimi danymi?"
IDOR z naszego przykładu to nie egzotyka, tylko codzienność. Kod pisany przez AI będzie wszędzie, więc dziury pisane przez AI też będą wszędzie. Człowiek, który wyłapuje je, zanim trafią na produkcję, będzie na wagę złota. Dosłownie.
3. Myślenie systemowe. Patrzysz szerzej niż na pojedynczą funkcję:
• myślisz nie "jak napisać funkcję", tylko "jak 40 serwisów ma ze sobą gadać",
• rozumiesz trade-offy: prostota vs skalowalność, spójność vs dostępność,
• podejmujesz decyzje, których zmiana po roku kosztuje fortunę - i wiesz, że takie są.
AI świetnie zoptymalizuje funkcję. Ale nie powie Ci, że cała ta funkcja jest niepotrzebna, bo problem trzeba było rozwiązać warstwę wyżej. Widzenia całości nie da się wygenerować promptem.
4. Rozumienie biznesu. Kod jest dla Ciebie narzędziem, a nie celem:
• tłumaczysz "chcemy zwiększyć konwersję" na konkretną zmianę w systemie,
• wiesz, kiedy powiedzieć: "nie budujmy tego wcale",
• znasz koszt swojej pracy i wartość, którą tworzy.
Prompt do AI umie dziś napisać każdy. Wiedzieć, co w nim wpisać i czy w ogóle warto - to jest praca. Najdroższe zdanie w IT w 2035 będzie brzmiało: "tego nie musimy budować".
5. Komunikacja i odpowiedzialność. I ostatnia umiejętność, która spina wszystkie poprzednie:
• tłumaczysz decyzje techniczne ludziom nietechnicznym,
• robisz code review - ludziom i AI,
• podpisujesz się pod systemem własnym nazwiskiem.
I tu dochodzimy do sedna. "Senior" nigdy nie był poziomem kodowania. To poziom zaufania, jakim obdarza Cię firma. AI może pisać kod. Ale zaufać można tylko człowiekowi.
Twój plan: tydzień, miesiąc, rok
Teraz konkret. Oto plan, który możesz zacząć realizować od razu.
W tym tygodniu. Weź 1 kod wygenerowany przez AI i zrób mu brutalne code review - znajdź minimum 3 problemy. Możesz zacząć od wersji "od AI" z tego artykułu: przenieś ją do swojego projektu i spróbuj sam dojść do wersji Dyrygenta, zanim do niej zajrzysz.
W tym miesiącu.
• Rozbierz na części 1 temat "pod maską": LINQ na SQL, async/await albo indeksy.
• Przejdź OWASP Top 10 i sprawdź swój obecny projekt punkt po punkcie.
W tym roku.
• Zbuduj 1 projekt end-to-end, w którym to Ty podejmujesz decyzje architektoniczne i spisujesz, dlaczego (krótkie notatki decyzyjne wystarczą).
• Zrób z AI swojego pracownika: deleguj boilerplate, weryfikuj wszystko.
Zauważ jedną rzecz: żaden punkt nie brzmi "naucz się nowego frameworka". Frameworki przeminą. Umiejętność oceny kodu, podejmowania decyzji i brania odpowiedzialności - nie.
Podsumowanie
W 2035 kod będzie tani jak prąd. Drogie będą 3 rzeczy: ocena kodu, decyzje i odpowiedzialność. W skrócie:
• 3 typy programistów: Pasażer wypadnie z rynku, Dinozaur zostanie z tyłu, a Dyrygent będzie zarabiał więcej niż dzisiejszy senior - bo 1 Dyrygent z AI zrobi robotę całego zespołu sprzed dekady.
• Kod od AI działa w demo, ale nie zawsze na produkcji: w 1 prostym endpoincie znaleźliśmy 5 problemów - IDOR, N+1, brak paginacji, wyciek pól i brak CancellationToken.
• 8 sekund vs 3 minuty: nikt nie zapłaci za wygenerowanie kodu, wszyscy zapłacą za jego ocenę.
• 5 umiejętności seniora 2035: fundamenty pod maską, bezpieczeństwo jako odruch, myślenie systemowe, rozumienie biznesu oraz komunikacja i odpowiedzialność.
• Plan: w tym tygodniu brutalne code review kodu od AI, w tym miesiącu 1 temat pod maską i OWASP Top 10, w tym roku projekt end-to-end z Twoimi decyzjami.
I dobra wiadomość na koniec: masz ok. 9 lat przewagi. Bo większość ludzi przeczyta ten tekst, pokiwa głową... i nie zrobi nic.
Jeśli nie chcesz być większością i chcesz zacząć od razu, mam coś dla Ciebie. Przygotowałem darmowy PDF: 25 promptów AI dla programistów C#/.NET do Claude Code i Copilota. Refaktoryzacja, testy jednostkowe, debugowanie, code review i legacy - czyli praca w stylu Dyrygenta: AI robi to, w czym jest dobre, a Ty wiesz, o co zapytać i co sprawdzić. Każdy prompt przetestowałem na prawdziwym kodzie, więc wystarczy skopiować, wkleić i zobaczyć różnicę.
Odbierzesz go tutaj: modestprogrammer.pl/25-promptow. Zostawiasz maila i PDF przychodzi od razu. Przy okazji dołączasz do społeczności ponad 10 000 programistów .NET: raz w tygodniu 1 konkretny mail - najważniejsze newsy ze świata C# i .NET, analizy takie jak ta i rabaty na moje kursy, których nie znajdziesz nigdzie indziej. Zero spamu - wypisujesz się jednym kliknięciem.
Do zobaczenia po stronie Dyrygentów :)