Programowanie przez eksplorację

20

Tyle się mówi o obowiązku ciągłego rozwoju w zawodzie programisty. “Przynajmniej jeden nowy język w roku!” “Przynajmniej jeden nowy framework na kwartał!” “Przynajmniej jedna książka techniczna na miesiąc!” Znacie te zawołania?

Wiecie co… nie do końca widzę w tym sens. Na pewno nie zamierzam twierdzić, że taki sposób zagospodarowania wolnego czasu jest zły – bo nie jest – ale na mnie takie podejście po prostu NIE DZIAŁA. Co z tego że rok temu przeczytałem od deski do deski książkę o WCF? Co z tego że półtora roku temu z zapałem pochłaniałem kolejne tutoriale o F#? Wreszcie: co z tego, że kiedyś poświęciłem wieczór na poznanie kilku najpopularniejszych bibliotek IoC/DI? Mówiąc szczerze: absolutnie nic. Nie tak dawno urzekły mnie Reactive Extensions. “Poznam tą bibliotekę na wylot, choćby nie wiem co!” – obiecywałem sobie. Ale niestety, bez możliwości praktycznego ich zastosowania w prawdziwym projekcie takie deklaracje umierają śmiercią tragiczną, jak zarazki polane Domestosem. To samo tyczy się wszelakich innych technologicznych zakamarów.

Przyczyna jest oczywista: w programowaniu teoria bez praktyki oznacza tyle samo co w jeździe samochodem. Można wyryć kodeks drogowy na pamięć, naczytać się o silnikach, zapisać do 10 forów motoryzacyjnych, i po stwierdzeniu “teraz to jestem zajebistym kierowcą” wylecieć z drogi na pierwszym zakręcie. Tak naprawdę jedyny efektywny sposób programistycznej edukacji to wykorzystanie w praktyce wybranych rozwiązań. Banalne? Może. Ale z pewnością dla wielu nie jest to takie jasne. Ja postanowiłem nie tracić czasu na puste zagłębianie się we wszelkie nowinki, bo NIC mi to nie dawało.

Czy nawołuję zatem do stagnacji i zastoju? Skądże znowu – nawołuję do czegoś wręcz przeciwnego. Think for yourself, question authority! Albo: Challenge everything! Znasz na wylot Unity? W następnym projekcie skorzystaj ze Structure Map. Znasz NHibernate? Użyj Entity Framework. Z zamkniętymi oczami napiszesz testy w nUnit? Zobacz co oferuje mbUnit albo MSTest.

Et cetera, et cetera… (cobym inteligentniej brzmiał).

To wszystko było tylko wstępem do podzielenia się swoimi doświadczeniami z kilku ostatnich tygodni. Mając do napisania niewielki projekt z bardzo luźnym harmonogramem postanowiłem wykorzystać jak najwięcej nowych… “rzeczy”. Sam się dziwię, że tak bardzo udało mi się poszerzyć horyzonty w tak krótkim czasie! Zobaczcie:

Visual Studio 2010

Mój “główny” projekt rozwijany jest cały czas w VS 2008, więc tutaj pozwoliłem sobie pobawić się trochę nowym Visualem i nowym Resharperem. Wrażenia jak najbardziej pozytywne. Nie wiem tylko które z mega-wypasionych ficzerów (szczególnie support dla javascript/css w edytorze HTML) znajdę w “gołym” VS, a które dodał Resharper. Ale nie jest to ważne, skoro mam jedno i drugie:).

MySQL

Do tej pory korzystałem właściwie tylko z Sql Server i (bardzo pobieżnie) Oracle. Tym razem zerknąłem jak można sobie poprogramować w .NET trzymając dane w darmowej MySQL. Rewelacji się nie spodziewałem – w końcu relacyjna baza to relacyjna baza. Bardziej ciekawiły mnie narzędzia (odpowiedniki Management Studio) oraz współpraca z .NET/Visual Studio. Wniosek: “skoro nie widać różnicy, to po co przepłacać?”;). W efekcie planuję przynajmniej dwa posty na ten temat.

Entity Framework

Używałem LLBLGen, używałem NHibernate, używałem Linq2Sql. Tym razem zdecydowałem się zerknąć na EF. Szczerze mówiąc na początku byłem bardzo pozytywnie zaskoczony – autentycznie fajnie się z tym pracuje! Niestety okazało się, że im dalej w las, tym więcej papierzaków. Spodziewałem się że wszystko załatwię LINQ, a rzeczywistość jest bardziej brutalna. Momentami od Entity SQL nie ma ucieczki, a zarządzanie z relacjami “wiele do wielu” czy operacje na całych kolekcjach obiektów są dość uciążliwe. Ale i tak ogólne wrażenie in plus. Do tego współpraca z MySQL, po przeskoczeniu pierwszych trudności, okazała się bezproblemowa.

Quartz .NET

Do tej pory wszystkie mechanizmy “operations” pisałem ręcznie. Coś ma się wykonać raz na sekundę, coś innego za 10 minut, coś jeszcze innego ma się wykonywać nieustannie w tle… nie ma problemu. Tu wątek, tam timer, trochę konfiguracji i śmiga. Pytanie tylko: po co, skoro jest narzędzie oferujące dokładnie takie funkcjonalności? Polecam zapoznać się z Quartz.NET, wraz z kilkoma spojrzeniami w kod źródłowy. Kawał porządnej roboty. A jakość dokumentacji/komentarzy wręcz mnie poraziła. W tym przypadku faktycznie WIDAĆ SENS komentowania kodu. I że ktoś to zrobił za darmo… Szacun. (uwaga: zwróćcie uwagę, że strona projektu wcale nie jest taka jak się na początku wydaje; domena przychodząca na myśl jako pierwsza oferuje… trochę inne treści;) ).

nLog

Od wielu miesięcy jestem oczarowany biblioteką log4net. Po obrzydzającym życie Logging Application Block z EntLiba to małe cudko wydawało mi się prawdziwym zbawieniem. Postanowiłem jednak zerwać z przyzwyczajeniem (i de facto standardem jeśli chodzi o logowanie w .NET) i dać szansę alternatywie – naszemu rodzimemu nLog. Ciężko było mi w to uwierzyć, ale już wiem – nie wrócę do log4net. nLog jest po prostu jeszcze wygodniejszy! A dlaczego? O tym postaram się skrobnąć kiedy indziej, jednak zagorzałym wykorzystywaczom log4neta polecam spojrzenie w jego stronę.

VsVim / ViEmu

Czy ktoś nie zna edytora tekstu Vi/Vim? Jeśli tak to… poznajcie:). Prezentuje on bardzo ciekawe podejście wywodzące się z obserwacji, że tak naprawdę więcej czasu traci się na nawigację po pliku niż faktyczne generowanie treści. Od bardzo dawna chciałem sprawdzić czy takie podejście sprawdzi się podczas programowania. Najpierw zainstalowałem bezpłatny dodatek do VS 2010 VsVim, potem wypróbowałem płatne ViEmu…. i niestety po dwóch dniach męczarni odinstalowałem oba. Okazało się, że Resharper wystarczy i takie zabawy bardziej mi przeszkadzały niż pomagały. Może gdybym pomęczył się jeszcze z tydzień dłużej, może gdybym dał kolejną szansę to coś produktywnego by z tego wyszło… ale na dzień dzisiejszy zostaję przy aktualnym edytorze.

MS Test

Przyznam, że do tej pory zawsze omijałem MS Test szerokim łukiem. Korzystałem albo z nUnit, albo z mbUnit. I w sumie nie wiedziałem dlaczego… przecież wbudowany w VS runner dla MSTest to naprawdę wielka zaleta, jeśli ktoś nie ma Resharpera. A czym takim niby może różnić się jedna biblioteka do testów jednostkowych od innej biblioteki do testów jednostkowych?

Ano… okazało się że może. Ludu mój ludu, nigdy więcej. Bardziej się tego chyba skomplikować nie da. Tu nie wystarczy dorzucić jednego i drugiego atrybutu żeby stworzyć test. Dodatkowo trzeba wypełnić jakąś chorą konfigurację, poczytać instrukcję… a ja chcę tylko odpalić test! Po TRZECH GODZINACH walki z atrybutem DeploymentItemAttribute, ustawianiem przeróżnych konfiguracji, męczenia się z tak banalnym zadaniem jak wykorzystanie zewnętrznego pliku w teście… powiedziałem sobie DOŚĆ. WIEM że to musi jakoś działać, WIEM że z pewnością wiele osób z tego korzysta, WIEM że dokumentacja na pewno jest poprawna… ale mimo to mi, zwykłemu kmiotowi, nie udało się zmusić tego szatańskiego pomiotu do wykonania odpowiednich zadań. Trzęsąc się z irytacji wróciłem do nUnit i po 5 minutach miałem wszystko działające tak jak chciałem, bez żadnego zbędnego wnikania w durne obejścia. Ale co się klawiaturze oberwało ciosów zadanych w furii, to się oberwało.

Moq

Bardzo dużo korzystałem z doskonałej biblioteki Rhino Mocks, a tylko raz kiedyś zerknąłem pobieżnie na Moq. Zdecydowałem zagłębić się w nią bardziej… i teraz już jestem pewny, że (przynajmniej na chwilę obecną) nie ma dla mnie powrotu do Rhino. Life is brutal.

FAKE – F# Make

Jeżeli chodzi o tzw. “build tools” to miałem do tej pory doświadczenie jedynie z MSBuildem. Welcome to XML-hell! Narzędzie co prawda robi co do niego należy, a po dodaniu darmowych MSBuild Community Tasks jego potęga wzrasta. Tylko co z tego, skoro korzystanie z niego jest po prostu… skomplikowane? Nie lubię skomplikowanych rozwiązań. W tym konkretnym projekcie spokojnie wystarczyłoby mi ‘ctrl+shift+B’ w Visualu, ale mimo to zachciało mi się i w tym zakresie spróbować czegoś… nowego. Odpada nAnt (nie po to uciekam od XMLa żeby wpaść w niego ponownie), odpada psake (nie lubię, nie lubię powershella – jak bym pracował z połamanymi palcami?). Pierwotnie zdecydowałem się na Rake (Ruby rulz! przynajmniej w teorii, bo nie miałem okazji pobawić się nim “na serio”), ale zaraz później oczom moim ukazał się Fake – F# Make. Jak się łatwo domyślić skrypty pisze się tam w F#. Wreszcie okazja żeby choć trochę liznąć tego języka w praktyce! Z efektów jestem bardzo zadowolony. A szczegóły – zapewne wkrótce.

Html Agility Pack

Dawno temu napisałem programik analizujący stronę komunikacji miejskiej i ściągający rozkład jazdy autobusów. string.IndexOf(), string.Substring(), string.SkomplikujSobieZycieJeszczeBardziej()… to byś wówczas mój świat. Trochę później ponownie parsowałem stronki HTML – tym razem szpanowałem sam przed sobą wyrażeniami regularnymi. Ależ to było głupie! Dla każdego potrzebującego dobrać się do zewnętrznych stron HTML rozwiązanie jest naprawdę jedno: Html Agility Pack. Reszta jest milczeniem (owies).

Mercurial ciąg dalszy

Po odejściu od SVN przerzuciłem się na Gita. Mniejsze projekciki przechowuję jednak w Mercurialu – nie dlatego, że Git mi w czymś nie odpowiada, tylko po prostu “aby zobaczyć jak to jest”. I jest… po prostu dobrze. Z każdym projektem przekonuję się do tego narzędzia coraz bardziej. Z pewnością nie daje tyle swobody i elastyczności co Git, jednak i tak poleciłbym go każdemu chcącemu zasmakować zdecentralizowanego podejścia do problemu kontroli wersji.

Autofac z ASP.NET MVC

Niejednokrotnie pisałem już o Autofac. Tym razem przekonałem się jak BARDZO prostsze jest wykorzystanie go w połączeniu z ASP.NET MVC w porównaniu do Unity. Miód i orzeszki (bez cudu).

Co niesie przyszłość

Planowałem też kilka innych “technologicznych odchyleń”, ale doszedłem do (chyba rozsądnego) wniosku że “co za dużo to niezdrowo”. Lista “tools to use” zawiera kilka interesujących pozycji. Na pierwszy ogień prawdopodobnie pójdzie mooTools jako alternatywa dla über-doskonałego jQuery (i mniej doskonałego, przynajmniej dla mnie, Prototype). Kiedyś spróbuję też doinstalować CodeRush do Resharpera – może produktywność skoczy jeszcze bardziej? Ciekawi mnie również Bazaar – kolejny popularny DVCS – cobym miał wyrobione zdanie na temat wszystkich najważniejszych graczy w tej konkurencji.

Jak widać, prawie wszystko jest u mnie póki co “wokoło-MS”. Ale… co tam, w końcu z tego właśnie pieca kładę ciepły żytni chleb na swój sękaty stół. Mam jednak nadzieję, że kiedyś uda mi się na poważne dać nura w cały ekosystem Ruby On Rails – ciągnie mnie tam od dłuższego czasu.

A co Wy sądzicie na ten temat? Macie okazje do eksperymentów? Czy raczej uczycie się “na sucho”? A może wcale, bo “po co, skoro to co mam dobrze działa?”

Share.

About Author

Programista, trener, prelegent, pasjonat, blogger. Autor podcasta programistycznego: DevTalk.pl. Jeden z liderów Białostockiej Grupy .NET i współorganizator konferencji Programistok. Od 2008 Microsoft MVP w kategorii .NET. Więcej informacji znajdziesz na stronie O autorze. Napisz do mnie ze strony Kontakt. Dodatkowo: Twitter, Facebook, YouTube.

20 Comments

  1. Różnie to bywa… Staram się wprowadzać nowe elementy, ale jak się pracuje w firmie i w zespole, to nie ma tak łatwo. Z technologią trzeba się dostosować do "reszty". A "reszta" bardzo często nie ma parcia do nauki. Znam nUnit, to po co będę się uczył mbUnita? Mamy sprawdzone rozwiązanie klasy ORM, to po co nam nHibernate? Z jednej strony to może smutne, ale z drugiej strony na naukę jest czas w domu i po godzinach, a nie w trakcie pracy. Jeżeli "tracisz" czas na naukę nowych technologii zamiast pisać kod produkcyjny, to zwyczajnie okradasz klienta. Z drugiej strony, jak jesteś głupi i zamiast skorzystać z dostępnego rozwiązania piszesz własne, bo nie wiesz, że tamto istnieje, to też okradasz klienta.
    Temat ogólnie jest trudny. Skłaniam się jednak ku temu, żeby próbować zachęcać ludzi do użycia nowej technologii (mi się ostatnio z dużym sukcesem udaje), ale równocześnie trzeba usiąść w domu i się czymś pobawić, jak się tego chce potem w pracy użyć.

  2. Bardzo ładny zestaw. Proponuję jeszcze spojrzeć na PostgreSQL.

  3. @leszcz:
    W sumie znam ten ból i był on jednym z powodów przejścia na freelancing. Jeśli mam płatne za projekt, a nie od godziny, to mogę sobie użyć czego mi się chce i spędzić nad nim 2x więcej czasu niż ze sprawdzonymi rozwiązaniami.

  4. Z oceną, że aby poznać trzeba cokolwiek naprawdę zrobić, zgadzam się w całej rozciągłości. Choć czytanie na pusto też sens ma, zdarzało mi się pożyczać pomysły na abstrakcje czy api z innych języków

    Staram się szukać sobie drobnych hobbistycznych projektów. Do poznawania nowych języków per se fajne są konkursy typu projecteuler albo spoj. Biblioteki – różnie, ale najlepiej się nadają hobbistyczne projekty….

    W firmie często jest problem o którym pisał już wyżej leszcz – nie programuję sam, jeśli coś wprowadzam to już od razu przynajmniej na tyle na poważnie, by mieć odwagę powiedzieć paru kolegom "też teraz to macie poużywać". A to łatwiej mówię gdy jakkolwiek przetestowałem.

    Narzędzi jako linuksiarz nie będę Ci komentował, może z dwoma wyjątkami: a) postgresql jest trochę bardziej wypieszczony niż mysql (i bardziej pod wieloma względami przypomina Oracle), b) opinia o wielkiej elastyczności gita w porównaniu z mercurialem to mit trochę, z grubsza to samo umieją tylko mercurial unika dziwacznych nazw i c) ciekawe makeidalne narzędzia to cons/scons

  5. mercurial ma rebase (http://mercurial.selenic.com/wiki/RebaseExtension), ma swoiste rebase z historią (http://mercurial.selenic.com/wiki/TransplantExtension) – formalnie jedno i drugie to pluginy ale "oficjalne", w pełni wspierany i dystrybuowany wraz z samym mercurialem (mercurialowcy zasadniczo starają się sporo funkcjonalności wyjąć do dodatków by można było to sobie selektywnie włączać i unikać listy stu komend). Nie jestem pewien co masz na myśli mówiąc o indeksie, jeśli inkrementalne zbieranie zmian do zacommitowania to jest parę sposobów, min. http://mercurial.selenic.com/wiki/RecordExtension.

    No ale dobra, nie o konkurencji dvcs-ów ten post ;-)

  6. @Gutek:
    Bardziej zależy mi na mojej wydajności przy korzystaniu z frameworka niż na wydajności samego frameworka:)

  7. @Mekk:
    No to zadałeś mi bobu:) Przy rozszerzeniach zatrzymałem się na "hg purge" więc na pewno zerknę na podesłane sugestie. A co do indexu to tak, chodziło mi dokładnie o to co napisałeś.

  8. Już myślałem że dałeś sobie spokój z (b)logowaniem, ale fajnie że znowu piszesz.
    Ciekawy artykuł – lista sprawdzonych rozwiązań zawsze pomaga w selekcji narzędzi do wykonywanych prac, fajnie jakbyś jeszcze proces poznawania/migracji do nowych toolsów dokumentował blogami :)

  9. Nie pial bym tak nad MySQL bo to straszna niedorobka jest jesli chodzi o skladnie MySQL, bezpieczenstwo danych, lista uprawnie (np trriger i wersje mysql), brak wielu kluczowych elementow (lub ich udawanie bez dzialania jak np. CHECK) etc.

    Dwa nie pewna (albo pewna …) przyszlosc mysql’a kupionego przez oracle

    Jak ktos wspomnial postgresql znacznie bardziej wypieszczony jest. I nalicencji BSD !

    Licencja mysql wcale nie jest darmowa …
    http://www.mysql.com/about/legal/licensing/index.html

    dodatkowo bardzo czesto sie zmieniala (szczegoly) wraz z przechodzeniem miedzy firmami tak wiec ostatecznie jest to dla mnie bardzo niepewny produkt.

    ZALETA: Latwa, szybka instalacja. Niestety po latach uzytkowania roznych baz SQL tylko to mi przychodzi do glowy. Dzis do malych projektow nawet sklonny bylbym uzyc SQLite zamiast MySQL.

  10. Co do VsVim i ViEmu, to żeby przyzwyczaić się do środowisk edyto-centrycznych potrzeba sporo czasu, a żeby opanować – jeszcze więcej :) Patrząc na guru vim-a i emacsa przyznam, że chyba warto. Sam aktualnie jestem na etapie opanowywania emacsa – i jest całkiem przyjemnie.

    Co do buildowania, to podpiszę się pod tym co napisał Mekk – scons jest całkiem fajne, spójrz w wolnej chwili na to.

  11. Z niecierpliwością czekałem na Twój kolejny post i jest :)
    Masz racje, że nauka bez praktyki to strata czasu, miałem tak gdy czytałem C# i .NET i się zapomniało :/

    Jeśli ma się luźniejszy harmonogram to można a nawet jest zalecane poeksperymentować z innymi narzędziami/ technologiami, aby odejść od monotonii ;)

    Ja teraz na projekcie dyplomowym korzystam z Silverlight, WCF i NHibernate i potwierdzam, że praktyka bardzo pomaga w nauce !

  12. Fajnie podsumowane. Co do założeń teoretycznych i późniejszego "prawdziwego życia" to absolutnie masz rację.

    Co do narzędzi:
    – Aż dziw mnie bierze, że tak super biblioteczka jak NLog ma tak małe "wzięcie" w naszym rodzimym kraju. Log4Net w porównaniu do NLog’a to taki koszmarek. Nawet do NHibernate napisałem swoją wtyczkę, aby móc logować querki NLogiem. :)

    – MySQL dla .NET’a przećwiczyłem razem z NHibernate’em i przekładaniec jednego projektu z MsSQL na MySQL i z powrotem to nic jak tylko zmiana konfiguracji, Amen. :)

    – IoC/DI – StructureMap. Innych nie próbowałem, a że mam własny frejmwork oparty o ten kontener i działa, więc właśnie nie ruszam! :))

    – jQuery – używam i nie zamierzam ruszać (jak na razie) nic innego. Mam trochę własnego (common’owego) kodu, który w prosty sposób używam w kolejnych projektach i wiele różnych problemów z UI mam rozwiązanych.

    Z nowych rzeczy, które stosunkowo niedawno ruszyłem to NServiceBus i powiem szczerze, że to narządko jest zarąbiste (do pewnych zastosowań oczywiście) :))

  13. Pozwolę sobie tak trochę pobocznie odnośnie głównego tematu. Rzeczywiście jest dużo świetnych narzędzi, komponentów czy bibliotek gotowych do natychmiastowego użycia. Np. NLog, log4Net itp. narzędzia umożliwiają dostosowanie operacji logowania do swoich własnych indywidualnych i często wyszukanych potrzeb. Co zrobić jednak w sytuacji, gdy chcę po prostu zapisać datę i komunikat do pliku tekstowego i tylko do pliku tekstowego (nie chce tego wysyłać mailem, zapisywać do dziennika zdarzeń)? Napisanie prostej klasy, która to zrobi jest banalne. Dopisanie mechanizmów zabezpieczających, synchronizacji wątkowej zajmie jeszcze dodatkowo odrobinę czasu. Dostanę jednak produkt skrojony na miarę własnych potrzeb i robiący dokładnie to, czego potrzebuję. Stawiam na wydajność, by kod nie realizował 50/500/5000 niepotrzebnych operacji, niech zrobi jedną: zapisze do pliku. Czy pisać więc własne rozwiązania, które zajmują oczywiście czas pracodawcy ale są też skrojone na miarę czy użyć gotowca? Podobny dylemat mam z pisaniem własnej warstwy dostępu do danych. To co napisaliśmy w firmie przez ileś miesięcy czy lat przy okazji różnych projektów teraz sprawdza się świetnie. Oczywiście nie jest tak "trendi" jak LinQ, NHibernate itd. Ale działa szybko, nie mam problemu z wielkim konfiguracyjnym plikiem XML. Mam możliwość przełączenia się nawet na inny silnik bazy danych, ale oczywiście muszę pisać kod tak, by dało się to potem zrobić. Nie jest tak automagicznie jak w przypadku np. NHibernate (podobno). Czy oprócz tego że NHibernate ma zalety, to czy ma też jakieś wady? Może wolno działa? Słyszałem, że w Entity Framework jest tak, że by usunąć jakieś rekordy EF musi je najpierw wczytać do pamięci, zbudować strukturę i potem dopiero Save() i usuwa z bazy danych. Trochę to dziwne. Czy koszt wywołania metody Save() nie jest w tym przypadku przesadnie za duży? Podsumowując: rozum mówi, że lepiej użyć jest gotowca, serce jeszcze nie. Co radzicie?

  14. @Gość:
    Polecam zapoznać się z zagadnieniem "Not invented here":) ( http://en.wikipedia.org/wiki/Not_Invented_Here ).

    Generalnie jeśli ktoś już coś zrobił to po co robić to od nowa? Przykład logowania: log4net i nLog już SĄ zoptymalizowane pod kątem wydajności. Nawet jeśli chcę tylko zapisać do pliku to z nich korzystam pokornie przyznając że sam tego lepiej nie napiszę. Co jak dojdzie potrzeba tworzenia co godzinę nowego pliku i kasowania starych, jeśli jest ich więcej niż 10? One to potrafią. Albo: co jeśli chcę wypisać na konsolę logi pokolorowane w zależności od poziomu logowania? One to potrafią. I tak dalej.

    A dostęp do danych… zawsze jak słyszę "mamy własną warstwe dostepu do danych" to niedobrze mi sie robi. ORMy oferują bardzo dużo bardzo przydatnej funkcjonalności, a "ogromne pliki XML" nie są już wcale koniecznością. DataAccess nie musi być "trendi" – musi być wydajny, elastyczny i wygodny w wykorzystaniu.
    NH ma wadę – jest TRUDNE do opanowania.
    Co do EF – da się osiągnąć to w inny sposób, uciekając się do ESQL (dokładnie ten przypadek miałem na myśli pisząc o nim w poście).
    ORMy nie są głupie – to juz nie wyłącznie durne generatory SQLa, tylko o wiele więcej.

Newsletter: devstyle weekly!
Dołącz do 1000 programistów!
  Zero spamu. Tylko ciekawe treści.
Dzięki za zaufanie!
Do przeczytania w najbliższy piątek!
Niech DEV będzie z Tobą!