Sytuacja kobiet w IT w 2024 roku
4.02.20196 min
Maciej Radziński
IDEMIA

Maciej RadzińskiSenior Developer, Team Leader, ArchitectIDEMIA

Pożegnanie z Javą 8?

Sprawdź, dlaczego powinienieś wypróbować nowe wersje Javy i jak się za to zabrać.

Pożegnanie z Javą 8?

Ludzie z natury boją się zmian. Często rezygnują z planów czy marzeń, znajdując wymówki, choć jedynym, co stoi im na drodze, jest strach przed nieznanym. „Stare, ale przecież działa, po co to ruszać”. Takie słowa słychać zarówno w domu, jak i w projekcie. Java 9 i jej moduły są z nami ponad półtora roku, a jednak większość moich znajomych nadal pisze projekty w Javie 8. Dlaczego? Czyżby bali się zmiany?

Nie ma powodu! Dobrze natomiast jest wiedzieć, czego się spodziewać, planując migrację do nowej wersji. Jak się za to zabrać i co mieć na uwadze, gdy do tego siadamy?

Po co w ogóle to robimy?

Z natury jestem przeciwny upgrade’om „na hurra”. Zawsze jestem wersję czy dwie do tyłu. I nie dlatego, że jestem leniwy czy zapracowany, ale raczej nieufny. Jak się często okazuje - słusznie. Ale nowa Java już dojrzała i dorobiła się łatek na krytyczne błędy. Być może to już moment, w którym jedynym problemem stojącym na drodze do upgrade’u jesteśmy my sami?

Nie będę pisał kolejnego artykułu z serii „po co podbijać wersję Javy?” -  Google ma na to pytanie dziesiątki milionów odpowiedzi. Jest jednak kilka ważnych powodów, które powinienem wymienić:

Po pierwsze

Możecie korzystać z nowych fajnych feature’ów Javy, jak na przykład module-path (pytanie, czy chcecie?), który eliminuje niechciany i nieoczekiwany class shadowing, czy nowej składni, dającej – na przykład – więcej możliwych operacji na streamach.

Po drugie

Będziecie używać wersji, która dłużej będzie miała support  (zakładając, że Wasza aktualnie używana wersja w ogóle jeszcze go ma).

Po trzecie

Będziecie mieli łatwiejsze update’y do kolejnych wersji wydawanych w nowym, co 6-miesięcznym trybie.

Po czwarte

I ten punkt jest szczególnie ważny dla PMów, PO, leaderów i HRu – Wasz projekt będzie atrakcyjniejszy dla programistów. Do takiego projektu łatwiej z rekrutować nowe osoby.

I przede wszystkim

Nie chcecie odkładać tego w czasie! Większość z Was i tak będzie musiała to kiedyś zrobić (chociażby ze względu na support), a im wcześniej to zrobicie, tym będzie łatwiej.

Jak się do tego zabraliśmy?

Chyba większość z nas woli praktykę od teorii. Dlatego najlepszy sposób, aby zacząć migrację to po prostu podbić wersję Javy (w Mavenie, Gradle’u itp.), zmienić JAVA_HOME i zbudować.

O dziwo, build bez testów i budowania obrazu dockerowego przeszedł u nas śpiewająco. Niestety, przy uruchomieniu nie było już tak kolorowo. Po zgooglaniu błędu okazało się, iż serwer, którego używaliśmy, nie wspierał Javy 9+. A jeśli błąd jest w Jirze od ponad roku, to raczej szybko stamtąd nie zniknie.  

To była pierwsza lekcja, jaką dała nam ta migracja: Niektóre kawałki kodu będzie trzeba przepisać. Wiele bibliotek, od których zależymy, po prostu się zbuntowało i nawet nie pracuje nad dostosowaniem się do nowej Javy. Dobrze jest je szybko zidentyfikować. Najlepiej, jeśli zrobimy to, zanim jeszcze zaczniemy ich używać.

Kubeł zimnej wody, wracamy do początku

Tym razem chcieliśmy poznać jak najwięcej problemów - żadnego skipowania, puszczamy wszystko. Mvn clean install i… wywaliliśmy się już na pierwszym module...

Okazało się, że biblioteki, z których korzystaliśmy w testach, wymagają zmian (podobnie jak wcześniej serwer) lub w najlepszym razie update’ów do nowszych wersji.

Po podbiciu i drobnych zmianach (w niektórych bibliotekach wraz z nową wersją odbyło się przepakietowanie, gdzie indziej biblioteka została „depraceted” na rzecz nowej i należało ją podmienić) aplikacja zbudowała się, a testy poszły pomyślnie.

To była lekcja numer dwa: Dbajmy o to, aby podbijać zależności. Ktoś mógłby powiedzieć „po co?”, ja zapytam „dlaczego nie?”. Nowe zależności to często łatki bezpieczeństwa, czasem nowe, fajne funkcjonalności a czasem po prostu możliwość łatwego podbicia czegoś innego, gdy przyjdzie nam to zrobić. Wiem, że z nową wersją można wprowadzić nowy błąd, ale od tego przecież mamy testy.

JDK Incubator

Skoro się kompiluje i testy przechodzą, to chyba skończyliśmy? Niestety, nie do końca. W naszym przypadku dopiero integracja z prawdziwymi systemami po drugiej stronie ukazała kolejną słabość - komunikację po HTTP/2. Wcześniej dostarczone mocki działały w testach, gdyż miały fallback do HTTP 1.1, jednak prawdziwa platforma, z którą się integrowaliśmy - nie.

Z pomocą przyszedł nam klient HTTP, który od wersji 11 jest w standardowym API Javy. W Javie 10 był w tak zwanym incubatorze.

JDK incubator to nowy sposób, podobno „gamechanging”, w jaki wprowadzane są niefinalne API do Javy. Idea jest taka, że moduły z inkubatora przy następnym releasie są zatwierdzane i są pełnoprawnymi modułami Javy, bądź też są z niej usuwane, jeśli nie zostały zatwierdzone. Opieranie się na nich jest więc ryzykowne, gdyż po kolejnym updacie mogą zniknąć.

Jigsaw i Jlink

W tym momencie można śmiało powiedzieć, że zmigrowaliśmy nasz projekt do nowszej Javy, jednak my chcieliśmy więcej: chcieliśmy wprowadzić moduły Javy do naszej aplikacji, tak aby używać ich zgodnie ze sztuką. Ta historia jednak też nie była tak kolorowa.

Musiałbym napisać książkę, żeby opisać całości naszych przejść, jakie mieliśmy z modularyzacją, dlatego przejdę przez ten temat w telegraficznym skrócie.

Na początku próbowaliśmy się pozbyć classpatha i zastąpić go w całości module-pathem.  Dodaliśmy module-info do każdego z modułów, jednak nadal nie rozwiązywało to problemów z zależnościami zewnętrznymi. Choć Java daje nam mechanizm „automatycznych modułów”, jego działanie powoduje, że na module-pathie często pojawiają się konflikty. Używanie zarówno module-patha, jak i classpatha, nie dało nam z kolei korzyści, na które liczyliśmy.  

Co więcej, gdy próbowaliśmy zminimalizować wielkość obrazu dockerowego, używając w tym celu multi-stage dockerfile’a i JLinka, okazało się, iż JLink nie może wspierać modułów automatycznych tak, jak robi to module-path. Po pierwszych próbach patchowania zależności zobaczyliśmy, jak absurdalne jest to zadanie i odpuściliśmy.

Wtedy przyszedł czas na trzecią lekcję, na którą i tak byliśmy gotowi: Keep It Simple, Stupid. Jeśli migrujemy się do nowej Javy, dobrze ograniczyć się do samej migracji, zwłaszcza na początku. Jeśli później będziemy chcieli walczyć z modułami - ok. Ale podstawą jest po prostu kompilacja i uruchamianie na nowym JVM jako nowa Java, niekoniecznie ze wszystkimi jej feature’ami.

Zawsze najnowsza wersja?

W naszym projekcie podbiliśmy się do Javy 10 i dalej jej używamy. Pewnie teraz pomyślicie: gość mówi, aby się upgrade’ować, po czym spoczęli na starej wersji? WTF?.  

Javę 11 też przez chwilę mieliśmy.

Przejście z Javy 10 do Javy 11 jest banalne - zajęło mi ok. 15 minut i to tylko z powodu incubatora. Zmiany miałem przez to w kodzie (pakiety), zależnościach (nazwa) oraz dockerfile’ach (moduły z inkubatora trzeba ręcznie wskazywać). Wycofaliśmy się jednak z tej zmiany, gdyż pierwszy release Javy 11 miał problemy z pewną funkcjonalnością związaną z TLS 1.2, której akurat używaliśmy. Na dniach zapowiedziana jest poprawka, i zapewne odkopiemy brancha z Javą 11 i sprawdzimy, czy na nowym buildzie Javy zadziała. Podejrzewam, że tak i na dobre przejdziemy na Javę 11.

Ważne jest, żeby spróbować. Jednak, jak się okazuje, mój wrodzony sceptycyzm tym razem nie był bezpodstawny – najnowsza wersja mnie zawiodła. Ale tak nie musi być w Waszych projektach.

Podsumowanie

Java 9, 10 czy 11, a moduły, to dwa różne tematy. Te mają nadal wielu krytyków, a nawet wśród ludzi nastawionych neutralnie, często słyszę: Teraz jest za późno, już się nie przyjmie. To trzeba było zrobić 15 lat temu. I chyba muszę im przyznać rację.

Ważne jest natomiast, aby nie wylewać dziecka z kąpielą. Nowe wersje Javy dają dużo nowych funkcjonalności, API, no i mają dłuższy support. Podbicie „w wersji minimum” jest naprawdę szybkie i proste, a korzyści spore, dlatego zdecydowanie polecam zrobić to jak najszybciej.

<p>Loading...</p>