/ Inżynier wydajności
← Wszystkie wpisy
lip 9, 2026 · 3 min czytania · Wydajność buildów

Powolne shaded i fat JAR-y

Po parallel-zip mój kolejny cel: powolne budowanie fat i shaded JAR-ów. Dlaczego ta nisza jest warta lat moich wieczorów, czy ktokolwiek faktycznie tego użyje i pierwsze spojrzenie na to, co plugin Shadow zostawia na stole.

Smutny procesor patrzący na plik ZIP

Po stworzeniu parallel-zip-gradle-plugin, który dramatycznie przyspiesza standardowe zadanie ‘zip’ w Gradle, zastanawiałem się, jaki mógłby być mój kolejny projekt. Najpierw musiałem zadać sobie pytanie, dlaczego w ogóle chcę pracować nad takimi projektami. Nie jest tak, że potrzebuję dodatkowej gotówki. Nie jest też tak, że nie mam co robić po całym dniu w biurze - wręcz przeciwnie, hobby, ćwiczenia, zakupy, sprzątanie, właściwe odżywianie i suplementacja już teraz walczą o ‘sekcję krytyczną’ między 18:00 a 22:00. Żeby kolejny projekt miał sens, musi być w jakiś sposób znaczący. Musi dawać mi trochę satysfakcji, gdy myślę o nim, zasypiając.

Z długiej listy pomysłów w backlogu ostatecznie zdecydowałem się zmierzyć z problemem powolnego budowania fat i shaded JAR-ów.

Dlaczego?

Rozumowanie przebiegało mniej więcej tak:

Niezliczone firmy na całym świecie budują swoje oprogramowanie w Javie. Większość z nich buduje JAR-y wiele razy dziennie. Tylko w firmie, w której pracuję, mamy około ~100 buildów dziennie na GitHub CI. Dodaj do tego kolejne 100 buildów lokalnych. A to tylko jedno repozytorium. Pomnóż to przez liczbę repozytoriów, a łatwo otrzymasz liczbę rzędu dziesiątek tysięcy buildów dziennie. Każdy z nich kończy się zbudowaniem nie jednego, ale często nawet czterech shaded JAR-ów. Pomnóż to przez liczbę programistów Javy albo firm na świecie… rozumiesz, o co chodzi.

Każdy z tych shaded JAR-ów często znajduje się na ścieżce krytycznej w pętli sprzężenia zwrotnego Claude’a lub (jeśli nadal masz ludzi wykonujących jakąkolwiek ‘prawdziwą’ pracę) programisty. Każdy z tych JAR-ów jest wgrywany i pobierany z/do cache’u na CI. Każdy z tych shaded JAR-ów zużywa cykle procesora, żeby go zbudować. Zajmuje miejsce na jakimś prawdziwym, fizycznym SSD (nieważne, jak abstrakcyjne stały się dzięki usługom chmurowym - ostatecznie i tak lądują na jakimś SSD).

Wszystko to wpływa na zużycie przepustowości, dysku i procesora na całym świecie. Ma to nie tylko wpływ na środowisko, ale - co prawdopodobnie ważniejsze dla menedżerów i prezesów - wpływ na koszty. Oczywiście wpływa też na tempo, w jakim zespół może dostarczać funkcje albo naprawiać błędy. Jakakolwiek poprawa w tym obszarze, choćby najmniejsza, miałaby niesamowity wpływ na świat (nieważne, jak ‘niewidzialny’ mógłby się wydawać - matematyka nie kłamie).

Czy ktokolwiek tego użyje?

Oczywiście trudno powiedzieć z pewnością. Zwłaszcza znając moje podejście i pogląd na marketing - nienawidzę autopromocyjnych postów na LinkedInie, Reddicie czy YouTubie. Naprawdę chciałbym zobaczyć wyidealizowany świat, w którym cała praca jest oceniana według jej wartości, a nie tego, jak bardzo jest ‘widoczna’ (to właśnie liczy się najbardziej w biurze, smutna prawda).

Niemniej jednak jakiś rodzaj popytu może istnieć. Jakiś czas temu (no dobra, dawno temu) HubSpot napisał post (https://product.hubspot.com/blog/the-fault-in-our-jars-why-we-stopped-building-fat-jars) o tym, jak ich irytacja fat JAR-ami wysłała ich w podróż w poszukiwaniu innych rozwiązań. Można też znaleźć kilka rozmów na Reddicie na ten temat (np. https://www.reddit.com/r/java/comments/1945ba1/the_evils_of_the_fatjar/), a myślenie stadne wydaje się skłaniać ku trzymaniu się starych, dobrych fat JAR-ów zamiast rozwiązań, które z nich rezygnują i dostarczają zależności w inny sposób.

Ogólnie… spodziewałem się znaleźć znacznie więcej na ten temat, ale wygląda na to, że niewielu programistów Javy przejmuje się wydajnością (co pokrywa się z moim doświadczeniem w korporacyjnym świecie z ostatnich dwóch dekad).

Niemniej jednak jestem ciekaw, jak złe są nasze obecne domyślne rozwiązania do fat i shaded JAR-ów oraz co można z tym zrobić.

Krajobraz

Zanim odpowiemy na to pytanie, krótkie przypomnienie, z czym mamy do czynienia:

  • Fat JAR to po prostu plik zip ze wszystkimi skompilowanymi plikami .class oraz wszystkimi zależnościami
  • Shaded JAR to to samo co fat JAR z jedną różnicą - klasy są czasami zmieniane, żeby uniknąć konfliktów

Fajną rzeczą w nich jest to, że można je bardzo łatwo uruchomić za pomocą java -jar my-fat.jar. No dobrze - jak większość ludzi buduje te JAR-y? Jeśli nie używasz Spring Boota, to szanse są takie, że używasz porzuconego com.github.johnrengelman.shadow albo jego przegrupowanej wersji com.gradleup.shadow. Są bardzo niezawodne i całkiem nieźle utrzymywane, jednak wciąż wydają się wolne przy większych projektach. To z nimi będziemy konkurować.

Co można zoptymalizować?

To właśnie odkryjemy. Głównym celem pluginu Shadow nie była ekstremalna wydajność, lecz raczej niezawodność. Dlatego, jak w wielu innych projektach, szybkość wykonania nie była na pierwszym planie przy każdym commicie czy merge’owanym pull requeście. Jedną rzeczą, którą z pewnością można by zaprojektować lepiej, jest jego wsparcie (a raczej jego brak) dla zrównoleglenia. Ale to łatwy cel - można by argumentować, że nie fair jest się do tego przyczepiać.

Szczera odpowiedź brzmi - jeszcze nie wiem. Na podstawie mojego doświadczenia mam silne przekonanie, że jest to dalekie od bycia zoptymalizowanym. Przekonajmy się.

ŁJ
Łukasz Jarocki
Inżynier wydajności