<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="pl"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://koziolekweb.pl/feed.xml" rel="self" type="application/atom+xml" /><link href="https://koziolekweb.pl/" rel="alternate" type="text/html" hreflang="pl" /><updated>2025-07-13T20:22:21+00:00</updated><id>https://koziolekweb.pl/feed.xml</id><title type="html">Koziołekweb</title><subtitle>Sięgam tam gdzie wzrok nie sięga, a tam NullPointerException</subtitle><author><name>Koziołek</name></author><entry><title type="html">Javowy homelab – artifactory i npm</title><link href="https://koziolekweb.pl/2025/06/30/javowy-homelab-artifactory-i-npm" rel="alternate" type="text/html" title="Javowy homelab – artifactory i npm" /><published>2025-06-30T06:37:00+00:00</published><updated>2025-06-30T06:37:00+00:00</updated><id>https://koziolekweb.pl/2025/06/30/javowy-homelab-artifactory-i-npm</id><content type="html" xml:base="https://koziolekweb.pl/2025/06/30/javowy-homelab-artifactory-i-npm"><![CDATA[<p>W <a href="/2025/06/19/javowy-homelab-podstawy">poprzednim wpisie</a> omówiłem pokrótce proces konfiguracji podstawowych narzędzi.
Wspomniałem też, że Artifactory zostało zamienione na Nexusa, ale nie powiedziałem dlaczego. Dziś dwa błędy, które
kosztowały mnie czas i dużo nerwów.</p>

<h2 id="artifactory">Artifactory</h2>

<p>W chwili, gdy mamy dockera, to instalacja artifactory wydaje się bajecznie prosta. Tworzymy bazę danych, podajemy
namiary na nią w konfiguracji <code class="language-plaintext highlighter-rouge">docker compose</code> i gotowe. Nie do końca. Artifactory działa z własnym <code class="language-plaintext highlighter-rouge">UID</code> i <code class="language-plaintext highlighter-rouge">GUID</code> (
<code class="language-plaintext highlighter-rouge">1030:1030</code>). To oznacza, że jeżeli chcemy przechowywać wolumen artifactory poza domyślnym (lub skonfigurowanym
odgórnie) katalogiem dockera, to trzeba pobawić się z odpowiednimi dostępami.</p>

<p>Tu mamy dwie opcje. Najpierw możemy uruchomić całość z użytkownikiem <code class="language-plaintext highlighter-rouge">root</code> tak jak zrobiłem to z nexusem, pozwolić się
temu skonfigurować i potem zrobić <code class="language-plaintext highlighter-rouge">chown</code>. To zadziała, ale jest duża szansa, że po restarcie już z właściwym
użytkownikiem nie wstanie. Dlaczego? Problem leży gdzieś między <code class="language-plaintext highlighter-rouge">/</code> a katalogiem docelowym. Gdzieś nie ma odpowiednich
uprawnień, czegoś brakuje itd. Po dwóch wieczorach walki z tym rozwiązaniem stwierdziłem, że jebać to zrobię
konfigurację domyślną. I ta, druga opcja zadziałała. Co prawda, oznacza to, że późniejszy backup całości będzie
upierdliwy, ale jakoś przeżyję. Przynajmniej miałem takie założenie.</p>

<p>To, co jednak nie chciało działać, to sklejenie ze sobą proxy nginx i artifactory działającego w ścieżce <code class="language-plaintext highlighter-rouge">/artifactory</code>.
Tutaj poległem po całości, ponieważ nie udało mi się tak skonfigurować tego wszystkiego, żeby chciało działać. Nie wiem,
może głupi jestem. Artifactory wystawia dwa porty <code class="language-plaintext highlighter-rouge">8081</code> i <code class="language-plaintext highlighter-rouge">8082</code>. Oba powinny być mapowane na <code class="language-plaintext highlighter-rouge">localhost/artifactory</code>,
a w konfiguracji samego artifactory wystarczy dodać odpowiednią ścieżkę i powinno działać… no
nie. Po prostu nie chciało to tak zadziałać. Albo wywalało zwykłe 404, albo doklejało do URL-a dodatkową ścieżkę budując
<code class="language-plaintext highlighter-rouge">localhost/artifactory/artifactory</code>. Żeby jednak nie było, to da się to zrobić, ale trzeba mieć większą kontrolę nad
własną siecią (i wiedzieć, jak nią zarządzać), ponieważ „sztuczka” polega na użyciu odpowiednich domen. Jak nie masz
DNS, to nawet nie podchodź.</p>

<p>Poddałem się i pierdolnąłem to w kąt. Zainstalowałem Nexusa…</p>

<h2 id="npm">npm</h2>

<p>… i na tym Nexusie skonfigurowałem sobie lokalne proxy repozytorium npm. Pomysł jest niezły, bo przy moim talencie do
JS, wielokrotnie usuwam <code class="language-plaintext highlighter-rouge">node_modules</code> i wykonuję <code class="language-plaintext highlighter-rouge">npm install</code>. Zatem oszczędzanie transferu jest całkiem niezłym
pomysłem. Jednak nie do końca.</p>

<p>Skonfigurowałem zatem sobie proxy na nexusie, a następnie w <code class="language-plaintext highlighter-rouge">/etc/hosts</code> dodałem:</p>

<p class="listing"> Listing 1. Konfiguracja <code class="language-plaintext highlighter-rouge">hosts</code></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
127.0.0.1 view-localhost localhost koziolek koziolek.home
</code></pre></div></div>

<p>Pierwsza instalacja za pomocą <code class="language-plaintext highlighter-rouge">npm</code> trwała wieki, ale kolejne poszły już bardzo szybko i zgrabnie. Mogłem odtrąbić
sukces. Do czasu…</p>

<h3 id="github-action-i-dziwny-błąd">Github action i dziwny błąd</h3>

<p>„Dla jednego z moich klientów, lidera na rynku…” w każdym razie potrzebuję czasami coś zbudować za pomocą Github
Actions (GHA). Wypchnąłem kod, poszedłem na kawusię, wracam, a tam niespodzianka. Build wyłożył się na etapie
<code class="language-plaintext highlighter-rouge">npm install</code>. Jednak sam komunikat był mocno enigmatyczny. Npm nie mógł znaleźć pakietu. Sprawdziłem lokalnie i działa.
Dodałem pakiet bezpośrednio, ale GHA mówi, że nie może znaleźć. Nosz kurwa mać, twoja krzemowa. Termin goni, klient
pyta, co się stało, że się zesrało, a ja nie mam pojęcia. Dżepetto podsuwa rozwiązania, które powodują jeszcze większe
<em>fakapy</em> (nie działa lokalnie), a jeszcze człowiek chce skończyć pracę na dziś. Koniec końców usunąłem <code class="language-plaintext highlighter-rouge">node_modules</code>,
lockfile <code class="language-plaintext highlighter-rouge">npm</code> i <code class="language-plaintext highlighter-rouge">yarn</code> wypchnąłem i po kolejnej nieudanej próbie zacząłem analizować, czego tak naprawdę npm szuka.</p>

<p>Okazało się, że w <code class="language-plaintext highlighter-rouge">package-lock.json</code> mam wskazane moje domowe domeny. Po prostu <code class="language-plaintext highlighter-rouge">npm</code> działa inaczej niż <code class="language-plaintext highlighter-rouge">maven</code> i
zaszywa informację o pochodzeniu pakietu w pliku. Czy to ma sens? Moim zdaniem nie do końca, bo utrudnia pracę
z wieloma repozytoriami pakietów. Jednocześnie rozumiem motywacje twórców, którzy wyszli z założenia, że bezpieczeństwo
pakietów JS i powtarzalność procesu jest ważniejsza niż wygoda użycia.</p>

<h3 id="dlaczego-działa-to-w-twojej-firmie">Dlaczego działa to w twojej firmie?</h3>

<p>Ponieważ cały proces zarówno lokalnego budowania, jak i CI/CD masz ogarnięty w ramach sieci wewnętrznej. Twój Jenkins,
TeamCity, czy inne narzędzie widzi wszystkie potrzebne adresy. Względnie twoje firmowe repozytorium jest wystawione na
świat i można się do niego dobić z zewnątrz.</p>

<h2 id="podsumowanie">Podsumowanie</h2>

<p>Domowe środowisko pozwala na samodzielne przejście przez pewne procesy, których nie widzimy w codziennej pracy. Nie
oznacza to jednak, że będziemy mieli łatwo. Odkręcenie błędu z <code class="language-plaintext highlighter-rouge">npm</code> zajęło mi dłuższą chwilę, ale udało się to ogarnąć.
Pomysł na użycie Artifactory zarzuciłem, ale kiedyś wrócę do niego w ramach ćwiczeń z dockera. Na razie jednak czas na
hardware.</p>]]></content><author><name>Koziołek</name></author><category term="Programowanie" /><category term="HomeLab" /><category term="Java" /><category term="Docker" /><category term="programowanie" /><category term="homelab" /><category term="java" /><category term="docker" /><summary type="html"><![CDATA[W poprzednim wpisie omówiłem pokrótce proces konfiguracji podstawowych narzędzi. Wspomniałem też, że Artifactory zostało zamienione na Nexusa, ale nie powiedziałem dlaczego. Dziś dwa błędy, które kosztowały mnie czas i dużo nerwów.]]></summary></entry><entry><title type="html">Javowy homelab – podstawy</title><link href="https://koziolekweb.pl/2025/06/19/javowy-homelab-podstawy" rel="alternate" type="text/html" title="Javowy homelab – podstawy" /><published>2025-06-19T06:37:00+00:00</published><updated>2025-06-19T06:37:00+00:00</updated><id>https://koziolekweb.pl/2025/06/19/javowy-homelab-podstawy</id><content type="html" xml:base="https://koziolekweb.pl/2025/06/19/javowy-homelab-podstawy"><![CDATA[<p>Coś mnie wzięło na robienie porządków na strychu przy okazji przygotowań do świat wielkanocnych i powoli, powoli to
robię. Przy okazji „odkryłem” starego kompa, a że młody chce rozwijać się w kierunku IT, to wakacje spędzimy na budowie
homelabu. Dziś „jak zawsze pierwsza” część opowieści o home labie na bazie starego PC’ta. Zanim przejdziemy do
hardware’u (bo ten wymaga wizyty u specjalisty) zajmiemy się częścią softwarową, a dokładnie takim minimum usług, które
chcę ustawić na labie.</p>

<h2 id="co-chcę-mieć--minimum">Co chcę mieć – minimum?</h2>

<p>Pomysł jest następujący. Na docelowej maszynie uruchomimy dockera, a następnie w ramach tego dockera uruchomimy:</p>

<ul>
  <li>Postgresa do zarządzania różnymi bazami</li>
  <li>Jakiegoś menadżera repozytoriów do lokalnej pracy z mavenem, dockerem itp.</li>
  <li>Sewer www do domowych rzeczy (współdzielenie plików, linków itd.)</li>
  <li>NAS</li>
</ul>

<p>NAS jest już hardwarowym tematem, więc na razie musi poczekać. Cała reszta nadaje się do przetestowania na moim obecnym
komputerze, by później nie mieć fakapów i nie tracić czasu na duperele.</p>

<h2 id="jak-zacząć">Jak zacząć?</h2>

<p>Najlepiej od początku, ale wbrew pozorom nie jest to takie oczywiste. W tym przypadku podejście „odpal docker compose i
nara” nie do końca zadziała. Inaczej. Zadziała, ale tak naprawdę tracimy z oczu całe tło, które przy migracji na
docelową maszynę będzie bardzo istotne. Przecież sama migracja będzie gdzieś w połowie wakacji, więc też część wiedzy
„wyparuje”.</p>

<p>Jeżeli nie jest się pełnoetatowym administratorem lub nie pracuje się w zespole hołdującym zasadom DevOps (DevOps to
podejście do pracy, a nie stanowisko!), to też nie ma się wprawy w takich administracyjnych zadaniach. Zatem warto
spędzić chwilę i zaplanować sobie pracę. Dodatkowo warto całość na koniec gdzieś wypchnąć, żeby później na środowisku
docelowym nie musieć klepać wszystkiego od zera.</p>

<h3 id="plan">Plan</h3>

<p>Plan był taki jak zawsze, czyli, że kiedyś to zrobimy. Jednak jakieś ~1,5 tygodnia
temu <a href="https://x.com/koziolek/status/1931314717512843508">stwierdziłem</a>, że potrzebuję lokalne repo Artifactory, żeby
ogarnąć jeden z moich projektów. I postanowiłem zrobić to dobrze :D</p>

<p>Plan był zatem następujący.</p>

<ol>
  <li>Stawiamy Artifactory jako kontener</li>
  <li>Stawiamy bazę Postgres, jako kontener</li>
  <li>Całość „zasłaniamy” nginx.</li>
</ol>

<p>Niby proste, ale jak zwykle okazało się, że chciałem zrobić to dobrze, więc będzie pod górkę.</p>

<h2 id="przygotowania">Przygotowania</h2>

<p>Na moim GH znajdziecie <a href="git@github.com:Koziolek/koziolek-configuration.git">repozytorium</a>, w którym trzymam różne swoje
konfiguracje. Jest to jedno z tych miejsc, które kiedyś sprzątnę, ale nie teraz :D Jakoże jest to taki „zakwas” dla
moich komputerów, więc naturalną rzecz jest umieszczenie wszystkiego właśnie w tym repo. Nie chcę teraz dokładnie
omawiać, co i jak w tym repo, bo to zupełnie osobny temat. W skrócie, mamy część w katalogu <code class="language-plaintext highlighter-rouge">services</code>, która będzie
zawierać potrzebne mi rzeczy.</p>

<h2 id="postgres">Postgres</h2>

<p>Najprostszym do ogarnięcia wydawał się postres. I rzeczywiście, konfiguracja w kontenerze jest bardzo prosta, o ile nie
chcesz nic zmieniać. Jedyne co dodałem, to <code class="language-plaintext highlighter-rouge">init.sql</code>, który sprawdza i w razie czego tworzy kilka baz danych. Ale…</p>

<p>Jeżeli chcemy przykryć wszystko za pomocą nginx, to mamy „mały” problem. Do takiej bazy danych nie dostaniemy się z
zewnątrz. Należy zatem zrobić kilka rzeczy. Po pierwsze mapowanie portu <code class="language-plaintext highlighter-rouge">5432</code> na port hosta. To jest standardowe
podejście, jeżeli używamy dockera. Jednak w docelowym systemie, nie możemy sobie pozwolić na dostęp do tego portu nikomu
spoza wyznaczonej listy hostów. Hosty możemy zdefiniować na dwa sposoby:</p>

<ol>
  <li>Możemy utworzyć sieć dockerową, zakładając, że wszyscy zainteresowani będą łączyć się z tej sieci.</li>
  <li>Możemy wystawić port, a następnie na hoście zablokować ruch do tego portu spoza naszej sieci za pomocą firewalla.</li>
</ol>

<p>I tak źle i tak niedobrze. Pierwsze rozwiązanie pozbawia nas dostępu do bazy z aplikacji działających na innych
maszynach. Ten postgres ma służyć do pracy, a nie do udawania, więc trzeba inaczej. Druga opcja jest lepsza, bo domowe
komputery będą mogły się połączyć, ale nie mam dostępu „jakby co” spoza domu. Nie mamy dostępu do portu. Trzeba zatem
postawić pgadmina i w nim skonfigurować dostęp do bazy. Zwykły obraz wystarcza. Kontener z pgAdminem działa w tej samej
sieci dockerowej co postgres, więc formalnie łączy się z hostem <code class="language-plaintext highlighter-rouge">postgres:5432</code>. Komputery w LAN-ie będą łączyć się z
hostem po <code class="language-plaintext highlighter-rouge">IP_HOST:5432</code>. Firewall załatwi resztę. Więcej nie potrzeba, bo poważne awarie i tak będe musiał obarnąć z
domu.</p>

<h2 id="nginx">nginx</h2>

<p>Okazał się prostszy niż postgres :D Co jednak warto zrobić, to trzymać konfiguracje poszczególnych usług w osobnych
plikach oraz nazwać serwer inaczej niż localhost. To przyda się później jak postawimy DNS, który ułatwi dostęp do
maszyny innym komputerom w domowej sieci. Ale to później.</p>

<h2 id="artifactory-a-w-zasadzie-nexus">Artifactory, a w zasadzie Nexus</h2>

<p>Największym problemem okazała się konfiguracja Artifactory. I nie mówię tuo prostej konfiguracji obrazu, ale o
odpowiedniej konfiguracji proxy. Po kilku dniach wojowania stwierdziłem, że Artifactory nie jest moim must have, a Nexus
też jest OK. Koniec końców zainstalowałem nexusa. Tutaj jedna sztuczka – przy pierwszym uruchomieniu należy to zrobić
jako <code class="language-plaintext highlighter-rouge">root</code> ustawiając <code class="language-plaintext highlighter-rouge">user: root</code> w <code class="language-plaintext highlighter-rouge">docer-compose.yml</code>. Po wstępnym skonfigurowaniu hasła administratora należy
zaaplikować <code class="language-plaintext highlighter-rouge">chown -R 200:200</code> na pliki nexusa i zrestartować kontener. Ta magia jest potrzebna, jeżeli chcemy trzymać
pliki poza domyślnym katalogiem z wolumenami dockera. Czyli w praktyce zawsze :D O tym dlaczego kiedy indziej.</p>

<p>Po skonfigurowaniu nexusa warto też sprawdzić, czy nasze lokalne buildy działają, jak należy. Pierwsze uruchomienie może
być znacznie dłuższe, bo nexus będzie zaciągać pliki z głównego repozytorium.</p>

<h2 id="podsumowanie">Podsumowanie</h2>

<p>Zabawa jest przednia. Dżepetto i Klaudyna pomagają pisać żmudne rzeczy, ale nie można im ufać. Modele AI mają wiedzę,
która nie zawsze jest aktualna i odnosi się do starszych wersji narzędzi. Nie warto też walczyć, jeżeli coś nie chce
działać, a nie mamy przymusu użycia. Zmarnowałem kilka dni an potyczkę z Artifactory, teraz wiem, że było to zbędne.</p>

<p>W miarę rozwoju projektu będę wrzucać kolejne opisy.</p>]]></content><author><name>Koziołek</name></author><category term="Programowanie" /><category term="HomeLab" /><category term="Java" /><category term="Docker" /><category term="programowanie" /><category term="homelab" /><category term="java" /><category term="docker" /><summary type="html"><![CDATA[Coś mnie wzięło na robienie porządków na strychu przy okazji przygotowań do świat wielkanocnych i powoli, powoli to robię. Przy okazji „odkryłem” starego kompa, a że młody chce rozwijać się w kierunku IT, to wakacje spędzimy na budowie homelabu. Dziś „jak zawsze pierwsza” część opowieści o home labie na bazie starego PC’ta. Zanim przejdziemy do hardware’u (bo ten wymaga wizyty u specjalisty) zajmiemy się częścią softwarową, a dokładnie takim minimum usług, które chcę ustawić na labie.]]></summary></entry><entry><title type="html">Rozkład – żeby zrozumieć USA</title><link href="https://koziolekweb.pl/2025/06/15/rozklad-zeby-zrozumiec-usa" rel="alternate" type="text/html" title="Rozkład – żeby zrozumieć USA" /><published>2025-06-15T06:37:00+00:00</published><updated>2025-06-15T06:37:00+00:00</updated><id>https://koziolekweb.pl/2025/06/15/rozklad-zeby-zrozumiec-usa</id><content type="html" xml:base="https://koziolekweb.pl/2025/06/15/rozklad-zeby-zrozumiec-usa"><![CDATA[<div class="book">
  <img src="
/wp-content/uploads/2025/06/rozklad.jpg" alt="Cover of the book Rozkład. O niedemokracji w Ameryce by Piotr Tarczyński" title="Rozkład. O niedemokracji w Ameryce" class="cover" />
  <div class="book_desc">
    <ul>
      <li><span>Title: </span>Rozkład. O niedemokracji w Ameryce</li>
      <li><span>Author: </span>Piotr Tarczyński</li>
      <li><span>Year: </span>2023</li>
      <li><span>ISBN: </span>978-83-240-9230-7</li>
    </ul>
  </div>
</div>

<p>Czy słyszeliście o niedawnym zamachu i
zabójstwie <a href="https://en.wikipedia.org/wiki/Melissa_Hortman?useskin=vector">Melissy Hortman</a><sup class="wikiicon">
<em>W</em></sup>? Nie? Cóż nie dziwota, bo sprawa została przykryta napadem Izraela na Iran. W dodatku pani Hortman, była
zajomowała się polityką na poziomie stanowym, a nie krajowym. A może słyszeliście, że zamachowiec zaatakował też (
stanowego)
senatora <a href="https://en.wikipedia.org/wiki/John_Hoffman_(Minnesota_politician)?useskin=vector">Johna Hoffmana</a><sup class="wikiicon">
<em>W</em></sup>? Zapewne też nie. Jeżeli jednak coś tam słyszeliście i wasza wiedza ogranicza się, że zamachowiec to
ultraprawicowy zwolennik pro-life (hehehe), to już dużo. Jeżeli jednak chcecie zrozumie DLACZEGO w USA dochodzi do tego
typu zamachów, DLACZEGO w Kalifornii są tak silne protesty przeciwko deportacją i ogólnie rządom Trumpa, to polecam
waszej uwadze książkę Piotra Tarczyńskiego pt. „Rozkład. O niedemokracji w Ameryce”.</p>

<p>Książka ta zabiera czytelnika w fascynującą podróż przez czas i przestrzeń historii USA. Zaczyna się niewinnie, od
trochę dokłądniejszego opisania co stało się 6 stycznia 2020 roku w czasie tzw. „Szturmu na Kapitol”. Następnie autor
zadaje pytanie, dlaczego do tego doszło w „nalepszej demokracji na świecie” (ponoć). I przystepuje do dekompozycji
systemu politycznego USA.</p>

<p>Dawno na lekcjach WOS-u w moim najukochańszym VI LO psor Małgorzata Tudek (dziś Pani Dyrektor) opowiadała nam o różnych
systemach politycznych. Jednym z nich był system USA. Zdawał się banalnie prosty, bo poza systemem elektorskim w
wyborach prezydenckich, całość ma konstrukcję cepa. No nie koniecznie…</p>

<p>Po omówieniu wydarzeń z 2020r. Fundowana nam jest podróż w czasie. XIX wiek, XX wiek i historia dwóch głównych sił
politycznych we współczesnym USA – Demokratów i Republikanów. Dla Europejczyka zaskakujące wydaj się, że jeszcze w
latach 60-tych XX wieku, to Demokraci byli „tymi złymi”, którzy wspierali segregację rasową w wersji „hard”, a
Republikanie byli ostoją liberalizmu. Nagle okazuje się, że nie kto inny, jak turbo-republikanin Regan podpisuje jedną z
najbardziej liberalnych ustaw aborcyjnych (jako gubernator Kalifornii). I takich przykładów jest więcej. Podróż kończymy
historią zmian jakie zaszły w GOP w latach 90-tych, gdy partia ta zaczęła się radykalizować.</p>

<p>Kolejne rozdziały, to „rozbiór” systemu wyborczego. Jak to jest możliwe, że prezydentem zostaje ktoś, kto ma niższe
poparcie? I o ile jesteśmy to w stanie sobie wytłumaczyć na poziomie wiedzy wyniesionej ze szkoły (Elektorzy + zwycięzca
bierze wszystko), to już odkrycie, że ten system jest wynikiem „dżentelmeńskiej umowy”, oraz wielu zgniłych kompromisów,
jest pewnym zaskoczeniem. Podróżujemy też przez Senat, który z izby zadumy stał się cmentarzyskiem ustaw, bo w wyniku
różnych sztuczek prawno-regulaminowych nie ma możliwości przeprowadzenia przez niego ustawy w sensowny sposób. I nagle
odkrywamy, że Kongres ma wiele cech świetnie nam znanej demokracji szlacheckiej. Po czym autor funduje nam kolejny
zestaw zaskakujących faktów o… prawach wyborczych.</p>

<p>Prawa te nie są aż tak powszechne, jak nam, z perspektywy Starego Kontynentu się wydaje. Co więcej, okazuje się, że cały
system jest tak skonstruowany, że pozwala na wyeliminowanie „niewygodnego” elektoratu. I to nie tylko
przez <a href="https://pl.wikipedia.org/wiki/Gerrymandering">gerrymandering</a>. Możemy odkryć, z pewnym przerażeniem, że w USA
rasizm ma się całkiem nieźle i federalny Sąd Najwyższy go w sumie wspiera. Zresztą o tej instytucji też możemy
dowiedzieć się wielu ciekawych rzeczy.</p>

<p>Autor kończy książkę próbą odpowiedzi na pytanie co będzie po wyborach w 2024r. Dziś wiemy, że jego predykcja jest
nietrafna, bo nie założył „jeszcze gorszego scenariusza”. Jest też trochę o wewnętrznym terroryźmie, i to nie jest
optymistyczny opis.</p>

<p>„Rozkład” to książka o amerykańskim systemie politycznym i o tym, jak wiele jego elementów, to tylko dżentelmeńska
umowa. Jak w ciągu istnienia USA, poszczególne „prawa” były przedmiotem zmian i niepisanych umów. Książka jest ponura i
z dzisiejszej perspektywy jeszcze bardziej zmusza do przemyślenia naszej wizji świata.</p>

<blockquote>
  <p>Prezydent to jakaś kiepska wersja polskiego króla.</p>
</blockquote>

<p>THOMAS JEFFERSON W LIŚCIE DO JOHNA ADAMSA, 13.11.1787</p>

<p>No cóż…</p>]]></content><author><name>Koziołek</name></author><category term="Książka" /><category term="recenzja" /><category term="książka" /><category term="recenzja" /><summary type="html"><![CDATA[Title: Rozkład. O niedemokracji w Ameryce Author: Piotr Tarczyński Year: 2023 ISBN: 978-83-240-9230-7]]></summary></entry><entry><title type="html">Critical CSS, czy się opłaca?</title><link href="https://koziolekweb.pl/2025/05/17/critical-css-czy-sie-oplaca" rel="alternate" type="text/html" title="Critical CSS, czy się opłaca?" /><published>2025-05-17T06:37:00+00:00</published><updated>2025-05-17T06:37:00+00:00</updated><id>https://koziolekweb.pl/2025/05/17/critical-css-czy-sie-oplaca</id><content type="html" xml:base="https://koziolekweb.pl/2025/05/17/critical-css-czy-sie-oplaca"><![CDATA[<h2 id="soa1--to-zależy">SOA#1 – to zależy</h2>

<p>Od wielu czynników, m.in. tego jaki jest stan początkowy. I o tym dziś opowiem.</p>

<p>W piątek pojawił się cotygodniowy <a href="https://sendy.uw-team.org/w/TtuTEqUpfgK763bSKOSMsUSQ/39qJ1eGZjg5ebtupYGIFhQ/9pyg5Bgj5DwpySaOTwSfxw">newsletter</a>
od <a href="https://x.com/uwteam">unteam</a>, w którym był link do <a href="https://critical-css-extractor.kigo.studio/">narzędzia</a> pozwalającego na wydobycie ze strony
tzw. Critical CSS. Pobawiłem się nim trochę przy współpracy Dżepetto i mam kilka wniosków. Po kolei…</p>

<h2 id="critical-css-co-to">Critical CSS, co to?</h2>

<p>Założenie jest następujące. Przeglądarki wczytują i renderują kontent od góry do dołu, inaczej mówiąc, przetwarzają HTML strumieniowo. Jeżeli natkną
się na plik CSS, to go pobiorą. Jak trafią na nieznany plik z fontami, to go pobiorą. I tak też będą renderować zawartość strony. Najpierw wyrenderują
to co jest widoczne, potem przejdą dalej. Procesem tym możemy sterować na wiele różnych sposobów. Możemy używać cache, możemy używać prefetch i
preload. Możemy w końcu zadbać o to, żeby zasoby potrzebne do wyrenderowania strony były dostępne jak najwcześniej. I tu właśnie wchodzi Critical CSS.
Jest to podzbiór reguł CSS używanych na danej stronie, które aplikują się do tej części strony, która będzie widoczna jako pierwsza.</p>

<h3 id="jak-umieścić-css-na-stronie">Jak umieścić CSS na stronie?</h3>

<p>Mamy dwa wyjścia (poza stylowaniem elementów ad-hoc). Możemy użyć tagu <code class="language-plaintext highlighter-rouge">&lt;style&gt;</code> w kodzie html lub możemy użyć <code class="language-plaintext highlighter-rouge">&lt;link&gt;</code> i wskazać plik z arkuszem
stylu. Jeżeli wykorzystamy pierwszą metodę, to style zostaną przesłane wraz ze stroną, to zwiększa objętość dokumentu, ale zmniejszy liczbę żądań (
pomijam użycie cache). Kłopotliwe może być jednak utrzymanie spójnych styli pomiędzy różnymi dokumentami, ale to pomijalna kwestia, bo zakładam, że
wykorzystujemy jakiś generator, a nie lecimy z palca w notepadzie. Druga metoda ładnie nam separuje style i dokument. Pozwala na użycie cache i
całkiem zgrabne zarządzanie całością. Wadą jest ten nieszczęsny dodatkowy request, który czasami chcemy wykonań, a czasami niekoniecznie.</p>

<p>Ważne jednak jest też to w którym miejscu w kodzie stony umieścimy nasze znaczniki.</p>

<h3 id="critical-css-jak-działa">Critical CSS, jak działa?</h3>

<p>Pomysł jest następujący. W nagłówku dokumentu, czyli w <code class="language-plaintext highlighter-rouge">&lt;head&gt;</code> umieszczamy znacznik <code class="language-plaintext highlighter-rouge">&lt;style&gt;</code> zawierający nasz krytyczny arkusz stylów. Przeglądarka,
przystępując do renderowania, nie musi czekać na zewnętrzny arkusz. W dodatku jest to „minimal effective sheet”, czyli arkusz zawierający tylko
potrzebne style w dodatku w ich efektywnej postaci, czyli w czasie ich aplikacji nie będzie następować nadpisywanie (cascade). Nasze właściwe style w
postaci <code class="language-plaintext highlighter-rouge">&lt;link&gt;</code> umieszczmy przed znacznikiem <code class="language-plaintext highlighter-rouge">&lt;/body&gt;</code>. Zostaną pobrane na samym końcu, zmniejszając „First Contentful Paint”, czyli czas jaki upływa
do momentu wyrenderowania pierwszego elemantu na stronie. Mówiąc po ludzku, czas, po jakim użytkownik zobaczy, że coś się załadowało.</p>

<h2 id="praktyka">Praktyka</h2>

<p>Korzystając z narzędzia z newslettera oraz przy małej pomocy Dżepetto przystąpiłem do wdrożenia tej techniki na blogasku. Pierwszym krokiem było
określenie stanu obecnego. W tym celu wykorzystałem <a href="https://pagespeed.web.dev">PageSpeed Insights</a> i przygotowałem
pierwszy <a href="https://pagespeed.web.dev/analysis/https-koziolekweb-pl/wttyerhzci">raport</a>. Jest bardzo dobrze. FCP na poziomie 0,4s dla
desktopa i 1,5s dla mobilków, to dobry wynik. Kolejnym krokiem były wygenerowanie zestawu styli oraz podpięcie ich do bloga. Wygenerowałem więc zestaw
dla strony głównej i kilku pierwszych artykułów. Następnie Dżepetto przetworzył je, wybierając część wspólną, sotrując wg. kolejności użycia, a na
koniec zminifikował całość. Wysłałem i gotowe. Po chwili przygotowałem
kolejny <a href="https://pagespeed.web.dev/analysis/https-koziolekweb-pl/z2w95d9cpu">raport</a>. I dupa. Okazało się, że wydajność strony spadła. FCP nie zmienił
się, ale pojawił się problem z „Cumulative Layout Shift”.</p>

<h3 id="cumulative-layout-shift-co-to">Cumulative Layout Shift, co to?</h3>

<p>Jest to metryka, która mówi, jak bardzo wygląd strony, zmienia się w czasie jej ładowania. Po ludzku, jak bardzo strona skacze „po załadowaniu”, czyli
jak bardzo wkurza użytkownika. Tutaj wkurzał bardzo. Wartość 0,648 dla desktopu i 0,399 dla mobilki są dalekie od akceptowalnych. Szczególnie, że
wcześniej wynosiły 0.</p>

<p>W efekcie wycofałem zmianę i parametry wróciły do <a href="https://pagespeed.web.dev/analysis/https-koziolekweb-pl/wqhmt6f1p4">normy</a>.</p>

<h3 id="jeszcze-inne-podejście">Jeszcze inne podejście.</h3>

<p>A co jeżeli potraktować cały arkusz css jako krytyczny? Korzystam z Jekylla, więc przynajmniej teoretycznie nie powinno być problemu z osadzeniem
styli na stronie w elemencie <code class="language-plaintext highlighter-rouge">&lt;style&gt;</code>. Powtórzyłem więc zabawę, tym razem wstawiając cały css w nagłówek. Spowodowało to znaczne powiększenie
wielkości dokumentu, co jest takie sobie. Z 15kb mamy nagle 35kb, a w dodatku średnio możemy używać cache, ale jakoś to ogarnę definicjami <code class="language-plaintext highlighter-rouge">&lt;meta&gt;</code>.
Efekty były… cóż średnio zadowalające, o ile na dektopie <a href="https://pagespeed.web.dev/analysis/https-koziolekweb-pl/7wzbn3ciw1">raport</a> nie
wykazał zmian, to już na wersji mobilnej wydajność spadła. Zmianę wycofałem.</p>

<h2 id="podsumowanie">Podsumowanie</h2>

<p>Dobra wiadomość jest taka, że z blogaska już zbyt wiele nie wycisnę, jeśli chodzi o wydajność :D Critical CSS jest ciekawym rozwiązaniem, ale trzeba
mierzyć, mierzyć i jeszcze raz mierzyć, bo zamiast polepszyć, można popieprzyć. Wiem też, że brakuje mi wiedzy o nowoczesnych narzędziach, choć nie
boli to aż tak bardzo. Nie jestem webmasterem ani frontasiem. Z rzeczy do zrobienia jest jeszcze ogarnięcie trackingu, ale to już zupełnie inna bajka.</p>]]></content><author><name>Koziołek</name></author><category term="Programowanie" /><category term="css" /><category term="web" /><category term="Programowanie" /><category term="css" /><category term="web" /><summary type="html"><![CDATA[SOA#1 – to zależy]]></summary></entry><entry><title type="html">Gdzie kompilator nie może, tam ArchUnita pośle</title><link href="https://koziolekweb.pl/2025/05/14/gdzie-kompilator-nie-moze-tam-archunita-posle" rel="alternate" type="text/html" title="Gdzie kompilator nie może, tam ArchUnita pośle" /><published>2025-05-14T06:37:00+00:00</published><updated>2025-05-14T06:37:00+00:00</updated><id>https://koziolekweb.pl/2025/05/14/gdzie-kompilator-nie-moze-tam-archunita-posle</id><content type="html" xml:base="https://koziolekweb.pl/2025/05/14/gdzie-kompilator-nie-moze-tam-archunita-posle"><![CDATA[<p>We wszystkich językach programowania istnieje jakiś system typów. Może on być skrajnie prymitywny, jak w asemblerze – typ to 4 bajty lub 2 bajty. Może
być ukryty jak w <a href="https://pl.wikipedia.org/wiki/Forth_(j%C4%99zyk_programowania)">Forth</a><sup class="wikiicon"><em>W</em></sup> – co masz na stosie, to masz
na
stosie, i ty za to odpowiadasz. Może być niejawny, jak w bashu – wszystko jest strigiem, chyba że używamy tego inaczej. Typy mogą być sprawdzane
statycznie, jak w Rustcie i Javie, albo dynamicznie jak w Pythonie i Ruby. Może być silne (Java) lub słabe (JavaScript). Może w końcu być nominalne,
czyli dane „pasują” do typu, jeżeli nazwaliśmy je tak jak typ (Java), albo strukturalne (duck typing), gdzie dane pasują do typu, jeżeli mają taką
sama strukturę. Tych podziałów jest wiele i na różnym poziomie. Koniec końców przychodzi interpreter lub kompilator i próbuje na podstawie definicji
języka oraz naszego kodu coś wyprodukować. Jeżeli popełnimy jakiś błąd przy określeniu typów, to dostaniemy czy to błąd kompilacji, czy to błąd w
czasie wykonywania kodu. Takie zachowanie jest „mentalnie” proste, choć reguły rządzące poszczególnymi systemami typów mogą być bardzo skomplikowane.</p>

<p>Wraz z systemem typów w parze idzie też opis widoczności, czyli to w jaki sposób poszczególne typy mogą wchodzić ze sobą w interakcje. Opis ten to
sławetne „modyfikatory dostępu”, które mogą, ale nie muszą, istnieć w danym języku. Jak to działa w
Javie <a href="https://koziolekweb.pl/2016/05/16/czego-oczy-nie-widza/">wiadomo</a>. Problem pojawia się, gdy z jakiegoś powodu musimy dołożyć jeszcze jedną
„warstwę” widoczności związaną z umiejscowieniem i rolą danego typu w naszej architekturze. I tu na pomoc przychodzą… interfejsy. Przynajmniej w
Javie, bo założenie jest takie, że pomiędzy pakietami komunikujemy się za pomocą interfejsów. Stąd też domyślna widoczność metod jest publiczna.
Jeżeli do tego dołożymy rekordy, które zdefiniujemy w ramach tych interfejsów, to mamy bardzo ładnie zdefiniowane API.</p>

<p>A co z konkretnymi klasami? Słuszne pytanie, bo przecież nie można stworzyć instancji interfejsu. Tu mamy tą straszną <code class="language-plaintext highlighter-rouge">AbstractBeanFactoryFactory</code>,
czyli wzorce kreacyjne. Opakowaliśmy je jakieś 23 lata temu w Springa, a jeszcze chwile wcześniej w JEE. Zatem nie jest źle, prawda? No nie do końca,
ponieważ nadal mamy problem z dostępem na poziomie architektury. Jeżeli interfejs <code class="language-plaintext highlighter-rouge">Repository</code> jest publiczny, to zarówno <code class="language-plaintext highlighter-rouge">Service</code> jak i <code class="language-plaintext highlighter-rouge">Controller</code>
będą mieć do niego dostęp. Niby mamy moduły, ale… nikt nam nie zabroni wpisać <code class="language-plaintext highlighter-rouge">requires repository</code> w <code class="language-plaintext highlighter-rouge">module-info</code> kontrolera :D</p>

<p>Tu z pomocą przychodzi <a href="https://www.archunit.org/">ArchUnit</a>, czyli narzędzie do „testowaina architektury”. Nawet kiedyś dawno temu <a href="https://slides.com/koziolek/archunit-vmpl">mówiłem o tym na
SegFaulcie</a>. Definiujemy kilka prostych regułek, które mówią które pakiety, gdzie powinny być używane i
pora na CSa. I tak mniej więcej 95% programistów używa ArchUnita. Pozostałe 5% używa go jeszcze do sprawdzenia czy nie wołamy jakiś głupot w stylu
<code class="language-plaintext highlighter-rouge">System.out.println</code>. Jednak to nie rozwiązuje wszystkich naszych problemów.</p>

<h2 id="publiczne-ale-pakietowe-ale-nie-w-testach">Publiczne, ale pakietowe, ale nie w testach</h2>

<p>Mamy sobie taką biblioteczkę jak Quarkus Mailer. Definiujemy sobie interfejs za pomocą którego, będziemy wysyłać maile.</p>

<p class="listing"> Listing 1. <code class="language-plaintext highlighter-rouge">MailerService</code> do wysyłki maili</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">MailerService</span> <span class="o">{</span>

	<span class="kt">boolean</span> <span class="nf">sendPasswordReset</span><span class="o">(</span><span class="nc">PasswordReset</span> <span class="n">passwordReset</span><span class="o">);</span>

	<span class="kd">record</span> <span class="nf">LostPassword</span><span class="o">(</span><span class="nc">String</span> <span class="n">passwordResetToken</span><span class="o">,</span> <span class="nc">String</span> <span class="n">expirationDate</span><span class="o">)</span> <span class="kd">implements</span> <span class="nc">MailTemplate</span><span class="o">.</span><span class="na">MailTemplateInstance</span> <span class="o">{</span>
	<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>I wszystko byłoby fajnie, gdyby nie ten nieszczęsny <code class="language-plaintext highlighter-rouge">MailTemplate.MailTemplateInstance</code>. Biblioteka wymaga, żeby rekordy implementujące ten interfejs
były publiczne, żeby biblioteka mogła się do nich dostać bez problemów. Jednocześnie takie upublicznienie powoduje, że możemy wysłać maila z dowolnego
miejsca w kodzie, bo interfejs zawiera m.in. metody do obsługi wysyłki. Co można z tym zrobić?</p>

<h3 id="napisać-dużo-kodu">Napisać dużo kodu</h3>

<p>Usuwamy z definicji rekordu implementację interfejsu. Następnie w niepublicznej implementacji <code class="language-plaintext highlighter-rouge">MailerService</code> definiujemy statyczny, publiczny rekord,
który już będzie implementować interfejs <code class="language-plaintext highlighter-rouge">MailTemplate.MailTemplateInstance</code>. Następnie za pomocą MapStructa lub z palca piszemy maper, który zmienia
rekord z interfejsu na rekord z implementacji. Biblioteka jest zadowolona, mechanizm mailingu niewidoczny, a i liczba linii kodu się zgadza. Problem
rozwiązany za pomocą systemu typów i ich przepakowywania.</p>

<p>Tylko, że w ten sposób powstaje nam ciężki do przetestowania kod, bo w testach musimy czasami tego maila wysłać z pominięciem serwisu i niekoniecznie
z poziomu pakietu z mailingiem.</p>

<h3 id="napisać-regułkę-archunit">Napisać regułkę ArchUnit</h3>

<p>To jest trochę bardziej skomplikowane, ale możemy pokusić się o napisanie generycznej reguły, która będzie brzmieć:</p>

<blockquote>
  <p>Sprawdź, czy dany rekord jest tworzony jedynie w klasach implementujących interfejs w którym zdefiniowano ten rekord lub w metodach oznaczonych jako
<code class="language-plaintext highlighter-rouge">@Test</code>.</p>
</blockquote>

<p>I tego już żaden system typów nie ogarnie. Dlaczego? Ponieważ nie istnieje język, którego system typów pozwoliłby na jednoczesne sprawdzenie:</p>

<ul>
  <li>miejsca użycia typu – tylko w klasach implementujących dany interfejs</li>
  <li>kontekstu wywołania – lub metodach oznaczonych jako <code class="language-plaintext highlighter-rouge">@Test</code></li>
</ul>

<p>Dlaczego? Ponieważ systemy typów co do zasady są pomyślane tak, żeby sprawdzać poprawność danych i zgodność interfejsów (w szerszym znaczeniu – co
możemy użyć). Nie są jednak przeznaczone do weryfikowania kontekstu wywołania. Do weryfikacji kontekstu wywołania służy analiza statyczna. Do analizy
statycznej posłuży nam ArchUnit, w którym napiszemy taka oto regułkę:</p>

<p class="listing"> Listing 2. Nasz weryfikator</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="kn">import</span> <span class="nn">static</span> <span class="n">com</span><span class="o">.</span><span class="na">tngtech</span><span class="o">.</span><span class="na">archunit</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">SimpleConditionEvent</span><span class="o">.</span><span class="na">violated</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">com.tngtech.archunit.core.domain.JavaClass</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.tngtech.archunit.core.domain.JavaConstructorCall</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.tngtech.archunit.lang.ArchCondition</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.tngtech.archunit.lang.ConditionEvent</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.tngtech.archunit.lang.ConditionEvents</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Arrays</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.jupiter.api.Test</span><span class="o">;</span>

<span class="kd">class</span> <span class="nc">NoInnerRecordsAreUsedOutsideOfItsOwnerOrTest</span> <span class="kd">extends</span> <span class="nc">ArchCondition</span><span class="o">&lt;</span><span class="nc">JavaClass</span><span class="o">&gt;</span> <span class="o">{</span>
	<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">String</span> <span class="no">TEST_FULL_NAME</span> <span class="o">=</span> <span class="nc">Test</span><span class="o">.</span><span class="na">class</span><span class="o">.</span><span class="na">getName</span><span class="o">();</span>

	<span class="kd">private</span> <span class="kd">final</span> <span class="nc">Class</span><span class="o">&lt;?&gt;</span> <span class="n">owner</span><span class="o">;</span>
	<span class="kd">private</span> <span class="kd">final</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">forbiddenNames</span><span class="o">;</span>

	<span class="kd">public</span> <span class="nf">NoInnerRecordsAreUsedOutsideOfItsOwnerOrTest</span><span class="o">(</span><span class="nc">Class</span><span class="o">&lt;?&gt;</span> <span class="n">owner</span><span class="o">)</span> <span class="o">{</span>
		<span class="kd">super</span><span class="o">(</span><span class="s">"Use %s defined records only in %s"</span><span class="o">,</span> <span class="n">owner</span><span class="o">.</span><span class="na">getSimpleName</span><span class="o">(),</span> <span class="n">owner</span><span class="o">.</span><span class="na">getSimpleName</span><span class="o">());</span>
		<span class="k">this</span><span class="o">.</span><span class="na">owner</span> <span class="o">=</span> <span class="n">owner</span><span class="o">;</span>
		<span class="k">this</span><span class="o">.</span><span class="na">forbiddenNames</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">stream</span><span class="o">(</span><span class="n">owner</span><span class="o">.</span><span class="na">getDeclaredClasses</span><span class="o">())</span>
			<span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="n">c</span> <span class="o">-&gt;</span> <span class="n">c</span><span class="o">.</span><span class="na">isRecord</span><span class="o">())</span>
			<span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="nl">Class:</span><span class="o">:</span><span class="n">getName</span><span class="o">)</span>
			<span class="o">.</span><span class="na">toList</span><span class="o">();</span>
		<span class="k">if</span> <span class="o">(</span><span class="n">forbiddenNames</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">())</span> <span class="o">{</span>
			<span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"Class "</span> <span class="o">+</span> <span class="n">owner</span><span class="o">.</span><span class="na">getName</span><span class="o">()</span> <span class="o">+</span> <span class="s">" has no inner classes"</span><span class="o">);</span>
		<span class="o">}</span>
	<span class="o">}</span>

	<span class="nd">@Override</span>
	<span class="kd">public</span> <span class="kt">void</span> <span class="nf">check</span><span class="o">(</span><span class="nc">JavaClass</span> <span class="n">javaClass</span><span class="o">,</span> <span class="nc">ConditionEvents</span> <span class="n">events</span><span class="o">)</span> <span class="o">{</span>
		<span class="n">javaClass</span><span class="o">.</span><span class="na">getConstructorCallsFromSelf</span><span class="o">()</span>
			<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
			<span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="n">call</span> <span class="o">-&gt;</span> <span class="n">isInNames</span><span class="o">(</span><span class="n">call</span><span class="o">)</span> <span class="o">&amp;&amp;</span> <span class="n">isNotImplementOwner</span><span class="o">(</span><span class="n">javaClass</span><span class="o">)</span> <span class="o">&amp;&amp;</span> <span class="n">isNotCallInTestMethod</span><span class="o">(</span><span class="n">call</span><span class="o">))</span>
			<span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="k">this</span><span class="o">::</span><span class="n">createViolationEvent</span><span class="o">)</span>
			<span class="o">.</span><span class="na">forEach</span><span class="o">(</span><span class="nl">events:</span><span class="o">:</span><span class="n">add</span><span class="o">);</span>
	<span class="o">}</span>

	<span class="kd">private</span> <span class="nc">ConditionEvent</span> <span class="nf">createViolationEvent</span><span class="o">(</span><span class="nc">JavaConstructorCall</span> <span class="n">call</span><span class="o">)</span> <span class="o">{</span>
		<span class="kd">final</span> <span class="nc">String</span> <span class="n">calledName</span> <span class="o">=</span> <span class="n">call</span><span class="o">.</span><span class="na">getOrigin</span><span class="o">().</span><span class="na">getOwner</span><span class="o">().</span><span class="na">getFullName</span><span class="o">();</span>
		<span class="kd">final</span> <span class="nc">String</span> <span class="n">calledPlace</span> <span class="o">=</span> <span class="n">call</span><span class="o">.</span><span class="na">getOrigin</span><span class="o">().</span><span class="na">getFullName</span><span class="o">();</span>
		<span class="k">return</span> <span class="nf">violated</span><span class="o">(</span><span class="n">call</span><span class="o">,</span> <span class="s">"Invalid call of "</span> <span class="o">+</span> <span class="n">calledName</span> <span class="o">+</span> <span class="s">" in "</span> <span class="o">+</span> <span class="n">calledPlace</span><span class="o">);</span>
	<span class="o">}</span>

	<span class="kd">private</span> <span class="kt">boolean</span> <span class="nf">isNotCallInTestMethod</span><span class="o">(</span><span class="nc">JavaConstructorCall</span> <span class="n">call</span><span class="o">)</span> <span class="o">{</span>
		<span class="k">return</span> <span class="o">!</span><span class="n">call</span><span class="o">.</span><span class="na">getOrigin</span><span class="o">()</span>
			<span class="o">.</span><span class="na">getAnnotations</span><span class="o">()</span>
			<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
			<span class="o">.</span><span class="na">anyMatch</span><span class="o">(</span><span class="n">a</span> <span class="o">-&gt;</span> <span class="n">a</span><span class="o">.</span><span class="na">getRawType</span><span class="o">().</span><span class="na">getName</span><span class="o">().</span><span class="na">equals</span><span class="o">(</span><span class="no">TEST_FULL_NAME</span><span class="o">));</span>
	<span class="o">}</span>

	<span class="kd">private</span> <span class="kt">boolean</span> <span class="nf">isInNames</span><span class="o">(</span><span class="nc">JavaConstructorCall</span> <span class="n">call</span><span class="o">)</span> <span class="o">{</span>
		<span class="k">return</span> <span class="n">forbiddenNames</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="n">call</span><span class="o">.</span><span class="na">getTarget</span><span class="o">().</span><span class="na">getOwner</span><span class="o">().</span><span class="na">getName</span><span class="o">());</span>
	<span class="o">}</span>

	<span class="kd">private</span> <span class="kt">boolean</span> <span class="nf">isNotImplementOwner</span><span class="o">(</span><span class="nc">JavaClass</span> <span class="n">javaClass</span><span class="o">)</span> <span class="o">{</span>
		<span class="k">return</span> <span class="o">!</span><span class="n">javaClass</span><span class="o">.</span><span class="na">getAllRawInterfaces</span><span class="o">().</span><span class="na">stream</span><span class="o">()</span>
			<span class="o">.</span><span class="na">anyMatch</span><span class="o">(</span><span class="n">i</span> <span class="o">-&gt;</span> <span class="n">i</span><span class="o">.</span><span class="na">getName</span><span class="o">().</span><span class="na">equals</span><span class="o">(</span><span class="n">owner</span><span class="o">.</span><span class="na">getName</span><span class="o">()));</span>
	<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<h4 id="co-tu-się-dzieje">Co tu się dzieje?</h4>

<p>Po pierwsze w konstruktorze pobieramy listę rekordów zdefiniowanych w danej klasie. Jeżeli nie ma rekordów, to leci wyjątek. Następnie w metodzie
<code class="language-plaintext highlighter-rouge">check</code> dzieje się „magia”. ArchUnit uruchamia tę metodę dla każdej klasy w naszym kontekście weryfikacji (o tym za chwilę) i pobiera z niej wszystkie
wywołane w tej klasie konstruktory » <code class="language-plaintext highlighter-rouge">javaClass.getConstructorCallsFromSelf()</code>. Dla każdego wywołania konstruktora sprawdzamy, czy jest to wywołanie
konstruktora naszego rekordu » <code class="language-plaintext highlighter-rouge">isInNames</code> i czy klasa wywołująca nie implementuje interfejsu » <code class="language-plaintext highlighter-rouge">isNotImplementOwner</code> i czy wywołanie nie nastąpiło w
testach » <code class="language-plaintext highlighter-rouge">isNotCallInTestMethod</code>. Jeżeli wszystkie te założenia są prawdziwe, to tworzymy (<code class="language-plaintext highlighter-rouge">map</code>) i zgłaszamy (<code class="language-plaintext highlighter-rouge">forEach-&gt;add</code>) naruszenie.</p>

<h4 id="czym-jest-kontekst-weryfikacji">Czym jest kontekst weryfikacji?</h4>

<p>Jest to zbiór klas, które chcemy zweryfikować. W naszym przypadku chcemy zweryfikować wszystkie klasy z pakietu bazowego naszego projektu, więc możemy
wywołać test w następujący sposób:</p>

<p class="listing"> Listing 3. Wywołanie testu</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="kd">class</span> <span class="nc">MailerServiceRecordsUsageArchTest</span> <span class="kd">implements</span> <span class="nc">ArchTestTrait</span> <span class="o">{</span>

	<span class="nd">@Test</span>
	<span class="kt">void</span> <span class="nf">recordsInMailerServiceShouldBeCreatedOnlyInSpecificClasses</span><span class="o">()</span> <span class="o">{</span>
		<span class="n">classes</span><span class="o">()</span>
			<span class="o">.</span><span class="na">should</span><span class="o">(</span><span class="n">innerRecordUsage</span><span class="o">(</span><span class="nc">MailerService</span><span class="o">.</span><span class="na">class</span><span class="o">))</span>
			<span class="o">.</span><span class="na">check</span><span class="o">(</span><span class="k">new</span> <span class="nc">ClassFileImporter</span><span class="o">()</span>
					   <span class="o">.</span><span class="na">withImportOption</span><span class="o">(</span><span class="nc">ImportOption</span><span class="o">.</span><span class="na">Predefined</span><span class="o">.</span><span class="na">DO_NOT_INCLUDE_TESTS</span><span class="o">)</span>
					   <span class="o">.</span><span class="na">importPackages</span><span class="o">(</span><span class="s">"pl.koziolekweb.."</span><span class="o">));</span>
	<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Przy czym tutaj jest to trochę preoptymalizowany, ponieważ ArchUnit udostępnia filtr <code class="language-plaintext highlighter-rouge">ImportOption.Predefined.DO_NOT_INCLUDE_TESTS</code>, który na
podstawie ścieżki do pliku odsiewa klasy testowe. Uwzględnia jednak jedynie domyślną strukturę mavena, gradle i IntelliJ, więc w przypadku gdy używamy
inne IDE, lub zmieniliśmy konfigurację, to będzie problem.</p>

<p>Tak przygotowana regułę możemy spakować w paczkę i wystawić w firmowym repo.</p>

<h2 id="podsumowanie">Podsumowanie</h2>

<p>ArchUnit, podobnie jak inne narzędzia do statycznej analizy kodu dopełnia system typów o weryfikację kontekstu wykonania. Pozwala to na ograniczenie
ilości kodu, którego jedynym zadaniem mapowanie typów, by zaspokoić potrzeby związane z izolacją poszczególnych modułów. Niby niewiele, ale jednak
bardzo dużo.</p>]]></content><author><name>Koziołek</name></author><category term="Programowanie" /><category term="Java" /><category term="ArchUnit" /><category term="Programowanie" /><category term="java" /><category term="archunit" /><summary type="html"><![CDATA[We wszystkich językach programowania istnieje jakiś system typów. Może on być skrajnie prymitywny, jak w asemblerze – typ to 4 bajty lub 2 bajty. Może być ukryty jak w ForthW – co masz na stosie, to masz na stosie, i ty za to odpowiadasz. Może być niejawny, jak w bashu – wszystko jest strigiem, chyba że używamy tego inaczej. Typy mogą być sprawdzane statycznie, jak w Rustcie i Javie, albo dynamicznie jak w Pythonie i Ruby. Może być silne (Java) lub słabe (JavaScript). Może w końcu być nominalne, czyli dane „pasują” do typu, jeżeli nazwaliśmy je tak jak typ (Java), albo strukturalne (duck typing), gdzie dane pasują do typu, jeżeli mają taką sama strukturę. Tych podziałów jest wiele i na różnym poziomie. Koniec końców przychodzi interpreter lub kompilator i próbuje na podstawie definicji języka oraz naszego kodu coś wyprodukować. Jeżeli popełnimy jakiś błąd przy określeniu typów, to dostaniemy czy to błąd kompilacji, czy to błąd w czasie wykonywania kodu. Takie zachowanie jest „mentalnie” proste, choć reguły rządzące poszczególnymi systemami typów mogą być bardzo skomplikowane.]]></summary></entry><entry><title type="html">Książka zamiast kawy - Patopaństwo</title><link href="https://koziolekweb.pl/2025/04/17/ksiazka-zamiast-kawy-patopanstwo" rel="alternate" type="text/html" title="Książka zamiast kawy - Patopaństwo" /><published>2025-04-17T06:37:00+00:00</published><updated>2025-04-17T06:37:00+00:00</updated><id>https://koziolekweb.pl/2025/04/17/ksiazka-zamiast-kawy-patopanstwo</id><content type="html" xml:base="https://koziolekweb.pl/2025/04/17/ksiazka-zamiast-kawy-patopanstwo"><![CDATA[<div class="book">
  <img src="/wp-content/uploads/2025/04/p-patopanstwo.webp" alt="Cover of the book Patopaństwo by Jan Śpiewak" title="Patopaństwo" class="cover" />
  <div class="book_desc">
    <ul>
      <li><span>Title: </span>Patopaństwo</li>
      <li><span>Author: </span>Jan Śpiewak</li>
      <li><span>Year: </span>2025</li>
      <li><span>ISBN: </span>978-83-838-7613-9</li>
    </ul>
  </div>
</div>

<p>Czasami wstajesz rano, pijesz jedną kawę, drugą – i nic. Nadal zamuła i ogólnie niski poziom wszystkiego. Normalnie w takiej sytuacji należałoby
walnąć energola, który pozbawiony jest dodatków uspokajających (kawa ma takowe) poczekać 10 minut i gotowe. Co prawda chęć do życia nadal niska, ale
przynajmniej masz energię, by ją wyrazić.</p>

<p>Jednak istnieje alternatywą i jest nią lektura „Patopaństwa” Janka Śpiewaka. I podnosi ciśnienie lepiej niż kawa zalana redbullem i posłodzona amfą.</p>

<p>W tej książce wkurwia wszystko. Jednak po kolei.</p>

<h2 id="o-czym-mówimy">O czym mówimy?</h2>

<p>Mówimy o historii najnowszej naszej Najukochańszej Rzeczpospolitej i o patologiach jakie w pocie czoła wypracowaliśmy. A czego tu nie ma. Począwszy od
naszej radosnej codzienności z wariatami za kierownicą i wkurwiajacymi szefami, poprzez trochę większe patologie w szkole, na uczelni, w urzędzie, czy
u lekarza, a na grubych kończąc. Mamy zatem opowieść o tym, co na co dzień widzimy i o tym, z czym niektórzy z nas już się musieli zmierzyć (kredyty
frankowe), albo zmierzyć będą się musieli (wychowanie dzieci), albo nie powinni się mierzyć (wypadki drogowe, uzależnienia). I każda opisana w książce
historia powoduje wkurw, bo jednak widzisz, że to dzieje się dookoła ciebie.</p>

<h2 id="struktura">Struktura</h2>

<p>Na początek mamy małą dawkę historii. Źródła naszego państwa z gówna i patyków sięgają drugiej połowy lat 70-tych i rodzącego się wtedy ruchu
robotniczo-inteligenckiego. Z niego wyrosły nasze współczesne elity, które w wyniku przemian ustrojowych zafundowały nam… no cóż… całkiem fajne
miejsce do życia. Fajne – o ile miałeś trochę farta i prawidłowo wybrałeś rodziców, bo poza tym to już nie do końca.</p>

<p>Sam autor opisuje swoje korzenie w sposób, który dobrze obrazuje, z jakiego rodzaju elitami mamy do czynienia. Mam nieodparte wrażenie, że w którymś
momencie życia dostał o po głowie i go trochę otrzeźwiło. Dzięki temu krytycznie podchodzi do otaczającej nas rzeczywistości, ale też bardzo odważnie
opisuje „swoje” plemię.</p>

<p>Nie do końca rozumiem co kierowało nim, przy takiej, a nie innej organizacji treści, bo o ile pierwsze trzy-cztery rozdziały mają sens (elity,
inteligencja, transformacja, polityka). Pokazują, jakie są historyczne i społeczne przyczyny obecnego stanu rzeczy, to późniejsze „bloki tematyczne”
są trochę pomieszane. I choć czytając, miałem wrażenie, że każdy rozdział kończy się swoistym wprowadzeniem do kolejnego, to jednak coś mi tu zgrzyta.
W niektórych miejscach brakuje też tego jednego sensownego przypisu. O ile źródeł autor podał dużo, to jednak czasami brakuje tego odnośnika, do
wspominanych badań, książki, artykułu.</p>

<h2 id="ta-książka-wkurwia">Ta książka wkurwia</h2>

<p>I to nie tylko treścią, ale też formą. I teraz się będę czepiał. Wspomniana przed chwilą pewna losowość poruszanych spraw nie jest aż tak męcząca, jak
samo przedstawienie treści.</p>

<p>Książka ma strukturę, którą można opisać w skrócie jako „zrzut z bloga”. Stosunkowo krótkie podrozdziały, czasami suche przedstawienie faktów. W
dodatku im dalej, tym gorzej. W pewnym momencie czułem się, jakbym czytał blogaska, a nie książkę. Cóż… taka współczesna moda albo ja jestem
przewrażliwiony po lekturze amerykańskich reportaży Czarnego i Herezji Horusa.</p>

<p>Z punktu widzenia e-czytelnika męczący jest też pomysł z zamieszczeniem korespondencji i komentarzy z sieci w postaci nieobrobionych screenshotów. Sam
pomysł super, ale forma nie na moje zmęczone oczy. Parafrazując klasyka „większa czcionka!”, bo inaczej nie idzie tego czytać. No i już zupełnie
technicznie – od tego książka „spuchła” do nieprzyzwoitej wielkości prawie 10MB.</p>

<h2 id="dla-kogo">Dla kogo?</h2>

<p>Powiem tak. Przyjmowanie krytyki to sztuka. Współczesne czasy uczyniły z niej bardzo trudną sztukę, bo pierwsze co robimy, słysząc krytyczne uwagi pod
naszym adresem, to „ustawienie” krytykującego na drabinie hierarchii społecznej (często w określonej w dodatkowym kontekście). Dlatego wiele osób,
które „odniosły sukces” może czuć się źle, czytając tę książkę. Po prostu ciężko przeskoczyć z myśleniem w niektórych kwestiach.</p>

<p>Mnie osobiście wkurwia moja bezsilność. Możesz robić, starać się, albo rozumieć, że coś nie działa tak, jak powinno, a pomimo to nie mieć możliwości
naprawienia.</p>

<p>Jednak jestem optymistą, może za mojego życia uda się naprawić większość z opisywanych patologii. W końcu patologię drogową zaczęliśmy ogarniać i mamy
efekty. Może uda się i na innych polach…</p>]]></content><author><name>Koziołek</name></author><category term="Ksiazka" /><category term="Recenzja" /><category term="ksiazka" /><category term="recenzja" /><summary type="html"><![CDATA[Title: Patopaństwo Author: Jan Śpiewak Year: 2025 ISBN: 978-83-838-7613-9]]></summary></entry><entry><title type="html">Alt+7 Aby umowa była ważna, musi mieć formę pisemną</title><link href="https://koziolekweb.pl/2025/01/30/alt-7-aby-umowa-byla-wazna-musi-miec-forme-pisemna" rel="alternate" type="text/html" title="Alt+7 Aby umowa była ważna, musi mieć formę pisemną" /><published>2025-01-30T06:37:00+00:00</published><updated>2025-01-30T06:37:00+00:00</updated><id>https://koziolekweb.pl/2025/01/30/alt7-aby-umowa-byla-wazna-musi-miec-forme-pisemna</id><content type="html" xml:base="https://koziolekweb.pl/2025/01/30/alt-7-aby-umowa-byla-wazna-musi-miec-forme-pisemna"><![CDATA[<p>I myk z zaskoczenia. Dziś o tym jak podpisać umowę… bo prawnicy to lubią komplikować życie :D</p>

<hr />

<p>Ostatnio skończyliśmy na tym, że majątkowe prawa autorskie to prawo do rozporządzania programem komputerowym na wszystkich polach eksploatacji oraz do
wynagrodzenia za korzystanie z utworu. Aby taka umowa była ważna, musi mieć formę pisemną - i tą formą pisemną zajmiemy się dzisiaj.</p>

<h2 id="co-to-w-ogóle-znaczy-że-umowa-musi-mieć-formę-pisemną">Co to w ogóle znaczy, że umowa musi mieć formę pisemną?</h2>

<p>Forma pisemna oznacza, że dokument musi mieć formę papierową i zostać <strong>podpisany odręcznie</strong>. Umowa może też zostać podpisana elektronicznie pod
warunkiem, że będzie to <strong>kwalifikowany podpis elektroniczny</strong> (ten z certyfikatem).</p>

<p>Wysłanie skanu podpisanej umowy nie spełnia warunku formy pisemnej - skan oczywiście można wysłać, ale ponadto należy wysłać również papierową wersję
dokumentu podpisanego odręcznie. Pamiętajcie przy tym o odpowiedniej ilości egzemplarzy (są wskazywane na końcu umowy), żebyście też mieli swoją umowę
z podpisami wszystkich stron (a nie tylko ze swoim).</p>

<p>Wszelkie zmiany umowy (aneksy) również muszą mieć formę pisemną.</p>

<h2 id="a-jak-nie-ma-formy-pisemnej-to-co">A jak nie ma formy pisemnej, to co?</h2>

<p>Zgodnie z art. 53 ustawy o prawie autorskim i prawach pokrewnych umowa o przeniesienie autorskich praw majątkowych wymaga zachowania formy pisemnej *
*pod rygorem nieważności<strong>. Jeżeli nie dopilnujecie formy pisemnej, albo uznacie, że jej nie potrzebujecie, bo przecież ufacie drugiej stronie, to
konsekwencje poczujecie, gdy dojdzie do jakiegoś sporu. Pamiętajcie, że **wszystkie umowy pisze się w czasie pokoju na wypadek wojny</strong> ;) Jeżeli
będziecie chcieli zmienić jakieś warunki umowy (np. wynagrodzenie), a druga strona nie będzie chciała Wam pójść na rękę, to wtedy może posłużyć się
argumentem, że umowa nie została zawarta w formie pisemnej, więc umowa jest nieważna (lub aneks jest nieważny, jeżeli to aneks nie został podpisany w
prawidłowej formie).</p>

<h2 id="jakie-są-konsekwencje-tego-że-umowa-jest-nieważna">Jakie są konsekwencje tego, że umowa jest nieważna?</h2>

<p>Jeżeli jedna ze stron wykonała swoje obowiązki wynikające z umowy, to może żądać zwrotu swojego świadczenia. Na przykład: jeżeli kontrahent zapłacił
Wam za przeniesienie na niego majątkowych praw autorskich do programu komputerowego, ale umowa okazała się nieważna, to może żądać zwrotu
wynagrodzenia, które zapłacił z tego tytułu. Wy natomiast możecie w takiej sytuacji żądać zapłaty za bezumowne korzystanie programu komputerowego.</p>

<p>Jeżeli zachowaliście formę pisemną dla umowy, a dla aneksu nie - to nieważny jest tylko aneks. Jeżeli nie zachowaliście formy pisemnej dla umowy, a
aneks zawarliście w formie pisemnej - to oba dokumenty są nieważne (nie może być ważnego aneksu do nieważnej umowy, bo jej po prostu nie ma).</p>

<p>r. pr. Joanna Kuczyńska</p>]]></content><author><name>Koziołek</name></author><category term="Programowanie" /><category term="Prawo" /><category term="Alt7" /><category term="programowanie" /><category term="prawo" /><category term="alt7" /><summary type="html"><![CDATA[I myk z zaskoczenia. Dziś o tym jak podpisać umowę… bo prawnicy to lubią komplikować życie :D]]></summary></entry><entry><title type="html">Sprawdź prawo jazdy – co, jak, dlaczego</title><link href="https://koziolekweb.pl/2025/01/07/sprawdz-prawo-jazdy-co-jak-dlaczego" rel="alternate" type="text/html" title="Sprawdź prawo jazdy – co, jak, dlaczego" /><published>2025-01-07T06:37:00+00:00</published><updated>2025-01-07T06:37:00+00:00</updated><id>https://koziolekweb.pl/2025/01/07/sprawdz-prawo-jazdy-co-jak-dlaczego</id><content type="html" xml:base="https://koziolekweb.pl/2025/01/07/sprawdz-prawo-jazdy-co-jak-dlaczego"><![CDATA[<p>No i się narobiło. Kilka dni temu doszło do tragedii. Pijany kurier zabił 14-latka. Kurier nie miał prawa jazdy (stracił
je), ale miał zakaz prowadzenia. I jest to coś o czym już od dłuższego czasu dyskutuje się w różnych mediach – zakazy
nie działają. Teraz doszedł jeszcze jeden wątek – w jaki sposób firmy mogą weryfikować uprawnienia u kierowców. I tu
zaczyna się robić naprawdę ciekawie.</p>

<p>Po pierwsze mamy taką usługę
jak <a href="https://moj.gov.pl/uslugi/engine/ng/index?xFormsAppName=UprawnieniaKierowcow">Uprawnienia Kierowców</a>, gdzie mając
imię, nazwisko i numer blankietu PJ/pozwolenia na kierowanie tramwajem, możemy sprawdzić czy dokument jest ważny.</p>

<p>Po drugie problemem nie jest sama usługa (o tym za chwilę), co brak sensownego API do tej usługi. W dodatku korpy chcą
mieć coś więcej, czytaj chcą, żeby państwo zrobiło (prawie)wszystko za nich i poniosło większość kosztów.</p>

<p>Po trzecie stan prawny jest „ciekawy”. W dodatku jego zmiana nie jest aż tak trywialna, jak wydaje się na pierwszy rzut
oka. Dotykamy tutaj kilku aspektów m.in. relacji pomiędzy firmą kurierską, a kurierem, czyli sposobu zatrudniania
kurierów, odpowiedzialności za poprawność i prawdziwość informacji, a to wszystko w kraju, w którym prezes UODO na
bezczelnego łamie prawo (o tym też będzie).</p>

<p>Zacznijmy jednak od tego czego mniej więcej oczekują firmy.</p>

<h2 id="o-takie-api-nic-nie-robiłem--prezes-na-kurierce">„O takie API nic nie robiłem” – prezes na kurierce</h2>

<p>Jak wspomniałem we wstępie w ramach mObywatela istnieje usługa pozwalająca na sprawdzenie statusu dokumentu – Prawa
Jazdy lub Pozwolenia na Kierowanie Tramwajem. Podajemy dane, klikamy „Sprawdź uprawnienia” i dostajemy informację co i
jak. Każda pani Kasia w dziale HR przy zatrudnianiu kierowcy może mając jego dane zweryfikować faktyczny stan uprawnień.
To teoretycznie wystarczy. Problem pojawia się gdy tych kierowców mamy dziesiątki, setki czy tysiące. Ręczne wklepywanie
danych będzie problemem. Zostawmy to jednak na chwilę i skupmy się na oczekiwaniach.</p>

<p>O oczekiwaniach mówił <a href="https://x.com/RBrzoska/status/1876336031932314091">Brzoska</a>, ale gdzieś po drodze wbił się
jeszcze (były)minister Cieszyński ze swoim <a href="https://x.com/jciesz/status/1876386609584554419">nie da się</a>. Jest jednak
jeszcze drugie dno tej całej sytuacji. Z różnych dyskusji na ten temat wnioskuję, że korposy, to by najchętniej chciały
jakieś PUSH-API, które automatycznie będzie powiadamiało ichnie HR-owe końcówki, że dany delikwent utracił uprawnienia.</p>

<h2 id="zmiana-niemożliwa-bo--minister-na-mobywatelu">„Zmiana niemożliwa, bo…” – minister na mObywatelu</h2>

<p>Może nie tyle niemożliwa, co wymagająca pewnej „inteligencji prawnej” ze strony ustawodawcy. A o to, jak powszechnie
wiadomo, może być ciężko. Tu wchodzi cały na biało nasz chory system prawny, który powoduje, że kierowców zatrudnia się
na JDG, często przez pośrednika. Tak działają kurierzy, taksówki, transport kołowy. NO dobra, ale w czym problem? Ano w
tym, że „zatrudniając” kogoś na B2B tak naprawdę kupujesz usługę. Jako konsumenta usługi nie powinno cię interesować,
jak to działa pod spodem. Na takiej samej zasadzie w spożywczaku nie pytasz każdej ekspedientki o książeczkę sanepidu,
czy nauczyciela w szkole o przygotowanie do wykonywania zawodu. Oczywiście firmy mogą trochę więcej i zawierając umowę
zazwyczaj proszą kontrahenta odpowiednie dokumenty potwierdzające umiejętności, uprawnienia, edukację.</p>

<aside class="offtopic f-right">
Tu przypomina mi się zdarzenie sprzed 16-17 lat, gdy nasz nadmenadżer latał po firmie pytając, kto już skończył studia,
bo musiał do przetargu przedstawić dokumenty potwierdzające, że ma odpowiednio wykształconą kadrę. A firma zatrudniała
prawie samych studentów na umowach o dzieło… piękne czasy…
</aside>

<p>W przypadku umów b2b, trzeba by na dłuższą metę ogarnąć nie tylko samo dostarczanie danych, ale też zgodność z
przepisami o ochronie danych osobowych. I problem ten poruszyła, co prawda pośrednio, posłanka Anna Maria
Żukowska <a href="https://x.com/AM_Zukowska/status/1876637631016255733">tutaj</a>. Niestety jest prawniczką, co oznacza, że potrafi
identyfikować problemy, ale nie potrafi syntezować posiadanej wiedzy w celu wytworzenia odpowiedniego rozwiązania (nie
polegającego na obejściu przepisów). Czysto teoretycznie wystarczy ukazać uzasadniony interes prawny, żeby móc swobodnie
przetwarzać informacje o statusie PJ.</p>

<p>I tu na białym koniu wjeżdża płodo z uodo, czyli prezes nad UODO. Urzędnik ten przypomina mi dolnośląskiego konserwatora
zabytków. Zrobi wszystko, żeby nie dopuścić do przebudowy i unowocześnienia ulicy/usługi w imię źle pojętego interesu.
Pamiętajmy, że ten orzeł zakazał geoportalowi publikacji numerów ksiąg wieczystych (i
przywalił <a href="https://niebezpiecznik.pl/post/numery-ksiag-wieczystych-w-geoportalu-kara-100-tys-zl-dla-ggk-ale-to-nie-koniec/">100 koła kary</a>),
co jest w jawnej sprzeczności z ustawą o księgach wieczystych. Oczywiście nie przeszkadzają mu firemki z Seszeli, które
za te numery biorą hajs.</p>

<p>Podsumowując, zmiana prawa będzie cholernie ciężka. Szczególnie, że będą robić to prawnicy.</p>

<h2 id="co-się-nie-da-jak-się-da--ja">„Co się nie, da jak się da” – Ja</h2>

<p>Bycie hakerem, to nie tylko uruchamianie pociągów, ale przede wszystkim nieustanna ciekawość. Chcesz wiedzieć jak coś
działa czy jak rozwiązać dany problem. I coś mnie podkusiło, żeby zobaczyć jak działa ta usługa. Jakie komunikaty tam
latają, co dzieje się, gdy naciskam przycisk.</p>

<p>W efekcie spłodziłem <a href="https://github.com/Koziolek/sprawdz-pj">takie maleństwo</a>. Bierzcie i forkujcie z tego wszyscy,
albowiem na licencji MIT jest to wam dane.</p>

<p>Następnie napisałem <a href="https://x.com/koziolek/status/1876587074754617702">tweeta</a> z linkiem do „mięska” i mini-bluzgiem na
lenistwo korporacji. Dyskusja jaka się wywiązała jest ciekawa i wyłania się z niej kilka aspektów.</p>

<h3 id="co-na-to-uodo">Co na to UODO?</h3>

<p>IMO, nic, bo usługa działa jest wystawiona publicznie. Mój skrypt pomija webową część i uderza bezpośrednio do serwisu.
Dzięki temu można go nakarmić plikiem z danymi i pobrać odpowiedź.</p>

<h3 id="czy-odpowiadam-za-błędy">Czy odpowiadam za błędy?</h3>

<p>Nie. W dodatku ludzie nie mają pojęcia, że większość softu z jakiego korzystają ma w licencji wyłączenie
odpowiedzialności.</p>

<h3 id="przecież-można-ręcznie">Przecież można ręcznie…</h3>

<p>To <a href="https://x.com/jawne_poufne/status/1876637902970695685">napisał</a> prawnik… po
czym <a href="https://x.com/jawne_poufne/status/1876646241758826717">poprawił</a> i trochę racji ma, bo Kodeks Pracy dotyczy UoP,
ale nie do końca, bo przepisy BHP dotyczą wszystkich niezależnie od formy zatrudnienia. Zatem wydanie pojazdu kierowcy
bez uprawnień jest chyba lekko „nie teges”.</p>

<p>Ale było też kilka sensowniejszych głosów.</p>

<h3 id="pośrednicy-i-model-zatrudnienia-w-pl">Pośrednicy i model zatrudnienia w PL</h3>

<p>To jest trochę szerszy problem. Choć @IntelijG <a href="https://x.com/IntellijG/status/1876635952887832595">drąży</a> w odpowiednim
kierunku. Mając całe stadko pośredników trzeba zadbać o odpowiednie przekazywanie informacji. Co ciekawe, skoro od
pewnego czasu kierowcy spoza UE muszą mieć polskie PJ, to odpada problem zagranicznych dokumentów.</p>

<h2 id="podsumowanko">Podsumowanko</h2>

<p>Skrypt będzie rozwijany i mam nadzieję, że nie skończy się wojną na podobieństwo tej, którą
prowadzi <a href="https://informatykzakladowy.pl/">Informatyk Zakładowy</a> z Min. Sprawiedliwości w sprawie losowarki sędziów.
Chcę w ramach zabawy z Rustem dopisać kilka wersji „gotowców” w różnych językach. Takie Hello World, ale nic na siłę. I
to chyba tyle :)</p>]]></content><author><name>Koziołek</name></author><category term="Programowanie" /><category term="prawo" /><category term="Programowanie" /><category term="prawo" /><summary type="html"><![CDATA[No i się narobiło. Kilka dni temu doszło do tragedii. Pijany kurier zabił 14-latka. Kurier nie miał prawa jazdy (stracił je), ale miał zakaz prowadzenia. I jest to coś o czym już od dłuższego czasu dyskutuje się w różnych mediach – zakazy nie działają. Teraz doszedł jeszcze jeden wątek – w jaki sposób firmy mogą weryfikować uprawnienia u kierowców. I tu zaczyna się robić naprawdę ciekawie.]]></summary></entry><entry><title type="html">Kod bezpieczny na dwa sposoby</title><link href="https://koziolekweb.pl/2024/12/31/kod-bezpieczny-na-dwa-sposoby" rel="alternate" type="text/html" title="Kod bezpieczny na dwa sposoby" /><published>2024-12-31T06:37:00+00:00</published><updated>2024-12-31T06:37:00+00:00</updated><id>https://koziolekweb.pl/2024/12/31/kod-bezpieczny-na-dwa-sposoby</id><content type="html" xml:base="https://koziolekweb.pl/2024/12/31/kod-bezpieczny-na-dwa-sposoby"><![CDATA[<p>Kończmy rok zatem czas na szybką wrzutkę. Dziś będzie o bezpieczeństwie kodu. Ale nie pokażę wam żadnych przykładów.
Będzie trochę filozofii i tego jak patrzeć na pojęcie „bezpieczny kod”.</p>

<h2 id="dwa-rodzaje-bezpieczeństwa">Dwa rodzaje bezpieczeństwa</h2>

<p>W języku polskim ciężko wyrazić jednym słowem różnicę pomiędzy angielskim <em>safe</em> a <em>secure</em>. Oba te słowa tłumaczy się
jako „bezpieczny”, co powoduje pewne zamieszanie. Spróbuję je trochę wyprostować.</p>

<h2 id="safe-czyli-bezpieczeństwo-i-higiena-pracy"><em>safe</em>, czyli bezpieczeństwo i higiena pracy</h2>

<p>Słówko <em>safe</em> można rozumieć jako bezpieczne zachowania. Postępowanie w sposób bezpieczny, nie narażając siebie i
innych. W przypadku wytwarzania oprogramowania będzie to odpowiedni dobór narzędzi i praktyk. Wśród nich znajdą się
takie, jak testy, czy code review. Jeżeli mamy testy, to możemy powiedzieć, że sprawdzamy nasz kod pod względem
działania. Raczej nie powinniśmy zostać zaskoczeni przez oczywiste błędy. Podobnie CR pozwala na wyłapanie różnych
baboli. Jednak to nie tylko te dwa zachowania. Na tego rodzaju bezpieczeństwo aplikacji wpływają różne czynniki. Jeżeli
mamy dokumentację dla użytkownika końcowego, to duża szansa, że nie zepsuje nam aplikacji. Wykorzystanie CI/CD pozwala
na dbanie o kod. Tak samo jak wzorce projektowe, które ułatwiają zrozumienie aplikacji.</p>

<p>Czasami są to duże rzeczy jak na przykład odpowiedni projekt architektury. Czasami są to małe rzeczy, jak użycie
odpowiedniego <a href="/2011/11/06/ekstremalna-obiektowosc-w-praktyce-–-czesc-3-opakowuj-wszystkie-prymitywy-i-stringi/">typu danych</a>.
Wszystko to na co mamy wpływ tworząc i zarządzając aplikacją można wpisać w ten kontekst.</p>

<p>W tym kontekście działa policja. Chroni obywateli wewnątrz kraju.</p>

<h2 id="secure-czyli-bronić-i-chronić"><em>secure</em>, czyli bronić i chronić</h2>

<p>Inny aspekt bezpieczeństwa reprezentuje słówko <em>secure</em>. Oznacza ono bezpieczeństwo wobec czynników zewnętrznych. Ataki
hakerskie, ale też niestabilność zasilania w serwerowni, bo bezpieczeństwo, to nie tylko software. W świecie chmur
trochę na dalszy plan zeszły różne problemy związane z zabezpieczeniem przed czynnikami zewnętrznymi. Cloudflare chroni
nas przed DDoSami, aplikacje umieszczamy na serwerach wirtualnych, które zarządzane są przez odpowiednie oprogramowanie.
W dodatku komunikujemy się z API, a nie z fizyczną maszyną. I to wszystko jest obłożone backupami, które robi dostawca
rozwiązania. W teorii wystarczy zatem odpowiednio przechowywać hasła, łączyć się po HTTPS i wykonywać regularnie kopie
bezpieczeństwa.</p>

<p>A przecież czynniki zewnętrzne bywają
różne. <a href="https://niebezpiecznik.pl/post/splonela-serwerownia-ovh/">Przeniesienie się OVH do chmury w 2021</a>, czy
przypadkowe <a href="https://about.gitlab.com/blog/2017/02/01/gitlab-dot-com-database-incident/">skasowanie gitlaba przez gitlaba w 2017</a>,
to tylko dwa przykłady gdy coś idzie nie tak. I na takie przypadki też musimy być gotowi. Tylko problem polega na tym,
że nie przygotujemy się na wszystko. Po prostu jest to niemożliwe.</p>

<p>W tym kontekście działa wojsko. Chroni obywateli przed zagrożeniem z zewnątrz.</p>

<h2 id="a-jak-zaatakują-zombie">A jak zaatakują zombie?</h2>

<p>Atak zombie, czyli jeden z najbardziej przeruchany przez kino, gry i literaturę motywów niespodziewanego,
niebezpiecznego i nieprzewidywalnego zdarzenia… i jednocześnie podstawowe pytanie, gdy tworzysz modele bezpieczeństwa.
Zarówno w kontekście <em>safe</em> jak i <em>secure</em>. Nie przewidzisz wszystkiego. Nie jesteś w stanie zabezpieczyć się na każdą
ewentualność. Jednak warto na bieżąco dbać o oba rodzaje bezpieczeństwa, żeby potencjalny problem nie wyrządził nam aż
takiej krzywdy.</p>]]></content><author><name>Koziołek</name></author><category term="Programowanie" /><category term="Programowanie" /><summary type="html"><![CDATA[Kończmy rok zatem czas na szybką wrzutkę. Dziś będzie o bezpieczeństwie kodu. Ale nie pokażę wam żadnych przykładów. Będzie trochę filozofii i tego jak patrzeć na pojęcie „bezpieczny kod”.]]></summary></entry><entry><title type="html">Alt+7 Co to znaczy, że program komputerowy jest chroniony prawem?</title><link href="https://koziolekweb.pl/2024/12/12/alt7-co-to-znaczy-ze-program-komputerowy-jest-chroniony-prawem" rel="alternate" type="text/html" title="Alt+7 Co to znaczy, że program komputerowy jest chroniony prawem?" /><published>2024-12-12T06:37:00+00:00</published><updated>2024-12-12T06:37:00+00:00</updated><id>https://koziolekweb.pl/2024/12/12/alt7-co-to-znaczy-ze-program-komputerowy-jest-chroniony-prawem</id><content type="html" xml:base="https://koziolekweb.pl/2024/12/12/alt7-co-to-znaczy-ze-program-komputerowy-jest-chroniony-prawem"><![CDATA[<p>Po niezamierzonej przerwie w październiku i powtórce w listopadzie, wracamy z tematami prawnymi. Dziś kilka słów o
prawie autorskim.</p>

<h2 id="co-to-znaczy-że-program-komputerowy-jest-chroniony-prawem">Co to znaczy, że program komputerowy jest chroniony prawem?</h2>

<p>Wierzcie lub nie, ale programy komputerowe są szczególnie chronione prawem autorskim i to już od 30 lat! A w prawie Unii
Europejskiej jeszcze dłużej, bo nasza polska ustawa o prawie autorskim i prawach pokrewnych z 1994 r. dokonuje wdrożenia
dyrektywy 91/250/WE z 1991 r. w sprawie ochrony prawnej programów komputerowych. W dodatku programy komputerowe zostały
wymienione w ustawie już na samym początku i otwierają katalog utworów będących przedmiotem prawa autorskiego.</p>

<p>Ale co to w ogóle znaczy, że program komputerowy jest chroniony prawem?</p>

<p>Ochrona przyznana programowi komputerowemu obejmuje wszystkie formy jego wyrażenia (kod źródłowy i wynikowy, a inne
elementy tylko wtedy, gdy na ich podstawie jest możliwe napisanie programu). Natomiast idee i zasady będące podstawą
jakiegokolwiek elementu programu komputerowego nie podlegają ochronie.</p>

<p>Prawo chroni prawa twórcy (osoby, która napisała oprogramowanie) oraz osób, którym twórca sprzedał prawa do programu lub
udzielił im licencji. W przypadku licencji prawa autorskie nadal należą do twórcy, który zgadza się, żeby ktoś inny
odpłatnie lub nieodpłatnie korzystał z jego programu komputerowego.
Jakie są prawa do programu komputerowego?</p>

<p>Twórcy przysługują dwa rodzaje praw: osobiste i majątkowe.</p>

<p>Osobiste prawa autorskie są niezbywalne i na stałe łączą twórcę z jego programem. Są to: prawo do autorstwa utworu oraz
do oznaczenia utworu swoim nazwiskiem lub pseudonimem albo do udostępniania go anonimowo. Możecie spotkać takie zapisy w
umowie, gdzie jako twórca zezwalacie na udostępnienie programu bez podania swojego nazwiska. Dopóki twórca nie ujawnił
swojego autorstwa, w wykonywaniu prawa autorskiego zastępuje go producent.</p>

<p>Z kolei majątkowe prawa autorskie to prawo do rozporządzania programem na wszystkich polach eksploatacji oraz do
wynagrodzenia za korzystanie z utworu. Pamiętajcie, że aby umowa była ważna, musi mieć formę pisemną - ale to już
dłuższa sprawa, więc opowiem Wam o tym innym razem.</p>

<p>r. pr. Joanna Kuczyńska</p>]]></content><author><name>Koziołek</name></author><category term="Programowanie" /><category term="Prawo" /><category term="Alt7" /><category term="programowanie" /><category term="prawo" /><category term="alt7" /><summary type="html"><![CDATA[Po niezamierzonej przerwie w październiku i powtórce w listopadzie, wracamy z tematami prawnymi. Dziś kilka słów o prawie autorskim.]]></summary></entry></feed>