Przez ostatnie 10 lat programiści .NET wkuwali rzeczy, które dziś AI często robi szybciej i taniej od nich. A czasem - nie ma co się oszukiwać - po prostu lepiej. Formaty dat, regexy, składnia YAML-a w pipeline'ach, mapowanie DTO właściwość po właściwości. Jeśli dalej uczysz się tego "na zapas", to palisz swój czas w kominku.
Nie znaczy to jednak, że nauka programowania traci sens. Zmienia się tylko to, czego warto się uczyć: mniej pamięci do detali, a więcej rozumienia tego, co dzieje się pod maską, i oceny kodu, którego nie napisałeś sam.
W tym artykule dostaniesz 3 rzeczy. Najpierw 7 rzeczy, które możesz z czystym sumieniem odpuścić - z przykładami kodu C#, które pokazują, dlaczego wkuwanie ich to zły deal. Potem 6 rzeczy, których AI za Ciebie nie zrobi - i to od nich zależy, czy za 5 lat ktoś będzie chciał Ci płacić za kod. A na koniec mój workflow: 4 zasady pracy z AI w .NET, których trzymam się na co dzień.
Wersje: przykłady kodu działają w .NET 8, 9 i 10 (testy w xUnit z Moq, pipeline w Azure DevOps). Zasady są takie same niezależnie od tego, czy korzystasz z GitHub Copilota, Claude Code czy ChatGPT.
Czego nie musisz się już uczyć: wkuwanie to zły deal
Zanim przejdziemy do listy, jedno ważne zastrzeżenie. Nie twierdzę, że wiedza z tej listy jest bezużyteczna. Twierdzę, że wkuwanie jej na pamięć to zły deal. Różnica jest ogromna. Rozumieć, co robi regex albo pipeline CI/CD - nadal warto. Pamiętać na wyrywki każdy znak i każde wcięcie - już nie, bo maszyna zrobi to szybciej, a Ty w tym czasie możesz nauczyć się czegoś, czego maszyna za Ciebie nie zrobi.
Oto 7 rzeczy, które możesz odpuścić:
1. Składnia na pamięć.
2. Boilerplate.
3. Ręczne pisanie regexów.
4. Szkielety testów.
5. Pliki konfiguracyjne.
6. Dokumentacja i komentarze.
7. Konwersje i migracje.
Przejdźmy przez nie po kolei.
Odpuść #1 i #2: składnia na pamięć i boilerplate
#1 Składnia na pamięć. Formaty dat, przeciążenia metod, kolejność parametrów. Kiedyś znajomość takich detali robiła z Ciebie "tego dobrego" w zespole - tego, który bez zaglądania do dokumentacji wie, jak sformatować datę. Dziś AI przekopie dokumentację .NET szybciej od Ciebie. Twoja robota to wiedzieć, co chcesz osiągnąć. Literowanie zostaw maszynie.
Spójrz na ten przykład:
var date = new DateTime(2026, 3, 7, 14, 5, 0);
date.ToString("yyyy-MM-dd HH:mm"); /* 2026-03-07 14:05 - tak miało być */
date.ToString("yyyy-mm-dd hh:MM"); /* 2026-05-07 02:03 - minuty zamiast miesiąca i odwrotnie */Pamiętasz, czym się różni wielkie M od małego m? Duże H od małego h? M to miesiąc, m - minuty, H to godzina w formacie 24-godzinnym, a h - w 12-godzinnym. Druga linijka kompiluje się, działa i zwraca datę, która wygląda zupełnie wiarygodnie. Tyle że jest błędna.
No właśnie. I nie musisz tego pamiętać. AI sprawdzi to za Ciebie - szybko i bez kawy. Wystarczy, że wiesz, że taka pułapka w ogóle istnieje, i rzucisz okiem na format, zanim kod trafi do repozytorium.
#2 Boilerplate. DTO, mappery, konstruktory, CRUD-y. Kod, który wygląda tak samo w każdym projekcie na świecie. AI widziało go miliony razy - generuje go w 3 sekundy i nie robi literówek. Typowy przykład to mapowanie encji na DTO:
public static UserDto ToDto(this User user) => new()
{
Id = user.Id,
FirstName = user.FirstName,
LastName = user.LastName,
Email = user.Email,
PhoneNumber = user.PhoneNumber,
Street = user.Street,
City = user.City,
PostalCode = user.PostalCode,
CreatedAt = user.CreatedAt,
IsActive = user.IsActive
/* ...i tak dalej, przez kolejne 15 właściwości */
};To kwadrans Twojego życia: przepisywanie właściwości z klasy do klasy. Serio uważasz, że to czyni Cię lepszym programistą? To robota dla maszyny. Oddaj ją maszynie.
Twoja rola przy takim kodzie jest zupełnie inna niż klepanie: sprawdzić, czy AI nie przepisało do DTO czegoś, co nigdy nie powinno opuścić serwera, np. hasha hasła albo wewnętrznych flag.
Odpuść #3 i #4: regexy i szkielety testów
#3 Regexy pisane ręcznie. Wiem, niektórzy to kochają. Ale AI napisze wyrażenie regularne, wyjaśni Ci je krok po kroku i wygeneruje do niego testy. Za znajomość składni regexów na pamięć nikt Ci dziś nie zapłaci. Oto - w cudzysłowie - "prosta" walidacja adresu e-mail, jaką można spotkać w niejednym projekcie:
var emailRegex = new Regex(
@"^(?!\.)(""([^""\r\\]|\\[""\r\\])*""|([-a-z0-9!#$%&'*+/=?^_`{|}~]|(?<!\.)\.)*)(?<!\.)@[a-z0-9][\w\.-]*[a-z0-9]\.[a-z][a-z\.]*[a-z]$",
RegexOptions.IgnoreCase);Powodzenia w debugowaniu tego o 23:00. Ja dziękuję, postoję :)
A teraz najciekawsze: ten wzorzec odrzuca całkowicie poprawny adres a@b.co, bo wymaga, żeby nazwa domeny przed kropką miała co najmniej 2 znaki. I właśnie tu zaczyna się Twoja rola. Nie pisanie wyrażenia, tylko sprawdzenie przypadków brzegowych: krótkie domeny, plus w adresie (jan+test@firma.pl), poddomeny, kropki na początku i na końcu nazwy. Poproś AI o testy dla tych przypadków i sprawdź, które przechodzą.
Przy okazji: do walidacji e-maila często w ogóle nie potrzebujesz takiego potwora. Wystarczy prosta kontrola (np. MailAddress.TryCreate albo atrybut [EmailAddress]), a jedynym naprawdę pewnym testem adresu i tak jest mail z linkiem aktywacyjnym.
#4 Szkielety testów. Arrange, Act, Assert, setup mocków, dane testowe. AI wygeneruje większość mechaniki testu w kilka sekund. Na przykład taki test jednostkowy:
[Fact]
public async Task GetUserAsync_WhenUserDoesNotExist_ReturnsNull()
{
/* Arrange */
var repository = new Mock<IUserRepository>();
repository
.Setup(r => r.GetByIdAsync(42))
.ReturnsAsync((User?)null);
var service = new UserService(repository.Object);
/* Act */
var result = await service.GetUserAsync(42);
/* Assert */
Assert.Null(result);
}Szkielet jak z podręcznika, wygenerowany w 5 sekund. Ale zwróć uwagę, gdzie leży wartość tego testu. Nie w składni Moq ani w atrybucie [Fact], tylko w scenariuszu: co się stanie, gdy użytkownika nie ma. Składnia to tylko opakowanie.
I tu uwaga: to Ty decydujesz, co testować. Użytkownik nie istnieje, jest zablokowany, ma niekompletne dane, repozytorium rzuca wyjątek - wymyślenie takich przypadków brzegowych i reguł logiki biznesowej to nadal Twoja głowa. AI chętnie napisze testy do wszystkiego, ale nie wie, który scenariusz kosztowałby Twoją firmę najwięcej.
Odpuść #5-#7: konfiguracja, dokumentacja i migracje
#5 Pliki konfiguracyjne. Dockerfile, docker-compose, pipeline'y CI/CD. Klasyka gatunku "napisz raz, googluj do końca życia". AI odtworzy tę składnię szybciej, niż Ty ją wygooglujesz. Przykład: pipeline dla .NET w Azure DevOps, który buduje projekt i uruchamia testy:
trigger:
- main
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '10.0.x'
- script: dotnet build --configuration Release
displayName: 'Build'
- script: dotnet test --configuration Release --no-build
displayName: 'Testy'Wygenerowany w 5 sekund. Działa. Następny temat.
Ty masz rozumieć, co konfigurujesz: że build idzie w konfiguracji Release, że testy nie budują projektu drugi raz (--no-build), na jakim obrazie to wszystko działa. A nie pamiętać, ile spacji ma wcięcie w YAML-u.
#6 Dokumentacja i komentarze. XML docs, README, opisy pull requestów. AI może napisać je bezpośrednio z kodu - szybko i zwykle wystarczająco dobrze. Opis pull requesta dostajesz na przykład jednym poleceniem:
"Na podstawie tego diffu napisz opis pull requesta: co się zmieniło, dlaczego i jak to przetestować."
Twoja rola to sprawdzić, czy dokumentacja jest prawdziwa, kompletna i oddaje intencję. AI dobrze opisze, co kod robi, ale nie zawsze wie, dlaczego zrobiliście to akurat tak. To "dlaczego" dopisujesz Ty. Klepanie dokumentacji ręcznie od zera? Strata czasu.
#7 Konwersje i migracje. JSON na klasy C#. SQL na LINQ. Newtonsoft.Json na System.Text.Json. Stary .NET Framework na nowoczesny .NET. To mechaniczna robota według znanych reguł - czyli dokładnie to, w czym AI jest najlepsze. Przykład z migracji Newtonsoft.Json:
/* Przed: Newtonsoft.Json */
[JsonProperty("first_name")]
public string FirstName { get; set; }
var user = JsonConvert.DeserializeObject<User>(json);
/* Po: System.Text.Json */
[JsonPropertyName("first_name")]
public string FirstName { get; set; }
var user = JsonSerializer.Deserialize<User>(json);W dużym projekcie takich zamian są setki i AI zrobi je bez zmęczenia. Ty pilnujesz różnic w zachowaniu, a nie w składni. Przykład: System.Text.Json domyślnie rozróżnia wielkość liter w nazwach właściwości przy deserializacji (poza ASP.NET Core, które używa ustawień webowych), a Newtonsoft.Json nie. Kod się skompiluje, a część pól nagle przestanie się wczytywać. Takie rzeczy wyłapują testy i uważne oko, a nie kompilator.
Ściąga: co odpuszczasz i co zostaje po Twojej stronie. Zbierzmy całą pierwszą listę w jednym miejscu:
| Czego nie wkuwać | Co robi AI | Co zostaje po Twojej stronie |
| Składnia na pamięć | przekopuje dokumentację, podpowiada formaty i przeciążenia | wiedzieć, co chcesz osiągnąć, i sprawdzić wynik |
| Boilerplate (DTO, mappery, CRUD-y) | generuje go w kilka sekund, bez literówek | sprawdzić, czy nic nie wycieka poza serwer |
| Regexy | pisze wyrażenie, wyjaśnia je i generuje testy | przypadki brzegowe |
| Szkielety testów | Arrange, Act, Assert, mocki, dane testowe | decyzja, co testować: edge case'y i logika biznesowa |
| Pliki konfiguracyjne | składnia Dockerfile'a, docker-compose i YAML-a | rozumieć, co konfigurujesz |
| Dokumentacja | XML docs, README i opisy PR prosto z kodu | prawdziwość, kompletność i intencja ("dlaczego") |
| Konwersje i migracje | mechaniczne zamiany według znanych reguł | różnice w zachowaniu i testy |
Skoro AI pisze kod, to po co uczyć się programowania?
No dobra. Skoro AI pisze składnię, boilerplate, testy i konfiguracje, to po co w ogóle uczyć się programowania?
Odpowiedź może zaskoczyć: dziś musisz umieć więcej niż programiści przed erą AI. Tylko zupełnie co innego. Spójrz jeszcze raz na ostatnią kolumnę tabeli powyżej. W każdym wierszu AI pisze, a Ty sprawdzasz, decydujesz albo rozumiesz. Tyle że żeby sensownie ocenić cudzy kod, często trzeba wiedzieć więcej, niż żeby go napisać. A tej wiedzy nie da się wygenerować promptem.
Dlatego teraz najważniejsza część tego artykułu: 6 rzeczy, których AI za Ciebie nie zrobi. To tutaj rozstrzyga się, kto za 5 lat będzie jeszcze w tym zawodzie.
Opanuj #1: fundamenty .NET (pułapka async void)
#1 Fundamenty .NET. Garbage collector. Typy wartościowe kontra referencyjne. Async/await - ale pod maską, a nie na poziomie "dodaję słówko await i działa". Dlaczego to takie ważne? Bo AI generuje kod, który się kompiluje. A to nie zawsze jest kod, który działa.
Przykład z życia. AI potrafi wygenerować coś takiego:
/* Wygenerowane przez AI - kompiluje się bez ostrzeżeń */
public async void SendWelcomeEmail(User user)
{
await _emailSender.SendAsync(user.Email, "Witaj na pokładzie!");
}
/* Gdzieś w kontrolerze */
try
{
SendWelcomeEmail(user); /* nie ma Taska - nie ma na co czekać */
}
catch (Exception ex)
{
/* ten catch NIGDY nie złapie wyjątku z SendAsync */
_logger.LogError(ex, "Nie udało się wysłać maila");
}Kompilator? Ani słowa. Aplikacja? Działa. Do momentu, aż poleci wyjątek.
Metoda async void nie zwraca Taska, więc wywołujący nie ma na co czekać ani czego obsłużyć. Wyjątek nie trafi do bloku catch, tylko zostanie rzucony "obok". W ASP.NET Core, gdzie nie ma SynchronizationContext, ląduje na wątku z puli i potrafi położyć cały proces. Do tego wywołujący nie wie, kiedy metoda się skończyła, więc request może się zakończyć wcześniej, a razem z nim zwolnione zostaną zależności, z których ta metoda jeszcze korzysta (np. DbContext). I zaczyna się polowanie na błąd, który "czasem występuje".
Poprawka jest banalna - pod warunkiem, że wiesz, czego szukać:
public async Task SendWelcomeEmailAsync(User user)
{
await _emailSender.SendAsync(user.Email, "Witaj na pokładzie!");
}
/* Wywołanie */
await SendWelcomeEmailAsync(user); /* wyjątek wraca normalnie do wywołującego */Jedyny rozsądny wyjątek od tej reguły to handlery zdarzeń (np. w WinForms czy WPF), których sygnatura wymusza void. Wszędzie indziej async void to sygnał alarmowy. Wyłapiesz go jednak tylko wtedy, gdy rozumiesz, jak działa async/await. Nie ma drogi na skróty.
Opanuj #2-#4: architektura, czytanie kodu i debugowanie
#2 Architektura i decyzje. Monolit czy mikroserwisy? Jaka baza danych? Gdzie postawić granice modułów? AI z radością zaimplementuje każdą Twoją decyzję. Również tę fatalną.
Trade-offy wymagają kontekstu: zespołu, budżetu, deadline'ów. A tego kontekstu AI samo z siebie nie ma. Zapytane o mikroserwisy, wypisze Ci zgrabną listę zalet i wad. Ale nie wie, że Twój zespół to kilka osób, że nikt u Was nie utrzymywał wcześniej systemu rozproszonego i że za pół roku firma może zmienić model biznesowy. Decyzja - i odpowiedzialność za nią - zostaje po Twojej stronie.
#3 Czytanie i ocena kodu. To zmiana, której większość programistów nie zauważyła: piszesz coraz mniej, a czytasz coraz więcej. Code review kodu od AI staje się Twoją główną umiejętnością. Musisz widzieć błąd szybciej, niż AI go generuje. A generuje szybko.
W praktyce oznacza to, że trening czytania kodu - cudzego, starego, wygenerowanego - jest dziś wart więcej niż trening szybkiego pisania. async void z poprzedniego punktu i SQL injection z następnego to dokładnie te rzeczy, przy których powinna Ci się zapalić lampka już przy pierwszym spojrzeniu.
#4 Debugowanie. Produkcja padła o 3 w nocy. AI samo z siebie nie zna Twojego systemu, Twoich logów, Twojej infrastruktury i tego dziwnego serwisu, którego nikt nie chce dotykać. Może pomóc przeanalizować stack trace albo fragment logów, ale najpierw ktoś musi wiedzieć, gdzie szukać, co mu pokazać i które tropy odrzucić. Ktoś musi połączyć kropki. I zgadnij co - to jesteś Ty.
Opanuj #5: bezpieczeństwo (SQL injection od AI)
#5 Bezpieczeństwo. Mało kto o tym mówi: modele AI uczono między innymi na publicznym kodzie. Czyli także na kodzie słabym. SQL injection, sekrety wklejone na sztywno, brak autoryzacji - to wszystko było w danych treningowych i potrafi wrócić w wygenerowanym kodzie. Jesteś ostatnią linią obrony przed tym, co trafia na produkcję.
To nie jest teoretyczny straszak. Takie rzeczy potrafią wypaść z modeli:
/* Wygenerowane przez AI */
var sql = $"SELECT * FROM Users WHERE UserName = '{userName}'";
using var command = new SqlCommand(sql, connection);
using var reader = await command.ExecuteReaderAsync();Sklejanie SQL-a ze stringów. Podatność jak z podręcznika. Wystarczy, że ktoś wpisze jako nazwę użytkownika ' OR 1=1 -- i zapytanie zamienia się w SELECT * FROM Users WHERE UserName = '' OR 1=1 --', czyli zwraca wszystkich użytkowników z tabeli. A to dopiero łagodna wersja ataku.
Poprawka to parametry - wartość od użytkownika nigdy nie staje się częścią tekstu zapytania:
const string sql = "SELECT * FROM Users WHERE UserName = @userName";
using var command = new SqlCommand(sql, connection);
command.Parameters.Add("@userName", SqlDbType.NVarChar, 100).Value = userName;
using var reader = await command.ExecuteReaderAsync();Jeśli używasz EF Core, zasada jest ta sama: FromSql z interpolowanym stringiem zamienia {userName} na parametr, ale FromSqlRaw ze sklejonym stringiem już nie. Kilka liter różnicy w nazwie metody, a dzielą bezpieczny kod od podatności.
AI nie poniesie odpowiedzialności za taki kod. Ty tak.
Opanuj #6: rozumienie biznesu. Twoja nowa rola
#6 Rozumienie biznesu. AI samo z siebie nie wie, po co powstaje aplikacja, nad którą pracujesz. Nie porozmawia za Ciebie z klientem, nie wyłapie kontekstu z pokoju i nie powie: "tego nie róbmy, bo za rok będzie problem". A często to właśnie takie jedno zdanie oszczędza firmie więcej niż najlepiej napisany kod.
Programista, który rozumie domenę - wie, jak w jego firmie działa fakturowanie, logistyka czy rozliczenia - jest dziś dużo trudniejszy do zastąpienia. I dobrze opłacany.
Twoja nowa rola: wczoraj i dziś. Widzisz już, co się stało? Twoja rola się zmienia:
• wczoraj: klepiesz kod linijka po linijce,
• dziś: projektujesz, delegujesz i oceniasz.
To jest awans - o ile go przyjmiesz.
Jak pracować z AI w .NET: moje 4 zasady
Wszystko powyżej brzmi dobrze w teorii, więc na koniec konkret: 4 zasady, których sam trzymam się na co dzień.
Zasada 1: Ty myślisz, AI pisze. Zanim napiszę prompt, mam plan: co budujemy, po co i jakie są przypadki brzegowe. Prosta zależność: im lepiej rozumiesz problem, tym lepszy kod dostajesz. Śmieciowy prompt - śmieciowy kod. Porównaj:
"Napisz rejestrację użytkownika."
z czymś takim:
"Napisz metodę RegisterAsync w UserService (ASP.NET Core, EF Core). Wejście: e-mail i hasło. Jeśli e-mail jest już zajęty, zwróć błąd walidacji zamiast rzucać wyjątek. Hasło hashuj przez PasswordHasher z ASP.NET Core Identity. Nie pisz kontrolera, tylko samą metodę."
Drugi prompt to nie żadna magia promptowania, tylko efekt tego, że autor wcześniej przemyślał problem.
Zasada 2: Małe kroki. 1 metoda. 1 klasa. Nie "napisz mi całą aplikację". Mały kawałek kodu jesteś w stanie zweryfikować w minutę. W dużym błędy chowają się tygodniami. Zauważ, że prompt z zasady 1 kończy się zdaniem "nie pisz kontrolera, tylko samą metodę" - to właśnie małe kroki w praktyce.
Zasada 3: Czytaj kod od AI jak pull request od juniora. Każdą linijkę. Bez wyjątków. Złota reguła: nie rozumiesz - nie wklejasz. Kod od AI to propozycja, nie wyrocznia. Gdyby junior przysłał Ci async void albo zapytanie sklejane ze stringów, nie zmergowałbyś tego tylko dlatego, że "u niego działa". Z AI jest dokładnie tak samo.
Zasada 4: Pytaj "dlaczego?". To moja ulubiona. Dlaczego użyłeś tutaj ConfigureAwait(false)? Dlaczego HashSet, a nie List? AI może być najtańszym mentorem na rynku, dostępnym 24 godziny na dobę. Każdy wygenerowany kod to lekcja - jeśli tylko zadasz pytanie.
Takie pytania często obnażają też słabości samego kodu. Pytając o HashSet, dowiesz się, że Contains w HashSet działa średnio w czasie stałym, a w List musi przejść po elementach - i od razu wiesz, kiedy ta zmiana ma sens. A pytając o ConfigureAwait(false) w kontrolerze ASP.NET Core, usłyszysz (o ile model odpowie uczciwie), że tutaj właściwie nic nie zmienia, bo ASP.NET Core nie ma SynchronizationContext - ma to znaczenie głównie w bibliotekach. 1 pytanie i zamiast kopiować nawyk, rozumiesz, skąd się wziął.
Podsumowanie
Zbierzmy wszystko w 2 listach.
Odpuszczasz - to oszczędność czasu:
• składnię na pamięć,
• boilerplate,
• ręczne regexy,
• szkielety testów,
• pliki konfiguracyjne,
• dokumentację pisaną od zera,
• konwersje i migracje.
Opanowujesz - to Twoja kariera:
• fundamenty .NET (GC, typy, async/await pod maską),
• architekturę i decyzje,
• czytanie i ocenę kodu,
• debugowanie,
• bezpieczeństwo,
• rozumienie biznesu.
A w codziennej pracy trzymasz się 4 zasad: Ty myślisz, AI pisze; małe kroki; czytasz kod od AI jak PR od juniora; pytasz "dlaczego?".
I tu jest cały paradoks: AI nie zabiera pracy wszystkim programistom. Najpierw zabiera przewagę tym, którzy zatrzymali się na pierwszej liście. Ci, którzy postawili na drugą, dostali jedno z najmocniejszych narzędzi w historii tego zawodu.
Żebyś mógł wykorzystać to od razu, przygotowałem darmowy PDF: 25 gotowych promptów AI dla programistów C#/.NET. 5 obszarów, w których AI realnie oszczędza czas: refaktoryzacja, testy jednostkowe, debugowanie, code review i legacy. Kopiujesz, wklejasz w Claude Code albo Copilota - i od razu masz punkt startowy.
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: co tydzień 1 konkretny mail o .NET i AI, plus rabaty na moje kursy, których nie znajdziesz nigdzie indziej. Zero spamu - wypisujesz się jednym kliknięciem.
Powodzenia w nauce - tej, która naprawdę się opłaca.