YAGNI w .NET - kiedy upraszcza architekturę, a kiedy szkodzi?

Radosław Krajewski 15 lipca 2026
Zgniecione kartki papieru wokół notatnika, symbolizujące frustrację i potrzebę zasady YAGNI – nie dodawaj funkcjonalności, której nie potrzebujesz.

Spis treści

Projekt zaczyna się niewinnie, a po kilku sprintach pojawiają się interfejsy bez drugiej implementacji, konfiguracja pod trzy nieistniejące wdrożenia i moduły, których nikt nie potrafi już bezpiecznie zmienić. Zasada YAGNI pomaga odróżnić rozsądne przygotowanie architektury od kodowania hipotetycznej przyszłości. Pokażę, jak stosować ją w aplikacjach .NET, gdzie daje największe korzyści, a także kiedy jej bezrefleksyjne użycie może stworzyć dług techniczny.

Najważniejsze decyzje architektoniczne wynikają z realnych potrzeb

  • YAGNI oznacza, że nie implementujemy funkcji, abstrakcji ani rozszerzeń, dopóki nie są rzeczywiście potrzebne.
  • Zasada ogranicza złożoność, koszt utrzymania i liczbę miejsc podatnych na błędy.
  • W .NET często dotyczy nadmiarowych interfejsów, generycznych repozytoriów, mikroserwisów i konfiguracji tworzonych na zapas.
  • YAGNI nie oznacza rezygnacji z jakości, testów ani bezpieczeństwa. Oznacza świadome odraczanie niepotrzebnych decyzji.
  • Nie warto stosować tej zasady do wymagań prawnych, bezpieczeństwa, obserwowalności ani kosztownych zmian infrastruktury.

YAGNI i DRY są dobre, ale KISS jest najlepszy. Unikaj zbędnych rzeczy, skup się na prostocie.

Czym jest YAGNI i jaki problem rozwiązuje

YAGNI to skrót od You Ain’t Gonna Need It, czyli „prawdopodobnie nie będzie ci to potrzebne”. W anglojęzycznych materiałach spotkasz określenie yagni principle, ale w praktyce najważniejsze jest nie brzmienie nazwy, tylko decyzja stojąca za tą zasadą. Nie tworzę kodu na podstawie przypuszczeń, jeśli obecne wymagania go nie uzasadniają.

Najczęstszy problem nie polega na tym, że programista nie potrafi zaprojektować rozbudowanej architektury. Problem w tym, że projektuje ją za wcześnie. Dodanie obsługi wielu baz danych, kilku dostawców płatności czy własnego systemu pluginów może wyglądać profesjonalnie, ale każda taka decyzja zwiększa liczbę elementów, które trzeba testować, dokumentować i utrzymywać.

Ważny jest też koszt odwrócenia decyzji. Nie każda zmiana wymaga przygotowania z wyprzedzeniem. W dobrze testowanym kodzie zamiana jednej implementacji na inną może być prostsza niż utrzymywanie przez rok abstrakcji, która od początku nie miała realnego użytkownika.

Patrzę na YAGNI jako na zasadę zarządzania niepewnością. Nie przewiduję dokładnie, jak produkt będzie wyglądał za dwa lata, tylko buduję taki punkt startowy, który pozwala bezpiecznie zareagować, gdy pojawią się nowe wymagania.

Jak YAGNI wpływa na architekturę aplikacji .NET

W ekosystemie .NET łatwo wygenerować dużo kodu zanim powstanie pierwsza działająca funkcja. Szablony, biblioteki i popularne wzorce są użyteczne, ale mogą też zachęcać do dokładania warstw tylko dlatego, że są modne. Architektura powinna wynikać z problemu biznesowego, a nie z liczby projektów w solution.

Interfejs dopiero wtedy, gdy daje konkretną wartość

Interfejs ma sens, gdy istnieje potrzeba wymiany implementacji, izolowania zależności, testowania granicy albo komunikacji między modułami. Jeśli klasa ma jednego konsumenta i jedną implementację, dodatkowe IFoo może być tylko pośrednikiem utrudniającym nawigację po kodzie.

Nie traktuję tego jako zakazu stosowania interfejsów. Dla adaptera do zewnętrznego API, zegara systemowego czy repozytorium będącego granicą infrastruktury interfejs może być bardzo praktyczny. YAGNI pyta raczej, czy dana abstrakcja rozwiązuje dzisiejszy problem, a nie czy wygląda zgodnie z podręcznikowym wzorcem.

Repozytorium generyczne nie jest obowiązkowym elementem

Wiele projektów zaczyna się od generycznego repozytorium z metodami Add, Update, Delete i GetAll. Przy Entity Framework Core taka warstwa często powiela możliwości DbContext, ukrywa zapytania i utrudnia korzystanie z mechanizmów ORM.

Jeśli aplikacja ma złożone zapytania, lepiej zacząć od konkretnych operacji związanych z przypadkami użycia. Abstrakcja powinna ukrywać zmienność, a nie tylko przenosić wywołania z jednej klasy do drugiej.

Nie każda aplikacja potrzebuje mikroserwisów

Rozdzielenie systemu na mikroserwisy oznacza osobne wdrożenia, monitoring, komunikację, obsługę błędów i często większe wymagania dotyczące DevOps. Dla niewielkiego produktu modularny monolit może dać lepszą szybkość pracy i prostsze debugowanie.

W praktyce wolę najpierw wydzielić granice modułów w kodzie, a dopiero później rozważać procesy i wdrożenia niezależne od siebie. Możliwość późniejszego podziału nie wymaga budowania mikroserwisów od pierwszego dnia, ale wymaga sensownego uporządkowania zależności.

Praktyczne przykłady stosowania zasady w C#

Najłatwiej zrozumieć YAGNI na małych decyzjach. Każda z nich wydaje się nieistotna, ale wiele podobnych wyjątków potrafi zbudować system trudny do rozwijania.

Jeden sposób wysyłki wiadomości

Załóżmy, że aplikacja wysyła powiadomienia e-mail. Nie ma jeszcze SMS-ów, pushy ani integracji z komunikatorem. W takiej sytuacji prosty serwis może być wystarczający.

public sealed class EmailNotificationService
{
    public Task SendAsync(string recipient, string message)
    {
        // Wysyłka wiadomości e-mail
        return Task.CompletedTask;
    }
}

Tworzenie od razu modelu NotificationChannel, fabryki kanałów, konfiguracji priorytetów i dziesięciu interfejsów nie przybliża produktu do działania. Gdy pojawi się drugi kanał, można wydzielić wspólną abstrakcję na podstawie rzeczywistych różnic. Najpierw poznaję kształt problemu, potem uogólniam rozwiązanie.

Jedna reguła biznesowa nie potrzebuje silnika reguł

Jeżeli rabat wynika dziś z jednej prostej zasady, warunek w serwisie aplikacyjnym może być czytelniejszy niż własny silnik reguł. Rozbudowany mechanizm będzie uzasadniony dopiero wtedy, gdy reguły są liczne, zmienne, konfigurowalne przez użytkowników albo wymagają wersjonowania.

To nie znaczy, że należy pisać wszystko w kontrolerze. Regułę można umieścić w nazwanej metodzie domenowej lub serwisie, ale bez projektowania języka deklaratywnego, którego nikt jeszcze nie potrzebuje.

Jedna baza i jedna chmura na początek

W aplikacji wdrażanej w Azure ktoś może zaplanować jednocześnie obsługę SQL Database, PostgreSQL, Cosmos DB i lokalnego cache. Jeśli wymagania wskazują jedną relacyjną bazę danych, taki plan najczęściej tylko zwiększy koszty oraz liczbę decyzji operacyjnych.

Rozsądniej jest zadbać o backup, migracje, monitoring i limity kosztów dla używanej usługi. Przenośność do innego dostawcy może być ważna, ale powinna wynikać z umowy, regulacji, ryzyka biznesowego lub rzeczywistego planu migracji.

Funkcja AI bez całej platformy agentowej

Jeśli produkt potrzebuje jednego scenariusza generowania opisu, nie trzeba od razu budować orkiestratora agentów, systemu pluginów i abstrakcji dla pięciu modeli. Wystarczy ograniczona integracja z jasno określonym wejściem, wyjściem, logowaniem i obsługą błędów.

W tym obszarze szczególnie łatwo pomylić eksperyment z produktem. Najpierw mierzę jakość i koszt konkretnego przypadku użycia, a dopiero później inwestuję w routing modeli, pamięć konwersacji czy wieloetapowe workflow.

Jak stosować YAGNI bez tworzenia długu technicznego

Odraczanie funkcji nie może oznaczać ignorowania jakości kodu. Dobra implementacja może być mała, ale nadal powinna mieć czytelne nazwy, testy dla ważnych reguł i granice, które chronią system przed przypadkowym bałaganem.

Przed dodaniem abstrakcji zadaję sobie kilka prostych pytań:

  • Czy istnieje dziś więcej niż jedna implementacja albo realny plan jej użycia?
  • Czy ta decyzja wynika z wymagania biznesowego, technicznego lub prawnego?
  • Czy późniejsza zmiana będzie droga, ryzykowna albo niemożliwa bez migracji danych?
  • Czy dodatkowa warstwa upraszcza kod, czy tylko zmienia miejsce, w którym znajduje się ta sama logika?
  • Czy potrafię wskazać konkretny przypadek użycia, który uzasadnia koszt rozwiązania?

Pomaga mi także rozróżnienie między decyzjami łatwymi i trudnymi do odwrócenia. Zmiana nazwy klasy albo wydzielenie interfejsu zwykle jest tania. Migracja danych, wybór modelu tożsamości, sposób przechowywania sekretów czy kontrakt publicznego API mogą wymagać wcześniejszego namysłu. YAGNI najlepiej działa przy decyzjach odwracalnych.

Warto zostawić ślad po świadomym odroczeniu. Krótka notatka w zadaniu, decyzji architektonicznej albo dokumentacji może wyjaśniać, dlaczego system obsługuje jeden wariant. Dzięki temu zespół nie będzie przypadkiem traktował ograniczenia jako przeoczenia.

Nie odkładam też fundamentów, które są tanie teraz, a drogie później. Dotyczy to między innymi automatycznych testów, logowania błędów, kontroli dostępu, migracji bazy i podstawowej obserwowalności. Brak funkcji to nie to samo co brak zabezpieczeń.

Kiedy YAGNI nie powinno decydować

Największy błąd polega na zamianie YAGNI w wymówkę dla krótkowzroczności. Zasada nie mówi, żeby ignorować wymagania, tylko żeby nie implementować rzeczy hipotetycznych. Są obszary, w których przewidywanie konsekwencji jest częścią profesjonalnej pracy.

Bezpieczeństwo i zgodność

Jeśli aplikacja przetwarza dane osobowe, płatności albo informacje poufne, mechanizmy ochronne trzeba zaplanować przed wdrożeniem. Nie odkładam szyfrowania, zarządzania sekretami, kontroli uprawnień czy audytowania tylko dlatego, że pierwszy użytkownik jeszcze nie zgłosił problemu.

Wymagania niefunkcjonalne

Wydajność, dostępność, limity i odzyskiwanie po awarii nie zawsze wymagają rozbudowanej architektury, ale powinny mieć określony poziom docelowy. Jeśli system musi obsłużyć 10 tysięcy żądań na minutę albo działać z dostępnością 99,9 procent, nie można udawać, że te wymagania pojawią się dopiero później.

Publiczne API i migracje danych

Każdy publiczny kontrakt może stać się zobowiązaniem na lata. Tutaj ostrożność jest rozsądna, szczególnie gdy z API korzystają zewnętrzni klienci. Podobnie z modelem danych. Zmiana schematu po uruchomieniu produktu bywa znacznie droższa niż przemyślenie kluczowych relacji i identyfikatorów wcześniej.

Przeczytaj również: Pętla do-while w C# - składnia, przykłady i pułapki

Decyzje narzucone przez infrastrukturę

Czasem konkretny wybór chmury, systemu kolejkowego albo dostawcy tożsamości wynika z umowy lub standardu organizacji. W takim przypadku nie buduję abstrakcji dla samej abstrakcji, ale też nie ignoruję ograniczeń, które mogą utrudnić zmianę. Trzeba zapisać, co jest świadomym kompromisem, a co faktyczną blokadą.

YAGNI, KISS, DRY i SOLID nie mówią tego samego

Te zasady często pojawiają się razem, lecz rozwiązują różne problemy. Ich mechaniczne stosowanie może prowadzić do sprzecznych decyzji, dlatego zawsze patrzę na konkretny koszt i korzyść.

Zasada Na czym się skupia Praktyczne pytanie
YAGNI Odraczaniu niepotrzebnych funkcji i abstrakcji Czy naprawdę potrzebujemy tego teraz?
KISS Prostocie rozwiązania Czy da się to wyjaśnić i utrzymać prościej?
DRY Ograniczaniu powielonej wiedzy Czy jedna reguła nie występuje w kilku miejscach?
SOLID Projektowaniu zależności i odpowiedzialności Czy kod da się zmieniać bez naruszania całego systemu?

Przykładowo, DRY może skłaniać do połączenia dwóch podobnych fragmentów, ale YAGNI przypomina, żeby nie tworzyć przedwcześnie uniwersalnego frameworka. Z kolei SOLID nie wymaga, by każda klasa miała interfejs. Dobra architektura jest kompromisem, a nie kolekcją zastosowanych akronimów.

Najbezpieczniejsza kolejność wygląda dla mnie tak. Najpierw rozumiem wymaganie, potem tworzę prostą implementację, a następnie obserwuję, gdzie naprawdę pojawia się powtarzalność lub zmienność. Dopiero wtedy refaktoryzuję. Testy i automatyzacja pozwalają zrobić to bez obawy, że każda poprawa naruszy istniejące zachowanie.

Najlepszy moment na rozszerzenie rozwiązania pojawia się wraz z dowodem

YAGNI nie zachęca do pisania byle jak. Zachęca do ograniczenia zakresu do tego, co ma dziś uzasadnienie, przy zachowaniu jakości, bezpieczeństwa i możliwości dalszej zmiany. W aplikacji .NET zwykle oznacza to mniej pustych interfejsów, mniej warstw tworzonych z przyzwyczajenia i więcej kodu, który odpowiada na realny przypadek użycia.

Gdy nowe wymaganie rzeczywiście się pojawi, decyzja architektoniczna będzie oparta na danych, a nie na wyobrażeniu przyszłości. To właśnie wtedy warto inwestować w abstrakcję, skalowanie lub dodatkowy moduł, bo system pokazał już, czego naprawdę będzie potrzebował.

FAQ - Najczęstsze pytania

Interfejs ma sens, gdy istnieje więcej niż jedna implementacja, trzeba odizolować zależność, testować granicę albo komunikować się między modułami. Przy jednej klasie, jednym konsumencie i braku realnej potrzeby wymiany implementacji może tylko utrudniać nawigację po kodzie.

Generyczne repozytorium z metodami Add, Update, Delete i GetAll często powiela możliwości DbContext, ukrywa zapytania i utrudnia korzystanie z ORM. Przy złożonych operacjach lepiej zacząć od konkretnych metod związanych z przypadkami użycia, a abstrakcję wprowadzić dopiero wtedy, gdy rzeczywiście ukrywa zmienność.

Nie należy odkładać wymagań bezpieczeństwa, zgodności, kontroli dostępu, zarządzania sekretami, audytowania ani podstawowej obserwowalności. Wcześniejszego namysłu mogą wymagać także publiczne API, migracje danych, wymagania dostępności i wydajności oraz decyzje narzucone przez infrastrukturę lub umowę.

YAGNI ogranicza przedwczesne funkcje i abstrakcje, KISS skupia się na prostocie, DRY na ograniczaniu powielonej wiedzy, a SOLID na zależnościach i odpowiedzialnościach. Zasady mogą prowadzić do różnych decyzji, dlatego najpierw warto zrozumieć wymaganie, wdrożyć prostą wersję i refaktoryzować po pojawieniu się rzeczywistej powtarzalności lub zmienności.

Oceń artykuł

Ocena: 0.00 Liczba głosów: 0

Tagi

mikroserwisy
repozytoria
refaktoryzacja
yagni
abstrakcje
Autor Radosław Krajewski
Radosław Krajewski
Nazywam się Radosław Krajewski i od 6 lat zgłębiam tajniki programowania .NET, chmury Azure oraz sztucznej inteligencji. Moja przygoda z tymi technologiami zaczęła się od fascynacji tym, jak złożone problemy można rozwiązywać za pomocą kodu i innowacyjnych narzędzi. Staram się przekazywać tę wiedzę w sposób zrozumiały, dzieląc się swoimi doświadczeniami i spostrzeżeniami na kursdotnet.pl. W moich artykułach skupiam się na praktycznych aspektach, porównuję różne rozwiązania i analizuję najnowsze trendy, aby dostarczyć Wam rzetelne i aktualne informacje, które pomogą Wam rozwijać się w tej dynamicznie zmieniającej się dziedzinie.

Udostępnij artykuł

Napisz komentarz