<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Robert Skarżycki - blog - RSS Feed]]></title><description><![CDATA[Blog osobisty Roberta Skarżyckiego, programisty .NET]]></description><link>https://robert.skarzycki.pl</link><generator>GatsbyJS</generator><lastBuildDate>Tue, 16 Jul 2024 04:56:07 GMT</lastBuildDate><item><title><![CDATA[30 dni z... Pythonem - dzień #2]]></title><description><![CDATA[ainspirowany znalezionym niedawno wystąpieniem Matta Cuttsa pt. “Try something new for 30 days” na konferencji TED, postanowiłem dać sobie…]]></description><link>https://robert.skarzycki.pl/54-30days-python-0/</link><guid isPermaLink="false">https://robert.skarzycki.pl/54-30days-python-0/</guid><pubDate>Tue, 16 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;ainspirowany znalezionym niedawno &lt;a href=&quot;https://www.ted.com/talks/matt_cutts_try_something_new_for_30_days?subtitle=en&quot;&gt;wystąpieniem Matta Cuttsa pt. “Try something new for 30 days” na konferencji TED&lt;/a&gt;, postanowiłem dać sobie następujące “wyzwanie”: przez 30 dni codziennie spędzać pewną porcję czasu na uczenie się Pythona.&lt;/p&gt;
&lt;p&gt;Do poznawania Pythona zbierałem się już kilka razy, jakieś podstawowe skrypty tworzyłem, ale brakuje mi elementarnej biegłości w tym języku. Idea 30-dniowego wyzwania wydała mi się ciekawa: brzmi jak coś, co będzie i angażujące, i motywujące, a jednocześnie jakoś określone, przynajmniej w czasie. Na koniec chciałbym mieć poczucie, że coś udało się stworzyć - nie jedynie przejść tutorial X, ale coś zbudować.&lt;/p&gt;
&lt;p&gt;Mająć powyższe na uwadze Wymyśliłem więc, że spróbuję sportować jakąś bibliotkę, najpewniej JavaScriptową/TypeScriptową do Pythona. Zamierzam przejść - przynajmniej na początku - poniższe kroki:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Znaleźć odpowiednią bibliotekę. Niech będzie wystarczająco mała, żebym był w stanie ją ogarnąć i żeby miała małe/proste API. Nie ma znaczenia, czy analogiczna biblioteka w Pythonie jest (na pewno jest). Zadanie ma być w celach edukacyjnych, nie dla sławy.&lt;/li&gt;
&lt;li&gt;Biblioteka musi mieć jakieś testy, dzięki temu będę mógł zacząć od przepisania testów jednostkowych do Pythona i trochę pójść w podejściu TDD.&lt;/li&gt;
&lt;li&gt;Wszystko wrzucę na Githuba, niech sobie leży w formie pamiętniczka i kopii zapasowej.&lt;/li&gt;
&lt;li&gt;Tutaj na blogu będę prowadził &lt;em&gt;developer’s diary&lt;/em&gt;, czyli dzienniczek, w którym będę starał się opisywać, co udało się danego dnia, na jakie trudności natrafiłem i co mnie zdziwiło, jak Pythonowego nowicjusza.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Chyba na te 30 dni więcej ambicji nie mam. Jeśli okazałoby się, że idzie szybko (albo że wybrałem zbyt małą bibliotekę), to mogę pójść albo w kierunku publikacji paczki w ekosystemie Pythonowym, albo wybrania jakiegoś tematu i np. poznania konkrentnych bibliotek, które na rynku są dla niego stosowane. Zobaczymy, co wyjdzie.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[30 dni z... Pythonem - dzień #0]]></title><description><![CDATA[…]]></description><link>https://robert.skarzycki.pl/54-30days-python-1/</link><guid isPermaLink="false">https://robert.skarzycki.pl/54-30days-python-1/</guid><pubDate>Thu, 11 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;…&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Zmigrowany do Gatsby v5!]]></title><description><![CDATA[Udało się zmigrować bloga z Gatsby’ego w wersji 4 do wersji 5 - polecam iść krok po kroku według przewodnika z oficjalnej dokumentacji.…]]></description><link>https://robert.skarzycki.pl/52-migrate-to-gatsby-5/</link><guid isPermaLink="false">https://robert.skarzycki.pl/52-migrate-to-gatsby-5/</guid><pubDate>Wed, 31 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Udało się zmigrować bloga z Gatsby’ego w wersji 4 do wersji 5 - polecam iść krok po kroku według &lt;a href=&quot;https://www.gatsbyjs.com/docs/reference/release-notes/migrating-from-v4-to-v5/&quot;&gt;przewodnika z oficjalnej dokumentacji&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Obyło się bez większych problemów, dodatkowym krokiem była migracja z pakietu &lt;code class=&quot;language-text&quot;&gt;typeface&lt;/code&gt; na &lt;code class=&quot;language-text&quot;&gt;@fontface&lt;/code&gt; - okazało się, że &lt;code class=&quot;language-text&quot;&gt;typeface&lt;/code&gt; jest już &lt;em&gt;deprecated&lt;/em&gt;. Przy okazji udało się rozwiązać problem z niewłaściwym wyświetlaniem literki “ż” - zwłaszcza w moim nazwisku. :-)&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Azure DevOps pipelines - tips & tricks (4)]]></title><description><![CDATA[cache]]></description><link>https://robert.skarzycki.pl/51-azure-devops-pipelines-tips-4/</link><guid isPermaLink="false">https://robert.skarzycki.pl/51-azure-devops-pipelines-tips-4/</guid><pubDate>Tue, 04 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;cache&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Azure DevOps pipelines - tips & tricks (3)]]></title><description><![CDATA[Kolejnym ciekawym “ficzerem” w pipeline’ach Azure DevOps jest możliwość uruchomienia całego joba lub pojedynczego stepu na kontenerze z…]]></description><link>https://robert.skarzycki.pl/50-azure-devops-pipelines-tips-3/</link><guid isPermaLink="false">https://robert.skarzycki.pl/50-azure-devops-pipelines-tips-3/</guid><pubDate>Sat, 01 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Kolejnym ciekawym “ficzerem” w pipeline’ach Azure DevOps jest możliwość uruchomienia całego joba lub pojedynczego stepu na kontenerze z określonego obrazu, innego niż te podstawowe, jakie Microsoft oferuje jako build maszyny.
Uzyskujemy to w ten sposób:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; u14
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;14.04&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; RunInContainer
  &lt;span class=&quot;token key atrule&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; u14  
  &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;    
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cat /etc/os&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;release
    &lt;span class=&quot;token key atrule&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Print OS version   &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Lub dla pojedynczego stepu:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; u14
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;14.04&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; RunInContainer
  &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;    
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cat /etc/os&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;release
    &lt;span class=&quot;token key atrule&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Print OS version
	&lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; u14&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Co więcej, możemy skorzystać nie tylko z obrazów dostępnych publicznie w Docker Hub, ale także naszych własnych, które zbudowaliśmy i wgraliśmy do naszego ACR-a.&lt;/p&gt;
&lt;p&gt;Po co w ogóle jednak mielibyśmy używać innego obrazu? Do głowy przychodzą mi trzy przypadki:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Potrzebujemy jakichś konkretnych narzędzi do wykonania zadania - zamiast tworzyć kroki, w których te narzędzia ściągamy i doinstalowujemy, możemy skorzystać z obrazu, który już je zawiera. Dzięki temu nie tylko scentralizujemy miejsce, w którym np. określamy jakiej wersji danego narzędzia używamy (wiele pipeline’ów skorzysta z jednego obrazu, zamiast wielu pipeline’ów “babrających” się w samodzielnego dociąganie zależności), a wręcz może i “outsource’ujemy” ten problem (jeśli taki obraz już jest na rynku), ale nawet przyspieszymy build: obraz już ma narzędzie gotowe do pracy, nie trzeba tracić czasu na jego ściąganie i instalowanie.&lt;/li&gt;
&lt;li&gt;Weryfikacja na różnych środowiskach: w połączeniu z użyciem &lt;code class=&quot;language-text&quot;&gt;matrix&lt;/code&gt; jako &lt;code class=&quot;language-text&quot;&gt;strategy&lt;/code&gt; dla danego joba możemy uruchomić ten sam zestaw kroków na różnych obrazach. Może na przykład weryfikacja jakiegoś narzędzia, czy działa na Windowsie i Linuksie?&lt;/li&gt;
&lt;li&gt;W świecie kontenerowym istnieje taki wzorzec, aby skonteneryzowaną aplikację budować też w kontenerze: dzięki temu nie tylko same środowisko uruchomieniowe aplkacji jest stabilne (&lt;em&gt;explicite&lt;/em&gt; wyrażone w &lt;code class=&quot;language-text&quot;&gt;Dockerfile&lt;/code&gt;), ale tak samo środowisko, w którym budujemy aplikację. Zamiast wieloetapowego &lt;code class=&quot;language-text&quot;&gt;Dockerfile&lt;/code&gt;‘a, który może być trudniejszy do zrozumienia - możemy tę informację o stabilnym środowisku uruchomieniowym przenieść do pipeline’u, uruchamiając build joba w kontenerze. To pewnie jest dyskusyjne, bo na pewno zaletą oryginalnego podejścia jest to, że tuż obok aplikacji (w jej &lt;code class=&quot;language-text&quot;&gt;Dockerfile&lt;/code&gt;) jest informacja o wszystkich zależnościach - &lt;em&gt;compile-time&lt;/em&gt; i &lt;em&gt;run-time&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title><![CDATA[Disaster recovery w Azure (1) - teoria]]></title><description><![CDATA[Chciałbym niniejszym zainicjować cykl wpisów o disaster recovery w Azure, czyli tych wszystkich rozwiązaniach, które oferuje nam chmura od…]]></description><link>https://robert.skarzycki.pl/49-disaster-recovery-1/</link><guid isPermaLink="false">https://robert.skarzycki.pl/49-disaster-recovery-1/</guid><pubDate>Wed, 17 Aug 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Chciałbym niniejszym zainicjować cykl wpisów o &lt;em&gt;disaster recovery&lt;/em&gt; w Azure, czyli tych wszystkich rozwiązaniach, które oferuje nam chmura od Microsoftu, a które mogą nas uchronić przed pewnymi poważnymi problemami.
Pomysł jest taki, że najpierw opiszę ogólnie, co kryje się pod tym terminem i jakie są możliwe rodzaje owych katastrof, a w kolejnych odcinkach będę się przyglądał konkrentym rodzajom zasobów na Azure i sprawdzał, jakie są oferowane “siatki bezpieczeństwa”.&lt;/p&gt;
&lt;h1&gt;Co może pójść nie tak?&lt;/h1&gt;
&lt;p&gt;Zaczynając od początku, podstawowym pojęciem jest &lt;em&gt;business continuity&lt;/em&gt;, czyli zapewnienie ciągłości “biznesu” (systemu, aplikacji etc.), na wypadek nieprzewidzianego zdarzenia, zaburzającego tę ciągłość. Pamiętajmy więc, że tutaj chodzi o to, żeby “przetrwać” po katastrofie, niż żeby do niej nie dopuścić (inna sprawa, że np. pewnym katastrofom zapobiec nie możemy, bo są od nas niezależne). Można więc powiedzieć, że procedury &lt;em&gt;disaster recovery&lt;/em&gt; i wszystkie zabiegi z tym związane są swoistym ubezpieczeniem na życie - nie zapobiegają wprost katastrofie bądź awarii, ale ułatwiają dalsze funkcjonowanie; tak samo jak ubezpieczenie na życie zdrowia (ani tym bardziej życia) ubezpieczonemu nie przywróci, ale wypłacone środki mogą pomóc w życiu po katastrofie.&lt;/p&gt;
&lt;p&gt;Jeśli mówimy o systemach IT, a zwłaszcza zasobach w chmurze, to w zasadzie możemy mieć dwa rodzaje “katastrof”:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;niedostępność serwisów chmurowych, czyli nieprzewidziana awaria po stronie Azure’a spowodowana dowolnym czynnikiem: katastrofą naturalną, błędem ludzkim itp.,&lt;/li&gt;
&lt;li&gt;“czynnik ludzki” - błąd ludzki “naszego” pracownika bądź wroga działalność włamywacza, powodująca np. usunięcie bazy danych.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;“Czynnik ludzki” może spowodować:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;utratę i/lub uszkodzenie danych,&lt;/li&gt;
&lt;li&gt;niedostępność naszej aplikacji (serwisu) na skutek błędnej konfiguracji.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Natomiast jeśli chodzi o niedostępność serwisów chmurowych, to sam Microsoft wyróżnia trzy poziomy awarii:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Lokalna - dotyczy danego serwera bądź serwerów.&lt;/li&gt;
&lt;li&gt;Strefowa - dotyczy całego &lt;em&gt;data center&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Regionalna - dotyczy całego regionu.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Te poziomy wynikają z architektury infrastruktury chmurowej, dokładniej zanurkujemy w temat trochę niżej.&lt;/p&gt;
&lt;h1&gt;RPO vs RTO&lt;/h1&gt;
&lt;p&gt;Gdy mowa o &lt;em&gt;business continuity&lt;/em&gt; bądź &lt;em&gt;disaster recovery&lt;/em&gt;, często pojawiają się dwa terminy: RPO i RTO. Wyjaśnijmy je pokrótce.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RPO&lt;/strong&gt; - &lt;em&gt;recovery point objective&lt;/em&gt; - to jest najdłuższy odcinek czasu (liczony wstecz od momentu awarii), dla którego utrata danych jest możliwa. Łopatologicznie: jeśli w Wordzie mamy auto-save co 5 minut, to maksymalnie 5 minut pracy możemy stracić.
&lt;strong&gt;RTO&lt;/strong&gt; - &lt;em&gt;recovery time objective&lt;/em&gt; - to jest czas, liczony od momentu awarii, w którym aplikacja/serwis wraca do normalnego funkcjonowania. W przykładzie z Wordem: jeśli awaria skończyła się bluescreenem, to RTO będzie równy czasowi potrzebnemu na restart komputera, włączenie Worda i wybranie opcji odzyskiwania plików.&lt;/p&gt;
&lt;p&gt;Gdy będziemy rozważać różne mechanizmy &lt;em&gt;disaster recovery&lt;/em&gt;, te dwa terminy będą się przewijać - i one pozwalają ocenić, ile daje nam konkretny mechanizm, zwłaszcza w porównaniu z pozostałymi opcjami.&lt;/p&gt;
&lt;h1&gt;Architektura infrastruktury Azure&lt;/h1&gt;
&lt;p&gt;Aby nie powtarzać tego przy omawianiu kolejnych zasobów, warto rzucić okiem, jak wysokopoziomowo zorganizowana jest infrastruktura chmury Azure.
Pomijając najniższy poziom konkrentych serwerów lub ich grup - podstawową jednostką jest centrum danych. Jedno lub kilka centrów danych tworzą strefę (&lt;em&gt;availability zone&lt;/em&gt;). Z kolei 3 &lt;em&gt;availability zones&lt;/em&gt; razem tworzą region. Regiony zaś mają swoje “siostrzane” regiony (powiedzmy najbliższego sąsiada), z którego mogą korzystać np. do tworzenia backupów odpornych na awarię oryginalnego regionu.
Oczywiście geografia i szczegóły rozmieszczenia centrów danych i stref mają znaczenie: z jednej strony muszą być położone na tyle blisko, aby zapewniać najmniejsze opóźnienia sieciowe (np. &lt;em&gt;availability zones&lt;/em&gt; muszą mieć opóźnienie poniżej 2ms w obie strony), a z drugiej - być jednak niezależne np. jeśli chodzi o źródło energii bądź narażenie na katastrofy naturalne. (Ciekawe, jak to będzie rozwiązane podczas budowy regionu Poland Central - która przecież właśnie trwa - czy można tak na oko przyjąć, że np. jedna zone’a będzie zasilana z Ostrołęki, a druga - z Kozienic? :D)&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Azure DevOps pipelines - tips & tricks (2)]]></title><description><![CDATA[W dzisiejszym odcinku pochylimy się nad zmiennymi i parametrami. Poniższa tabelka przedstawia podstawowe różnice:  parametry zmienne różne…]]></description><link>https://robert.skarzycki.pl/48-azure-devops-pipelines-tips-2/</link><guid isPermaLink="false">https://robert.skarzycki.pl/48-azure-devops-pipelines-tips-2/</guid><pubDate>Mon, 11 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;W dzisiejszym odcinku pochylimy się nad zmiennymi i parametrami. Poniższa tabelka przedstawia podstawowe różnice:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;parametry&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;zmienne&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;różne typy danych&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✔️&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌ tylko string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;zmiana w runtime&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✔️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kierunek przekazywania&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&lt;em&gt;top-down&lt;/em&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;⬆️⬇️➡️&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Warto o tych różnicach pamiętać, bo w wielu przypadkach może się wydawać, że w zasadzie możemy zmienne i parametery stosować zamiennie. Tymczasem rządzi nimi inna logika, a do tego - każda grupa ma swoje… dziwne pułapki.&lt;/p&gt;
&lt;h1&gt;Największe pułapki ze zmiennymi&lt;/h1&gt;
&lt;p&gt;Po pierwsze, zmienne są &lt;em&gt;by default&lt;/em&gt; przekazywane do skryptów Bash/CMD. Wydaje się na to pierwszy rzut oka sensowne, tym bardziej, że “gratis” dostajemy w skryptach dostęp do zmiennych, które są dostarczone przez platformę, np. &lt;code class=&quot;language-text&quot;&gt;Build.BuildId&lt;/code&gt;. Ale to ma też swoje konsekwencje&lt;/p&gt;
&lt;h2&gt;Nie ma nadpisywania zmiennych przy użyciu taska &lt;code class=&quot;language-text&quot;&gt;script&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Weźmy taki kawałek YAMLa:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;MY_VARIABLE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;original value&quot;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;bash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; echo &quot;$MY_VARIABLE&quot;
    &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;MY_VARIABLE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;overriden?&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;W 5. linii wołamy kawałek skryptu Bash, który ma wydrukować na ekran wartość zmiennej &lt;strong&gt;środowiskowej&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;MY_VARIABLE&lt;/code&gt;. Dla mnie logiczne było to, że skoro zmienna pipeline’owa &lt;code class=&quot;language-text&quot;&gt;MY_VARIABLE&lt;/code&gt; automatycznie jest wstrzykiwana do skryptu jako zmienna środowiskowa o tej samej nazwie, to jednak przekazanie jakiejś wartości w liście &lt;code class=&quot;language-text&quot;&gt;env&lt;/code&gt; spowoduje “wygraną” tej właśnie lokalnej wartości. Tak jednak nie jest. Powyższy kawałek kodu wydrukuje: &lt;code class=&quot;language-text&quot;&gt;original value&lt;/code&gt;. Przyznacie, że to &lt;em&gt;mindfuck&lt;/em&gt;?&lt;/p&gt;
&lt;h2&gt;Zmienne typu sekret nie są przekazywane do skryptów&lt;/h2&gt;
&lt;p&gt;W &lt;em&gt;variable groups&lt;/em&gt; możemy zdefiniować niektóre zmienne jako sekrety (chyba też wartości zaimportowane z key vaulta nimi są domyślnie), dzięki czemu uzyskujemy większe bezpieczeństwo w samym portalu: wartości sekretu nie można podejrzeć, ani skopiować, ani też zobaczyć jego użycia w logach konkrentego builda (wartość jest “wygwiazdkowana”).
Niestety, trzeba pamiętać, że taka zmienna typu sekret nie zostanie automatycznie przekazana do skryptu Bash/CMD, co powoduje, że musimy je przekazać przez argument &lt;code class=&quot;language-text&quot;&gt;env&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Service connection nie może być w zmiennej&lt;/h2&gt;
&lt;p&gt;Jeśli mamy taką sytuację, że np. środowiska testowe i produkcyjne są na innych subskrypcjach Azure, to wówczas potrzebujemy dwóch oddzielnych &lt;em&gt;service connections&lt;/em&gt;, aby móc np. wdrażać jakieś zasoby do chmury. Dobrze by było mieć takie rozgałęzienie w pipeline’ie, że w zależności od środowiska używamy konkretnego &lt;em&gt;service connection&lt;/em&gt;. Czyli można by chcieć mieć coś takiego:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# dev-vars.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;SERVICE_CONNECTION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dev-service-connection&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# prod-vars.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;SERVICE_CONNECTION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PROD-service-connection&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# main.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;parameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; environment
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
    &lt;span class=&quot;token key atrule&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; dev
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; prod

&lt;span class=&quot;token key atrule&quot;&gt;stages&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; parameters.environment &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;vars.yaml
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# -- omitted, but with same step using $(SERVICE_CONNECTION)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(Przy okazji - tutaj jeszcze jeden ciekawy przypadek użycia szablonów: jako “worków” ze zmiennymi, które możemy “importować” w stage’ach i jobach.)
Niestety, tak się nie da: przy próbie użycia zmiennej &lt;code class=&quot;language-text&quot;&gt;SERVICE_CONNECTION&lt;/code&gt; np. w tasku &lt;code class=&quot;language-text&quot;&gt;AzureResourceGroupDeployment@2&lt;/code&gt; - dostaniemy na twarz błąd, mówiący mniej więcej, iż “ten pipeline nie jest autoryzowany do service connection o nazwie ’$(SERVICE_CONNECTION)`“. A zatem widzimy od razu, że problem jest z rozwiązywaniem tej zmiennej.
Niestety, jedyny workaround to wprowadzenie sztucznego parametru, który będzie służył za słownik:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# main.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; environment
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
    &lt;span class=&quot;token key atrule&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; dev
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; prod
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; serviceConnections
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; object
    &lt;span class=&quot;token key atrule&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dev-service-connection&quot;&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;prod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PROD-service-connection&quot;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;stages&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;SERCICE_CONNECTION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; parameters.serviceConnections&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;parameters.environment&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# -- omitted, but with same step using $(SERVICE_CONNECTION)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To jest niestety hak, który w głowie miesza pomiędzy parametrami, które przychodzą z zewnątrz a parametrami, które są “internal use”. A żeby było jeszcze gorzej, to na portalu supportu Microsoftu jest na to zgłoszony błąd, który od… 3 lat czeka na rozwiązanie.&lt;/p&gt;
&lt;h3&gt;&lt;em&gt;Runtime parameter&lt;/em&gt; nie może być opcjonalny&lt;/h3&gt;
&lt;p&gt;Siłą parametrów “najwyższego rzędu” - czyli parametrów, które definiujemy w tym YAML-u, który potem jest użyty do konkretnej &lt;em&gt;build definition&lt;/em&gt; - jest to, że gratis dostajemy ładny UI do specyfikowania tychże dla konkretnego uruchomienia. W ten sposób przykładowo scenariusz z poprzedniego podrodziału mógłby być użyty tak, że wybór środowiska - &lt;code class=&quot;language-text&quot;&gt;dev&lt;/code&gt; bądź &lt;code class=&quot;language-text&quot;&gt;prod&lt;/code&gt; - dokonuje się z eleganckiego dropdowna.
Wszystko jest fajnie dopóty, dopóki nie zechcemy wprowadzić parametru opcjonalnego. Chciałoby się użyć konsekwentnie tej samej składni, której używamy, czyniąc “zwykły” parametr opcjonalnym - a więc określając jego wartość w polu &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt;; a w szczególnym przypadku tą wartością możę być pusty string.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# it does not work :(&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; firstName
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; optionalSecondName
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
    &lt;span class=&quot;token key atrule&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# it does not work :(&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; if eq(parameters.optionalSecondName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &apos;&apos;) &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; echo &quot;Hello&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; parameters.firstName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&quot;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; else &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; echo &quot;Hello&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; parameters.firstName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; parameters.optionalSecondName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Niestety, zamiast takiej oczywistej składni, musimy kombinować z wpisaniem jakiegoś słowa kluczowego w polu-które-ma-być-opcjonalne:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# it does work, but :(/&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; firstName
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; optionalSecondName
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
    &lt;span class=&quot;token key atrule&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;I DO NOT HAVE ONE&quot;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; if eq(parameters.optionalSecondName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &apos;I DO NOT HAVE ONE&apos;) &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; echo &quot;Hello&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; parameters.firstName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&quot;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; else &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; echo &quot;Hello&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; parameters.firstName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; parameters.optionalSecondName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Azure DevOps pipelines - tips & tricks (1)]]></title><description><![CDATA[Podstawową zasadą, którą wpaja się wszystkim młodym programistom, jest reguła reużywania kodu. Wiadomo - zamiast robić przeklęte ctrl+C i…]]></description><link>https://robert.skarzycki.pl/47-azure-devops-pipelines-tips-1/</link><guid isPermaLink="false">https://robert.skarzycki.pl/47-azure-devops-pipelines-tips-1/</guid><pubDate>Thu, 19 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Podstawową zasadą, którą wpaja się wszystkim młodym programistom, jest reguła reużywania kodu. Wiadomo - zamiast robić przeklęte &lt;em&gt;ctrl+C&lt;/em&gt; i &lt;em&gt;ctrl+V&lt;/em&gt;, lepiej jest zbudować reużywaną funkcję/moduł, dzięki czemu później zmianę wystarczy zrobić w jednym miejscu. Tę regułę można też zastosować do pipeline’ów YAML-owych w AzureDevops. W tym odcinku devopsowych &lt;em&gt;tips &amp;#x26; tricks&lt;/em&gt;, przyjrzymy się, jakie są opcje.&lt;/p&gt;
&lt;h1&gt;&lt;em&gt;Template nesting&lt;/em&gt;&lt;/h1&gt;
&lt;p&gt;Pierwsza opcja to “zagnieżdżanie” szablonów, czyli “referencjonowanie” za pomocą &lt;code class=&quot;language-text&quot;&gt;template&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; echo &quot;Hello world&quot;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./nested&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;template.yml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Jest to podstawowa opcja, oczywiście możemy przekazać do takiego zagnieżdżonego szablonu parametry. Co ciekawe, szablon może znajdować się w innym repozytorium - wówczas musimy zdefiniować owo repozytorium jako zasób w sekcji &lt;code class=&quot;language-text&quot;&gt;resources&lt;/code&gt; i potem po nazwie zasobu odwołać się do szablonu, np. &lt;code class=&quot;language-text&quot;&gt;nested-template.yml@yaml-repo&lt;/code&gt;. Takie rozwiązanie nie wydaje się naturalne (raczej chcemy trzymać YAMLe pipeline’ów obok aplikacji), ale przy większych projektach i/lub organizacjach możemy mieć np. bazowe szablony dla wielu projektów/zespołów w oddzielnym repozytorium, którym opiekuje się dedykowany zespół.&lt;/p&gt;
&lt;p&gt;Oficjlanie istnieją pewne ograniczenia ilości zagnieżdżeń oraz łącznego rozmiaru wszystkich YAMLów biorących udział w buildzie (zainteresowanych odsyłam do &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/process/templates?view=azure-devops#imposed-limits&quot;&gt;dokumentacji&lt;/a&gt;)- ale mnie się nie udało zderzyć ze ścianą. (Próbowałem dojść do limitu zagnieżdżeń.)&lt;/p&gt;
&lt;h1&gt;Pętle&lt;/h1&gt;
&lt;p&gt;Kolejną konstrukcją, którą adept programowania poznaje na wczesnym etapie, a którą też da się zastosować w pipeline’ach są pętle. Tutaj nie ma specjalnej filozofii: możemy iterować po listach. (Aczkolwiek &lt;em&gt;de facto&lt;/em&gt; nie ma typu danych “lista” w Azure DevOps, no ale typy danych to może temat na oddzielny odcinek.) Przykładowo:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; characters
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; object
    &lt;span class=&quot;token key atrule&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; Tytus de Zoo
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; Romek
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; A&apos;Tomek

  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; each character in parameters.characters &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; echo &quot;Hi&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; character &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Przy pisaniu trzeba tylko uważać na wcięcia i ważny dwukropek na końcu linijki, w której stosujemy słówko kluczowe &lt;code class=&quot;language-text&quot;&gt;each&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;&lt;em&gt;Matrix strategy&lt;/em&gt;&lt;/h1&gt;
&lt;p&gt;Bardziej wymyślną pętlą, zastosowaną na całym jobie i bardziej &lt;em&gt;explicite&lt;/em&gt; wyrażoną jest owo &lt;em&gt;matrix strategy&lt;/em&gt;. Zasada jest tu taka, że na poziomie joba określamy scenariusze jego uruchomienia, tzn. dla każdego scenariusza definiujemy wartość zmiennych, uzyskująć w ten sposób efekt podmiany ich wartości dla każdego scenariusza. Spójrzmy:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; MatrixSample
    &lt;span class=&quot;token key atrule&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//somwhere.com/old/api
        &lt;span class=&quot;token key atrule&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//somwhere.com/new/api

    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;bash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; curl $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; url &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;W powyższym przykładzie job wykona się dwa razy, dla scenarisuzy &lt;code class=&quot;language-text&quot;&gt;v1&lt;/code&gt; i &lt;code class=&quot;language-text&quot;&gt;v2&lt;/code&gt;, które wykonają request dla starej i nowej wersji api. Oczywiście tutaj przykład wydumany, API powinno być wersjonowane &lt;em&gt;explicite&lt;/em&gt; w URL czy w nagłówku, ale tak chyba lepiej widać, o co chodzi. Sam Microsoft w dokumentacji podaje przykład, że &lt;em&gt;matrix strategy&lt;/em&gt; może być używane np. do uruchamiania testów na różnych platformach. O tym będzie, ale w innym odcinku. :)&lt;/p&gt;
&lt;h1&gt;&lt;em&gt;Extend template&lt;/em&gt;&lt;/h1&gt;
&lt;p&gt;Ostatnia opcja to roszerzanie szablonów za pomocą słówka kluczowego &lt;em&gt;extend&lt;/em&gt;. Jest do &lt;em&gt;de facto&lt;/em&gt; dziedziczenie po innym szablonie. Może być przydatne w scenariuszu opisanym wyżej, czyli: bazowe szablony, wspólne dla wielu projektów, umieszczamy w innym repozytorium, a lokalnie dziedziczymy po nich i konstruujemy własne pipeline’y.&lt;/p&gt;
&lt;p&gt;Jak działa to dziedziczenie? W podstawowej opcji, jest to po prostu odwołanie się do szablonu bazowego i przekazanie parametrów. Czyli prawie to samo, co zagnieżdżanie, tyle że robione na poziomie całego szablonu.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# base.yml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; yourName
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string

&lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; echo &quot;Hello&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; parameters.yourName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; I am base template&quot;.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# extending.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;extend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; base.yml
  &lt;span class=&quot;token key atrule&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;yourName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Robert&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Sam Microsoft w dokumentacji sugeruje, że ten mechanizm można wykorzystać jak prawdziwe “dziedziczenie”: skoro typem parametru może być lista stepów, to dlaczego nie zaprojektować bazowego szablonu tak, aby przyjmował taki opcjonalny parametr. W ten sposób możemy zaimplementować odpowiednik funkcji wirtualnej lub abstrakcyjnej - konstruktów z programowania obiektowego.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# base.yml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; yourName
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; preSteps
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; stepList
    &lt;span class=&quot;token key atrule&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# thanks to default value this is &apos;virtual&apos;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; postSteps
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; stepList

&lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; each step in parameters.preSteps&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; step &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; echo &quot;Hello&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; parameters.yourName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; I am base template&quot;.
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; each step in parameters.postSteps&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; step &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# extending.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;extend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; base.yml
  &lt;span class=&quot;token key atrule&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;yourName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Robert
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;postSteps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; echo &quot;Goodbye&lt;span class=&quot;token tag&quot;&gt;!&lt;/span&gt;&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;Podsumowanie&lt;/h1&gt;
&lt;p&gt;Jak widać, przy głębszym poznaniu, AzureDevops oferują całkiem niezłe konstrukty do budowania zaawansowanych szablonów. Co więcej, dokumentacja jest całkiem nieźle napisana i zawiera przykłady. Z własnej praktyki mogę przyznać, że dotychczas “produkcyjnie” użyłem wszystkich wyżej wymienionych opcji, prócz &lt;em&gt;matrix strategy&lt;/em&gt; - czyli nie są to tylko zabawki, ale realne pomoce, które warto mieć w swojej skrzynce narzędziowej.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Migracja danych do Comos DB]]></title><description><![CDATA[Całkiem niedawno temu zmagałem się z zadaniem polegającym na migracji pewnej ilości danych (przyjmijmy, że setki tysięcy dokumentów) do…]]></description><link>https://robert.skarzycki.pl/46-migracja-danych-do-cosmos-db/</link><guid isPermaLink="false">https://robert.skarzycki.pl/46-migracja-danych-do-cosmos-db/</guid><pubDate>Mon, 09 Aug 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Całkiem niedawno temu zmagałem się z zadaniem polegającym na migracji pewnej ilości danych (przyjmijmy, że setki tysięcy dokumentów) do Cosmos DB, czyli Azure’owej bazy dokumentowej. Okazuje się, że strategia na “hurrra!” nie zadziała - jeśli będziemy wrzucać dokumenty jeden po drugim, po prostu przy którymś obrocie pętli, kod się nam wykrzaczy wyjątkiem. Co w takiej sytuacji należy zrobić?&lt;/p&gt;
&lt;h1&gt;Bulk SDK&lt;/h1&gt;
&lt;p&gt;W najprostszym podejściu, mając kolekcję danych (dokumentów) do wrzucenia do Cosmosa, możemy po prostu przeiterować po nich, dla każdego wywołując operację &lt;em&gt;upsert&lt;/em&gt;. Łatwo się domyślić, że będzie to jednak niewydajne, zwłaszcza, że każda operacja będzie oddzielnym requestem. Na początku z pomocą przychodzi &lt;a href=&quot;https://devblogs.microsoft.com/cosmosdb/introducing-bulk-support-in-the-net-sdk/&quot;&gt;“bulk support”&lt;/a&gt;, wprowadzony w wersji 3.4.0 SDK. Gdy ustawimy &lt;code class=&quot;language-text&quot;&gt;AllowBulkExecution = true&lt;/code&gt; w &lt;code class=&quot;language-text&quot;&gt;CosmosClientOptions&lt;/code&gt; klient (stworzony z użyciem takiego obiektu opcji) będzie miał nową właściwość: będzie kolejkować ileś-tam pojedynczych upsertów w jeden request po sieci do Azure’a. A w kodzie - wystarczy, że odpalimy wszystkie upserty jako taski i poczekamy na wszystkie, jak w poniższym przykładzie:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Task&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; tasks &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;List&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Task&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; itemToInsert &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; itemsToInsert&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    tasks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UpsertItemAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;itemToInsert&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; partitionKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; Task&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WhenAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tasks&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To robi dużą różnicę - cały proces przyspiesza, bo robimy po prostu mniej requestów sieciowych. Jak dokładnie to jest rozdzielane przez SDK i jaka jest pojemność “bufora”, tego autorzy nie SDK nie zdradzają.
Jednakże taka zamiana z “dużo mały kroczków” na “mało wielkich kroków” (upraszczam, rzecz jasna) może być niewystarczająca. Niby o tym piszą w dokumentacji, ale nie zwrócłem na to uwagi na początku…&lt;/p&gt;
&lt;h1&gt;Zwiększ throughput&lt;/h1&gt;
&lt;p&gt;Bulk API generuje jednak dodatkowy problem - skoro szybciej pakujemy dane do chmury, to innymi słowy przystawiamy do Azure’a grubszą rurę. Tyle, że Cosmos DB to nie papier i wszystkiego nie przyjmie. Komunikacja z Cosmos DB jest bowiem ograniczona przez ustawiony na instancji throughput. Troughput (przepustowość) to ta magiczna wartość, liczona w jeszcze bardziej magicznej jednostce RU, za którą płacimy - a dokładniej: płacimy za rezerwację danej przepustowości. Troughput może być ustawiony na sztywno albo autoskalować się w pewnym przedziale. W drugiej opcji polega to na tym, że określamy maksymalną przepustowość, a chmura sama skaluje ją pomiędzy 10% a 100&amp;#x26; maksymalnego limitu (dzięki temu w czasie mniejszego obciążenia można sporo zaoszczędzić).
W skrócie zatem - jeśli odpalimy bulk API bez zmiany przepustowości, to jest bardzo prawdopodobne, że ona się wyczerpie (no chyba, że mamy ustawiony bardzo duży ten górny limit, no ale to ma sens tylko wtedy gdy mamy naprawdę ogromną amplitudę ruchu). Rozwiązaniem jest po prostu czasowe podniesienie tego limitu - ja się nie patyczkowałem i dałem x10. To oczywiście podbije nam koszty, bo w czasie migrowania danych, bulk API będzie starał się pakować, ile się da - a więc przy autoskalowaniu przepustowości na pewno zostanie ona podniesiona w stronę 100%, ale dzięki temu z jednej strony przyspieszymy cały proces, a z drugiej - unikniemy odrzuconych upsertów.
Trzeba tylko wiedzieć, że sam proces podnoszenia górnego limitu przepustowości może trwać nawet godzinę (sic!) oraz pamiętać, żeby po całej operacji przywrócić wartość przepustowości - w przeciwnym wypadku będzie ona “wisieć” na za dużej wartości (przykładowo: jeśli mieliśmy dotychzas górny limit na 4000 RU, to przepustowość skalowała się między 400 a 4000 RU; jeśli zostawimy throughput na tymczasowej wartości np. x10, to zostaniemy na 4000-40000 RU, a więc najniższa wartość będzie taka, jak górny limit normalnego trybu…).&lt;/p&gt;</content:encoded></item><item><title><![CDATA[C# - historia (C# 8.0)]]></title><description><![CDATA[To jest kontynuacja przeglądu historii wersji C# (tu znajdziesz poprzedni artykuł). Readonly members Metoda w strukturze może być oznaczona…]]></description><link>https://robert.skarzycki.pl/45-csharp-historia-8-0/</link><guid isPermaLink="false">https://robert.skarzycki.pl/45-csharp-historia-8-0/</guid><pubDate>Tue, 20 Jul 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;To jest kontynuacja przeglądu historii wersji C# (&lt;a href=&quot;/blog/csharp-historia-9-0&quot;&gt;tu znajdziesz poprzedni artykuł&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;Readonly members&lt;/h2&gt;
&lt;p&gt;Metoda w strukturze może być oznaczona jako &lt;code class=&quot;language-text&quot;&gt;readonly&lt;/code&gt; - w ten sposób oznaczamy ją jako “instance-agnostic”, czyli po pierwsze nie może ona zmieniać stanu struktury, a po drugie - każde odwołanie do elementów struktury (tylko odczyt), będzie skutkować ostrzeżeniem.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WeatherMeasurement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;/span&gt; Temperature &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;$&quot;Temperature was &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token expression language-csharp&quot;&gt;Temperature&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;-- TU BĘDZIE WARNING&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ChangeTemperature&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;/span&gt; newTemperature&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; Temperature &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newTemperature&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;-- TU BĘDZIE BŁĄD KOMPILACJI&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Trzeba zapamiętać, że w przypadku “ostrzeżeniowym” kompilator pod spodem robi taki “myk”: tworzy kopię struktury i na tej kopii wywołuje tę metodę/właściwość. W ten sposób oryginalna instancja nie jest dotykana, ale to dodaje ukryty narzut.&lt;/p&gt;
&lt;p&gt;P.S. Tego &lt;code class=&quot;language-text&quot;&gt;readonly&lt;/code&gt; nie można użyć dla metod klasy - tam działa tylko na polach.&lt;/p&gt;
&lt;h2&gt;Domyślne implementacje w interfejsach&lt;/h2&gt;
&lt;p&gt;Dotychczas interfejsy mogły mieć tylko same sygnatury metod/właściwości. Jeśli mieliśmy wiele implementacji interfejsu, ale jakiś wspólny kod, to trzeba było wprowadzać pomiędzy te implementacje a interfejs abstrakcyjną klasę “superbazową” i tam umieszczać tę wspólną logikę. Natomiast od C# 8.0 można to zrobić od razu w interfejsie, bez sztucznej pośredniej klasy bazowej. Na przykład:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOrder&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;decimal&lt;/span&gt;&lt;/span&gt; NetTotal &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;decimal&lt;/span&gt;&lt;/span&gt; VatPercentage &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;decimal&lt;/span&gt;&lt;/span&gt; GrossTotal &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; NetTotal &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; VatPercentage&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;IOrder&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;decimal&lt;/span&gt;&lt;/span&gt; NetTotal &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;decimal&lt;/span&gt;&lt;/span&gt; VatPercentage &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;W tej sytuacji możemy korzystać z właściwości &lt;code class=&quot;language-text&quot;&gt;IOrder.GrossTotal&lt;/code&gt;, mimo że w klasie &lt;code class=&quot;language-text&quot;&gt;Order&lt;/code&gt; nie została ona zainicjalizowana. Ważne jest jednak to, że dostęp do niej mamy tylko, kiedy instancja klasy &lt;code class=&quot;language-text&quot;&gt;Order&lt;/code&gt; jest użyta w kontekście &lt;code class=&quot;language-text&quot;&gt;IOrder&lt;/code&gt; - inaczej nie będziemy mieli do niej dostępu. Porównaj:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;IOrder&lt;/span&gt; order1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; NetTotal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; VatPercentage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
Console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;order1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GrossTotal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;-- TU DZIAŁA&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; order2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; NetTotal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; VatPercentage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
Console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;order2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GrossTotal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;-- TO SIĘ NIE SKOMPILUJE&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;W drugim użyciu &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt; jes tylko &lt;em&gt;syntactic sugar&lt;/em&gt; - zmienna &lt;code class=&quot;language-text&quot;&gt;order2&lt;/code&gt; jest typu &lt;code class=&quot;language-text&quot;&gt;Order&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Pattern matching w switchach&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Pattern matching&lt;/em&gt; w połączeniu ze &lt;code class=&quot;language-text&quot;&gt;switch&lt;/code&gt; umożliwia bardzo zgrabny zapis:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetFuelString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;EngineType&lt;/span&gt; engineType&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; engineType &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    EngineType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Diesel &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ON&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    EngineType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Petroleum &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;95&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    EngineType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;LPG &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;LPG&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    _ &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;ArgumentOutOfRangeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token named-parameter punctuation&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Unrecognized engine type&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token named-parameter punctuation&quot;&gt;paramName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;engineType&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Co więcej, można też “matchować” za pomocą warunków określonych na właściwościach, np.:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;HasDiscount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Customer&lt;/span&gt; customer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; customer &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;IsVIP&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Age&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Age&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;FirstName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Adam&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token named-parameter punctuation&quot;&gt;LastName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Małysz&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;true&lt;/span&gt;
    _ &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;W mocy pozostaje jednak ograniczenie switcha: porównywana wartość musi być stałą.&lt;/p&gt;
&lt;h2&gt;IAsyncEnumerable&lt;/h2&gt;
&lt;p&gt;O tym &lt;a href=&quot;/blog/iasyncenumerable-to-jest-dobre&quot;&gt;isałem w oddzielnym wpisie&lt;/a&gt;, więc odsyłam po szczegóły do niego.&lt;/p&gt;
&lt;h2&gt;Using bez klamerek&lt;/h2&gt;
&lt;p&gt;Mniej wcięć dzięki temu, że &lt;code class=&quot;language-text&quot;&gt;using&lt;/code&gt; może być “inline’owy” - kompilator sam “ogarnie”, że “scope” sięga do końca “aktualnego scope’u”.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;WriteToFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; stream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;StreamWriter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;file.txt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;-- TU JESTEŚMY &quot;WEWNĄTRZ&quot; USINGA&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Operacje na indeksach&lt;/h2&gt;
&lt;p&gt;C# 8.0 zapewnia nam zgrabniejszy dostęp do fragmentów tablic i konkretnych pozycji, np.:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; weekdays &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Monday&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Tuesday&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Wednesday&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Thursday&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Friday&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Saturday&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Sunday&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; sunday &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; weekdays&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; weekend1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; weekdays&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token range operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;-- TO SAMO CO weekend2&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; weekend2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; weekdays&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token range operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;-- TO SAMO CO weekend1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Fajne, tylko czasem może być kłopotliwe - jak tutaj przy zmiennej &lt;code class=&quot;language-text&quot;&gt;weekend1&lt;/code&gt;: nie dość, że na dzień dobry jest &lt;em&gt;mindfuck&lt;/em&gt;, bo piątek to 4, to jeszcze drugi koniec takiego przedziału nie wchodzi do niego (dlatego jest indeks 7, którego nie ma w tablicy), czyli przedział jest jednostronnie domknięty; ale z drugiej strony jak odliczamy indeksy od końca (przykład z &lt;code class=&quot;language-text&quot;&gt;weekend2&lt;/code&gt;), to zasada jest odwrotna.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[C# - historia (C# 9.0)]]></title><description><![CDATA[Prześledźmy, jakie nowinki były wprowadzane w kolejnych wersjach C# (wybór) - w odwrotnie chronologicznej kolejności, najpierw wersja 9.…]]></description><link>https://robert.skarzycki.pl/44-csharp-historia-9-0/</link><guid isPermaLink="false">https://robert.skarzycki.pl/44-csharp-historia-9-0/</guid><pubDate>Sat, 10 Jul 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Prześledźmy, jakie nowinki były wprowadzane w kolejnych wersjach C# (wybór) - w odwrotnie chronologicznej kolejności, najpierw wersja 9.0.&lt;/p&gt;
&lt;h2&gt;Rekordy&lt;/h2&gt;
&lt;p&gt;Skrótowa składnia dla klas, które jedynie przechowują dane. Innymi słowy - zamiast rozwlekłego:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; FirstName &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; LastName &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; firstName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; lastName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        FirstName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; firstName&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        LastName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lastName&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Możemy to zapisać tak:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; FirstName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; LastName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;P.S. Zwróć uwagę na to, że zmienne w rekordzie są PascalCase - w ten sposób uzyskamy właściwości o “klasycznym” case’ingu.
P.P.S. Rekordy mogą dziedziczyć tylko po rekordach - i na odwrót (tylko przez rekordy mogą być dziedziczone).&lt;/p&gt;
&lt;h2&gt;Init-only setters&lt;/h2&gt;
&lt;p&gt;W powyższym, pierwotnym, przykładzie ze studentem cały “myk” polegał na tym, żeby uniemożliwić modyfikację właściwości, tj. żeby nadać wartość tylko raz, podczas inicjalizacji obiektu. W tej chwili możemy jednak napisać w klasie &lt;code class=&quot;language-text&quot;&gt;Student&lt;/code&gt; metodę, która, już po zainicjalizowaniu obiektu, będzie modyfikować wartość którejś z właściwości. Oczywiście, można temu zapobiec - po prostu mieć pole oznaczone jako &lt;code class=&quot;language-text&quot;&gt;readonly&lt;/code&gt;, a właściwość tylko jako getter odczytujący z tegoż pola. To by było jednak już zupełnie rozwlekłe. Stąd w C# 9.0 wprowadzono nowe słowo kluczowe: &lt;code class=&quot;language-text&quot;&gt;init&lt;/code&gt;. Zobaczmy je w akcji:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; FirstName &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; LastName &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ChangeFirstName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; firstName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        FirstName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; firstName&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;-- TO SIĘ NIE SKOMPILUJE!!!&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;W ten sposób otrzymujemy klasę, która też jest podobna do rekordu - ale inicjalizujemy ją inaczej:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; student &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Student&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    FirstName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;John&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    LastName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Smith&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

student&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;FirstName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Johnny&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;-- TO SIĘ NIE SKOMPILUJE!!!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Po zainicjalizowaniu obiektu nie ma opcji, żeby zmienić wartość właściwości: czy to z zewnątrz, czy z wewnątrz klasy.&lt;/p&gt;
&lt;p&gt;P.S. Oczywiście przykład z blokowaniem zmiany personaliów studenta może być mylący. W normalnym życiu przecież możliwa jest taka zmiana: od zwykłej omyłki na zmianie stanu cywilnego studenta kończąc. :)&lt;/p&gt;
&lt;h2&gt;Pomijanie typu przy słowie kluczowym &lt;code class=&quot;language-text&quot;&gt;new&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;W sytuacji, gdy kompilator potrafi domyślić się typu, wystarczy, że zawołamy samo &lt;code class=&quot;language-text&quot;&gt;new()&lt;/code&gt;, bez podawania typu, np. przy deklaracji pola:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; list &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Albo gdy przekazujemy parametr do metody:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Car&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Car&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Engine&lt;/span&gt; engine&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Engine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt;&lt;/span&gt; IsDiesel &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; dieselCar &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Car&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;IsDiesel&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To może ułatwić życie przy późniejszych refaktoryzacjach - zwłaszcza wtedy, gdy dokonujemy nie zmiany nazwy klasy (to nam załatwią narzędzia dziś dostępne), ale gdy np. rozbijamy jakąś klasę na dwie: nie musimy ręcznie poprawiać wszystkich wywołań konstruktorów (o ile oczywiście nie zmienią się parametry…).&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Podstawy architektury oprogramowania dla inżynierów #1 - parametry architektury]]></title><description><![CDATA[Parametry architektury Parametry architektury spełniają trzy kryteria: określają pozadziedzinowe względy projektowe, wpływają na niektóre…]]></description><link>https://robert.skarzycki.pl/43-podstawy-architektury-oprogramowania-dla-inzynierow-1-parametry-architektury/</link><guid isPermaLink="false">https://robert.skarzycki.pl/43-podstawy-architektury-oprogramowania-dla-inzynierow-1-parametry-architektury/</guid><pubDate>Mon, 14 Jun 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Parametry architektury&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;Parametry architektury spełniają trzy kryteria:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;określają pozadziedzinowe względy projektowe,&lt;/li&gt;
&lt;li&gt;wpływają na niektóre strukturalne aspekty projektu,&lt;/li&gt;
&lt;li&gt;mają decydujące lub duże znaczenie dla działania aplikacji.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;To cytat z M. Richardsa i N. Forda, autorów ksiązki &lt;em&gt;Podstawy architektury oprogramowania dla inżynierów&lt;/em&gt;. Co rozumieją przez te mądrze brzmiące frazy? Rozbijmy to na części.&lt;/p&gt;
&lt;h2&gt;“Określają pozadziedzinowe względy projektowe”&lt;/h2&gt;
&lt;p&gt;W skrócie chodzi o to, że “zwykłe” wymagania określają, co aplikacja ma robić (np. wysyłać mejle). Natomiast są też takie wymogi, zwane czasami “wymaganiami niefunkcjonalnymi”, które nie określają wprost działania aplikacji, ale mówią o jej cechach, np. jaka jest spodziewana wydajność aplikacji.&lt;/p&gt;
&lt;h2&gt;“Wpływają na niektóre strukturalne aspekty projektu”&lt;/h2&gt;
&lt;p&gt;Wybór konkretnej zewnętrznej usługi, która będzie wysyłać mejle (np. SendGrid) nie rzutuje na projekt tak, że zmiana dostawcy wywraca całą architekturę do góry nogami. Jeśli jednak warunkiem jest audytowalność (np. ze względów prawnych), wówczas może to wpływać na cały projekt.&lt;/p&gt;
&lt;h2&gt;“Mają decydujące lub duże znaczenie dla działania aplikacji”&lt;/h2&gt;
&lt;p&gt;Innymi słowy - nie każde “wymaganie niefunkcjonalne” jest tak samo ważne w danej aplikacji. W systemie do streamowania rozgrywek piłkarskich wydajność można być szczególnie ważna, gdy tymczasem w systemie bankowym jedną z głównych cech będzie bezpieczeństwo. Parametrami architektury będą dla danej aplikacji te cechy, które są szczególnie istotne - niby każdy projekt powinien być bezpieczny, ale np. w bankowości będzie to szczególnie istotne.&lt;/p&gt;
&lt;h1&gt;Typologia parametrów architektury&lt;/h1&gt;
&lt;p&gt;Istnieją dwa rodzaje parametrów architektury - sprecyzowane i dorozumiane. To ważne wyszczególnienie, bo nie wszystkie parametry architektury muszą być spisane na papierze &lt;em&gt;explicite&lt;/em&gt; jako “wymagania niefunkcjonalne_.&lt;/p&gt;
&lt;h1&gt;Lista parametrów architektury&lt;/h1&gt;
&lt;p&gt;Niepełna lista parametrów architektury (moje własne “tłumaczenia” na angielski, gdyż w książce są stosowane polskie terminy, które sieją zamęt):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;availability&lt;/em&gt; - czyli dostępność w czasie (jaką część czasu system może być wyłączony z użycia),&lt;/li&gt;
&lt;li&gt;&lt;em&gt;continuity&lt;/em&gt; - zdolność do przywrócenia do działania po awarii (tego trochę nie rozumiem…),&lt;/li&gt;
&lt;li&gt;&lt;em&gt;performance&lt;/em&gt; - wydajność, czyli jakie obciążenie ma system przyjmować,&lt;/li&gt;
&lt;li&gt;&lt;em&gt;recoverability&lt;/em&gt; - jak szybko system ma wracać do działania po awarii oraz czy możemy sobie pozwolić na utratę danych,&lt;/li&gt;
&lt;li&gt;&lt;em&gt;reliability&lt;/em&gt; - czy w przypadku nieprawdiłowego działania firma będzie narażona na duże koszty?&lt;/li&gt;
&lt;li&gt;&lt;em&gt;durability&lt;/em&gt; ? - odporność na błędy, np. brak połączenia sieciowego,&lt;/li&gt;
&lt;li&gt;&lt;em&gt;scalability&lt;/em&gt; - skalowalność,&lt;/li&gt;
&lt;li&gt;&lt;em&gt;configurability&lt;/em&gt; - konfigurowalność,&lt;/li&gt;
&lt;li&gt;&lt;em&gt;extendability&lt;/em&gt; - czy to się nie zawiera w poprzednim?&lt;/li&gt;
&lt;li&gt;&lt;em&gt;setupability&lt;/em&gt; - instalowalność na różnych platformach,&lt;/li&gt;
&lt;li&gt;&lt;em&gt;reusability&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;localizability&lt;/em&gt; - wsparcie dla wielu języków i regionalnych formatów danych,&lt;/li&gt;
&lt;li&gt;&lt;em&gt;supportability&lt;/em&gt; - łatwość w utrzymaniu (czy to nie jest za ogólne? i czy chodzi o wsparcie oferowane użytkownikom/klientom?),&lt;/li&gt;
&lt;li&gt;&lt;em&gt;portability&lt;/em&gt; - możliwość działania na różnych platformach (np. zmiana bazy danych - ?),&lt;/li&gt;
&lt;li&gt;&lt;em&gt;updateability&lt;/em&gt; - łatwość aktualizowania wersji aplikacji&lt;/li&gt;
&lt;li&gt;&lt;em&gt;accessibility&lt;/em&gt; - a11y&lt;/li&gt;
&lt;li&gt;&lt;em&gt;archivability&lt;/em&gt; - inaczej retencja danych&lt;/li&gt;
&lt;li&gt;&lt;em&gt;authenticaton&lt;/em&gt; - uwierzytelnianie&lt;/li&gt;
&lt;li&gt;&lt;em&gt;authorization&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;legal requirements&lt;/em&gt; - czy są jakieś sepcjalne wymagania prawne (np. RODO)?&lt;/li&gt;
&lt;li&gt;&lt;em&gt;privacy&lt;/em&gt; - możliwość ukrywania danych przed wewnętrznymi pracownikami firmy (np. dział IT w banku nie może zajrzeć Ci wprost na konto),&lt;/li&gt;
&lt;li&gt;&lt;em&gt;safety&lt;/em&gt; - bezpieczeństwo rozumiane jako zapobieganie przed nieautoryzowanym dostępem np. przez szyfrowanie danych,&lt;/li&gt;
&lt;li&gt;&lt;em&gt;usability&lt;/em&gt; - użyteczność&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lista jest długa, mnie dziwi obecność niektórych cech, które bardziej bym przypisał już do poziomu konkrentego rozwiązania technicznego, a nie do poziomu architektury (trudno mi wyobrazić sobie jak dostępność dla osób z niepełnosprawnościami miałaby wpływać znacząco na architekturą) - ale trzeba przyznać, że jest w miarę wyczerpująca.&lt;/p&gt;
&lt;p&gt;P.S. Wpis jest częścią cyklu, będącego serią notatek z lektury książki pt. &lt;em&gt;Podstawy architektury oprogramowania dla inżynierów&lt;/em&gt;, autorstwa Marka Richardsa i Neala Forda.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Mikroserwisy: rodzaje zdarzeń]]></title><description><![CDATA[Termin zdarzenie ma w programowaniu wiele znaczeń. Kiedyś mógł być kojarzony głównie z UI - wydarzenia (eventy ;) ) dotyczyły interakcji…]]></description><link>https://robert.skarzycki.pl/42-mikroserwisy-rodzaje-zdarzen/</link><guid isPermaLink="false">https://robert.skarzycki.pl/42-mikroserwisy-rodzaje-zdarzen/</guid><pubDate>Tue, 01 Jun 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Termin &lt;em&gt;zdarzenie&lt;/em&gt; ma w programowaniu wiele znaczeń. Kiedyś mógł być kojarzony głównie z UI - wydarzenia (&lt;em&gt;eventy&lt;/em&gt; ;) ) dotyczyły interakcji użytkownikiem, np. kliknięcia przeń w dany przycisk. W świecie mikroserwisów, DDD itp. - to znaczenie jednak się zmieniło (a może rozszerzyło). Dziś tych zakresów znaczeniowych jest więcej, mogą się one mieszać i nie zawsze wiadomo, o jaki rodzaj &lt;em&gt;eventu&lt;/em&gt; chodzi. Na przykładzie projektu, w którym pracowałem, a który był oparty o “referencyjną” architekturę &lt;a href=&quot;https://github.com/dotnet-architecture/eShopOnContainers&quot;&gt;eShopOnConatiners&lt;/a&gt; (czyli Microsoftowego przykładu rozwiązania mikroserwisowo-kontenerowego) - będę chciał pokazać typologię różnych znaczeń terminu &lt;em&gt;zdarzenie&lt;/em&gt;.&lt;/p&gt;
&lt;h1&gt;Zdarzenie w ramach encji&lt;/h1&gt;
&lt;p&gt;Gdy stosujemy &lt;em&gt;event sourcing&lt;/em&gt;, to w zasadzie wszelkie zmiany stanu domeny dzieją się za pomocą zdarzeń. Gdy zamówienie zostało złożone (&lt;code class=&quot;language-text&quot;&gt;OrderWasCreated&lt;/code&gt;), gdy zamówienie zostało opłacone (&lt;code class=&quot;language-text&quot;&gt;OrderWasPaid&lt;/code&gt;), gdy zamówienie zostało anulowane (&lt;code class=&quot;language-text&quot;&gt;OrderWasCancelled&lt;/code&gt;) i tak dalej. Te wydarzenia w zasadzie mapują się na wydarzenia, które możemy rozpoznać podczas sesji &lt;em&gt;event stormingu&lt;/em&gt;. One też są zapisywane w &lt;em&gt;event store&lt;/em&gt;.
O ile dobrze przejrzałem projekt eShopOnContainers, nie ma tam takich zdarzeń, jednak spotkałem się z takim rozwiązaniem właśnie w przypadku serwisu implementującego &lt;em&gt;event sourcing&lt;/em&gt;.&lt;/p&gt;
&lt;h1&gt;Zdarzenia domenowe&lt;/h1&gt;
&lt;p&gt;Jeśli stosujemy &lt;em&gt;event sourcing&lt;/em&gt;, być może to rozróżnienia na zdarzenia “wewnątrz encji” i “domenowe” wydaje się bez sensu, ale w przypadku modelowania DDD, ale bez &lt;em&gt;event sourcingu&lt;/em&gt;, nie będziemy mieć tych pierwszych, ale te drugie - już tak. Otóż, gdy zachodzi potrzeba reakcji jednego agregatu na zmianę w drugim agregacie - wówczas możemy posłużyć się takim zdarzeniem domenowym. W tym przykładowym repozytorium &lt;a href=&quot;https://github.com/dotnet-architecture/eShopOnContainers&quot;&gt;eShopOnConatiners&lt;/a&gt; reprezentowane są one przez klasy z sufiksem &lt;code class=&quot;language-text&quot;&gt;-DomainEvent&lt;/code&gt;.
Ich rolą jest “komunikacja” pomiędzy agregatami. Być może słowo “komunikacja” nie jest precyzyjne, gdyż mamy tu do czynienia raczej ze schematem producent oraz potencjalnie wielu konsumentów. Kiedy takie zdarzenie może być potrzebne? Jak wiadomo, agregaty nie mają możliwości wprost wywoływania swoich metod - byłyby wówczas ze sobą powiązane. Jeśli mamy np. skompilkowaną strukturę zarządzania magazynu, gdzie dostawa wymaga obsługi zarówno przez agregat &lt;code class=&quot;language-text&quot;&gt;Warehouse&lt;/code&gt; (np. ulokowanie paczki w odpowiednim miejscu fizycznego magazynu), ale też poinformowania agregatu &lt;code class=&quot;language-text&quot;&gt;StockProduct&lt;/code&gt; o zwiększeniu ilości stanu magazynowego - wówczas z agregatu &lt;code class=&quot;language-text&quot;&gt;Warehouse&lt;/code&gt; możemy wywołać wydarzenie &lt;code class=&quot;language-text&quot;&gt;ProductReceived&lt;/code&gt;, który będzie obsłużony przez inny agregat.
W praktyce przykładu eShopOnContainers wygląda to tak, że każdy agregar zawiera kolekcję “wydarzeń do opublikowania”, które jest “opróżniana” podczas zapisywania encji. Opróżnianie polega zaś na wysłaniu tych wydarzeń do mediatora, co z kolei wymaga napisania handlera dla takiego wydarzenia, w którym możemy zawołać metodę z innego agregatu. W efekcie uzyskujemy też to, że cała operacja jest wykonywana w transakcji (a przynajmniej może być) - ma to swój plus, gdyż nie ma możliwości, żeby takie wydarzenie gdzieś utknęło (dostawa dotarła do magazynu, ale nie został zwiększony stan magazynowy), ale z drugiej strony - błędy w handlerach powodują, że nie można wykonać “pierwotnej” operacji (z powodu wyjątku).&lt;/p&gt;
&lt;h1&gt;Zdarzenia integracyjne&lt;/h1&gt;
&lt;p&gt;eShopOnContainers operuje też trzecim typem wydarzeń - patrz klasy z sufiksem &lt;code class=&quot;language-text&quot;&gt;-IntegrationEvents&lt;/code&gt;. To są zdarzenia, które służą do poinformowania innych mikroserwisów o - &lt;em&gt;nomen omen&lt;/em&gt; - wydarzeniach w ramach pojedynczego serwisu. Przykładowo, gdy użytkownik został oznaczony jako “oszust”, serwis &lt;code class=&quot;language-text&quot;&gt;Users&lt;/code&gt; powinien poinformować o tym inne serwisy: na przykład serwis &lt;code class=&quot;language-text&quot;&gt;Orders&lt;/code&gt; może anulować wszystkie jego zamówienia i wstrzymać wysyłkę produktów.
W tym przykładowy repo jest to realizowane za pomocą Service Bus: serwisy “wrzucają” wydarzenia integracyjne na odpowiednie “tematy” na szynie, natomiast inne serwisy, zainteresowane danymi wydarzeniami, zawczasu subskrybują się na nie i zapewniają swój kod do obsługi takiego wydarzenia.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[AzureDevops pipeline + semantic versioning = da się?]]></title><link>https://robert.skarzycki.pl/41-azure-devops-pipeline-semantic-versioning-da-sie/</link><guid isPermaLink="false">https://robert.skarzycki.pl/41-azure-devops-pipeline-semantic-versioning-da-sie/</guid><pubDate>Sat, 20 Mar 2021 00:00:00 GMT</pubDate><content:encoded></content:encoded></item><item><title><![CDATA[Jak ustawić zmienną środowiskową w Azure DevOps pipeline?]]></title><description><![CDATA[Problem Mam pipline w Azure DevOps, a w nim - jakś zmienną. Chcę wartość tej zmiennej “wepchnąć” do zmiennej środowiskowej - tak aby jakieś…]]></description><link>https://robert.skarzycki.pl/40-jak-ustawic-zmienna-srodwiskowa-w-azure-devops-pipeline/</link><guid isPermaLink="false">https://robert.skarzycki.pl/40-jak-ustawic-zmienna-srodwiskowa-w-azure-devops-pipeline/</guid><pubDate>Tue, 12 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Problem&lt;/h1&gt;
&lt;p&gt;Mam &lt;em&gt;pipline&lt;/em&gt; w Azure DevOps, a w nim - jakś zmienną. Chcę wartość tej zmiennej “wepchnąć” do zmiennej środowiskowej - tak aby jakieś kolejne kroki mogły z niej skorzystać. (Przyjmijmy, że tę wartość mogę przekazać tylko przez &lt;em&gt;environment variable&lt;/em&gt; - a nie przez bezpośrednie odwołanie do “pajplajnowej” zmiennej w danym kroku.)&lt;/p&gt;
&lt;h1&gt;Rozwiązanie nr 1 (nie działa :( )&lt;/h1&gt;
&lt;p&gt;Szybki risercz internetów i sklecam takie coś:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yml&quot;&gt;&lt;pre class=&quot;language-yml&quot;&gt;&lt;code class=&quot;language-yml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;myVariable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; abc

&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; echo &quot;&lt;span class=&quot;token comment&quot;&gt;##vso[task.setvariable variable=sauce.userName;issecret=true]$(myVariable)&quot;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# other steps&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Efekt? Do zmiennej trafiła wartość następująca: &lt;code class=&quot;language-text&quot;&gt;abc&quot;&lt;/code&gt; (tak, z cudzysłowem na końcu…). &lt;em&gt;Facepalm&lt;/em&gt;. Nie szło to nijak ominąć - usunięcie w ogóle cudzysłowów, powodowało, że &lt;code class=&quot;language-text&quot;&gt;##vso...&lt;/code&gt; było traktowane jak komentarz w YAML-u (a więc odpalane było samo &lt;code class=&quot;language-text&quot;&gt;echo&lt;/code&gt;…), jakieś podmianki cudzysłowów na pojdyncze/podwójne - to samo.&lt;/p&gt;
&lt;h1&gt;Rozwiązanie nr 2 - działające!&lt;/h1&gt;
&lt;p&gt;Rozwiązaniem była przesiadka na PowerShella. Co ważne - reszta kroków może pozostać bez zmian (nie musimy innych kroków zmieniać na PowerShella). Takie krok wygląda wówczas następująco:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;- task: PowerShell@2
  inputs:
    targetType: &apos;inline&apos;
    script: |
      Write-Host &quot;##vso[task.setvariable variable=SAUCE_USERNAME;]$(sauceUserName)&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Upierdliwe, nieprawdaż?&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Print to file w C# na Windows 10]]></title><description><![CDATA[Jeśli zdarzy Ci się konieczność drukowania czegoś z C# za pomocą bibliotecznej klasy  - i będziesz chciał wydrukować coś do pliku (do PDFa…]]></description><link>https://robert.skarzycki.pl/39-print-to-file-w-csharp-na-windows-10/</link><guid isPermaLink="false">https://robert.skarzycki.pl/39-print-to-file-w-csharp-na-windows-10/</guid><pubDate>Thu, 07 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Jeśli zdarzy Ci się konieczność drukowania czegoś z C# za pomocą bibliotecznej klasy &lt;code class=&quot;language-text&quot;&gt;System.Drawing.Printing.PrintDocument&lt;/code&gt; - i będziesz chciał wydrukować coś do pliku (do PDFa), to wiedz, że musisz dodać jedną ważną linijkę. A mianowicie - podać &lt;code class=&quot;language-text&quot;&gt;PrinterName&lt;/code&gt; w &lt;code class=&quot;language-text&quot;&gt;PrinterSettings&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;printDocument&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PrinterSettings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PrinterName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Microsoft Print to PDF&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Jeśli tego nie zrobisz, a będziesz drukował do pliku (czyli ustawisz &lt;code class=&quot;language-text&quot;&gt;PrintToFile&lt;/code&gt; na &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;), to plik co prawda się stworzy, ale będzie jakiś taki &lt;em&gt;corrupted&lt;/em&gt; i przeglądarka PDFów nie będzie chciała go otworzyć. Taki przynajmniej miałem efekt na Windowsie 10.&lt;/p&gt;
&lt;p&gt;Finalnie całość konfiguracji wyglądałaby tak:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; printDocument &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;PrintDocument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

printDocument&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PrinterSettings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PrinterName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Microsoft Print to PDF&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
printDocument&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PrinterSettings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PrintToFile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
printDocument&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PrinterSettings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PrintFileName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;@&quot;some\path\to\file.pdf&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[SignalR + Azure + mikroserwisy = ? (cz.1)]]></title><description><![CDATA[Mamy następuje dramatis personae: mikroserwisowe środowisko w chmurze Azure, n aplikacji klienckich. Mamy nowy feature: chcemy, aby chmura…]]></description><link>https://robert.skarzycki.pl/38-singalr-azure-mikroserwisy-cz-1/</link><guid isPermaLink="false">https://robert.skarzycki.pl/38-singalr-azure-mikroserwisy-cz-1/</guid><pubDate>Sun, 03 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Mamy następuje &lt;em&gt;dramatis personae&lt;/em&gt;: mikroserwisowe środowisko w chmurze Azure, &lt;em&gt;n&lt;/em&gt; aplikacji klienckich. Mamy nowy &lt;em&gt;feature&lt;/em&gt;: chcemy, aby chmura sama informowała klienty o jakiejś zmianie - pojedynczego klienta lub całą ich grupę. Załóżmy, że klientami nie są aplikacje mobilne, więc &lt;em&gt;push notifications&lt;/em&gt; odpadają. Chcemy to ograć za pomocą SignalR. Ale jak to zrobić na Azure?&lt;/p&gt;
&lt;h1&gt;SignalR na Azure&lt;/h1&gt;
&lt;p&gt;Azure udostępnia usługę pod nazwą &lt;strong&gt;Azure SignalR Service&lt;/strong&gt;. Co ona nam oferuje, skoro SignalR zawsze mogliśmy sobie “postawić” na zwykłej aplikacji ASP.NET? W największym skrócie - umożliwia spięcie SignalR także z “nietypowymi” bytami takimi jak Azure Functions oraz załatwia za nas problemy ze skalowaniem. W zależności od tego, jaką konfigurację chcemy mieć (o tym niżej), możemy uruchomić tę usługę w trybie &lt;em&gt;Default&lt;/em&gt; lub &lt;em&gt;Serverless&lt;/em&gt;.&lt;/p&gt;
&lt;h1&gt;Tryb &lt;em&gt;Default&lt;/em&gt;&lt;/h1&gt;
&lt;p&gt;To jest “klasyczne” SignalR, gdzie Azure SignalR Service (dalej ASS) pełni rolę proxy pomiędzy klientami a “hub serverem”, czyli aplikacją webową, na której “stawiamy” SignalR (analogicznie, jakbyśmy stawiali je w zwykłej aplikacji ASP.NET, z tym, że potrzebujemy innych nugetów). Wówczas komunikacja wygląda tak, że klienty połączone są z ASS websocketem (oczywiście, o ile dany klient wspiera websockety, no ale to wiadomo z samej natury SignalR), a także nasz “hub server” jest jest połączony kilkoma websocketami z ASS.&lt;/p&gt;
&lt;p&gt;Do zaimplementowania mamy wówczas jedynie klasę “huba” (czyli właściwą “logikę” komunikacji) oraz kilka linijek konfiguracji w &lt;code class=&quot;language-text&quot;&gt;Startup.cs&lt;/code&gt;. To dołączone nugety zajmują się tym, żeby wystawić endpoint &lt;code class=&quot;language-text&quot;&gt;/negotiate&lt;/code&gt;, do którego uderzają klienty. Ten endpoint zwraca im już url (i token) bezpośrednio do ASS, z którym następuje połączenie websocketowe. To ważna uwaga, ponieważ Azure API Management nie wspiera socketów - więc to właściwie jedyne rozwiązanie: nasz “schowany” za gatewayem serwis wystawia tylko &lt;code class=&quot;language-text&quot;&gt;/negotiate&lt;/code&gt;, a reszta komunikacji idzie niejako z pominięciem gatewaya.&lt;/p&gt;
&lt;p&gt;Jeśli chodzi o pricing - to w tym układzie płacimy za każdą wiadomość, która wychodzi z ASS. A zatem - jeśli to “hub serwer” sam z siebie coś wysyła, to płacimy tylko za ilość wiadomości do klientów; gdy jednak do klient coś śle do “hub serwera”, to musimy zapłacić też za wiadomość wysłaną z ASS do tegoż “hub serwera”. Zatem takie “echo” od klienta to w zasadzie dwie płatne wiadomości - z ASS do huba i potem (już odpowiedź) z ASS do klienta. Tak przynajmniej zrozumiałem to, co opisane jest w dokumentacji.&lt;/p&gt;
&lt;h1&gt;Tryb &lt;em&gt;Serverless&lt;/em&gt;&lt;/h1&gt;
&lt;p&gt;A co z sytuacją, gdy naszym “hubem” ma być Azure Function? I to już nieważne, w którą stronę idzie komunikacja: od klientów czy do klientów. Problemem jest to, że funkcja nie będzie miec stałego połączenia z ASS. W tej sytuacji setup jest taki, że potrzebujemy w zasadzie dwóch funkcji:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;tej, która wystawia endpoint &lt;code class=&quot;language-text&quot;&gt;/negotiate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;oraz co najmniej drugiej do obsługi wiadomości.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Chodzi o to, że analogicznie, jak w scenariuszu &lt;em&gt;Default&lt;/em&gt;, musimy wystawić dla klientów endpoint HTTP &lt;code class=&quot;language-text&quot;&gt;/negotiate&lt;/code&gt;, który będzie działać tak samo, jak wyżej: zwróci url do ASS oraz token. Możemy to ograć zwykłą funkcją triggerowaną przez żądanie HTTP.&lt;/p&gt;
&lt;p&gt;Druga funkcja potrzebna jest natomiast do samej obsługi komunikatów. W zależności, w którą stronę ma ona działać, możemy mieć albo trigger HTTP (coś w stylu callbacku dla ASS, pod który ów będzie pukał, gdy klient wysyła wiadomość do huba), albo jakiś tam dowolny trigger typu kolejka lub cokolwiek, jeśli chcemy sami pchać wiadomości do klientów.&lt;/p&gt;
&lt;p&gt;Jedną i drugą funckję “ogrywamy” odpowiednim nugetem + atrybutami, które wstrzykują do funkcji klasy, które umożliwiają albo dostęp do konfiguracji, albo komunikację z klientami.&lt;/p&gt;
&lt;h1&gt;&lt;em&gt;To be continued…&lt;/em&gt;&lt;/h1&gt;
&lt;p&gt;Jak to wszystko się ma do mikroserwisów - o tym w części drugiej. :)&lt;/p&gt;</content:encoded></item><item><title><![CDATA[7 porad dla każdego, kto robi coś po godzinach]]></title><description><![CDATA[Zapiski do tego aryktułu z bloga Stack Overflow. 1. Wybierz proste zdanie Nie celuj zbyt wysoko - niech Twoim celem będzie proste zadanie…]]></description><link>https://robert.skarzycki.pl/37-7-porad-dla-kazdego-kto-robi-cos-po-godzinach/</link><guid isPermaLink="false">https://robert.skarzycki.pl/37-7-porad-dla-kazdego-kto-robi-cos-po-godzinach/</guid><pubDate>Tue, 22 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Zapiski do tego &lt;a href=&quot;https://stackoverflow.blog/2020/12/03/tips-to-stay-focused-and-finish-your-hobby-project/&quot;&gt;aryktułu z bloga Stack Overflow&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;1. Wybierz proste zdanie&lt;/h1&gt;
&lt;p&gt;Nie celuj zbyt wysoko - niech Twoim celem będzie proste zadanie. Dlaczego? Bo i tak natrafisz na różnorodne problemy, które skomplikują to proste zadanie.&lt;/p&gt;
&lt;h1&gt;2. Nie wynajduj koła na nowo&lt;/h1&gt;
&lt;p&gt;Jeśli natrafisz na jakiś problem - znajdź gotowe rozwiązanie: Google, StackOverflow itd. Jakaś trudna konfiguracja środowiska/biblioteki? Na pewno ktoś to opisał i możesz skorzystać z gotowca. (&lt;em&gt;Nie próbuj konfigurować Webpacka samemu. ;)&lt;/em&gt;)&lt;/p&gt;
&lt;h1&gt;3. Postaraj się mieć coś działającego jak najszybciej&lt;/h1&gt;
&lt;p&gt;To po prostu będzie Cię motywować. Wyobraź sobie, że tworzysz grę: nie skupiaj się na dopieszczonej grafice czy wielu rozgałęzieniach rozgrywki na starcie; lepiej, jak uda Ci się stworzyć całą rozgrywkę (&lt;em&gt;user journey&lt;/em&gt; ;) ), nawet w najprostszej szacie graficznej. Poczucie, że “to działa” doda Ci motywacji.&lt;/p&gt;
&lt;h1&gt;4. Będą trudności i problemy - to przyjmij za pewnik&lt;/h1&gt;
&lt;p&gt;Na pewno będą jakieś trudności i problemy. W końcu często w takim &lt;em&gt;hobby project&lt;/em&gt; na warsztat bierzesz coś, z czym nie masz do czynienia na co dzień. Ale gdy w końcu przebrniesz przez te trudności, będziesz miał poczucie zwycięstwa. No i bezcenne doświadczenie.&lt;/p&gt;
&lt;h1&gt;5. Opublikuj swoją &lt;em&gt;working beta&lt;/em&gt;&lt;/h1&gt;
&lt;p&gt;Gdy uda Ci się dojść do momentu “działającej wersji beta” - upublicznij to. Wyślij znajomym, wrzuć na swojego bloga, Twittera, cokolwiek. Dzięki temu masz szansę otrzymać jakąś informację zwrotną (że coś nie działa, że to jest fajne itp.) oraz zdobędziesz motywację do dalszej pracy.&lt;/p&gt;
&lt;h1&gt;6. Nie rób przerwy&lt;/h1&gt;
&lt;p&gt;Chodzi o to, że większość &lt;em&gt;side projects&lt;/em&gt; umiera dlatego, że w pewnym momencie odkładamy je naprawdę na bok - niby tylko na chwilę: bo tydzień wakacji, bo akurat-coś-ważnego - ale w rezultacie do tego nie wracamy. I zostajemy z takim rozgrzebanym czymś, do czego potem trudno wrócić: bo nie pamiętamy, na czym skończyliśmy, bo już ten pomysł nie wydaje się taki nęcący i wspaniały itp. Rada jest taka, żeby w jednym &lt;em&gt;batchu&lt;/em&gt; (oczywiście to nie znaczy: za jednym “posiedzeniem”) zrobić wersję działającą (no, powiedzmy, przynajmniej działającą betę).&lt;/p&gt;
&lt;h1&gt;7. &lt;em&gt;Enjoy the process&lt;/em&gt;&lt;/h1&gt;
&lt;p&gt;Jeśli &lt;em&gt;side project&lt;/em&gt; robisz z przyjemnością i okoliczności są sprzyjające: bo puściłeś sobie do tego ulubioną muzę, bo na jego potrzeby kupiłeś jakiś szalony gadżet, bo robisz go z przyjaciółmi - to wówczas możesz liczyć na prawdziwy &lt;em&gt;fun&lt;/em&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[IAsyncEnumerable - to jest dobre!]]></title><description><![CDATA[C# 8 wprowadził pewną nowinkę - . Co prawda na horyzoncie jest już C# 9, ale jakoś ta nowinka z poprzedniej wersji jakoś nie trafiła pod…]]></description><link>https://robert.skarzycki.pl/35-iasyncenumerable-to-jest-dobre/</link><guid isPermaLink="false">https://robert.skarzycki.pl/35-iasyncenumerable-to-jest-dobre/</guid><pubDate>Mon, 14 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;C# 8 wprowadził pewną nowinkę - &lt;code class=&quot;language-text&quot;&gt;IAsyncEnumerable&lt;/code&gt;. Co prawda na horyzoncie jest już C# 9, ale jakoś ta nowinka z poprzedniej wersji jakoś nie trafiła pod strzechy - a może być w pewnych sytuacjach bardzo przydatna.&lt;/p&gt;
&lt;p&gt;W największym skrócie &lt;code class=&quot;language-text&quot;&gt;IAsyncEnumerable&lt;/code&gt; umożliwia korzystanie z &lt;code class=&quot;language-text&quot;&gt;yield return&lt;/code&gt; w metodach oznaczonych słowem kluczowym &lt;code class=&quot;language-text&quot;&gt;async&lt;/code&gt; - wcześniej nie było to możliwe. Ktoś powie: “zaraz, zaraz - przecież dotychczas mogłem napisać sygnaturę metody w stylu &lt;code class=&quot;language-text&quot;&gt;async Task&amp;lt;IEnumerable&amp;lt;SomeType&gt;&gt;&lt;/code&gt;, co nie?“. To prawda - ale taka sygnatura oznaczała, że asynchronicznie zwracamy enumerację, jednak już samo enumerowanie asynchroniczne nie jest. Tymczasem &lt;code class=&quot;language-text&quot;&gt;IAsyncEnumerable&lt;/code&gt; to coś więcej - to asynchroniczne enumerowanie, to znaczy: każdy “krok” enumeracji jest asynchroniczny. Co to oznacza? Na poniższym przykładzie można zobaczyć różnicę. Mamy dwa podejścia do wybrania pierwszego elementu z “asynchronicznej enumeracji” - z &lt;code class=&quot;language-text&quot;&gt;Task&amp;lt;IEnumerable&amp;lt;int&gt;&gt;&lt;/code&gt; oraz &lt;code class=&quot;language-text&quot;&gt;IAsyncEnumerable&amp;lt;int&gt;&lt;/code&gt;. Jaki jest wynik na konsoli?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TestRun&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; test &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        Console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Task&amp;lt;IEnumerable&amp;lt;int&gt;&gt;:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; test&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DoWithIEnumerable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        Console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;\nIAsyncEnumerable&amp;lt;int&gt;:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; test&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DoWithIAsyncEnumerable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;FirstOrDefaultAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Test&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt; numbers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Task&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;IEnumerable&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;DoWithIEnumerable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;List&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; number &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; numbers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; item &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ReturnWithDelay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;IAsyncEnumerable&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;DoWithIAsyncEnumerable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; number &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; numbers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; item &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ReturnWithDelay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Task&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ReturnWithDelay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Task&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                Console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;$&quot;Waiting 2 seconds before returning... &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token expression language-csharp&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                Task&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Wyjście:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Task&amp;lt;IEnumerable&amp;lt;int&gt;&gt;:
Waiting 2 seconds before returning... 2
Waiting 2 seconds before returning... 4
Waiting 2 seconds before returning... 8
Waiting 2 seconds before returning... 16
Waiting 2 seconds before returning... 32

IAsyncEnumerable&amp;lt;int&gt;:
Waiting 2 seconds before returning... 2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Widzicie różnicę? W przypadku &lt;code class=&quot;language-text&quot;&gt;Task&amp;lt;IEnumerable&amp;lt;int&gt;&gt;&lt;/code&gt; musimy de facto najpierw asynchronicznie zbudować całą naszą kolekcję, aby ją zwrócić klientowi. Co z tego, że klient chce tylko pierwszy element, my i tak musimy zbudować całość. Tymczasem w &lt;code class=&quot;language-text&quot;&gt;IAsyncEnumerable&lt;/code&gt; enumerujemy tak, jak w “klasycznym” &lt;code class=&quot;language-text&quot;&gt;IEnumerable&lt;/code&gt;: jeśli &lt;em&gt;caller&lt;/em&gt; chce tylko jeden element, to “napracujemy” się tylko nad wybraniem tego jednego elementu.&lt;/p&gt;
&lt;p&gt;Przykład jest dosyć sztuczny, nasuwa się zatem pytanie, kiedy takie coś możne nam się przydać. Otóż, dobrym przykładem (sam ostatnio używałem w takiej sytuacji &lt;code class=&quot;language-text&quot;&gt;IAsyncEnumerable&lt;/code&gt;) jest wyciąganie listy dokumentów z Cosmos DB: ze względu na charakterystykę komunikacji z Cosmosem, de facto takiej listy nie dostaniemy jednym zapytaniem do bazy. Cosmos niejako “paginuje” (“porcjuje”) wyniki - czasem potrzeba kilku strzałów do Cosmosa, aby wybrać wyszystkie dokumenty z listy. A każdy taki strzał jest asynchroniczny. Widzimy więc od razu, że w przypadku, gdy &lt;em&gt;caller&lt;/em&gt; potrzebuje tylko podzbioru wyników (w skrajnym przypadku - chce woła tylko &lt;code class=&quot;language-text&quot;&gt;FirstOrDefault&lt;/code&gt;), nie musimy robić wszystkich zapytań do Cosmosa, by wpierw zbudować całą listę wynikową.&lt;/p&gt;
&lt;p&gt;Zatem wszędzie tam, gdzie budowanie enumeracji jest asynchroniczne - możemy zyskać stosując &lt;code class=&quot;language-text&quot;&gt;IAsyncEnumerable&lt;/code&gt;. Dzięki temu unikamy “ukrytego kosztu”: gdy metoda przedstawia się jako &lt;code class=&quot;language-text&quot;&gt;Task&amp;lt;IEnumerable&amp;lt;T&gt;&gt;&lt;/code&gt;, to nie wiemy (bez zaglądania do jej implementacji), co jest tak naprawdę asynchroniczne w budowaniu takiej enumeracji: czy każdy krok wymaga asynchronicznej operacji, czy może tylko jakiś krok wstępny/przygotowawczy. Natomiast przy &lt;code class=&quot;language-text&quot;&gt;IAsyncEnumerable&lt;/code&gt; jest to przejrzyste dla konsumenta naszej metody.&lt;/p&gt;
&lt;p&gt;Na koniec - dwie sprawy, na które należy zwrócić uwagę:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;po pierwsze - &lt;code class=&quot;language-text&quot;&gt;IAsyncEnumerable&lt;/code&gt; jest “awaitowalne”, czyli - jak w przykładzie powyżej - nie trzeba już go “opakowywać” w &lt;code class=&quot;language-text&quot;&gt;Task&amp;lt;...&gt;&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;jeśli chcemy coś wyciągnąć z &lt;code class=&quot;language-text&quot;&gt;IAsyncEnumerable&lt;/code&gt; za pomocą LINQ-owych metod, to musimy doinstalować NuGeta &lt;code class=&quot;language-text&quot;&gt;System.Linq.Async&lt;/code&gt; i korzystać z asyncowych odpowiedników - np. &lt;code class=&quot;language-text&quot;&gt;FirstOrDefaultAsync&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Learn in public]]></title><description><![CDATA[Gdy następnym razem będziesz się czegoś uczyć, zrób tak: upublicznij swój proces uczenia. Niech to będą wpisy na blogu, jakieś nagrywane…]]></description><link>https://robert.skarzycki.pl/34-learn-in-public/</link><guid isPermaLink="false">https://robert.skarzycki.pl/34-learn-in-public/</guid><pubDate>Sat, 12 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Gdy następnym razem będziesz się czegoś uczyć, zrób tak: upublicznij swój proces uczenia. Niech to będą wpisy na blogu, jakieś nagrywane filmiki albo nawet upublicznione notatki. Spróbuję Cię przekonać, że warto!&lt;/p&gt;
&lt;h1&gt;bo najlepiej nauczysz się, jeśli zaczniesz uczyć kogoś&lt;/h1&gt;
&lt;h1&gt;bo to nic nie kosztuje i po co zazdrośnie chronić np. swoje notatki&lt;/h1&gt;
&lt;h1&gt;bo świadomość publicznego “raportowania” postępów motywuje&lt;/h1&gt;
&lt;h1&gt;bo “oddajesz” społeczności to, co wziąłeś od innych&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.swyx.io/learn-in-public/&quot;&gt;https://www.swyx.io/learn-in-public/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.blog/2020/05/14/the-most-successful-developers-share-more-than-they-take/&quot;&gt;https://stackoverflow.blog/2020/05/14/the-most-successful-developers-share-more-than-they-take/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Identtyfikacja przeglądarki, czyli jak działają paywalle]]></title><description><![CDATA[…]]></description><link>https://robert.skarzycki.pl/33-identyfikacja-przegladarki-czyli-jak-dzialaja-paywalle/</link><guid isPermaLink="false">https://robert.skarzycki.pl/33-identyfikacja-przegladarki-czyli-jak-dzialaja-paywalle/</guid><pubDate>Mon, 23 Nov 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;…&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Śmierć Moment.js]]></title><description><![CDATA[We wrześniu autorzy biblioteki Moment.js ogłosili, że kończą prace nad tym projektem. Od teraz będą jedynie poprawiać oczywiste błędy…]]></description><link>https://robert.skarzycki.pl/32-smierc-momentjs/</link><guid isPermaLink="false">https://robert.skarzycki.pl/32-smierc-momentjs/</guid><pubDate>Mon, 09 Nov 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We wrześniu autorzy biblioteki Moment.js &lt;a href=&quot;https://momentjs.com/docs/#/-project-status/&quot;&gt;ogłosili, że kończą prace nad tym projektem&lt;/a&gt;. Od teraz będą jedynie poprawiać oczywiste błędy, natomiast nie będą dodawane nowe funkcje. Dlaczego i co to oznacza, jeśli w jakimś swoim projekcie używam Moment.js?&lt;/p&gt;
&lt;p&gt;Po pierwsze - dlaczego. Głównym zarzutem wobec biblioteki jest jej rozmiar. Nie za bardzo poddaje się ona procesowi &lt;em&gt;tree shakingu&lt;/em&gt;, który jest używany podczas budowania aplikacji frontendowych np. za pomocą Webpacka, polegający na odrzucaniu tego kodu, który nie jest używany w aplikacji. W efekcie na dzień dobry, dodając zależność do Moment.js, finalny bundle powiększa się o ca 250 kB. Sporo. Problem został już zauważony dawno, powstały alternatywne biblioteki, które są kilku- lub wręcz kilkudziesięciokrotnie “lżejsze” (tutaj znajdziesz &lt;a href=&quot;https://github.com/you-dont-need/You-Dont-Need-Momentjs/blob/master/README.md&quot;&gt;dobrą rozpiskę alternatywnych bibliotek&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Po drugie - co robić, jak żyć, jeśli używam Moment.js? Sami autorzy biblioteki przyznają, że jest kilka powodów, dla których nie trzeba od razu rzucać się na wymianę Moment.js na coś innego, m.in. gdy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;jeśli znasz tylko/głównie API Moment.js, to gwałtowna zmiana na inną bibliotekę może być trudna (a i projekt z dużą ilością odwołań do Moment.js może być trudny do szybkiego przepisania),&lt;/li&gt;
&lt;li&gt;jeśli musisz wspierać stare przeglądarki (Moment.js wspiera IE8, czego nie można powiedzieć np. o Luxonie),&lt;/li&gt;
&lt;li&gt;jeśli inne biblioteki w Twoim projekcie mają zależność od Moment.js - co z tego, że sam przesiądziesz się na co innego, skoro i tak ta inna biblioteka dociągnie Ci całego Momenta?
Jeśli chcesz rozważyć alternatywy, to na horyzoncie są m.in.:&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://moment.github.io/luxon/&quot;&gt;Luxon&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://day.js.org/&quot;&gt;Day.js&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://date-fns.org/&quot;&gt;date-fns&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Jeśli chodzi o przyszłość, to być może kiedyś w ogóle nie będą potrzebne te biblioteki - jest otwarta &lt;a href=&quot;https://momentjs.com/docs/#/-project-status/future/&quot;&gt;propozycja dodania do standardu ECMA&lt;/a&gt; globalnej klasy, na wzór &lt;code class=&quot;language-text&quot;&gt;Math&lt;/code&gt;, która nazywałaby się &lt;code class=&quot;language-text&quot;&gt;Temporal&lt;/code&gt; i obsługiwałaby całą problematykę dat, czasu i stref czasowych. Pewnie przyjdzie nam na to jeszcze poczekać, ale warto śledzić ten wątek. Moim zdaniem można sparafrazować znane powiedzenie, że najtrudniejsze w programowaniu jest nazywanie zmiennych i unieważnianie cache’a; ja bym dodał: obsługa dat/czasu.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Code Coverage w .NET Core]]></title><description><![CDATA[Code coverage to miara pokrycia kodu testami. Innymi słowy: procentowy wskaźnik, jaka część naszego kodu jest “otestowana”. To narzędzie…]]></description><link>https://robert.skarzycki.pl/31-code-coverage-w-netcore/</link><guid isPermaLink="false">https://robert.skarzycki.pl/31-code-coverage-w-netcore/</guid><pubDate>Mon, 02 Nov 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Code coverage&lt;/em&gt; to miara pokrycia kodu testami. Innymi słowy: procentowy wskaźnik, jaka część naszego kodu jest “otestowana”. To narzędzie może nam posłużyć w rozpoznaniu na niższym poziomie projektów lub modułów, które obszary są zabezpieczone testami, a w których ich brakuje. Czasami też - niestety - jakieś wymogi formalne przetargu lub zamówienia mogą wymuszać określony poziom pokrycia testami. Napisałem niestety, bo pojedyncza liczba lub liczba per moduł/projekt, komuś z zewnątrz projektu niewiele powie. Chyba lepiej mieć pokryty testami kod biznesowy, kluczowy, a nie np. jakąś pomocniczą bibliotekę; tymczasem “gołe” liczby mogą sprawić, że jest to równoważne. Co więcej, można też oszukać narzędzie do &lt;em&gt;code coverage&lt;/em&gt; pisząc testy… bez asercji: testy się nie wysypią, a na pewno uruchomią kod…&lt;/p&gt;
&lt;p&gt;Wracając jednak do korzyści dla zespołu programistów, można skorzystać z &lt;em&gt;code coverage&lt;/em&gt; zarówno w nowym, jak i istniejącym projekcie. Ta miara powinna być traktowana raczej “relatywnie”, trochę tak, jak “dług techniczny”, obliczany przez takie narzędzia jak Sonar. Chodzi o to, że pewnie trudno albo w ogóle bez niepotrzebnie byłoby po prostu dążyć do 100% pokrycia testami. Jednakże, jeśli w kolejnych iteracjach ten wskaźnik spada, to może oznaczać, że po prostu piszemy za mało testów (lub nie piszemy ich wcale) dla nowego kodu. A to byłoby bardzo złe. Podobnie z sytuacją istniejącego projektu - nawet jednorazowe uruchomienie &lt;em&gt;code coverage&lt;/em&gt; na początku pozwala ocenić, jak dużo jest testów i jakiej są one “jakości”.&lt;/p&gt;
&lt;p&gt;…&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Ciekawostki Typescript #4 - typowanie strukturalne]]></title><description><![CDATA[Jesteśmy przyzwyczajeni, że JS jest liberalny, dzięki stosowanemu przezeń typowaniu kaczemu (“jeśli coś chodzi jak kaczka i kwacze jak…]]></description><link>https://robert.skarzycki.pl/29-ciekawostki-typescript-4-typowanie-strukturalne/</link><guid isPermaLink="false">https://robert.skarzycki.pl/29-ciekawostki-typescript-4-typowanie-strukturalne/</guid><pubDate>Wed, 28 Oct 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Jesteśmy przyzwyczajeni, że JS jest liberalny, dzięki stosowanemu przezeń &lt;em&gt;typowaniu kaczemu&lt;/em&gt; (“jeśli coś chodzi jak kaczka i kwacze jak kaczka…”). Innymi słowy - póki nazwy pól się zgadzają, możemy obiekty stosować wymiennie:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dog &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Rex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;breed&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;German shepherd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cat &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Filemon&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;sayLoveTo&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;I love you, &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;sayLoveTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dog&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sayLoveTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cat&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Gdy jednak do tego wszystkiego dodamy typy, sprawy się komplikują - wydawać by się mogło, że obikety &lt;code class=&quot;language-text&quot;&gt;dog&lt;/code&gt; i &lt;code class=&quot;language-text&quot;&gt;cat&lt;/code&gt; (jeśli są różnych typów, np. &lt;code class=&quot;language-text&quot;&gt;Dog&lt;/code&gt; i &lt;code class=&quot;language-text&quot;&gt;Cat&lt;/code&gt;) muszą mieć wspólny typ bazowy, żeby można było je przekazać do metody. Okazuje się jednak - że nie:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dog&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  age&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;
  breed&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  age&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SomeoneToLove&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sayLoveTo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;someone&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SomeoneToLove&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;I love you, &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;someone&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Jak widać, nie ma żadnej relacji pomiędzy typami &lt;code class=&quot;language-text&quot;&gt;Dog&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Cat&lt;/code&gt; i &lt;code class=&quot;language-text&quot;&gt;SomeoneToLove&lt;/code&gt; - i właśnie nie musi być! Wszak TS idzie to w ślad (i musi iść) JS, de facto stosując &lt;em&gt;typowanie kacze&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Taka cecha języka może być niespodzianką i czasami prowadzić do pułapek, ale z drugiej strony w wielu miejscach ułatawia pisanie, na przykład w testach jednostkowych.&lt;/p&gt;
&lt;p&gt;Ten wpis jest częścią serii o TypeScripcie, inspirowaną lekturą &lt;a href=&quot;https://helion.pl/ksiazki/typescript-skuteczne-programowanie-dan-vanderkam,e_1lgp.htm&quot;&gt;książki D. Vanderkama pt. “TypeScript. Skuteczne programowanie”&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Ciekawostki Typescript #3 - z API może przyjść wszystko]]></title><description><![CDATA[Gdy piszemy w C# kod, który operuje na zdeserializowanym JSON-ie czujemy się pewnie: jeśli coś uda się zdeserializować do zdefiniowanego…]]></description><link>https://robert.skarzycki.pl/28-ciekawostki-typescript-3-z-api-moze-przyjsc-wszystko/</link><guid isPermaLink="false">https://robert.skarzycki.pl/28-ciekawostki-typescript-3-z-api-moze-przyjsc-wszystko/</guid><pubDate>Wed, 14 Oct 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Gdy piszemy w C# kod, który operuje na zdeserializowanym JSON-ie czujemy się pewnie: jeśli coś uda się zdeserializować do zdefiniowanego przez nas typu, to znaczy, że obiket jest poprawny i możemy normalnie na nim pracować. Początkującego programistę TyepScript może jednak zaskoczyć fakt - mimo że w zasadzie oczywisty, jeśli chwilę pomyśleć - iż jeśli “deserializujemy” (cudzysłowy intencjonalnie) coś w JSON-a do obiektu w TS, to możemy dostać “pod spodem” niepoprawne dane. Spójrzmy na przykład.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; json &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;{ &quot;firstName&quot;: 12 }&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  firstName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; person&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Person &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;json&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Hello, &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUpperCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ten kod będzie poprawny w TS, ale w &lt;em&gt;runtime&lt;/em&gt; wywali nam się z błędem: &lt;code class=&quot;language-text&quot;&gt;person.firstName.toUpperCase()&lt;/code&gt;. “Jak to?”, pomyślicie - “Przecież TypeScript miał sprawdzać typy!“. No, sprawdza, ale to już jest nasza odpowiedzialność, że to, co przychodzi z &lt;code class=&quot;language-text&quot;&gt;JSON.parse&lt;/code&gt;, chcemy traktować jako typ &lt;code class=&quot;language-text&quot;&gt;Person&lt;/code&gt;. Oczywiście ten przykład jest naciągany, ale łatwo w to “wdepnąć”, np. gdy korzystamy z biblioteki do wykonywania requestów, np. &lt;code class=&quot;language-text&quot;&gt;axios&lt;/code&gt; - wówczas ta linijka &lt;code class=&quot;language-text&quot;&gt;JSON.parse&lt;/code&gt; jest ukryta przed naszymi oczyma, ale ona tam w środku jest. Jeśli więc oczekujemy, że w odpowiedzi na request dostaniemy obiekt typu &lt;code class=&quot;language-text&quot;&gt;Person&lt;/code&gt;, to API może - wskutek błędu - spłatać figla i wstawić &lt;code class=&quot;language-text&quot;&gt;number&lt;/code&gt; zamiast &lt;code class=&quot;language-text&quot;&gt;stringa&lt;/code&gt;. Dowiemy się o tym niestety dopiero w &lt;em&gt;runtime&lt;/em&gt; i nie mamy możliwości “deserializacji ze sprawdzeniem typu” - bo przecież informacji o typie już w &lt;em&gt;runtime&lt;/em&gt; nie mamy.&lt;/p&gt;
&lt;p&gt;Oczywiście wniosek z tego &lt;strong&gt;nie jest&lt;/strong&gt; taki, żeby teraz wszystko sprawdzać: czy string jest stringiem itp. Wyczulam raczej na sytuacje w czasie developmentu, kiedy mogą nam się zdarzać rozjazdy pomiędzy deklaracjami typów, a tym, co zwraca nam np. API - zamiast wyrywać sobie włosy, zastanawiając się, dlaczego zamiast stringa mamy liczbę, sprawdźmy, czy nie jesteśmy ofiarami takiego błędnego założenia, że TS nam będzie typów strzegł także w czasie wykonywania.&lt;/p&gt;
&lt;p&gt;Ten wpis jest częścią serii o TypeScripcie, inspirowaną lekturą &lt;a href=&quot;https://helion.pl/ksiazki/typescript-skuteczne-programowanie-dan-vanderkam,e_1lgp.htm&quot;&gt;książki D. Vanderkama pt. “TypeScript. Skuteczne programowanie”&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Koniec darmowego open source, czyli IdentityServer będzie płatny]]></title><description><![CDATA[Kilka dni temu twórcy popularnej biblioteki (frameworka?) IdentitySever ogłosili, że “komercjalizują” projekt, tzn. zakładają własną firmę…]]></description><link>https://robert.skarzycki.pl/30-koniec-darmowego-open-source-czyli-identityserver-platny/</link><guid isPermaLink="false">https://robert.skarzycki.pl/30-koniec-darmowego-open-source-czyli-identityserver-platny/</guid><pubDate>Thu, 08 Oct 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Kilka dni temu twórcy popularnej biblioteki (frameworka?) &lt;a href=&quot;https://github.com/IdentityServer/IdentityServer4&quot;&gt;IdentitySever&lt;/a&gt; &lt;a href=&quot;https://leastprivilege.com/2020/10/01/the-future-of-identityserver/&quot;&gt;ogłosili&lt;/a&gt;, że “komercjalizują” projekt, tzn. zakładają własną firmę - &lt;a href=&quot;https://duendesoftware.com/&quot;&gt;Duende Software&lt;/a&gt; - i że będą IdentityServer sprzedawać jako produkt. Za pieniądze.&lt;/p&gt;
&lt;p&gt;W zasadzie nie powinno to nikogo dziwić: skoro ktoś stworzył bardzo popularny produkt, który rozdawał ludziom za darmo, a więc przy jego rozwijaniu pracował &lt;em&gt;pro bono&lt;/em&gt;, to racjonalną decyzją staje się w pewnym momencie monetyzacja projektu; innymi słowy: odcięcie kuponów od istniejącej bazy użytkowników/klientów. Ta sprawa jednak po raz kolejny pokazuje, że jest problem z pieniędzmi w &lt;em&gt;open source&lt;/em&gt;: zresztą Dominick Baier, współtwórca IdentityServer, sam opisuje jak razem z drugim autorem bibioteki, Brockiem Allenem, próbowali znaleźć finansowanie dla projektu, który stawał się ich pełnoetatową pracą. Najpierw finansowali pracę przy IdentityServerze z przychodów, które uzyskiwali jako trenerzy/konsultanci, pomagający przy jego wdrożeniu. Model bardzo przejrzysty: z biblioteki mogą korzystać wszyscy za darmo, ale jeśli chcesz mieć “autograf” autora, zapłać ekstra. Potem, próbowali oprzeć się na sponsorach: organizacjach lub indywidualnych darczyńcach, ale to okazało się niewypałem: przychody na tym polu wynosiły średnio kilka tysięcy dolarów rocznie. Żadna wielka firma nie była chętna wprost przyjąć ich pod swe skrzydła - co by mogło zapewnić stabilne finansowanie i rozwój projektu - ostatecznie podjęli decyzję o tym, żeby otworzyć firmę i po prostu sprzedawać IdentityServer - przy okazji przemianowany na Duende IdentityServer - jako zwykłe oprogramowanie za pieniądze.&lt;/p&gt;
&lt;p&gt;Dla mnie ta historia pokazuje, że &lt;em&gt;open source&lt;/em&gt; będzie jeszcze bardziej dryfował w kierunku podzielenia się na drobne projekciki tworzone przez entuzjastów oraz wielkie projekty, za którym stoją firmy (i pieniądze). Być może tak było już wcześniej, ale chyba to będzie się nasilać. Zagadkowy pozostaje jedynie sam moment przejścia od projektu hobbystycznego do stadium komercyjnego. Co z osobami, które nie są głównymi autorami biblioteki, a też pomagały współtworzyć projekt, cały czas pracując &lt;em&gt;pro bono&lt;/em&gt;? W przypadku IdentityServera jest kilka takich osób (wystarczy zajrzeć tutaj: [&lt;a href=&quot;https://github.com/IdentityServer/IdentityServer4/graphs/contributors&quot;&gt;https://github.com/IdentityServer/IdentityServer4/graphs/contributors&lt;/a&gt;]) - czy one nie poczują się wykorzystane, bo z jednej strony pomagały tworzyć darmowy projekt, który służył milionom, ale jednak przy okazji ktoś zacznie wprost zarabiać na ich pracy? I właściwie kto powinien mieć głos decydujący, co się dzieje z daną biblioteką - oryginalny autor, aktualny &lt;em&gt;maintainer&lt;/em&gt;, osoba, która najwięcej wcommitowała? (To nie jest problem w przypadku IdentityServera, przytłaczająca część pracy została wykonana przez Baiera i Allena, ale w innych projektach już nie musi tak to wyglądać.)&lt;/p&gt;
&lt;p&gt;Wracając z tych górnolotnych dywagacji na ziemię, pozostaje pytanie: jak żyć, jeśli w projekcie używam IdentityServera? Autorzy dają jeszcze 2 lata na wspieranie obecnej opensource’owej wersji, więc nie potrzeba gwałtownych ruchów. Na pewno jednak należy zastanowić się, jeśli Twój projekt jest albo na wczesnym stadium (może jeszcze można zrezygnować z IdentityServer?), albo jest to już produkt na produkcji (czy będzie nas stać na płacenie za licencję?). Dwa lata miną szybko, a w przypadku tak wrażliwej części systemu, jaką jest “zarządzanie tożsamością”, na pewno nie można sobie pozwolić na dłuższą metę na korzystanie z biblioteki, która nie jest już wspierana. Pewnie pocieszający powienien być fakt, że IdentityServer implementuje standard OpenID Connect / OAuth, więc ewentualnie przepięcie się na jakieś inne rozwiązanie nie powinno oznaczać przeorywania całego systemu. Oby. ;)&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Ciekawostki Typescript #2 - TS - typy = JS]]></title><description><![CDATA[TypeScript nadzbiorem JavaScriptu Mówi się, że “TypeScript jest nadzbiorem JavaScriptu”. Ale co to oznacza? W praktyce - wiadomo - każdy kod…]]></description><link>https://robert.skarzycki.pl/27-ciekawostki-typescript-2-ts-minus-typy-rowna-sie-js/</link><guid isPermaLink="false">https://robert.skarzycki.pl/27-ciekawostki-typescript-2-ts-minus-typy-rowna-sie-js/</guid><pubDate>Thu, 01 Oct 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;TypeScript nadzbiorem JavaScriptu&lt;/h2&gt;
&lt;p&gt;Mówi się, że “TypeScript jest nadzbiorem JavaScriptu”. Ale co to oznacza? W praktyce - wiadomo - każdy kod JavaScriptu jest poprawnym kodem JavaScriptowym. Zatem, co jest tym “nad” w stosunku do JavaScriptu? Otóż - system typów. I - przynajmniej koncepcyjnie, z pewnymi drobnym zastrzeżeniami, o których niżej - tylko system typów. To znaczy, że - “kanonicznie” (zastrzeżnia niżej) - konwersja TS do JS polega tylko na usunięciu informacji o typach. A zatem właściwie taką “konwersję” można by wykonać ręcznie usuwając wszystkie typy czy informacje, że dana klasa implementuje jakiś interfejs.&lt;/p&gt;
&lt;p&gt;To, co napisałem powyżej, można dobrze zaobserwować na przykładzie typescriptowych modyfikatorów dostępu w klasie. Wyobraźmy sobie, że mamy taką klasę, napisaną w TS:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; firstName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; lastName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;firstName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lastName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; firstName
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lastName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lastName
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sayHello&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Hi, I&apos;m &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lastName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Pola &lt;code class=&quot;language-text&quot;&gt;firstName&lt;/code&gt; i &lt;code class=&quot;language-text&quot;&gt;lastName&lt;/code&gt; zadeklarowaliśmy jako prywatne. Zatem w TS próby dostania się do nich zakończą się błędem:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; john &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;John&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Smith&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;john&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;- tu będzie błąd&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Jak ta klasa zostanie jednak przekonwertowana do JavaScriptu? Mniej więcej tak:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;firstName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lastName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; firstName
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lastName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lastName
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;sayHello&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Hi, I&apos;m &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lastName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A gdybyśmy w JavaScripcie chcieli się dostać do &lt;code class=&quot;language-text&quot;&gt;firstName&lt;/code&gt;? Proszę bardzo - &lt;strong&gt;błędu nie ma&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; john &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;John&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Smith&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;john&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;- błędu nie ma!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Gdy pierwszy raz się o tym dowiedziałem, to pomyślałem: “Ale jak to? Prywatne pole jest publiczne?… TypeScript, oszukałeś mnie…” Ale w zasadzie to jest konsekwencja tego równania:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;TypeScript - (system typów) = JavaScript&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Jeśli zatem chcielibyśmy na 100% traktować te pola jako prywatne - bo np. piszemy bibliotekę i naprawdę chcemy ukryć jakiś szczegół implementacyjny - to mamy dwa wyjścia:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;skorzystanie z eksperymentalnej notacji pól prywatnych w JS - &lt;code class=&quot;language-text&quot;&gt;#&lt;/code&gt; (&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields&quot;&gt;tutaj opis tej notacji&lt;/a&gt;),&lt;/li&gt;
&lt;li&gt;skorzystanie z domknięć (patrz niżej).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;W przypadku domknięć, należałoby napisać w TS naszą klasę mniej więcej tak:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;sayHello&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;firstName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lastName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sayHello&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Hi, I&apos;m &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;firstName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;lastName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To rozwiązanie też ma niestety swoje wady:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;jeśli chcemy skorzystać z ukrtych danych w metodzie klasy, to musimy ją zdefiniować w konstruktorze,&lt;/li&gt;
&lt;li&gt;każda instacja klasy ma kopię tych metod zdefiniowanych w konstruktorze, co zwiększa zużycie pamięci.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wyjątki od reguły&lt;/h2&gt;
&lt;p&gt;Na koniec muszę zwrócić uwagę na dwa wyjątki od tej zasady, że transpilator jedynie usuwa informacje o typach i modyfikatory dostępu. Tymi wyjątkami są enumy oraz pewien &lt;em&gt;syntactic sugar&lt;/em&gt; w postaci definiowania parametrów konstruktora od razu jako pól klasy.&lt;/p&gt;
&lt;h3&gt;Enumy&lt;/h3&gt;
&lt;p&gt;Dla programisty C#, który zaczyna przygodę z TypeScriptem - użycie enumów to coś naturalnego: przecież nie będziemy używać jakichś &lt;em&gt;magic numberów&lt;/em&gt;! Jednakże enumy w TS zachowują się nietypowo.
Po pierwsze, enum z wartościami liczbowymi pozwala na przypisanie do niego dowolnej liczby… (Co to za enum?!)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; WeekDay &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  Monday &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  Tuesday &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  Wednesday &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  Thursday &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  Friday &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  Saturday &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  Sunday &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; wtf&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; WeekDay &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;-- nie ma błędu!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Po drugie, jeśli do deklaracji enuma dodamy const (a więc &lt;code class=&quot;language-text&quot;&gt;const enum&lt;/code&gt;), to wszystkie jego użycia zostaną potraktowane jak &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt; w C# - w czasie transpilacji zostaną podmienione na wartości liczbowe.&lt;/p&gt;
&lt;p&gt;Po trzecie, enumy z wartościami stringowymi mają pewne pułapki: jeśli przypiszemy enuma do zmiennej, to zmiast typu string, otrzyma on typ literałowy, więc nie będziemy mogli go nadpisać, np.:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; Status &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  Pending &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pending&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  Done &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;done&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; statusText &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Status&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pending
statusText &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Status is: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;statusText&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;-- tu będzie błąd: &quot;Type &apos;string&apos; is not assignable to type &apos;Status&apos;.&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Zamiast enuma lepiej użyć tutaj “unię typów literałowych”, czyli:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Status&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pending&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;done&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Parametry konstruktora jako pola&lt;/h3&gt;
&lt;p&gt;TypeScript umożliwia zrobienie takie skrótowego zapisu:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; firstName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; lastName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;zamiast rozwlekłego:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; firstName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; stirng
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; lastName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;firstName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lastName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; firstName
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lastName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lastName
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ten &lt;em&gt;syntactic sugar&lt;/em&gt; jest dla TS wyjątkowy, bo właśnie też skutkuje generowanie kodu JS - czyli znów wyłamujemy się z reguły “TS minus typy to JS”. Autor książki, o której wspominam poniżej, krytykuje używanie tego “myku” w TS (właśnie z takich powodów, nazwijmy to, kanonicznych), jednak dla mnie jest to bardzo wygodne. Ponadto, podobną propozycję widziałem w którejś z przyszłych/eksperymentalnych wersji C#, więc być może to jest też jakiś trend wspólny dla różnych języków. :)&lt;/p&gt;
&lt;p&gt;Ten wpis jest częścią serii o TypeScripcie, inspirowaną lekturą &lt;a href=&quot;https://helion.pl/ksiazki/typescript-skuteczne-programowanie-dan-vanderkam,e_1lgp.htm&quot;&gt;książki D. Vanderkama pt. “TypeScript. Skuteczne programowanie”&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Ciekawostki Typescript #1 - typ vs. interfejs]]></title><description><![CDATA[W TypeScripcie mamy do dyspozycji dwa podobne byty, które przy deklarowaniu typów wydają się być zamienne -  oraz . Czy te dwa typy czymś…]]></description><link>https://robert.skarzycki.pl/26-ciekawostki-typescript-1-typ-vs-interfejs/</link><guid isPermaLink="false">https://robert.skarzycki.pl/26-ciekawostki-typescript-1-typ-vs-interfejs/</guid><pubDate>Tue, 01 Sep 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;W TypeScripcie mamy do dyspozycji dwa podobne byty, które przy deklarowaniu typów wydają się być zamienne - &lt;code class=&quot;language-text&quot;&gt;type&lt;/code&gt; oraz &lt;code class=&quot;language-text&quot;&gt;interface&lt;/code&gt;. Czy te dwa typy czymś się różnią?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;W ogólności, poza subtelną różnicą w składni - do &lt;code class=&quot;language-text&quot;&gt;type&lt;/code&gt; “przypisujemy” deklarację, a &lt;code class=&quot;language-text&quot;&gt;interface&lt;/code&gt; deklarujemy wprost, bez znaku &lt;code class=&quot;language-text&quot;&gt;=&lt;/code&gt; - te dwie konstrukcje można stosować zamiennie. Obie będą weryfikować typ, np.:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; alice&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Person1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Alice&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  age&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// -&gt; tu będzie błąd&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; bob&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Person1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Bob&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  age&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// -&gt; tu będzie błąd&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Obie konstrukcje możemy też używać zamiennie w bardziej złożonych przykładach, jak sygnatury indeksu czy typy funkcji, np.:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dictionary1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dictionary2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Zarówno typ, jak i interfejs możemy “podziedziczyć” (właściwe określenie w świecie TS to raczej - rozszerzyć):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Employee1&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Employee2&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Jakie zatem mamy różnice?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Jeśli definiujemy unię, to możemy zastosować tylko tylko &lt;code class=&quot;language-text&quot;&gt;type&lt;/code&gt;: &lt;code class=&quot;language-text&quot;&gt;type State = &apos;on&apos; | &apos;off&apos;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Tylko &lt;code class=&quot;language-text&quot;&gt;type&lt;/code&gt; może rozszerzać unię: &lt;code class=&quot;language-text&quot;&gt;type StateExtended = State &amp;amp; &apos;paused&apos;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Teoretycznie interfejsem można wyrazić krotkę, ale za pomocą typu jest to dużo bardziej czytelne i naturalne:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pair&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// zamiast jakiegoś dziwadła&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Tuple&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;
  &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;
  length&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Z drugiej strony &lt;code class=&quot;language-text&quot;&gt;interface&lt;/code&gt; umożliwia &lt;em&gt;scalanie deklaracji&lt;/em&gt;, czyli coś w stylu &lt;code class=&quot;language-text&quot;&gt;partial class&lt;/code&gt; z C#. Chodzi o to, że możemy w jednym pliku zadeklarować interfejs, a w innym - “dalszą jego część”, finalnie uzyskując interfejs, który będzie połączeniem obydwu deklaracji, np.:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// plik Person.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// plik PersonAddress.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  address&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ten myk nie jest potrzebny raczej w naszym kodzie (możemy przecież scalić deklaracje do jednego pliku), ale przydaje się, gdy chcemy zmodyfikować typ, który dostajemy z jakiejś zewnętrznej biblioteki. Ten mechanizm wykorzystuje też podstawowa biblioteka TS - nowości i zmiany w kolejnych standardach ES to kolejne pliki, które rozszerzają wcześniejsze deklaracje: zatem wybierając odpowiedni standard ES, &lt;em&gt;de facto&lt;/em&gt; wybieramy zestaw plików z deklaracjami, które są scalane.&lt;/p&gt;
&lt;p&gt;Ten wpis jest częścią serii o TypeScripcie, inspirowaną lekturą &lt;a href=&quot;https://helion.pl/ksiazki/typescript-skuteczne-programowanie-dan-vanderkam,e_1lgp.htm&quot;&gt;książki D. Vanderkama pt. “TypeScript. Skuteczne programowanie”&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[AutoMapper to zło - czy na pewno?]]></title><description><![CDATA[Jakiś czas temu przeczytałem artykuł pn. “AutoMapper to zło”. Autor w skrócie odradza używanie AutoMappera, z co najmniej dwóch powodów…]]></description><link>https://robert.skarzycki.pl/25-automapper-to-zlo-czy-na-pewno/</link><guid isPermaLink="false">https://robert.skarzycki.pl/25-automapper-to-zlo-czy-na-pewno/</guid><pubDate>Wed, 01 Jul 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Jakiś czas temu przeczytałem &lt;a href=&quot;https://www.admu.pl/automapper-to-zlo/&quot;&gt;artykuł pn. “AutoMapper to zło”&lt;/a&gt;. Autor w skrócie odradza używanie AutoMappera, z co najmniej dwóch powodów:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fakt, że mapowanie między różnymi typami mamy “gratis”, zachęca do nadmiernego tworzenia kolejnych “warstw” modeli, co często nie jest potrzebne&lt;/li&gt;
&lt;li&gt;niestandardowa logika mapowania jest potencjalnym źródłem poważnych i trudnych do wychwycenia błędów.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;O ile daruję autorowi clickbaitowy tytuł wpisu, to jednak pozwolę sobie nie zgodzić się z tak postawioną tezą: że AutoMapper to biblioteka, która więcej szkodzi, niż daje pożytku.&lt;/p&gt;
&lt;p&gt;Zacznijmy od początku - co sami autorzy AutoMappera o nim mówią:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;AutoMapper is a simple little library built to solve a deceptively complex problem - getting rid of code that mapped one object to another. This type of code is rather dreary and boring to write, so why not invent a tool to do it for us?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;W skrócie chodzi więc o to, by nie musieć pisać ręcznie kodu, który przekopiowuje dane z obiektu typu A do obiektu typu B. Kropka. Nie ma tu mowy o żadnych warstwach, DTOsach itp. - po prostu mamy obiekt typu A i chcemy go “przekonwertować” na typ B. Powinniśmy więc najpierw zadać sobie pytanie, kiedy z takimi sytuacjami możemy się spotkać, a dopiero potem - czy AutoMapper jest właściwym do tego narzędziem.&lt;/p&gt;
&lt;p&gt;Gdy myślę o użyciu AutoMappera - a więc o sytuacjach konwersji danych z typu A na typ B - to przychodzą mi do głowy następujące przykłady:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;chcemy odseparować / odróżnić model API od modelu, którego używamy w aplikacji,&lt;/li&gt;
&lt;li&gt;chcemy przenieść dane pomiędzy typami, które o sobie nic nie wiedzą&lt;/li&gt;
&lt;li&gt;checemy uprościć model zwracany do klienta, bo ten bazodanowy - ze względu na użycie ORMa - jest trochę niewygodny.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Poniżej opiszę te przypadki.&lt;/p&gt;
&lt;h1&gt;Jak odseparować model API od modelu aplikacji?&lt;/h1&gt;
&lt;p&gt;Wyobraźmy sobie, że mamy prostą REST-owo-CRUD-ową nakładkę na bazę danych. Mamy tam encję &lt;code class=&quot;language-text&quot;&gt;Product&lt;/code&gt;. Dopóki jest ona prosta, to po prostu w endpointach przyjmujemy i zwracamy dokładnie tę samą klasę &lt;code class=&quot;language-text&quot;&gt;Student&lt;/code&gt;, której używamy w modelu bazdanowym (np. używając EF Core).&lt;/p&gt;
&lt;p&gt;Co jeśli chcemy jednak zapisywać też informacje “audytowe”, tj. kto i kiedy zmodyfikował encję. Dodajemy pola &lt;code class=&quot;language-text&quot;&gt;LastModifiedDate&lt;/code&gt; i &lt;code class=&quot;language-text&quot;&gt;LastModifiedBy&lt;/code&gt;. Jeśli chcemy, by np. na stronie te dane były wyświetlane, to świetnie, od razu dostajemy je gratis, bo przecież używamy tej samej klasy &lt;code class=&quot;language-text&quot;&gt;Student&lt;/code&gt;. Ale co z endpointami od aktualizacji? Czy checmy, aby klient mógł “zapostować” na endpoint &lt;code class=&quot;language-text&quot;&gt;/students&lt;/code&gt; JSONa z dowolnie wypełnionymi polami &lt;code class=&quot;language-text&quot;&gt;LastModifiedDate&lt;/code&gt; i &lt;code class=&quot;language-text&quot;&gt;LastModifiedDate&lt;/code&gt;? Raczej nie, to by mogło pozowlić komuś podszyć się pod innego użytkownika albo antydatować zmianę. Możemy to oczywiście obsłużyć/zabezpieczyć na poziomie np. kontrolera, tj. zawsze odpowiednio nadpisywać te pola, jeśli robimy aktualizację. No ale wówczas po co te pola w modelu API? Jeśli opisujemy nasze API za pomocą np. Swaggera, to w wygenerowanej dokumentacji będą one uwzględnione - jak powiedzieć klientowi, że “nie, te pola są nieistotne, możesz je zignorować”. Trochę słabo. :/ W końcu, skoro używamy języka typowanego (C#), to typy powinny opisywać dokładnie to, co się dzieje.
W takiej sytuacji AutoMapper może być pomocą - wprowadzamy typy &lt;code class=&quot;language-text&quot;&gt;StudentViewApiModel&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;StudentUpsertApiModel&lt;/code&gt; i możemy z nich / do nich mapować naszą klasę &lt;code class=&quot;language-text&quot;&gt;Student&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;Jak przenieść dane pomiędzy typami, które o sobie nic nie wiedzą?&lt;/h1&gt;
&lt;p&gt;Weźmy teraz na tapetę inną sytuację - mamy trzy projekty:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Students.Domain&lt;/code&gt; - logika biznesowa&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Students.WebApi&lt;/code&gt; - Web API dla tej logiki&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Students.FunctionApp&lt;/code&gt; - cloudowa funkcja, która też operuje na tej logice (nie wiem, czyta z kolejki, uruchamia się w jakichś odstępach czasowych itp.)
Oczywiście dwa ostatnie projekty zależą od pierwszego. Jeślibyśmy chcieli korzystać wszędzie z klasy &lt;code class=&quot;language-text&quot;&gt;Student&lt;/code&gt; pochodzącej z projektu &lt;code class=&quot;language-text&quot;&gt;Students.Domain&lt;/code&gt;, to możemy natrafić na schody: jakiś problem typu opisany w poprzedniej części. Załóżmy zatem, że zdecydowaliśmy się wprowadzić oddzielny typ - powiedzmy &lt;code class=&quot;language-text&quot;&gt;StudentQueueModel&lt;/code&gt; - np. w projekcie &lt;code class=&quot;language-text&quot;&gt;Students.FunctionApp&lt;/code&gt; i chcemy jakoś przekonwertować dane z tego typu na ten domenowy. Klasa &lt;code class=&quot;language-text&quot;&gt;Student&lt;/code&gt; nic nie wie o klasie &lt;code class=&quot;language-text&quot;&gt;StudentQueueModel&lt;/code&gt;, nie możemy zrobić więc metody &lt;code class=&quot;language-text&quot;&gt;Student FromQueueModel(StudentQueueModel model)&lt;/code&gt;. Możemy w drugą stronę - w klasie &lt;code class=&quot;language-text&quot;&gt;StudentQueueModel&lt;/code&gt; zrobić metodę &lt;code class=&quot;language-text&quot;&gt;Student ToDomainModel()&lt;/code&gt; i pisać mapowanie ręcznie. Albo - możemy też użyć AutoMappera.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Jak uprościć model, który mamy “w środku”, zanim wyślemy go do klienta, “na zewnątrz”?&lt;/h1&gt;
&lt;p&gt;Może być tak, że biblioteka ORM wymaga od nas pewnych dodatkowych pól lub konstrukcji, których nie chcielibyśmy wystawiać klientowi. (Ja natrafiłem na taki problem z podkolekcją elementu typu prostego dla EF Core: zamiast &lt;code class=&quot;language-text&quot;&gt;ICollection&amp;lt;string&gt;&lt;/code&gt; musiałem stworzyć typ &lt;code class=&quot;language-text&quot;&gt;SubItem&lt;/code&gt;, z unikalnym kluczem i wartością typu &lt;code class=&quot;language-text&quot;&gt;string&lt;/code&gt; - i jego użyć w kolekcji &lt;code class=&quot;language-text&quot;&gt;ICollection&amp;lt;SubItem&gt;&lt;/code&gt;.) Może być tak, że jakieś dane ściągamy z zewnętrznego serwisu - albo w ogóle nasze API jest tylko nakładką na jakiś stary system - i ten oryginalny format jest niestrawny lub niewygodny dla klienta. Ot, choćby wartości logiczne są zapisane jako string (np. &lt;code class=&quot;language-text&quot;&gt;&quot;true&quot;&lt;/code&gt;).
Mapowanie możemy napisać ręcznie - ale możemy też użyć AutoMappera.&lt;/p&gt;
&lt;h1&gt;Podsumowanie&lt;/h1&gt;
&lt;p&gt;Moim zdaniem istnieją sytuacje, w których albo chcemy mieć dodatkową warstwę modelu, albo wręcz taką wprost mamy narzuconą z zewnątrz (ciągniemy dane ze starego systemu). Wówczas - AutoMapper może być pomocny, bo nie tylko przyspiesza &lt;em&gt;development&lt;/em&gt;, ale zabezpiecza przed błędami (nie trzeba pisać mapowania dla nowego pola - o ile występuje w obu typach mapowania, źródłowym i docelowym). Oczywiście, jak każde narzędzie, także i AutoMapper, może być źle użyty lub nadużyty: ale to już jest opowiedziane znanym powiedzeniem, mówiącym, że dla trzymającego tylko młotek wszystko wydaje się gwoździem.
Wydaje mi się więc, że autor rzeczonego artykułu wylewa dziecko z kąpielą. Jeśli czasami zdarza się, że dodajemy niepotrzebne warstwy modeli i mapowania między nimi, to nie znaczy, że narzędzie do mapowania jest złe - to znaczy, że podjęliśmy złą decyzję i niepotrzebnie doprowadziliśmy do sytuacji, kiedy mapowanie jest w ogóle użyte.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Meeting Web Accessibility Guidelines - kurs Pluralsight]]></title><description><![CDATA[Notatki do kursu Pluralsight o dostępności stron internetowych pn. “Meeting Web Accessibility Guidelines (Section 508/ WCAG 2.1)” Wybór…]]></description><link>https://robert.skarzycki.pl/22-meeting-web-accessibility-guidelines-kurs-pluralsight/</link><guid isPermaLink="false">https://robert.skarzycki.pl/22-meeting-web-accessibility-guidelines-kurs-pluralsight/</guid><pubDate>Sun, 10 May 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Notatki do &lt;a href=&quot;https://app.pluralsight.com/courses/4fd6f17e-022e-4461-9b82-c8a9c05c02b4/table-of-contents&quot;&gt;kursu Pluralsight o dostępności stron internetowych&lt;/a&gt; pn. “Meeting Web Accessibility Guidelines (Section 508/ WCAG 2.1)”&lt;/p&gt;
&lt;h1&gt;Wybór “kodeksu”&lt;/h1&gt;
&lt;p&gt;Istnieją w branży pewne “kodeksy” (&lt;em&gt;conformance guidelines&lt;/em&gt;), określające zasady dostosowania stron internetowych dla osób z niepełnosprawnościami.&lt;/p&gt;
&lt;p&gt;Jakie są korzyści z ich zastosowania?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;dostępność dla OzN&lt;/li&gt;
&lt;li&gt;zabezpiecza przed pozwami sądowymi (tak, w Stanach firma może być pozwana np. za to, że jej strona nie jest dostępna dla osób niewidomych)&lt;/li&gt;
&lt;li&gt;poprawa SEO&lt;/li&gt;
&lt;li&gt;ogólnie lepsze &lt;em&gt;usability&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;kiedy się zestarzejesz, łatwiej Ci będzie z tej aplikacji korzystać - bo z wiekiem i Ty będziesz mieć problemy np. ze wzrokiem&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Najbardziej znane “kodeksy” (z perspektywy amerykańskiej):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Section 508&lt;/li&gt;
&lt;li&gt;WCAG 2.1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Section 508&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;uchwalona w 1986 r. poprawka do &lt;em&gt;Rehabilitation Act&lt;/em&gt; z 1973 r., zaktualizowana też w 2017 r.&lt;/li&gt;
&lt;li&gt;dotyczy stron rządowych albo takich, które za rządowe środki powstały&lt;/li&gt;
&lt;li&gt;w przeciwieństwie do ogólnoświatowego WCAG, Section 508 jest używana głównie w USA&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;WCAG (Web Conformance Accessiblity Guideline)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;tworzone przez grupę, która jest częścią W3C (&lt;em&gt;industry standard&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;oparte na czterech zasadach (POUR):
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;perceivable&lt;/em&gt; - każdy użytkownik jest w stanie “odebrać” informację&lt;/li&gt;
&lt;li&gt;&lt;em&gt;operable&lt;/em&gt; - każdy użytkownik jest w stanie operować na stronie, bez względu na to, czy używa myszki, czy klawiatury&lt;/li&gt;
&lt;li&gt;&lt;em&gt;understandable&lt;/em&gt; - informacja na stronie jest zrozumiała, a interakcje przewidywalne (logiczne)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;robust&lt;/em&gt; - strona musi działać z różnymi technologiami asystującymi&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;poziomy: A, AA, AAA (im więcej A, tym bardziej “restrykcyjne” reguły)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/WCAG21/&quot;&gt;strona WCAG&lt;/a&gt; zawiera dokładnie rozpisanie kilkudziesięciu wskazówek&lt;/li&gt;
&lt;li&gt;podstawowa rekomendacja - osiągnąć WCAG 2.1 AA (co przy okazji wypełnia w 100% Section 508)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;HTML&lt;/h1&gt;
&lt;h2&gt;Struktura strony&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ściśle poprawny HTML:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;!doctype html&gt;&lt;/code&gt; - wymagane przez WCAG!&lt;/li&gt;
&lt;li&gt;brak zduplikowanych albo niedomkniętych tagów&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;określić język strony (WCAG A 3.1.1) - atrybut &lt;code class=&quot;language-text&quot;&gt;lang&lt;/code&gt; na elemencie &lt;code class=&quot;language-text&quot;&gt;&amp;lt;html&gt;&lt;/code&gt;; jeśli jakieś fragmenty są w innym języku - nawet kawałki &lt;code class=&quot;language-text&quot;&gt;span&lt;/code&gt; - tam też należy użyć atrybutu &lt;code class=&quot;language-text&quot;&gt;lang&lt;/code&gt;, żeby zmienić “lokalnie” język (WCAG AA 3.1.2)&lt;/li&gt;
&lt;li&gt;tytuł strony (WCAG A 2.4.2), inny dla każdej strony (żeby ułatwić orientację)&lt;/li&gt;
&lt;li&gt;poprawne skalowanie (WCAG AA 1.4.4):
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;meta name=&quot;viewport&quot;&gt;&lt;/code&gt; - nie stosować &lt;code class=&quot;language-text&quot;&gt;user-scalable=no&lt;/code&gt; ani &lt;code class=&quot;language-text&quot;&gt;maximum-scale=1&lt;/code&gt; (bo to wyłącza powiększenie)&lt;/li&gt;
&lt;li&gt;używaj w CSS &lt;em&gt;relative units&lt;/em&gt;, tzn. &lt;code class=&quot;language-text&quot;&gt;rem&lt;/code&gt; lub &lt;code class=&quot;language-text&quot;&gt;em&lt;/code&gt; do określania rozmiarów (bo WCAG AA 1.4.1 wymaga, żeby dało się korzystać ze strony powiększonej o 200%)&lt;/li&gt;
&lt;li&gt;strona musi działać i wyglądać, nawet jeśli użytkownik powiększy interlinię (do 1.5), odstęp po akapicie (do 2-krotności czcionki), kerning (do 1/8 rozmiaru czcionki) lub odstęp między wyrazami (do 1/6 rozmariu czcionki) - WCAG AA 1.4.12&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;używaj tagów z HTML5, tj. &lt;code class=&quot;language-text&quot;&gt;header&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;nav&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;main&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;footer&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;aside&lt;/code&gt; (WCAG A 1.3.1, WCAG A 2.4.1) - bo ujawniają strukturę treści oraz ułatwiają nawigację
&lt;ul&gt;
&lt;li&gt;jeśli jest kilka elementów &lt;code class=&quot;language-text&quot;&gt;nav&lt;/code&gt; na stronie - np. na górze, powtórka w stopce oraz oddzielny na bocznym pasku, to mozna je odróżnić nazwami, podanymi w atrybucie &lt;code class=&quot;language-text&quot;&gt;aria-label&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;main&lt;/code&gt; może być tylko jeden na stronie&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;używaj nagłówków (WCAG A 2.4.6)
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;h1&lt;/code&gt; może być tylko jeden na stronie&lt;/li&gt;
&lt;li&gt;nagłówki powinny iść po kolei, bez przeskakiwania, np. z &lt;code class=&quot;language-text&quot;&gt;h1&lt;/code&gt; od razu do &lt;code class=&quot;language-text&quot;&gt;h5&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;stosuj listy (&lt;code class=&quot;language-text&quot;&gt;li&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ul&lt;/code&gt;) zamiast grupy &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt;-ów - screen reader dzięki temu może rozpoznać, jaki to jest typ listy, ile ona zawiera elementów oraz poinformować, który element z listy jest aktywny (WCAG A 1.3.1)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Nawigacja&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;użyj tekstu zamiast obrazka z tekstem, np. w linkach (WCAG AA 1.4.5)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;linki nawigacyjne na różnych stronach poiwnny być w tej samej kolejności na wszystkich stronach (WCAG AA 3.2.3)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;spójna identyfikacja - jeśli na stronie jest np. wyszukiwarka na pasku, to przycisk “submit” powinien być wszędzie opisany tak samo (WCAG AA 3.2.4)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;linki powinny opisywać to, dokąd prowadzą - nie używaj czegoś w stylu: &lt;code class=&quot;language-text&quot;&gt;&amp;lt;p&gt;Kliknij &amp;lt;a&gt;tutaj&amp;lt;/a&gt;, żeby złożyć zamówienie&amp;lt;/p&gt;&lt;/code&gt; (WCAG A 2.4.4)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;unikaj samego “czytaj więcej” w opisie linku - jeśli użytkownik będzie iterował po samych linkach, to będzie miał tylko listę wielu “czytaj więcej” (np. na stronie głównej bloga) i nie będzie wiedział, dokąd one prowadzą; pewnym kompromisem jest mieć coś takiego:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/strona&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  Czytaj więcej &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;hidden&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;o nowościach w .NET&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;wówczas wystarczy dopowiednio ostylować CSS-ową klasę &lt;code class=&quot;language-text&quot;&gt;hidden&lt;/code&gt; tak, aby była niewiodczna - czytnik ekranu jednak ten tekst odczyta&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;bypass blocks&lt;/em&gt; (WCAG A 2.4.1) - czyli mechanizm do omijania np. nagłówka z linkami; taki &lt;em&gt;skip link&lt;/em&gt; powinien:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;być pierwszym elementem na stronie&lt;/li&gt;
&lt;li&gt;ostylowany tak, aby występował poza ekranem, chyba, że jest na nim fokus, np. tak:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.skip-link&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.skip-link:focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;jeśli robimy “skok” robimy za pomocą fokusa, to:
&lt;ul&gt;
&lt;li&gt;docelowy element (ten, do którego &lt;em&gt;skip link&lt;/em&gt; ma nawigować) powienien mieć atrybut &lt;em&gt;tabindex&lt;/em&gt; ustawiony na &lt;code class=&quot;language-text&quot;&gt;-1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;na &lt;code class=&quot;language-text&quot;&gt;onclick&lt;/code&gt; tego skip linka, powinniśmy sfokusować docelowy element&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Tabele&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Table should not be used for layout! It is not 1999.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;tytuł tabeli, poprzedzający ją, powienien być w tagu &lt;code class=&quot;language-text&quot;&gt;caption&lt;/code&gt; (pierwszy “child” elementu &lt;code class=&quot;language-text&quot;&gt;table&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;nagłówek: użyj &lt;code class=&quot;language-text&quot;&gt;th&lt;/code&gt; zamiast &lt;code class=&quot;language-text&quot;&gt;td&lt;/code&gt; i użyj atrybutu &lt;code class=&quot;language-text&quot;&gt;scope=&quot;col&quot;&lt;/code&gt; (bo tam jest “tytuł” dla kolumny)&lt;/li&gt;
&lt;li&gt;wiersz: jeśli w tabeli są grupy (wiersza z &lt;code class=&quot;language-text&quot;&gt;colspan&lt;/code&gt;, będące nagłówkami grupy), to to tam użyj &lt;code class=&quot;language-text&quot;&gt;th&lt;/code&gt;, ale &lt;code class=&quot;language-text&quot;&gt;scope=&quot;colgroup&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;jeśli nadasz nagłówkom &lt;code class=&quot;language-text&quot;&gt;th&lt;/code&gt; (dla kolumn i grup) unikalne identyfikatory, możesz ich użyć w komórkach &lt;code class=&quot;language-text&quot;&gt;td&lt;/code&gt; atrybutu &lt;code class=&quot;language-text&quot;&gt;headers&lt;/code&gt; z wartością będącą listą id-ków nagłówków (coś w stylu współrzędnych)&lt;/li&gt;
&lt;li&gt;za pomocą ukrywania CSS-em (tak jak było to z “read more”) możesz w tagu &lt;code class=&quot;language-text&quot;&gt;caption&lt;/code&gt; dać szerszy opis, jak dane są ustrykturyzowane, zwłaszcza jeśli tabela jest bardziej złożona, ma podgrupy itp.&lt;/li&gt;
&lt;li&gt;dzięki strukturze tabeli (zamiast &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt;-ów) VoiceOver będzie poprawnie opisywał komórki oraz umożliwi nawigację pomiędzy wierszami/kolumnami za pomocą skrótów klawiszowych&lt;/li&gt;
&lt;li&gt;ogólnie lepiej złożone tabele rozbijać na kilka mniejszych, bo to ułatwi zadanie czytnikowi ekranu&lt;/li&gt;
&lt;li&gt;tag &lt;code class=&quot;language-text&quot;&gt;tfoot&lt;/code&gt; powinen być przed tagiem &lt;code class=&quot;language-text&quot;&gt;tbody&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Formularze&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;odpowiednie opisy i etykiety do pól (WCAG A 1.3.1, WCAG A 3.3.2, WCAG AA 2.4.6)&lt;/li&gt;
&lt;li&gt;na inputach label musi się zgadzać z widocznym opisem pola (WCAG A 2.5.3)&lt;/li&gt;
&lt;li&gt;zgrupuj pola, które dotyczą logicznej całości (np. kilka inputów dla adresu: miasto, ulica etc.) - używaj &lt;code class=&quot;language-text&quot;&gt;fieldset&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;legend&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;walidacja, stan pola itp. nie mogą być wyróżnione tylko w sposób wizualny (WCAG 1.3.3)&lt;/li&gt;
&lt;li&gt;jeśli input jest niestandardowy, zadbaj o to, by się poprawnie fokusował, inaczej osoba korzystająca tylko z klawiatury, nie będzie w stanie “doscrollować się” do niego i go zobaczyć (WCAG A 2.1.1)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;no keyboard traps&lt;/em&gt; (WCAG A 2.1.2)&lt;/li&gt;
&lt;li&gt;jeśli obiekt jest fokusowalny, to stan &lt;em&gt;focus&lt;/em&gt; i zwykły muszą być wizualnie rozróżnialne (AA 2.4.7)&lt;/li&gt;
&lt;li&gt;błędy muszą być opisane tekstem (a nie tylko np. obrazkiem wykrzyknika) - WCAG A 3.3.; jeśli jest możliwe zasugerować, jak poprawić błąd (np. data podróży “tam” nie może być późniejsza niż podróż powrotna), to powinno być to przedstawione użytkownikowi (WCAG AA 3.3.3)&lt;/li&gt;
&lt;li&gt;element &lt;code class=&quot;language-text&quot;&gt;form&lt;/code&gt; powinien mieć atrybut &lt;code class=&quot;language-text&quot;&gt;novalidate&lt;/code&gt;, bo przeglądarkowe “tooltipy” takiej walidacji nie są dostępne&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;label&lt;/code&gt;: albo za pomocą atrybutu &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt; (wtedy &lt;code class=&quot;language-text&quot;&gt;label&lt;/code&gt; jest rodzeństwem opisywanego inputu), albo poprzez opakowanie (wówczas &lt;code class=&quot;language-text&quot;&gt;label&lt;/code&gt; jest rodzicem)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;tabindex&lt;/code&gt; - dwie dozwolone wartości: &lt;code class=&quot;language-text&quot;&gt;-1&lt;/code&gt; (wyłączamy z naturalnej kolejności focusowania, ale możemy sfocusować za pomocą JS) oraz &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt; (po prostu zmienia element niefokusowalny w fokusowalny)&lt;/li&gt;
&lt;li&gt;jeśli formularz ma konsekwencje w “realnym życiu” (np. zakup) albo w bazie danych (np. zapis edytowanego rekordu), końcowa akcja musi być zatwierdzana (WCAG AA 3.3.4)&lt;/li&gt;
&lt;li&gt;jeśli istnieje jakiś limit czasowy (np. automatyczne zakończenie sesji), użytkownik musi mieć co najmniej 20 sekund na podjęcie decyzji, czy chce przesunąć ten limit czasowy (WCAG A 2.2.1); oczywiście to nie dotyczy tych ograniczeń czasowych, które wynikają z “reguł biznesowych”, np. ograniczonej w czasie rezerwacji miejsca podczas zakupu biletu&lt;/li&gt;
&lt;li&gt;kontrast dla tekstu powinien być co najmniej 4,5:1 (WCAG AA 1.4.3)&lt;/li&gt;
&lt;li&gt;elementy interfejsu i grafiki powinny mieć kontrast co najmniej 3:1 (WCAG 1.4.11)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Obrazy, dźwięki i filmy&lt;/h1&gt;
&lt;h2&gt;Obrazy&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;nie wolno używać obrazków z tekstem zamiast zwykłego tekstu (WCAG AA 1.4.5)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;podstawowa zasada - WCAG A 1.1.1 - nietekstowe treści powinny być zaprezentowane w jakiejś alternatywnej, tekstowej formie&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;wszystkie&lt;/strong&gt; obrazki powinny mieć atrybut &lt;code class=&quot;language-text&quot;&gt;alt&lt;/code&gt;; jeśli dany obrazek jest tylko dekoracją, to powinny mieć pusty atrybut &lt;code class=&quot;language-text&quot;&gt;alt&lt;/code&gt; (inaczej czytnik ekranu będzie czytał nazwę pliku…)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;opis w atrybucie &lt;code class=&quot;language-text&quot;&gt;alt&lt;/code&gt; wcale nie musi być literalnym opisem tego, co jest na obrazku&lt;/li&gt;
&lt;li&gt;nie używaj fraz typu “obrazek przedstawia…”, bo czytnik ekranu już sam informuje, że dany element jest obrazem&lt;/li&gt;
&lt;li&gt;opisz znaczenie lub cel obrazu, a jeśli zawiera tekst, który ma znaczenie, to powinien on być też w atrybucie &lt;code class=&quot;language-text&quot;&gt;alt&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;jeśli obraz jest ustawiony jako tło w CSS (częsty przykład z ikonami, robiony po to, by poprawić wydajność strony), należy po prostu dodać ukryty tekst, który opisuje ikonę (skoro nie ma tagu &lt;code class=&quot;language-text&quot;&gt;img&lt;/code&gt;, nie możemy użyć &lt;code class=&quot;language-text&quot;&gt;alt&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;obrazek wektorowy (SVG) - należy dodać:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;role=&quot;img&quot;&lt;/code&gt;, żeby czytnik ekranu wiedział, że to jest obrazek&lt;/li&gt;
&lt;li&gt;wewnątrz tagu &lt;code class=&quot;language-text&quot;&gt;svg&lt;/code&gt; - tag &lt;code class=&quot;language-text&quot;&gt;title&lt;/code&gt;; opcjonalnie, jeśli chcemy szerzej opisać obrazek, można dodać też tag &lt;code class=&quot;language-text&quot;&gt;desc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;żeby zapewnić kompatybilność z różnymi przeglądarkami, należy za pomocą &lt;code class=&quot;language-text&quot;&gt;aria-labelledby&lt;/code&gt; zrobić odnośnik do tagu &lt;code class=&quot;language-text&quot;&gt;title&lt;/code&gt; (i analogicznie atrybutem &lt;code class=&quot;language-text&quot;&gt;aria-describedby&lt;/code&gt;, jeśli używamy tagu &lt;code class=&quot;language-text&quot;&gt;desc&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Dźwięk&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;każde nagranie audio, które nie jest “na żywo” (i.e. zostało nagrane wcześniej) powinno mieć reprezentację tekstową, która prezentuje tę samą treść (transkrypcję) - WCAG A 1.2.1&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Google Docs Voice Typing albo Windows Speech Recognition - narzędzia, które mogą zapewnić półautomatyczną transkrypcję (pewnie tylko lub głównie po angielsku…)&lt;/li&gt;
&lt;li&gt;w transkrypcji podaj imiona (nazwiska) rozmawiających oraz opisz też “pozawerbalne” dźwięki (śmiech, inny ton)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;jeśli na stronie automatycznie włącza się jakiś dźwięk na dłużej niż 3 sekundy, musi być mechanizm, żeby ten dźwięk wyłączyć lub zapauzować lub dostosować jego głośność niezależnie od poziomu głośności urządzenia (WCAG A 1.4.2) - (na marginesie: ja bym tu też dodał: Żadna strona nie powinna na dzień dobry atakować użytkownika dźwiękami. Nie jesteśmy w roku 1999. ;) )&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;analogicznie, jeśli jakaś treść, która się automatycznie przesuwa, zmienia lub pulsuje (czyli nie tylko filmy, ale np. “karuzele”) - musi być mechanizm, żeby taką treść zatrzymać lub ukryć (WCAG A 2.2.2)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;jeśli cokolwiek błyska/pulsuje, minimalna częstotliwość to 3 Hz - chodzi o uniknięcie napadów epilepsji (inna sprawa, że migające strony to znowu wygląda jak rok 1999…)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Filmy&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;po pierwsze - transkrypcja wypowiedzi (tak, jakby to było samo audio)&lt;/li&gt;
&lt;li&gt;po drugie: napisy do filmu; WCAG A 1.2.1 wymaga tylko dla filmów uprzednio nagranych, natomiast WCAG AA 1.2.4 - także dla nagrań na żywo
&lt;ul&gt;
&lt;li&gt;dwa rodzaje:
&lt;ul&gt;
&lt;li&gt;“open” - zawsze widoczne, “wgrane” do filmu&lt;/li&gt;
&lt;li&gt;“closed” - pokazywane tylko na życzenie użytkownika, jako nakładka na film&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;po trzecie: audiodeskrypcja (słowny opis tego, co widać i co się dzieje na filmie oraz odłgosów lub muzyki, budującej nastrój) - WCAG AA 1.2.5&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;&lt;em&gt;Responsive Web Design&lt;/em&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Visual order must match DOM order&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Czyli kolejność fokusów musi być sensowna i taka, jak to jest widoczne, bez przeskakiwania np. do stopki, żeby potem wrócić do głównej treści (WCAG A 1.3.2, WCAG A 2.4.3).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;strona musi działać niezależnie, jak obrócony jest telefon (portret/krajobraz) - WCAG AA 1.3.4&lt;/li&gt;
&lt;li&gt;jeśli operacja jest dostępna za pomocą gestów (np. zoom dwoma palcami), to musi być też ścieżka za pomocą pojdenyczego wskazania (np. przyciski +/-) (WCAG A 2.5.1)&lt;/li&gt;
&lt;li&gt;akcje powinny być podpięte pod eventy typu &lt;code class=&quot;language-text&quot;&gt;click&lt;/code&gt;/&lt;code class=&quot;language-text&quot;&gt;touch&lt;/code&gt;, a nie pod &lt;code class=&quot;language-text&quot;&gt;keydown&lt;/code&gt;/&lt;code class=&quot;language-text&quot;&gt;touchdown&lt;/code&gt; - tak aby można było wycofać się z akcji poprzez przesunięcie się (myszką, palcem) poza element (WCAG A 2.5.2)&lt;/li&gt;
&lt;li&gt;jeśli operacja jest wyzwalana poruszaniem urządzenia, powinna być też opcja, żeby wykonać ją za pomocą “zwykłego” UI (WCAG A 2.5.4)&lt;/li&gt;
&lt;li&gt;jak ukryć element?
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;display: none&lt;/code&gt; lub &lt;code class=&quot;language-text&quot;&gt;visibility:hidden&lt;/code&gt; w CSS-ie - niewidoczy dla wzorku, ale i czytnika ekranu&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;aria-hidden=&quot;true&quot;&lt;/code&gt; - ukrywa tylko przed czytnikiem ekranu&lt;/li&gt;
&lt;li&gt;wypozycjonowanie poza ekran - niewidoczny dla wzroku, ale widoczny dla czytnika - np. takim CSS-em:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.hidden&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -10000px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[QuaggaJS + React, czyli skaner kodów kreskowych]]></title><description><![CDATA[QuaggaJS to bardzo poręczna biblioteka do odczytywania kodów kreskowych za pomocą samego JavaScriptu. Daje radę z kilkoma formatami kodów i…]]></description><link>https://robert.skarzycki.pl/24-quagga-react-czyli-skaner-kodow-kreskowych/</link><guid isPermaLink="false">https://robert.skarzycki.pl/24-quagga-react-czyli-skaner-kodow-kreskowych/</guid><pubDate>Sun, 03 May 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://serratus.github.io/quaggaJS/&quot;&gt;QuaggaJS&lt;/a&gt; to bardzo poręczna biblioteka do odczytywania kodów kreskowych za pomocą samego JavaScriptu. Daje radę z kilkoma formatami kodów i odczytuje je bardzo sprawnie. Ostatnio chciałem zobaczyć, jak można to zintegrować z Reaktem, a że &lt;a href=&quot;https://github.com/serratus/quagga-react-example&quot;&gt;dostępny w internecie przykład&lt;/a&gt; jest baaaaardzo stary (React w wersji &lt;code class=&quot;language-text&quot;&gt;0.x&lt;/code&gt;…), postanowiłem przygotować na szybko coś z najnowszym Reaktem (i TypeScriptem): &lt;a href=&quot;https://github.com/robert-skarzycki/quagga-react-example&quot;&gt;https://github.com/robert-skarzycki/quagga-react-example&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Podlinkowane repozytorium to w zasadzie kod &lt;a href=&quot;https://quagga-react-example.surge.sh&quot;&gt;strony, która prezentuje bibliotekę w działaniu&lt;/a&gt;. Jak widać, wystarczy kilka linijek kodu, a od razu dostajemy i samo odczytywanie kodów kreskowych, jak i okienko podglądu z kamerki. Wow!&lt;/p&gt;
&lt;p&gt;Zapraszam do forkowania i eksperymentowania - na stronie z dokumentacją do biblioteki można przeczytać, jak pozmieniać konfigurację, na przykład:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;zmienić rozpoznawany format kodów,&lt;/li&gt;
&lt;li&gt;przełączyć na kamerkę &lt;em&gt;en face&lt;/em&gt; lub tylną,&lt;/li&gt;
&lt;li&gt;włączać i wyłączać dekodowanie (żeby nie obciążać urządzenia)&lt;/li&gt;
&lt;li&gt;itd.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Marten - dole i niedole]]></title><description><![CDATA[Jest sobie taka biblioteka o nazwie Marten, która umożliwia korzystanie z bazy PostgreSQL jako bazy dokumentowej albo bazy do event…]]></description><link>https://robert.skarzycki.pl/20-marten-dole-i-niedole/</link><guid isPermaLink="false">https://robert.skarzycki.pl/20-marten-dole-i-niedole/</guid><pubDate>Thu, 30 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Jest sobie taka biblioteka o nazwie &lt;a href=&quot;https://github.com/JasperFx/marten&quot;&gt;Marten&lt;/a&gt;, która umożliwia korzystanie z bazy PostgreSQL jako bazy dokumentowej albo bazy do event sourcingu - w aplikacji .NET. (Co ciekawe, jest współautorem - a przynajmniej jednym z tych, którzy obecnie ją utrzymują - jest nasz krajan, &lt;a href=&quot;https://github.com/oskardudycz&quot;&gt;Oskar Dudycz&lt;/a&gt;). Jeśli Ty lub ktoś w Twoim zespole rozważa jej użycie - przeczytajcie kilka przestróg.&lt;/p&gt;
&lt;p&gt;Dlaczego ludzie wpadają na taki pomysł? Wiadomo, &lt;em&gt;event sourcing&lt;/em&gt;, NoSQL - to wszystko brzmi fajnie i modnie. Kolejny argument - jeśli działasz na Azure, to fakt, że baza PostgreSQL jest tańsza od MS (sic!) oraz, że nie ma “z pudełka” innych baz NoSQL (typu MongoDB - co nie jest do końca prawdą): to wszystko może być kuszące. No i jeszcze do tego pokusa - robimy mikroserwisy, więc czemu nie zrobić w każdym serwisie innej bazy danych? ;)&lt;/p&gt;
&lt;p&gt;A teraz na serio - jeśli chcesz robić event sourcing, to ta biblioteka brzmi ciekawie i może pozwolić łatwo zrobić &lt;em&gt;event store&lt;/em&gt;. Ja tego nie sprawdzałem, więc się nie wypowiem. W ogóle mam jednak takie wrażenie, że autorzy oryginalnie chcieli zrobić po prostu &lt;em&gt;event store&lt;/em&gt; na PostgreSQL, więc potrzebowali do tego &lt;em&gt;document store&lt;/em&gt; - żeby trzymać eventy jako dokumenty. Tak więc niejako przy okazji możemy sobie za pomocą Martena zrobić po prostu bazę dokumentową. I tu zaczynają się schody.&lt;/p&gt;
&lt;p&gt;Technicznie rzecz biorąc od kilku wersji PostgreSQL posiada typ danych JSON (w także JSONB - czyli JSON z informacją o typach). Marten przykrywa to dla nas w ten sposób, że po prostu dla każdego obiektu (dokumentu) tworzy wiersz w odpowiedniej tabelce, gdzie oprócz kolumny z właściwymi JSON-owymi danymi, jest kilka kolumn z metadami (choćby identyfikator). To brzmi na pierwszą myśl dziwnie, ale gdybyśmy mieli to implementować sami, to szybko przyznamy: dobrze, że tę pracę ktoś wykonał za nas.&lt;/p&gt;
&lt;p&gt;Gdy wczytamy się w dokumentację, dowiemy się, że z tej roboty wykonanej za nas - to nawet ktoś zaimplementował Linq. Tak więc - wydawałoby się - bomba! Po prostu Entity Framework dla bazy dokumentowej. Tak więc podpinamy NuGeta, tworzymy projekt i… zaczynają się schody. Niestety, dokumentacja przedstawia najprostsze przykłady, o ograniczeniach milczy albo wspomina zdawkowo. Tymczasem rzeczywistość brutalnie rewiduje pierwotne założenia. Pierwsze schody zaczynają się, gdy chcemy zrobić trochę bardziej skomplikowane zapytanie - ot, choćby coś tam odfiltrować na bazie tego, co jest w podkolekcji. Dla prostych przykładów, np. listy stringów, możemy użyć &lt;code class=&quot;language-text&quot;&gt;Contains&lt;/code&gt;, ale gdy chcemy użyć czegoś bardziej skomplikowanego w predykacie &lt;code class=&quot;language-text&quot;&gt;Where&lt;/code&gt;, to niestety Marten się wysypuje wyjątkiem w runtime’ie. Co gorsze, to jest jakiś randomowy wyjątek z nullem lub o &lt;em&gt;collection has no elements&lt;/em&gt;. Kolejne schody zaczynają się, gdy chcemy zrobić sobie jakąś agregację lub wyciąć tylko fragment dokumentu (inna sprawa, jak dużo powinniśmy wrzucać do dokumentu…). Próby użycia Linqu i &lt;em&gt;extension methods&lt;/em&gt; kończą się wyjątkami, zostaje więc goły SQL zapisany w C#. Co gorsza, jest to stricte “postgresowy” SQL (dzień dobry standard, który jednak w detalach się różni), a do tego - operujemy na JSON-ie w PostgreSQL, czyli musimy nauczyć się całej nowej składni (te wszystkie &lt;code class=&quot;language-text&quot;&gt;-&gt;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;json_array_elements&lt;/code&gt; itp.). Dla człowieka, który - na studiach czy to w pracy - raczej głównie bazował na bazach typu MySQL bądź SQL Server, to jest bardzo przykre doświadczenie. Marten ma też opcję “projekcji”, czyli kodu javascriptowego, który jest za pomocą C# ładowany do bazy SQL-owej i tam trzymany jako bazodanowa funkcja. Słyszeliście? JS ładowany w C# do SQL-a! Mindfuck gotowy!&lt;/p&gt;
&lt;p&gt;Dodatkowym problemem z Martenem jest też to, że niestety nie jest zbyt aktywnie rozwijany - niby ostatnie wersje są wydawne co miesiąc, ale sporo &lt;em&gt;issues&lt;/em&gt; na Githubie wisi ponad rok. (Tu oczywiście nie mam pretensji do autorów, jedynie stweirdzam fakt.) To też dodaje frustracji, bo na ficzery, których nam brakuje, możemy się nie doczekać.&lt;/p&gt;
&lt;p&gt;To, co napisałem wyżej, jest oczywiście zabarwione pewnym uprzedzeniem do PostgreSQL (wychowałem się jednak na MySQL i SQL Serwerze od Microsoftu) oraz lenistwem (nie zawsze chce mi się czytać dokładnie dokumentację). Przyznaję jednak, że moje doświadczenia z Elasticsearchem lub MongoDB były o niebo lepsze (a przecież, gdy pierwszy raz z nimi się stykałem, też pewne koncepcje i składnia dla kwerend była &lt;em&gt;novum&lt;/em&gt;). Wydaje mi się zatem, że pomysł pt. Marten jako baza dokumentowa w C# - to nie jest najlepsze rozwiązanie. Prawdopodbnie popełniłem sporo błędów i częściowo używałem biblioteki w niewłaściwy sposób - ale DX (&lt;em&gt;developer experience&lt;/em&gt;) nie jest zadowalające: tych wszystkich frustracji, co miałem, to już nikt ich nie wymaże z pamięci. ;)&lt;/p&gt;</content:encoded></item><item><title><![CDATA[R.I.P. John Conway]]></title><description><![CDATA[Kilka dni temu dotarła do mnie wiadomość, że John Conway zmarł w wieku 82 lat. Był on twórcą słynnej “gry w życie” (Game of Life) -…]]></description><link>https://robert.skarzycki.pl/23-rip-john-conway/</link><guid isPermaLink="false">https://robert.skarzycki.pl/23-rip-john-conway/</guid><pubDate>Tue, 28 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Kilka dni temu dotarła do mnie wiadomość, że &lt;a href=&quot;https://arstechnica.com/science/2020/04/john-conway-inventor-of-the-game-of-life-has-died-of-covid-19/&quot;&gt;John Conway zmarł&lt;/a&gt; w wieku 82 lat. Był on twórcą słynnej “gry w życie” (&lt;em&gt;Game of Life&lt;/em&gt;) - wspaniałego automatu komórkowego, opartego na arcyprostych regułach, a mimo to pozwalającego na niemal nieograniczone analizy, symulacje i dociekania. Kto nigdy nie słyszał o tej “grze” (to przecież nie jest gra &lt;em&gt;sensu stricto&lt;/em&gt;), koniecznie niech coś sobie o niej przeczyta i zobaczy animacje różnych ponadkomorkówych struktur, takich jak działa, statki itp.&lt;/p&gt;
&lt;p&gt;Dla mnie jednak &lt;em&gt;Game of Life&lt;/em&gt; nie ma charakteru naukowego, ale przede wszystkim - sentymentalny. Pamiętam, że dowiedziałem się o tej “grze” w podstawówce, z archiwalnych numerów “Młodego Technika” z lat 80., gdzie zamieszczona była też implementacja w QBasiku. Przepisywałem to z bratem najpierw do QBasika, eksperymentując z różnymi regułami i zestawem startowym. Ze dwa lata później, reimplementowaliśmy to w C++. Ech, niesamowity czar końcówki lat 90. …&lt;/p&gt;
&lt;p&gt;Tak więc, &lt;em&gt;requiescat in pacem&lt;/em&gt;, Johnie Conwayu!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Programista COBOL-a poszukiwany...]]></title><description><![CDATA[Kilka tygodni temu gubernator stanu New Jersey poinformował, że stanowa administracja natychmiast potrzebuje programistów… COBOL-a. (Sic…]]></description><link>https://robert.skarzycki.pl/21-programista-cobola-poszukiwany/</link><guid isPermaLink="false">https://robert.skarzycki.pl/21-programista-cobola-poszukiwany/</guid><pubDate>Wed, 15 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Kilka tygodni temu gubernator stanu New Jersey &lt;a href=&quot;https://josephsteinberg.com/covid-19-response-new-jersey-urgently-needs-cobol-programmers-yes-you-read-that-correctly/&quot;&gt;poinformował&lt;/a&gt;, że stanowa administracja natychmiast potrzebuje programistów… COBOL-a. (Sic!) Okazało się bowiem, że system do obsługi zasiłków dla bezrobotnych nie wytrzymał natłoku chętnych, spowodowanego koronawirusem. Potrzebne są zatem natychmiastowe usprawnienia, ale system napisany jest w COBOL-u.&lt;/p&gt;
&lt;p&gt;Można się śmiać (o ile nie jest się bezrobotnym w New Jersey, bo ja nie wiem, czy zakorkowany system oznacza brak wypłaty zasiłków…), ale wydaje mi się, że to najlepsza lekcja w zakresie długu technologicznego i konieczności unowocześniania starych systemów. Nawet jeśli coś działa i wydawać by się mogło, że nie trzeba tego ruszać, to - okazuje się - zawsze może pojawić się czynnik zewnętrzny, spoza świata technologii, który wszystko zmienia i “pogrzebie stary świat”.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Enumerable.Range w JavaScripcie]]></title><description><![CDATA[W C# zdarza się czasami napisać taki kod: Ale jak to zrobić w JS? Oto sprytny myk: Źródło: https://stackoverflow.com/a/10050831/201788…]]></description><link>https://robert.skarzycki.pl/16-enumerable-range-w-javascripcie/</link><guid isPermaLink="false">https://robert.skarzycki.pl/16-enumerable-range-w-javascripcie/</guid><pubDate>Fri, 10 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;W C# zdarza się czasami napisać taki kod:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; collection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Enumerable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;$&quot;Item number &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token expression language-csharp&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ale jak to zrobić w JS? Oto sprytny myk:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; collection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Item number &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;i&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Źródło: &lt;a href=&quot;https://stackoverflow.com/a/10050831/2017882&quot;&gt;https://stackoverflow.com/a/10050831/2017882&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Dobre, nie? :)&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Jak zmusić Chrome, żeby zapomniał o redirekcie z http do https]]></title><description><![CDATA[Jeśli w dewelopmencie żonglujemy pomiędzy różnymi apkami, uruchamianymi lokalnie, może zdarzyć się taka sytuacja: odpalamy projekt A, na…]]></description><link>https://robert.skarzycki.pl/17-jak-zmusic-chrome-zeby-zapomnial-o-redirekcie-z-http-do-https/</link><guid isPermaLink="false">https://robert.skarzycki.pl/17-jak-zmusic-chrome-zeby-zapomnial-o-redirekcie-z-http-do-https/</guid><pubDate>Thu, 02 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Jeśli w dewelopmencie żonglujemy pomiędzy różnymi apkami, uruchamianymi lokalnie, może zdarzyć się taka sytuacja:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;odpalamy projekt A, na porcie 3000, który jest skonfigurowany na redirect z HTTP do HTTPS także lokalnie,&lt;/li&gt;
&lt;li&gt;potem odpalamy projekt B, na tym samym porcie, ale bez HTTPSa.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Wtedy Chrome możne nam zrobić pułapkę. Przy odpalaniu projektu B, czyli wpisując &lt;code class=&quot;language-text&quot;&gt;http://localhost:3000&lt;/code&gt;, Chrome będzie nas przekierowywał na HTTPS. Ale projekt B działa lokalnie tylko na HTTP, więc na twarz dostaniemy na twarz &lt;code class=&quot;language-text&quot;&gt;SSL_CERTIFICATE_ERROR&lt;/code&gt;. Odświeżanie strony, jakieś hard refreshe, restart przeglądarki - nic nie pomoże. Jak rozwiązać problem?&lt;/p&gt;
&lt;p&gt;Musimy Chrome’owi powiedzieć wprost: “weź zapomnij o tym redirekcie”. Oto recepta:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Wejdź na &lt;code class=&quot;language-text&quot;&gt;chrome://net-internals/#hsts&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Przejdź do “Delete domain security policies”.&lt;/li&gt;
&lt;li&gt;Wpisz nazwę domeny (w naszym wypadku np. “localhost” - bez portu) i kliknij “Delete”.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Voilà!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Migracja .NET Core 2.2 do 3.1]]></title><description><![CDATA[W wersjach .NET Core można się nie łapać - co jakiś wychodzą nowe, nie zawsze wiadomo, czy i kiedy przenosić się na nowsze wersje oraz jakie…]]></description><link>https://robert.skarzycki.pl/18-migracja-z-net-core-2-2-do-3-1/</link><guid isPermaLink="false">https://robert.skarzycki.pl/18-migracja-z-net-core-2-2-do-3-1/</guid><pubDate>Fri, 20 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;W wersjach .NET Core można się nie łapać - co jakiś wychodzą nowe, nie zawsze wiadomo, czy i kiedy przenosić się na nowsze wersje oraz jakie będą tego skutki. (Osoby ze świata “starego” .NETa pamiętają, że to nigdy nie była prosta decyzja.) Z pomocą przychodzi jednak sam Microsoft, mówiąc wprost, jakie wersje wspiera, a jakie już skazuje na zapomnienie. Stan na dzisiaj:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/20200317-netcore-31.png&quot; alt=&quot;Lista wersji .NET Core SDK wraz z datami zakończenia wsparcia&quot;&gt;&lt;/p&gt;
&lt;p&gt;Gdy ze zdziwieniem spostrzegłem, że wersja 2.2, na której oparty był ostatni projekt, w którym pracowałem - nie jest już wspierana od Bożego Narodzenia 2019, zaordynowałem przenosiny na 3.1. To jest wersja rekomendowana i będzie wspierana przez najbliższe 2 lata. Jak dla mnie spoko.&lt;/p&gt;
&lt;p&gt;No to myślę sobie, że zmieniam w csprojach tylko jedną linijkę:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;TargetFramework&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;netcoreapp3.1&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;TargetFramework&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;…i fajrant. 😎&lt;/p&gt;
&lt;p&gt;To jednak nie jest takie proste. Poniżej przedstawiam listę kolejnych kroków, które trzeba zrobić.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Podbij wersję w csprojach jak wyżej.&lt;/li&gt;
&lt;li&gt;Po skompilowaniu dostaniesz na pewno błędy z informacją o konieczności usunięcia referencji do niektórych nugetów lub podbcia wersji. Ja musiałem m.in.:&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;usunąć referencję do &lt;code class=&quot;language-text&quot;&gt;Microsoft.AspNetCore.App&lt;/code&gt; z projektu WebAPI,&lt;/li&gt;
&lt;li&gt;podbić wersję &lt;code class=&quot;language-text&quot;&gt;Microsoft.Extensions.DependencyInjection&lt;/code&gt; do &lt;code class=&quot;language-text&quot;&gt;3.1.x&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Zrób parę modyfikacji w &lt;code class=&quot;language-text&quot;&gt;Startup.cs&lt;/code&gt; (to jest przykład dla projektu WebAPI, bez żadnych widoków czy Razor pages):&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;dodaj &lt;code class=&quot;language-text&quot;&gt;services.AddControllers();&lt;/code&gt; na początku &lt;code class=&quot;language-text&quot;&gt;ConfigureServices&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;zmień &lt;code class=&quot;language-text&quot;&gt;CompatibilityVersion&lt;/code&gt; na &lt;code class=&quot;language-text&quot;&gt;Version_3_0&lt;/code&gt; w &lt;code class=&quot;language-text&quot;&gt;services.AddMvc(...).SetCompatibilityVersion(...)&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;zamień użycie &lt;code class=&quot;language-text&quot;&gt;IHostingEnvironment&lt;/code&gt; na &lt;code class=&quot;language-text&quot;&gt;IWebHostEnvironment&lt;/code&gt; w argumentach metod,&lt;/li&gt;
&lt;li&gt;w metodzie &lt;code class=&quot;language-text&quot;&gt;Configure&lt;/code&gt;:
&lt;ul&gt;
&lt;li&gt;dodaj &lt;code class=&quot;language-text&quot;&gt;app.UseRouting();&lt;/code&gt; (raczej na początku),&lt;/li&gt;
&lt;li&gt;dodaj &lt;code class=&quot;language-text&quot;&gt;app.UseAuthorization();&lt;/code&gt; (o ile jest uwierzytelnianie w projekcie),&lt;/li&gt;
&lt;li&gt;dodaj na końcu:&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UseEndpoints&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;endpoints &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    endpoints&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;MapControllers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Potem podbij EF Core do 3.1 (oczywiście, jeśli jest używane w Twoim projekcie). Nie zapomnij też o podbiciu &lt;code class=&quot;language-text&quot;&gt;Microsoft.EntityFrameworkDesign&lt;/code&gt;, jeśli Twój kontekst jest w innym projekcie niż apka webowa.&lt;/li&gt;
&lt;li&gt;Jeśli masz testy Twoich repozytoriów (lub innego kodu czytającego coś bazy) - na pewno testy się wywalą. Chodzi tu o to, że w EF Core 3 została wprowadzona zasadnicza zmiana: w poprzedniej wersji EF Core tam, gdzie nie potrafił zbudować poprawnego, bardziej złożonego query - na chama wykonywał operację po stronie klienta (co skutkowało miejscami niespodziewanie niską wydajnością zapytań); w wersji 3 inżynierowie Microsoftu postanowili zrobić odwrotnie: tam, gdzie query byłoby w wersji 2 wykonane po stronie klienta, teraz framework rzuca wyjątek - i sugeruje, żeby zrobić tę kwerendę explicite po stronie klienta. Umownie można powiedzieć, że w tych miejscach trzeba dodać to przysłowiowe &lt;code class=&quot;language-text&quot;&gt;ToList&lt;/code&gt;, żeby najpierw dane ściągnąć do klienta.&lt;/li&gt;
&lt;li&gt;Jeśli sam utrzymujesz build maszynę, to czeka Cię jeszcze jedna niespodzianka - odpowiednie sparowanie wersji .NET Core SDK i EF Core CLI Tool. Tutaj też nastąpiła zmiana - otóż, dotychczas EF Core CLI Tool (czyli coś, co umożliwia pisanie &lt;code class=&quot;language-text&quot;&gt;dotnet ef ...&lt;/code&gt;) było częścią całego SDK. Teraz już tak nie jest i należy tego toola zainstalować oddzielnie. Żeby było jeszcze trudniej, wersja tego narzędzia musi być “sparowana” z wersją .NET Core SDK. U mnie zadziałał zestaw:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;dev@build-machine:~$ dotnet &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;3.1&lt;/span&gt;.102
dev@build-machine:~$ dotnet-ef &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt;
Entity Framework Core .NET Core Command-line Tools
&lt;span class=&quot;token number&quot;&gt;3.1&lt;/span&gt;.1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To ważne, bo na ten moment najnowszą wersję EF Core CLI Tools jest 3.1.2, które nijak nie chce współgrać z .NET Core 3.1.102. Tak więc EF Core CLI Tool musi być zainstalowany z podaniem konkretnej wersji, a nie domyślnie.&lt;/p&gt;
&lt;ol start=&quot;7&quot;&gt;
&lt;li&gt;Po drodze jeszcze podbiłem wersję xUnit, ale już nie pamiętam, czy to było konieczne. :) (Ale wówczas trzeba też podbić &lt;code class=&quot;language-text&quot;&gt;xunit.runner.visualstudio&lt;/code&gt; i - chyba? - &lt;code class=&quot;language-text&quot;&gt;Microsoft.NET.Test.Sdk&lt;/code&gt; - żeby można było odpalać unit testy w Resharperze.)&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title><![CDATA[Boiling Frogs 2020 - relacja]]></title><description><![CDATA[Na chwilę przed wprowadzeniem zakazów organizowania imprez masowych odbyła się we Wrocławiu konferencja Boiling Frogs. Byłem, widziałem,…]]></description><link>https://robert.skarzycki.pl/19-boiling-frogs-2020-relacja/</link><guid isPermaLink="false">https://robert.skarzycki.pl/19-boiling-frogs-2020-relacja/</guid><pubDate>Sat, 14 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Na chwilę przed wprowadzeniem zakazów organizowania imprez masowych odbyła się we Wrocławiu konferencja Boiling Frogs. Byłem, widziałem, kawę tam piłem - a com widział: opisuję.&lt;/p&gt;
&lt;h3&gt;N-warstwowe modele domenowe&lt;/h3&gt;
&lt;h4&gt;Mariusz Gil&lt;/h4&gt;
&lt;p&gt;Prelegent zwrócił uwagę na możliwość wyróżnienia innej typologii “klocków”, jakich używamy w DDD - w dużych projektach. Używając metafory systemu sygnalizacji świetlnej w mieście, wyróżnił następujące byty (idąc za Erikiem Evansem):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Nazwa&lt;/th&gt;
&lt;th&gt;Metaforyczny odpowiednik&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;capabilities&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;sygnalizator na skrzyżowaniu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;operations&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;cykle sygnalizacji na skrzyżowaniu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;policies&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;“zielona fala”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;decision support&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;automatyczna regulacja cyklami świateł na podstawie bieżącego ruchu samochodów&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;commitments&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;To są przecież podstawowe rzeczy&lt;/h3&gt;
&lt;h4&gt;Piotr Stapp&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Warte przemyślenia rozróżnienie pomiędzy rzemieślnikiem a architektem (inżynierem). (Trochę na przekór ruchowi “software craftsmanship”. ;) ) W skrócie: rzemieślnik po prostu umie powtarzać jakieś wzorce, a architekt (inżynier) rozumie te wzorce i potrafi tworzyć nowe. Tak jak w budowlance, zwykły majster ma po prostu wiedzieć, jak dobrze wylać beton na strop, a inżynier - wie, dlaczego ten strop się nie zawala. :) Jeśli chcemy być inżynierami, musimy rozumieć, dlaczego coś działa w dany sposób.&lt;/li&gt;
&lt;li&gt;12 factor app - &lt;a href=&quot;https://12factor.net/pl/&quot;&gt;https://12factor.net/pl/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Logi w rozproszonym systemie:
&lt;ul&gt;
&lt;li&gt;nie można opierać się na kolejności w czasie, bo serwery mają zawsze rozjazd pomiaru czasu pomiędzy sobą,&lt;/li&gt;
&lt;li&gt;można stosować &lt;em&gt;correlation id&lt;/em&gt;, ale warto przy kolejnym wywołaniu kolejnego serwisu w łańcuszku, dodawać numerek z oznaczeniem zagłębienia - tak aby móc zbudować nie tyle łańcuch, ale drzewko “wywołań” pomiędzy serwisami.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Prezentacja była bardzo luźno opowiedziana, ale merytorycznie - zdecydowanie polecam Piotra Stappa na każdą konferencję. :)&lt;/p&gt;
&lt;h3&gt;Microfrontends - czyli jak wdrożyć idee microservices na frontendzie&lt;/h3&gt;
&lt;h4&gt;Marcin Milewicz&lt;/h4&gt;
&lt;p&gt;Minusy monolitycznego frontendu:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;duże repozytorium (dużo plików),&lt;/li&gt;
&lt;li&gt;zależności między zespołami,&lt;/li&gt;
&lt;li&gt;długi czas budowania.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Jak zrobić &lt;em&gt;microfrontends&lt;/em&gt; na froncie?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;build-time integration&lt;/em&gt; - czyli po prostu jedno repozytorium, ale moduły w &lt;em&gt;sub-packages&lt;/em&gt;; to ma jednak nadal minusy jednego, dużego repo,&lt;/li&gt;
&lt;li&gt;&lt;em&gt;iframes&lt;/em&gt; - minusem trudność w ostylowaniu, i niewygodne dla SEO,&lt;/li&gt;
&lt;li&gt;moduły w odrębnych bundlach w JS, czyli “dociągnij bundla i renderuj”&lt;/li&gt;
&lt;li&gt;WebComponents&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;O pracy zdalnej dla firmy z zagranicy&lt;/h3&gt;
&lt;h4&gt;Maciej Sławik&lt;/h4&gt;
&lt;p&gt;Bardzo dobra, krótka (15 min) prezentacja o tym, jak szukać i jak znaleźć pracę zdalną dla firmy z zagranicy.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Portale strcite z oferatmi pracy zdalnej są słabe - te firmy są zalewane ofertami, mogą nawet nie odpowiedzieć na Twoją ofertę.&lt;/li&gt;
&lt;li&gt;Lepiej znaleźć potencjalnego pracodawcę samemu i do niej zaaplikować (oczywiście trzeba sprawdzić, czy firma jest &lt;em&gt;remote-friednly&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;Warto być specjalistą w czymś konkretnym - pracując zdalnie sam, musisz być ekspertem, żeby być w stanie rozwiązywać problemy samemu.&lt;/li&gt;
&lt;li&gt;Postaraj się o profesjonalny profil na Linkedin - zdjęcie od fotografa, można poprosić o rekomendacje.&lt;/li&gt;
&lt;li&gt;Wysyłając CV do różnych krajów, sprawdź, jakie tam są zwyczaje i dostosuj do nich swoje CV - np. w Wielkiej Brytanii nie daje się zdjęcia do CV.&lt;/li&gt;
&lt;li&gt;Lepsza jest firma mała lub średnia, bo łatwiej się dogadać, że chce się być 100% zdalnie.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Czy wydajność to jakość?&lt;/h3&gt;
&lt;h4&gt;Jarek Pałka&lt;/h4&gt;
&lt;p&gt;Jarek to standupowiec polskich konferencji IT, więc wiadomo, że jest wesoło (i często nie na temat). Najciekawszy był jednak &lt;a href=&quot;https://tonsky.me/blog/disenchantment/&quot;&gt;ten link&lt;/a&gt; - do artykułu o tym, że, mówiąc w skrócie, obecny przemysł IT jest do dupy, bo w ogóle nie dbamy podstawowe rzeczy związane z wydajnością. No bo jak to możliwe, że np. aplikacja Messenger na telefon z wersji na wersję zajmuje coraz więcej, a funkcjonalności nie jest więcej.&lt;/p&gt;
&lt;h3&gt;#ąęszcz Rzecz o języku informatyków&lt;/h3&gt;
&lt;h4&gt;Piotr Przybył&lt;/h4&gt;
&lt;p&gt;Świetna, lekka prezentacja o tym, jak bardzo nasz język branżowy jest odległy od normalnej polszczyzny. Te wszystkie “merdżuję”, “fokusuję się na taskach”, “mitingi”, “tajmlajny” itp. to jest nowomowa. To jest oczywiście w porządku, gdy tego używamy między sobą (każda branża na swoje terminy zawodowe), ale naprawdę warto używać normalnych, polskich odpowiedników, gdy komunikujemy się na zewnątrz. Dzięki temu będziemy lepiej rozumiani.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://przybyl.org/pres/2020/BoilingFrogs-aeszcz/#/title-slide&quot;&gt;Tutaj&lt;/a&gt; znajdziesz link do prezentacji.&lt;/p&gt;
&lt;p&gt;Na Githubie istnieje &lt;a href=&quot;https://github.com/nurkiewicz/polski-w-it&quot;&gt;repozytorium&lt;/a&gt;, gdzie wspólnymi siłami próbuje się znaleźć polskie odpowiedniki.&lt;/p&gt;
&lt;h3&gt;Czy pracując w software housie zmarnuję sobie życie?&lt;/h3&gt;
&lt;h4&gt;Marcin Zabawa&lt;/h4&gt;
&lt;p&gt;Ciekawa typologia potencjalnych miejsc pracy dla programisty. W skrócie mamy do wyboru takie opcje:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;startup: będzie przygoda, ale i zapierdziel, no i raczej nie ma szans na “craftsmanship”, bo gonimy deadline’y&lt;/li&gt;
&lt;li&gt;firma produktowe: nadgodzin nie ma, można się z produktem identyfikować i trochę wykazać się w “craftsmanshipie”, ale na pewno nie będzie przygód&lt;/li&gt;
&lt;li&gt;software house: w porównaniu do firmy produktowej na pewno poczucie misji jest słabsze (jednak od projektu do projektu), ale więcej przygód i więcej możliwości na wykazanie się jako inżynier&lt;/li&gt;
&lt;li&gt;korpo (czyli “centra usług wspólnych”) - tego wątku prelegent nie poruszył, bo stwierdził, że nie ma osobistego doświadczenia w tym zakresie.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[GovTech 2019 - walidator zdjęć]]></title><description><![CDATA[W listopadzie 2019 roku zgłosiłem się do konkursu GovTech. Wybrałem zadanie konkursowe polegajce na przygotowaniu aplikacji mobilnej, która…]]></description><link>https://robert.skarzycki.pl/12-govtech-2019-walidator-zdjec/</link><guid isPermaLink="false">https://robert.skarzycki.pl/12-govtech-2019-walidator-zdjec/</guid><pubDate>Thu, 27 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;W listopadzie 2019 roku zgłosiłem się do konkursu GovTech. Wybrałem zadanie konkursowe polegajce na przygotowaniu aplikacji mobilnej, która miała pomóc osobom wgrywającym zdjęcia do dowodu osobistego. Chodziło to, żeby aplikacja - podczas robienia sobie zdjęcia selfie - od razu wskazywała, dlaczego dane ustawienie do zdjęcia jest nieprawidłowe: a to za ciemne tło,a to niedozwolone okulary.&lt;/p&gt;
&lt;p&gt;Mój pomysł polegał na tym, że zabrać się za zadanie w 3 krokach:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Przygotować aplikację webową, za pomocą której mógbłym zebrać dane testowe od znajomych i rodziny.&lt;/li&gt;
&lt;li&gt;Wytrenować model &lt;em&gt;machine learning&lt;/em&gt; za pomocą tych danych.&lt;/li&gt;
&lt;li&gt;Przygotować aplikację mobilną wykorzystującą ten wytrenowany model - apka była właściwym elementem konkursu.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Niestety, nie udało mi się dobrnąć do kroków numer 2 i 3, bo przygotowanie apki z kroku pierwszego zajęło mi sporo czasu, no i też nie zebrałem wystarczającej ilości zdjęć, żeby móc to jakoś wrzucić do &lt;em&gt;machine learning&lt;/em&gt;. Jednakże to i tak było ciekawe doświadczenie, dlatego dzielę się tym, co udało mi się zrobić.&lt;/p&gt;
&lt;p&gt;Wszystko, co zrobiłem, można znaleźć na moim Githubie: &lt;a href=&quot;https://github.com/robert-skarzycki/govtech-2019-collect-id-photos&quot;&gt;https://github.com/robert-skarzycki/govtech-2019-collect-id-photos&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To repozytorium zawiera dwie aplikacje:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;apkę webową do zbierania zdjęć,&lt;/li&gt;
&lt;li&gt;funkcję Azure do przepychania tych zdjęć do Blob storage.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Apkę webową stworzyłem za pomocą Gatsby CLI oraz &lt;a href=&quot;https://www.gatsbyjs.org/starters/niklasmtj/gatsby-starter-julia/&quot;&gt;startera Julia&lt;/a&gt; - tego samego, na którym działa mój blog. Sama strona to po prostu zwykły “wizard”, który prowadzi użytkownika krok po kroku, prosząc o wykonanie kolejnych zdjęć - prawidłowych i nieprawidłowych. Natomiast funkcja Azure działa na .NET Core i wykorzystuje Azure Blob SDK (to było moje pierwsze spotkanie z tym SDK).&lt;/p&gt;</content:encoded></item><item><title><![CDATA[AutoMapper - flatten]]></title><description><![CDATA[Od dawna wiedziałem, że AutoMapper ma bardzo wygodną - domyślną - funkcję, automatycznego rozpoznawania poprawnego mapowania między…]]></description><link>https://robert.skarzycki.pl/11-automapper-flatten/</link><guid isPermaLink="false">https://robert.skarzycki.pl/11-automapper-flatten/</guid><pubDate>Tue, 18 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Od dawna wiedziałem, że &lt;a href=&quot;https://automapper.org/&quot;&gt;AutoMapper&lt;/a&gt; ma bardzo wygodną - domyślną - funkcję, automatycznego rozpoznawania poprawnego mapowania między zagnieżdżonymi obiektami a ich spłaszczonymi wersjami. Przykładowo:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Car&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; Vin &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; Model &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Person&lt;/span&gt; Owner &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; Id &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; FirstName &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; LastName &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Możemy bez problemu zmapować na spłaszczoną wersję:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CarDto&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; Vin &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; Model &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; OwnerId &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; OwnerFirstName &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; OwnerLastName &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Wystarczy zdefiniować zwykłe mapowanie między typami &lt;code class=&quot;language-text&quot;&gt;Car&lt;/code&gt; i &lt;code class=&quot;language-text&quot;&gt;CarDto&lt;/code&gt; - AutoMapper sam rozpozna, że “podobiekt” &lt;code class=&quot;language-text&quot;&gt;Owner&lt;/code&gt; należy “spłaszyczyć” do właściwości z prefiksem &lt;code class=&quot;language-text&quot;&gt;Owner*&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;configuration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;CreateMap&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Car&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; CarDto&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Co zrobić, gdy potrzebujemy wykonywać mapowanie w drugą stronę? To znaczy - mamy spłaszczony obiekt, a chcemy mapować do zagnieżdżonego obiektu (każdy przyzna, że lepsza jest taka struktura niż litania właściwości z takim samym prefiksem). Okazuje się, że to jest banalnie proste - wysatrczy przy definiowaniu mapowania dodać &lt;code class=&quot;language-text&quot;&gt;ReverseMap&lt;/code&gt;, tj.:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;configuration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;CreateMap&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Car&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; CarDto&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ReverseMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Co więcej, dodając specjalne metody typu &lt;code class=&quot;language-text&quot;&gt;ForMember&lt;/code&gt; czy &lt;code class=&quot;language-text&quot;&gt;ForPath&lt;/code&gt; możemy “customizować” tę odwrotną mapę - tak jakbyśmy konfigurowali zwykłe mapowanie.&lt;/p&gt;
&lt;p&gt;Całkiem zgrabne, prawda?&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Organizacja folderów w projekcie frontendowym]]></title><description><![CDATA[Rozbudowane narzędzia IDE typu Visual Studio mają tę zaletę (albo wadę - o tym potem :)), że udostępniają szablony projektów, wprowadzając…]]></description><link>https://robert.skarzycki.pl/10-organizacja-folderow-w-projekcie-frontendowym/</link><guid isPermaLink="false">https://robert.skarzycki.pl/10-organizacja-folderow-w-projekcie-frontendowym/</guid><pubDate>Sat, 01 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Rozbudowane narzędzia IDE typu Visual Studio mają tę zaletę (albo wadę - o tym potem :)), że udostępniają szablony projektów, wprowadzając pewną standaryzację do tego, jak zorganizowane są pliki projektu. Sztandarowy przykład to szablon ASP.NET MVC z Visual Studio, który domyślnie tworzy foldery typu &lt;code class=&quot;language-text&quot;&gt;Controllers&lt;/code&gt; czy &lt;code class=&quot;language-text&quot;&gt;Views&lt;/code&gt;, będące domyślnym miejscem do umieszczenia - odpowiednio - kontrolerów i widoków.&lt;/p&gt;
&lt;p&gt;W świecie frontendowym sytuacja jest dużo bardziej “rozmyta”. Tworząc aplikacje Reactowe w zasadzie narzędziami pracy są konsola oraz edytor (Visual Studio Code :heart:). Ale nawet skorzystanie z CLI, które “scaffolduje” projekt (np. &lt;code class=&quot;language-text&quot;&gt;create-react-app&lt;/code&gt;) nie wprowadza (narzuca?) struktury folderów, która by była umożliwiała “ustandaryzowany” układ folderów nawet, gdy aplikacja się rozrośnie. &lt;code class=&quot;language-text&quot;&gt;create-react-app&lt;/code&gt; w zasadzie “standaryzuje” tylko istnienie folderu &lt;code class=&quot;language-text&quot;&gt;src&lt;/code&gt;. Przyznacie, że to trochę mało. Gdzie umieszczać testy? Gdzie umieścić komponenty, a gdzie moduły “niewizualne” (np. serwisy kontaktujące się z zewnętrznym API)? Każdy deweloper jakoś odpowiada na te pytania, ale to skutkuje tym, że w ramach zespołu trudniej utrzymać spójność, a szerzej - jeśli każdy projekt przyjmuje inną filozofię organizacji plików, to trudniej się w tym połapać.
Jeśli ktoś korzysta z Reacta wraz z Reduxem, to mógł spotkać się z propozycją, aby zgrupować komponenty w dwóch folderach: &lt;code class=&quot;language-text&quot;&gt;containers&lt;/code&gt; i &lt;code class=&quot;language-text&quot;&gt;components&lt;/code&gt;. W tym pierwszym powinny znaleźć się komponenty “wyższego rzędu”, tj. te, które są “podpięte” pod Reduxa, w drugim folderze - pozostałe. Takie rozwiązanie wydaje mi się jednak niepraktyczne - zmiana komponentu z Redux-agnostic na Redux-wise nie powinna oznaczać konieczności przenosin pliku do innego folderu. Inna sprawa, że to nie jest szablon/standard, tylko rekomendacja.&lt;/p&gt;
&lt;p&gt;Wobec braku standaryzacji organizacji plików i folderów w świecie frontendowym, mogę i ja zaproponować własne rozwiązanie. :) Stosuję je ostatnio w projektach, które - chyba można tak powiedzieć - osiągnęły już rozmiar &lt;em&gt;midi&lt;/em&gt;. Zasada jest następująca:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Grupuj pliki według funkcjonalności, a nie według technicznego podobieństwa.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Oznacza to tyle - na przekór np. struktury szablonu projektu ASP.NET MVC - że nie mamy folderu typu &lt;code class=&quot;language-text&quot;&gt;views&lt;/code&gt;, zbierającego widoki, albo &lt;code class=&quot;language-text&quot;&gt;reducers&lt;/code&gt;, do którego, jak do worka, wrzucamy wszystkie reducery. Moim zdaniem lepiej grupować pliki według fragmentów aplikacji. Przykładowo, jeśli mamy “podstronę” do administracji (użytkownicy, role itp.), to niech wszystko, co jest potrzebne do tej części systemu niech będzie w folderze &lt;code class=&quot;language-text&quot;&gt;administration&lt;/code&gt;. Oczywiście dalej można to pokroić na dalsze podfoldery, czy to dalej według kolejnych “podsystemów” (&lt;code class=&quot;language-text&quot;&gt;users&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;roles&lt;/code&gt;), czy już bardziej technicznie.
Co nam daje takie rozwiązanie? Otóż, moim zdaniem, aplikacja przestaje być jedną wielką kulą błota, gdzie mieszamy style CSS, komponenty Reactowe, testy itp., ale raczej wprowadzamy hierarchię: aplikacja składa się z modułów (np. ów moduł administracyjny, reprezentowany przez folder &lt;code class=&quot;language-text&quot;&gt;administration&lt;/code&gt;), a te moduły składają się z technicznych “cegieł” - np. plików z komponentami Reactowymi. Dodatkowo, niejako gratis, dostajemy jeszcze możliwość stosowania krótszych nazw plików (nie trzeba powtarzać wszędzie prefiksu, bo nadrzędny folder może mówić, jaki tu jest obszar zaimplementowany) oraz potencjalne łatwiejsze wydzielenie podprojektów i skorzystanie z narzędzi typu Lerna.
Innymi słowy - niech struktura plików w projekcie będzie sugerowała, że mamy do czyniania z “modularnym monolitem”. ;)&lt;/p&gt;
&lt;p&gt;W praktyce pewnie nie warto wydzielać takie foldery-moduły jako pierwszy krok po stworzeniu projektu za pomocą &lt;code class=&quot;language-text&quot;&gt;create-react-app&lt;/code&gt;: jesli aplikacja jest na wczesnej fazie rozwoju, to nie musimy się wiązać konkretnym podziałem. Chyba lepiej najpierw zacząć szybko, kosztem lekkiego bałaganu, a w odpowiednim momencie, gdy już wyklaruje się, z jakimi obszarami mamy do czynienia, wprowadzić taki podział na foldery-moduły.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Potęga podzapytań w Cosmos DB]]></title><description><![CDATA[Cosmos DB Query Playground to interaktywne środowisko do eksperymentowania z kwerendami do Cosmos DB. Jako dane testowe zawiera ono bazę…]]></description><link>https://robert.skarzycki.pl/15-potega-podzapytan-w-cosmos-db/</link><guid isPermaLink="false">https://robert.skarzycki.pl/15-potega-podzapytan-w-cosmos-db/</guid><pubDate>Thu, 30 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.documentdb.com/sql/demo&quot;&gt;Cosmos DB Query Playground&lt;/a&gt; to interaktywne środowisko do eksperymentowania z kwerendami do Cosmos DB. Jako dane testowe zawiera ono bazę produktów spożywczych wraz z informacjami o ich wartościach odżywczych. Przykładowy dokument wygląda mniej-więcej tak:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;food&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;14203&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Coffee, instant, regular, powder, half the caffeine&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;tags&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;coffee&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;instant&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      ...
      &lt;span class=&quot;token comment&quot;&gt;// pomijam resztę&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;foodGroup&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Beverages&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;nutrients&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;204&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Total lipid (fat)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;nutritionValue&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;units&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;g&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;205&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Carbohydrate, by difference&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;nutritionValue&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;73.18&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;units&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;g&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      ...
      &lt;span class=&quot;token comment&quot;&gt;// pomijam resztę&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    ...
    &lt;span class=&quot;token comment&quot;&gt;// pomijam resztę&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Posłużmy się tym “typem” danych jako przykładem dla zobrazowania, jak bardzo mogą się różnić koszty zapytania.&lt;/p&gt;
&lt;p&gt;Załóżmy, że chcemy wybrać wszystkie produkty, które zawierają 1g lub więcej kofeiny. A zatem musimy dla każdego produkty wyszukać w liście &lt;code class=&quot;language-text&quot;&gt;nutrients&lt;/code&gt; elementu o &lt;code class=&quot;language-text&quot;&gt;description&lt;/code&gt; równym &lt;code class=&quot;language-text&quot;&gt;&quot;Caffeine&quot;&lt;/code&gt; oraz &lt;code class=&quot;language-text&quot;&gt;nutritionValue &gt;= 1000&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Podejście “łopatologiczno-proceduralne” może nas zaprowadzić do pomysłu, żeby dodać sobie user-defined-function, która “opakuje” te warunki, powiedzmy coś takiego:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hasHighLevelOfCaffeine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;food&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; food&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nutrients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token parameter&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; n&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Caffeine&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; n&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nutritionValue &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Wówczas główne zaptanie się “upraszcza”:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; food &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; food &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; udf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hasHighLevelOfCaffeine&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;food&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Problem z tym zapytaniem jest jednak taki, że zżera ono sporo RU. Na potrzeby obliczeń zrobiłem sobie przykładową bazę, gdzie miałem niecałe 2000 produktów i tylko jeden z “wysokim poziomem kofeiny”. Dla takich dancyh zwykły select zeżarł ponad 600 RUs. Co gorsza - im więcej dokumentów w kolekcji, tym koszt będzie jeszcze większy. Wynika to prawdopodobnie z tego, że Cosmos nie potrafi sobie “zoptymalizować” zapytania z użyciem UDF, operującej na podkolekcji.&lt;/p&gt;
&lt;p&gt;Spróbujmy zatem do problemu podejść inaczej. Korzystając z &lt;a href=&quot;https://docs.microsoft.com/pl-pl/azure/cosmos-db/sql-query-subquery&quot;&gt;dokumentacji&lt;/a&gt; możemy spróbować użyć podzapytania - tak byśmy przecież postąpili w “zwykłym SQL-u”. (Mamy tu relację jeden-do-wielu, więc za pomocą &lt;code class=&quot;language-text&quot;&gt;EXISTS&lt;/code&gt; i podzapytania moglibyśmy to ograć.) W Cosmosie spraw wygląda podobnie, z tą różnicą, że używamy &lt;code class=&quot;language-text&quot;&gt;JOIN&lt;/code&gt; i podzapytania. Finalnie query wygląda tak:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt;
food
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; food
&lt;span class=&quot;token keyword&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;VALUE&lt;/span&gt; n &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;IN&lt;/span&gt; food&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nutrients
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt;
	n&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Caffeine&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; n&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nutritionValue &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Dla wspomnianej wyżej “benchmarkowej” bazy z 2000 produktów - to zapytanie “kosztowało” tylko 2 RU. I to w zasadzie niezależnie od ilości dokumentów.&lt;/p&gt;
&lt;p&gt;Ciekawe, prawda? :)&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ITCorner Tech Meetup #1 Skalowanie]]></title><description><![CDATA[Kilka dni temu wziąłem udział w spotkaniu ITCorner Tech Meetup, którego tematem przewodnim było skalowanie. Poniżej garść notatek. Temat W…]]></description><link>https://robert.skarzycki.pl/14-itcorner-tech-meetup-1-skalowanie/</link><guid isPermaLink="false">https://robert.skarzycki.pl/14-itcorner-tech-meetup-1-skalowanie/</guid><pubDate>Sun, 19 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Kilka dni temu wziąłem udział w spotkaniu &lt;a href=&quot;https://www.meetup.com/ITCorner-Tech-Meetup/events/267393261/&quot;&gt;ITCorner Tech Meetup&lt;/a&gt;, którego tematem przewodnim było skalowanie. Poniżej garść notatek.&lt;/p&gt;
&lt;h2&gt;Temat&lt;/h2&gt;
&lt;p&gt;W zasadzie tylko 50% spotkania było o skalowaniu - reszta była o refaktorze legacy. Temat ciekawy jeden i drugi, ale tytuł spotkania jednak mylący.&lt;/p&gt;
&lt;h2&gt;Skalowanie&lt;/h2&gt;
&lt;p&gt;Jarosław Jedliński w swojej prezentacji opowiedział, jak jego zespół dba o to, żeby strona popularnego sklepu Euro RTV AGD nie wysypywała się w czasie czarnego piątku. Według zamieszczony statystyk - ruch na stornie w ten dzień jest kilka razy większy, dając kilka tysięcy requestów na sekundę. Ten zespół rozwija portal [&lt;a href=&quot;https://www.euro.com.pl/&quot;&gt;https://www.euro.com.pl/&lt;/a&gt;] już od kilkunastu lat, co pozwoliło w fajny sposób pokazać ewolucję architektury i używanych rozwiązań. Kroki mniej więcej były takie:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Odseparować aplikacje “analityczne” od tej głównej, sklepowej.&lt;/li&gt;
&lt;li&gt;Zduplikować bazę, żeby aplikacje “analityczne” nie zamulały dostępu do bazy dla sklepu.&lt;/li&gt;
&lt;li&gt;Dać więcej instancji aplikacji sklepowych i jakiś load balancer.&lt;/li&gt;
&lt;li&gt;Dodać cache w aplikacjach sklepowych - żeby zredukować round-tripy do bazy.&lt;/li&gt;
&lt;li&gt;Wprowadzić centralny cache, żeby oszczędzić pamięć i nie trzymać powtórek w kilku cache’ach.&lt;/li&gt;
&lt;li&gt;Wprowadzić cache dla całych requestów (Varnish) - umożliwia chwilowy offline sklepu (np. podczas wdrożenia w ciągu dnia).&lt;/li&gt;
&lt;li&gt;Dodać drugą serwerownię ze zdkuplikowaną infrastrukturą, na wypadek awarii tej pierwszej.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Co ciekawe, cały rozwiązanie jest trzymane we własnych serwerowniach. Wydawało mi się, że to śmiga w chmurze, a jednak. Inna ciekawostka: nad rozwojem i utrzymaniem sklepu pracuje ponad 20-osobowy zespół programistów, testerów i administratorów.&lt;/p&gt;
&lt;p&gt;Piotr Gołofit z Accesto opowiadał bardziej o mądrym przepisywaniu legacy (o tym niżej), ale w działce skalowania zwrócił uwagę na ciekawy aspekt. Bardziej mówił o skalowaniu biznesowy niż technicznym, tzn. o skalowaniu produktu (aplikacji) na większy rynek (międzynarodowy). W takich wypadkach trzeba pomyśleć m.in. o:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;i18n&lt;/em&gt; = czyli obsłudze wielu języków: zarówno o przetłumaczonym interfejsie, jak i potencjalnym multi-języczną obsługą danych,&lt;/li&gt;
&lt;li&gt;różnych strefach czasowych: wyświetlanie wg strefy czasowej użytkownika plus odpowiednie “tłumaczenie” dat i godzin wprowadzanych,&lt;/li&gt;
&lt;li&gt;obsługa różnych walut - przede wszystkim jeśli chodzi o płatności.
Co ciekawe, w przypadku tego projektu, o którym opowiadał Piotr, były to bliskie geograficznie rynki - wyjście z Irlandii na całe Wyspy Brytyjskie - to poza ww. sprawami dochodziły różnice kulturowe (wpływające &lt;em&gt;de facto&lt;/em&gt; na sposób prezentowania danych) i polityczne (potencjalny &lt;em&gt;brexit&lt;/em&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Przepisywanie legacy&lt;/h2&gt;
&lt;p&gt;Piotr Gołofit i Łukasz Urbański w swoich prezentacjach poruszali podobny temat: dostajemy do utrzymania (i rozwijania) starą aplikację, co robimy?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ciągniemy to tak, jak jest - babrzemy się w legacy, conieco refaktorując.&lt;/li&gt;
&lt;li&gt;Nowe rzeczy idą po nowemu, stare powoli próbujemy przepisywać na nowo.&lt;/li&gt;
&lt;li&gt;Wywalamy wszystko i startujemy z &lt;em&gt;carte blanche&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Żadna z opcji nie jest idealna, obaj prelegenci udowadniali, że opcja druga - nazwana &lt;em&gt;strangler pattern&lt;/em&gt; (termin wprowadził &lt;a href=&quot;https://martinfowler.com/bliki/StranglerFigApplication.html&quot;&gt;Martin Fowler&lt;/a&gt;) - jest sensownym rozwiązaniem. Jeżeli jesteśmy w stanie na poziomie routingu rozdzilić ruch do starej i nowej aplikacji, to możemy sobie stworzyć takie poletko z nową aplikacją w nowej technologii. Nowe moduły trafiają do nowej aplikacji, stare są po kolei przepisywane na nowo. Ale cały czas mamy jedno i drugie aktywne - na produkcji. Co więcej, analizując użycie poszczególnych funkcji, możemy wybierać do przepisana te części, które są najczęściej używane przez użytkowników. Finalnie - w świecie idealnym - na koniec w starej aplikacji nie zostaje nic, można więc w całości przepiąć się na nową wersję. Oczywiście to nie jest takie hop-siup: przykład projektu, o którym opowiadał Piotr Gołofit, to były 3 lata stopniowego naprawiania sytuacji. To wymaga metodycznego podejścia, tym bardziej, im bardziej bagniste jest legacy, do którego siadamy. Trzeba najpierw zadbać o elementarne sprawy, jak kontrola wersji, CI/CD, podstawowe testy, załatanie najważniejszych luk bezpieczeństwa - żeby móc iść do przodu. W pewnym sensie podziwiam konsekwencję, bo wiele osób widząc bagniste legacy, do którego nikt nie chce się dotykać, szybko by uciekło, byleby nie babarać się w jakichś technologiach sprzed 10 lat i byleby nie sprzątać bajzlu po kimś innym.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Team tourism]]></title><description><![CDATA[Kilka lat temu zaproponowałem przełożonym, żebyśmy spróbowali zrobić trochę wymian personalnych pomiędzy zespołami. Celem, jaki mi…]]></description><link>https://robert.skarzycki.pl/09-team-tourism/</link><guid isPermaLink="false">https://robert.skarzycki.pl/09-team-tourism/</guid><pubDate>Fri, 10 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Kilka lat temu zaproponowałem przełożonym, żebyśmy spróbowali zrobić trochę wymian personalnych pomiędzy zespołami. Celem, jaki mi przyświecał, było usprawnienie wymiany wiedzy - pracowaliśmy wszyscy nad jednym dużym projektem, ale każdy zespół siedział w swojej “działce”. Co więcej, duża część pracowników to były osoby z mniejszym doświadczeniem, chciałem więc pomóc młodszym kolegom w jego zdobywaniu, a “starszakom” - dać okazję do dzielenia się wiedzą i “mentorowania”.
Niestety, pomysł upadł. Już nawet nie pamiętam dokładnych przyczyn - czy to było po prostu “nie da się”, czy zła organizacja pracy powodująca konieczność gaszenia pożarów, a może za słaba argumentacja z mojej strony.
Ucieszyłem się jednak po jakimś czasie, gdy zobaczyłem &lt;a href=&quot;https://allegro.tech/2019/09/team-tourism-at-allegro.html&quot;&gt;ten wpis&lt;/a&gt; z blogu allegro.tech, w którym autor dokładnie wypunktowuje zalety “team tourism” (m.in. możliwość “odskoczni” od obecnego projektu/zespołu, wymiana wiedzy o częściach dużego systemu, zdobycie nowych umiejętności) oraz przekazuje parę praktycznych wskazówek, jak taki proces w firmie zorganizować i jakie mogą pojawić się problemy.
Jeśli taką inicjatywę chciałbyś zorganizować w swoim miejscu pracy i potrzebujesz przekonać “górę” - koniecznie przeczytaj!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[..]]></title><description><![CDATA[Po co API Gateway w mikroserwisach? https://tyk.io/microservices-api-gateway/ https://microservices.io/patterns/apigateway.html https://docs…]]></description><link>https://robert.skarzycki.pl/36-api-gateway/</link><guid isPermaLink="false">https://robert.skarzycki.pl/36-api-gateway/</guid><pubDate>Tue, 07 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Po co API Gateway w mikroserwisach?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tyk.io/microservices-api-gateway/&quot;&gt;https://tyk.io/microservices-api-gateway/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://microservices.io/patterns/apigateway.html&quot;&gt;https://microservices.io/patterns/apigateway.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/pl-pl/dotnet/architecture/microservices/architect-microservice-container-applications/direct-client-to-microservice-communication-versus-the-api-gateway-pattern&quot;&gt;https://docs.microsoft.com/pl-pl/dotnet/architecture/microservices/architect-microservice-container-applications/direct-client-to-microservice-communication-versus-the-api-gateway-pattern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/pl-pl/azure/architecture/patterns/gateway-offloading&quot;&gt;https://docs.microsoft.com/pl-pl/azure/architecture/patterns/gateway-offloading&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=rXi5CLjIQ9k&quot;&gt;https://www.youtube.com/watch?v=rXi5CLjIQ9k&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.christianposta.com/microservices/do-i-need-an-api-gateway-if-i-have-a-service-mesh/&quot;&gt;https://blog.christianposta.com/microservices/do-i-need-an-api-gateway-if-i-have-a-service-mesh/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aspenmesh.io/api-gateway-vs-service-mesh/&quot;&gt;https://aspenmesh.io/api-gateway-vs-service-mesh/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.christianposta.com/microservices/api-gateways-are-going-through-an-identity-crisis/&quot;&gt;https://blog.christianposta.com/microservices/api-gateways-are-going-through-an-identity-crisis/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dashbird.io/blog/importance-of-api-gateway/&quot;&gt;https://dashbird.io/blog/importance-of-api-gateway/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-overview-developer-experience.html&quot;&gt;https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-overview-developer-experience.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Accessible states in design systems #id24]]></title><description><![CDATA[Polecam poniższe nagranie z konferencji Inclusive Design 24, w którym autor, Russ Weakley, opisuje podstawowe stany elementów HTML (np…]]></description><link>https://robert.skarzycki.pl/07-accesible-states-in-design-systems/</link><guid isPermaLink="false">https://robert.skarzycki.pl/07-accesible-states-in-design-systems/</guid><pubDate>Sun, 24 Nov 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Polecam poniższe nagranie z konferencji Inclusive Design 24, w którym autor, &lt;a href=&quot;http://maxdesign.com.au/about/russweakley/&quot;&gt;Russ Weakley&lt;/a&gt;, opisuje podstawowe stany elementów HTML (np. &lt;em&gt;visited/unvisited link&lt;/em&gt;) i to, jak łatwo zadbać o ich dostępność:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=k_7yqLyHc3U&quot;&gt;https://www.youtube.com/watch?v=k_7yqLyHc3U&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[TypeScript 3.7!]]></title><description><![CDATA[Oto jest - TypeScript 3.7. Hurra! A co tym razem dostajemy? Optional chaining - czyli ficzer znany choćby z C#. Dotychczas - aby uniknąć…]]></description><link>https://robert.skarzycki.pl/06-typescript-3-7/</link><guid isPermaLink="false">https://robert.skarzycki.pl/06-typescript-3-7/</guid><pubDate>Tue, 12 Nov 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Oto jest - TypeScript 3.7. Hurra!&lt;/p&gt;
&lt;p&gt;A co tym razem dostajemy?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Optional chaining&lt;/strong&gt; - czyli ficzer znany choćby z C#. Dotychczas - aby uniknąć błędu z komunikatem &lt;code class=&quot;language-text&quot;&gt;Cannot read property &apos;x&apos; of undefined&lt;/code&gt; - trzeba było &lt;em&gt;explicite&lt;/em&gt; sprawdzać, czy dana zmienna jest &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; lub &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt;. Teraz - wystarczy użyć operatora &lt;code class=&quot;language-text&quot;&gt;?.&lt;/code&gt;, który zrobi to sprawdzenie za nas. Innymi słowy - zamiast&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; foo &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; foo &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; foo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;baz&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;można napisać po prostu&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; foo&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;bar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;baz&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Niezłe, co nie?&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;strong&gt;Nullish Coalescing&lt;/strong&gt; - znowu rzecz znana z C#. Jak łatwo przypisać do zmiennej wartość domyślną, na wypadek, gdyby podstawowa ścieżka skutkowała &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;? Dotychczas stosować musiałem sztuczkę z JavaScriptu, tj.:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; model &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;volume &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Jeśli w obiekcie &lt;code class=&quot;language-text&quot;&gt;options&lt;/code&gt; nie ma właściwości &lt;code class=&quot;language-text&quot;&gt;volume&lt;/code&gt; lub jest ona ustawiona na &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; - mogę mieć “fallback” do jakiejś tam wartości domyślnej (np. &lt;code class=&quot;language-text&quot;&gt;0.5&lt;/code&gt;). Problem jest jednak taki, że ten operator &lt;code class=&quot;language-text&quot;&gt;||&lt;/code&gt; “wybiera” prawą stronę, gdy lewa strona jest “falsy”,czyli gdy np. ktoś ustawi &lt;code class=&quot;language-text&quot;&gt;options.volume = 0&lt;/code&gt;, to i tak zostanie wybrana prawa strona, czyli wartość domyślna.
Ten problem rozwiązuje właśnie “nullish coalescing” - czyli operator &lt;code class=&quot;language-text&quot;&gt;??&lt;/code&gt;. Dzięki temu powyższy przykład można opisać jako:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; model &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;volume &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I w ten sposób problem “falsy” wartości po lewej stronie mamy rozwiązany.&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;strong&gt;Uncalled Function Checks&lt;/strong&gt; - ile to już razy zdarzało się, że zamiast wywołania funkcji - użyłem samego odwołania się do niej, zwłaszcza w warunku w ifie. Mam na myśli:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isAdministrator&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;zamiast tego&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isAdministrator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Co gorsza, kod w pierwszej opcji będzie się transpilował bez błędu, zawsze zwracając &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;, no bo właściwość, która jest funkcją zawsze jest “truthy”. Tak było do teraz. Od wersji 3.7 w powyższej sytuacji dosteniemy błąd.&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;&quot;&quot;Flatter Error Reporting** - tworząc coś z użyciem React i TS, często zdarza się, że zmieniamy coś w propsach komponentu. W miejscu, w którym z niego korzystamy, po takiej zmianie “interfejsu” komponentu, oczywiście pokazać się może błąd. Często jednak ten błąd jest tak bardzo nieczytelny - zwłaszcza, gdy zmiana w interfejsie jest głęboko - jakiś atrybut, do którego przyisujemy obiekt przyjmuje teraz obiekt o lekko innej strukturze. TypeScript 3.7 dostarcza teraz komunikatów bardziej “spłaszczonych”, tj. konkretnie informujących, że w tym miejscu strutktury zagnieżdżonych obiektów jest “rozjazd”.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Ja się przesiadam na 3.7 jak najszybciej, a Ty?&lt;/p&gt;
&lt;p&gt;PS. Pełną listę zmian można znaleźć tu: &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/&quot;&gt;https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[WinForms is dead]]></title><description><![CDATA[Polemika z tym tekstem:
https://blog.submain.com/death-winforms-greatly-exaggerated/ Tekst stary (sprzed ponad roku), ale gdzieś mi wpadł w…]]></description><link>https://robert.skarzycki.pl/05-winforms-is-dead/</link><guid isPermaLink="false">https://robert.skarzycki.pl/05-winforms-is-dead/</guid><pubDate>Thu, 07 Nov 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Polemika z tym tekstem:
&lt;a href=&quot;https://blog.submain.com/death-winforms-greatly-exaggerated/&quot;&gt;https://blog.submain.com/death-winforms-greatly-exaggerated/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Tekst stary (sprzed ponad roku), ale gdzieś mi wpadł w ręce - no i jak przeczytałem, to postanowiłem wysmarowąć polemikę.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Tak, WinForms jest wciąż żywy - ale to dlatego, że ciągle istnieją stare aplikacje, z których ktoś korzysta i których - z różnych powodów - nie wymienia się na rozwiązania oparte na nowszych technologiach. Ale to nie jest argument - znam przypadki produkcyjnego wykorzystywania aplikacji “dosowych” z UI w stylu Norton Commander. Ale czy to znaczy, że te technologie są żywe?&lt;/li&gt;
&lt;li&gt;Tak, WinForms jest martwy - nikt chyba dzisiaj nie tworzy od nowa projektów w oparciu o WinForms: po pierwsze mamy dzisiaj inne standardy i oczekiwania (np. dotyczące wyglądu GUI); po drugie ciężko przekonać programistów do pracy w technologii, która jeśli nie jest już martwa, to na pewno zmierza do tego; po trzecie - nie jestem pewien, czy .NET Core 3 coś tu zmienia, ale bazowo WinForms jest tylko na Windowsa, więc odpadają inne platformy; po czwarte - inne technologie mają lepsze wsparcie narzędziowe (ot choćby to, że - jak pisze sam autor - w Visual Studio 2019 nie ma designera do WinFormsów…).&lt;/li&gt;
&lt;li&gt;Szybkość prototypowania? Nie sądzę. Jeśli naprawdę chcemy zrobić szybki &lt;em&gt;proof of concept&lt;/em&gt; aplikacji desktopowej, a nie za bardzo znamy się na WPF-ie, to chyba dzisiaj najlepiej wybrać któreś z rozwiązań typu Electron: czyli osadzenia aplikacji webowej w desktopowym okienku. Ja bym szedł dzisiaj w tym kierunku, tym bardziej, że napisanie aplikacji Elektronowej np. w Reakcie daje od razu potencjał na pójście w innych kierunkach - czy to czystej webówki, czy urządzeń mobilnych za pomocą React Native. To po co robić coś prototypować w WinFormsach?&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title><![CDATA[Generic type inference in C#]]></title><description><![CDATA[Próbowałem ostatnio zrobić takie coś: Następnie chciałem, żeby kompilator sam domyślił się drugiego argumentu generycznego, gdy wołam…]]></description><link>https://robert.skarzycki.pl/04-generic-type-inference-in-csharp/</link><guid isPermaLink="false">https://robert.skarzycki.pl/04-generic-type-inference-in-csharp/</guid><pubDate>Thu, 31 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Próbowałem ostatnio zrobić takie coś:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IWrapper&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;TItem&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token return-type class-name&quot;&gt;ICollection&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;TItem&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; Items &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyWrapper&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;IWrapper&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;sting&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;ICollection&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; Items&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Operator&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;TWrapper&lt;/span&gt; &lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;TWrapper&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; TItem&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TWrapper&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;IWrapper&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;TItem&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Następnie chciałem, żeby kompilator sam domyślił się drugiego argumentu generycznego, gdy wołam &lt;code class=&quot;language-text&quot;&gt;DoSomething&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Wrapper&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;zamiast&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Wrapper&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Niestety, pierwsze opcja wywalała kompilację z komunikatem, że kompilator nie potrafi domyślić się typu tego drugiego argumentu generycznego. No ale - jak to? Skoro określam w &lt;code class=&quot;language-text&quot;&gt;where&lt;/code&gt;, że &lt;code class=&quot;language-text&quot;&gt;TRequest&lt;/code&gt; ma implementować interfejs &lt;code class=&quot;language-text&quot;&gt;IWrapper&amp;lt;TItem&gt;&lt;/code&gt;, a &lt;code class=&quot;language-text&quot;&gt;TItem&lt;/code&gt; jest drugim argumentem, to chyba wiadomo, że np. dla &lt;code class=&quot;language-text&quot;&gt;TRequest = IWapper&amp;lt;int&gt;&lt;/code&gt; - &lt;code class=&quot;language-text&quot;&gt;TItem = int&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;A jednak - nie! Jak zwykle odpowiedź przychodzi ze Stack Overflow - tym razem sam Eric Lippert, który pracował onegdaj przy kompilatorze C# &lt;a href=&quot;https://stackoverflow.com/questions/8511066/why-doesnt-c-sharp-infer-my-generic-types/8511493#8511493&quot;&gt;wyjaśnia&lt;/a&gt;, że:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A bunch of people have pointed out that C# does not make inferences based on constraints. That is correct, and relevant to the question. Inferences are made by examining arguments and their corresponding formal parameter types and that is the only source of inference information.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;:(&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Azure Pipelines - uważaj na stages i jobs]]></title><description><![CDATA[Odkąd Azure Pipelines (lub szerzej - Azure Devops) umożliwił deklarowanie pipeline’ów w plikach YAML zamiast klikania po stronie - bardzo…]]></description><link>https://robert.skarzycki.pl/03-azure-pipelines-uwazaj-na-stages-i-jobs/</link><guid isPermaLink="false">https://robert.skarzycki.pl/03-azure-pipelines-uwazaj-na-stages-i-jobs/</guid><pubDate>Tue, 22 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Odkąd Azure Pipelines (lub szerzej - Azure Devops) umożliwił deklarowanie pipeline’ów w plikach YAML zamiast klikania po stronie - bardzo spodobał mi się ten sposób organizacji CI/CD. Mimo że struktura YAML-a jest przejrzysta, umożliwia składanie procesu budowania/wdrazania z klocków i w zasadzie koncepcyjnie odzwierciedla to, co dało się zrobić w wersji UI-owej - to jednak o pomyłkę łatwo.&lt;/p&gt;
&lt;p&gt;Ostatnio dużo czasu spędziłem na tym, że wysypywał mi się bardzo prosty pipeline: w zasadzie tam było tylko w pierwszym kroku zbudowanie projektu przez &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt;; w drugim zaś kroku: uruchomienie testów UI, napisanych z pomocą biblioteki Cypress. Mimo że wszystkie skrypty były poprawnie zdefiniowane, build agent też miał wszystko, co było potrzebne i wszystko działało na maszynie deweloperskiej, to Azure Devops potykał się na drugim kroku, stwierdzając, że &lt;code class=&quot;language-text&quot;&gt;cypress not found&lt;/code&gt;. Sprawdzałem pathy, kombinowałem z różnymi opcjami, ale nic nie chciało działać. Okazało się finalnie, że - krok pierwszy (&lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt;) i drugi zdefiniowałem jako oddzielne joby w ramach jednego stage’a. (Czyli miałem jeden stage, z dwoma jobami, a w każdym jobie po jednym stepie.) No i nie uwzględniłem tego, że joby są od siebie niezależne, a zatem stworzony w pierwszym kroku folder &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; nie był dostępny w drugim jobie. Czyli &lt;em&gt;de facto&lt;/em&gt; próbowałem uruchomić skrypt npm (do uruchomineia Cypressa) bez wcześniejszego odpalnia &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt;. Na świeżo sklonowanym repo to nie może się udać.&lt;/p&gt;
&lt;p&gt;Na przyszłość warto więc pamiętać, jaka jest struktura pipeline’a:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;STAGE 1
   - JOB 1
     - STEP 1
     - STEP 2
     ...
   - JOB 2
     - STEP 1
     ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Przykładowo można to sobie wytłumaczyć tak:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;stages służą do oddzielenia głównych operacji, np. budowania od wdrażnia&lt;/li&gt;
&lt;li&gt;joby są poiom niżej i “enkapsulują” jakiś konkretny kawałek takiego dużego procesu, czyli np. jeśli budujemy oddzielnie frontend i backend, to możemy mieć w dwa joby na to; joby możemy puszczać równolegle albo określać, że dany job ma czekać wynik innego joba&lt;/li&gt;
&lt;li&gt;stepy to już kolejne kroki w ramach danego joba, np. &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt;, potem &lt;code class=&quot;language-text&quot;&gt;npm run build&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Wyniki z jednego joba można dzielić z innymi, ale trzeba to zrobić poprzez &lt;em&gt;pipeline artefacts&lt;/em&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[CSS in JS]]></title><description><![CDATA[Gdy widzę kogoś, kto używa Reacta, a stosuje globalne pliki CSS, to bolą mnie zęby (choć nie tak bardzo, gdy widzę, jak ktoś tworzy większą…]]></description><link>https://robert.skarzycki.pl/02-css-in-js/</link><guid isPermaLink="false">https://robert.skarzycki.pl/02-css-in-js/</guid><pubDate>Tue, 15 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Gdy widzę kogoś, kto używa Reacta, a stosuje globalne pliki CSS, to bolą mnie zęby (choć nie tak bardzo, gdy widzę, jak ktoś tworzy większą aplikację w Reakcie-Reduksie, a nie korzysta z TypeScripta - żal mi takiego biedaka; ale to opowieść na inną okazję). Od kiedy zacząłem korzystać z biblioteki (Material UI)[&lt;a href=&quot;https://material-ui.com/&quot;&gt;https://material-ui.com/&lt;/a&gt;] (Reaktowe komponent implementujące Material Design), bardzo mi się spodobało to, że mogę stylować komponenty lokalnie za pomocą CSS-in-JS (czyli reguły CSSa pisane w JS/TS). To po prostu jest wygodne, a do tego ma kilka innych plusów, o których można sobie poczytać przeglądając poniższą prezentację:
&lt;a href=&quot;https://speakerdeck.com/vjeux/react-css-in-js&quot;&gt;https://speakerdeck.com/vjeux/react-css-in-js&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Jeśli jeszcze nie czujesz się przekonany, zajrzyj też tu: &lt;a href=&quot;https://medium.com/seek-blog/a-unified-styling-language-d0c208de2660&quot;&gt;https://medium.com/seek-blog/a-unified-styling-language-d0c208de2660&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;P.S. Istnieje wiele rozwiązań typu CSS-in-JS, nie tylko to, które jest “zaszyte” w Material UI, np.:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Styled Components,&lt;/li&gt;
&lt;li&gt;Galmorous,&lt;/li&gt;
&lt;li&gt;Emotion.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Blog - jaką platformę wybrać?]]></title><description><![CDATA[Na dziś odpowiedź brzmi: Gatsby. Próbowałem wcześniej z WordPressem, potem eksperymentowałem ze statycznym generatorem (Hexo), potem…]]></description><link>https://robert.skarzycki.pl/01-blog-jaka-platforme-wybrac/</link><guid isPermaLink="false">https://robert.skarzycki.pl/01-blog-jaka-platforme-wybrac/</guid><pubDate>Tue, 01 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Na dziś odpowiedź brzmi: &lt;a href=&quot;https://www.gatsbyjs.org/&quot;&gt;Gatsby&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Próbowałem wcześniej z WordPressem, potem eksperymentowałem ze statycznym generatorem (&lt;a href=&quot;https://hexo.io/&quot;&gt;Hexo&lt;/a&gt;), potem wróciłem do WordPressa, by na końcu wylądować tutaj, czyli statycznym generatorem, jakim jest Gatsby.&lt;/p&gt;
&lt;p&gt;WordPress wydawał mi się dotychczas domyślnym rozwiązaniem dla strony typu blog. Ma on kilka plusów:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;znana platforma - wiele lat temu z niego korzystałem (kiedy byłem programistą PHP :P),&lt;/li&gt;
&lt;li&gt;bardzo dużo gotowych - darmowych lub płatnych - szablonów, co pozwala szybko osiągnąć atrakcyjny efekt wizualny,&lt;/li&gt;
&lt;li&gt;trylion wtyczek, a więc: “jeśli masz jakiś problem, to na pewno został on rozwiązany”&lt;/li&gt;
&lt;li&gt;rozbudowany CMS - mamy posty, grafikę, tagi, kategorie itp.&lt;/li&gt;
&lt;li&gt;na wielu hostingach można go zainstalować “1 kliknięciem”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;W zderzeniu z rzeczywistością Anno Domini 2019 okazało się, że są jednak minusy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;instalacja, aktualizacja i konfiguracja WordPressa, MySQL to na dzisiejsze za dużo pracy; hostowanie z własną domeną na wordpress.com nie opłaca się finansowo&lt;/li&gt;
&lt;li&gt;tam jest jednak pod spodem PHP! ;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Dlaczego statyczny generator?&lt;/h3&gt;
&lt;p&gt;Statyczny generator stron to narzędzie oparte np. o Node.js, dzięki któremu przetwarzamy jedną postać strony/bloga (np. pliki markdown) - w statyczną stronę, opartą tylko o HTML, JS i CSS. Na rynku jest całkiem sporo, można sobie przebierać, zależnie od silnika, możliwości, “dojrzałości” społeczności - polecam stronę &lt;a href=&quot;https://www.staticgen.com/&quot;&gt;https://www.staticgen.com/&lt;/a&gt;, z listą generatorów.&lt;/p&gt;
&lt;p&gt;Jakie możliwości daje statyczny generator?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;możliwość hostowania “wszędzie” - finalna strona to są same pliki HTML, CSS i JS, więc można nawet wrzucić ten content na Azure Blob Storage i już&lt;/li&gt;
&lt;li&gt;interfejs command-line (nie wiem, czy we wszystkich, ale na pewno w wielu, zwłaszcza tych opartych o Node.js), bliski programistycznemu sercu&lt;/li&gt;
&lt;li&gt;bezpieczeństwo - to nie jest PHP z dziurami, które trzeba pilnować, aktualizować itp.&lt;/li&gt;
&lt;li&gt;prostsze zarządzanie - nie muszę logować się do jakiegoś CMSa, podawać hasła itp.; mogę całą stronę/bloga trzymać na GitHubie i po porstu &lt;code class=&quot;language-text&quot;&gt;git commit&lt;/code&gt;/&lt;code class=&quot;language-text&quot;&gt;git push&lt;/code&gt; i mam zaktualizowaną treść&lt;/li&gt;
&lt;li&gt;większa elastyczność i łatwiejsze rozszerzanie funkcjonalości - to nie jest PHP, tylko np. JavaScript/TypeSciprt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Czemu więc niw udało się z Hexo? Pewnie było kilka powodów:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;może jeszcze nie byłem gotowy na zmianę w myśleniu o “silniku do bloga”&lt;/li&gt;
&lt;li&gt;próg wejścia był spory (konfiguracja, szablony), a w zasadzie silnik działał jedynie w oparciu o pliki markdown&lt;/li&gt;
&lt;li&gt;nie znalazłem satysfakcjonującego gotowego szablonu&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;A teraz cały na biało - Gatsby&lt;/h3&gt;
&lt;p&gt;Gatsby zainteresował mnie dwoma “ficzerami”:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;wykorzystanie Reacta - czyli przyjazne środowisko: nie muszę samemu pisać szablonu w gołym HTML-u/CSS-ie&lt;/li&gt;
&lt;li&gt;możliwość podpięcia jako wejścia dowolnego źródła z contentem - via GraphQL.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Okazało się, że uruchomienie jest bardzo proste, zwłaszcza przy wykorzystaniu gotowych (starterów)[&lt;a href=&quot;https://www.gatsbyjs.org/starters/&quot;&gt;https://www.gatsbyjs.org/starters/&lt;/a&gt;]. Dodatkowo, za rękę prowadzi (tutorial)[https://www.gatsbyjs.org/tutorial/], więc próg wejścia jest bezbolesny. Dzięki temu, że renderowanie “contentu” jest oddzielone od źródła danych (“separation of concerns” ;)), możemy w zasadzie tworzyć treść strony/bloga w dowolny sposób - albo po prostu jako treść pomiędzy komponentami Reactowymi, albo z wykorzystaniem wtyczki, która będzie źródłem danych dla kwerend GraphQL, np. w plikach Markdown, ale też wykorzystaniem zewnętrznego CMSa, w tym - WordPressa. A do tego z Gatsby’ego łatwo zdiplojujemy na (Surge)[https://surge.sh/] czy (Netlify)[https://www.gatsbyjs.org/docs/deploying-to-netlify/], tak więc byłskawicznie możemy stworzyć jakiś proof-of-concept i podzielić się nim z kimś, przesyłając linka.&lt;/p&gt;
&lt;p&gt;I ten właśnie miks - z jednej strony pełna elastyczność, a z drugiej łatwość osiągnięcia zadowalającego efektu, startując z poziomu 0 - jest wielką siłą Gatsby’ego. A do tego cały sztafaż znajomych narzędzi - Node’owe CLI, git, VS Code, React - czyni pracę bardzo przyjazną. Wydaje mi się zatem, że Gatsby stanie się moim podstawowym narzędziem do budowy blogów i prostych stron - ten Gatsby to naprawdę “Wielki Gatsby”. ;)&lt;/p&gt;
&lt;p&gt;P.S. Dla ciekawych - tę stronę zbudowałem w oparciu o starter &lt;code class=&quot;language-text&quot;&gt;gatsby-starter-julia&lt;/code&gt; ((link)[&lt;a href=&quot;https://www.gatsbyjs.org/starters/niklasmtj/gatsby-starter-julia/&quot;&gt;https://www.gatsbyjs.org/starters/niklasmtj/gatsby-starter-julia/&lt;/a&gt;]).&lt;/p&gt;</content:encoded></item></channel></rss>