Ostatnio działo się całkiem sporo i nie byłem pewien, w jakiej kolejności powinienem się zająć tymi tematami, więc postanowiłem opisać wszystko w jednym wpisie. jeżeli coś wymaga wyjaśnienia lub dodatkowych szczegółów, daj mi znać na priv, a postaram się to jakoś ogarnąć (może w kolejnym wpisie).
Przebudowa Structured Concurrency w Javie 25
Zacznijmy od Javy 25, która jest już na horyzoncie. Dla wielu dostawców (jeśli nie wszystkich) będzie to wydanie LTS. A Structured Concurrency to świetny przykład tego, dlaczego potrzebujemy preview features. Można by powiedzieć, iż trzy wydania wystarczą do testowania i funkcjonalność powinna zostać już ustandaryzowana. Jednak ludzie z OpenJDK zdecydowali inaczej i bardzo się z tego cieszę. Myślę, iż odpuszczę sobie zanudzanie was wszystkimi szczegółami na temat tego, jak Structured Concurrency będzie wyglądać w Javie 25 w każdym najdrobniejszym detalu. Moim zdaniem, przeczytanie JEP 505 i obejrzenie filmu Nicolaia może tutaj bardzo pomóc, oprócz moich własnych prezentacji, np. Butcher virtual threads like a pr0.
Moim zdaniem super jest to, iż throwIfFailed jest teraz domyślne, więc nie trzeba go już dodawać, bo za każdym razem, gdy testowałem Structured Concurrency, rzucanie wyjątków okazywało się przydatne. Następnie, zapieczętowanie StructuredTaskScope i tworzenie nowych zakresów dzięki open(), gdzie można dostarczyć dodatkową konfigurację, jest po prostu świetne z kilku powodów:
- Nie chodzi o to, iż dziedziczenie zawsze jest złe - myślę, iż widziałem wiele przypadków, w których faktycznie miało sens i było najlepszą opcją (więc mój argument z CONTEXTVS STVLTE, iż nie zawsze mądre jest zastępowanie dziedziczenia kompozycją, przez cały czas jest aktualny). Jednak dostarczanie konfiguracji w formie podklasy nie wydaje mi się dobrym pomysłem. Kiedy pisałem własną implementację StructuredTaskScope (gdzie sukces był rozumiany inaczej niż domyślnie, zawsze wyglądało to jakoś tak dziwnie. Poza tym, jeżeli ewolucja Stream Collectors i Gatherers czegoś nas nauczyła, to tego, iż do konfiguracji prawdopodobnie lepiej jest używać kompozycji.
- Dla mnie try-with-resources - które ma automatycznie zamykać zasoby - jakoś lepiej działa z open. Nie wiem, może jestem stronniczy, ale po prostu… wygląda lepiej niż konstruktor.
- Uszczelnienie StructuredTaskScope wydaje się być dokładnie tym, do czego Sealed Classes (and Interfaces) w Javie 17 miały służyć: ułatwianie życia osobom utrzymującym kod i zapewnienie, iż liczba “przydatnych niestandardowych wynalazków” pozostanie ograniczona.
- Możliwość (ponownego) wykorzystania niestandardowej puli wątków (bo wątki wirtualne mogą nie być optymalnym wyborem dla niektórych rodzajów zadań) lub konfigurowania innych aspektów jest również przydatne.
- I na koniec (co nie jest najmniej ważne), dowodzi to również użyteczności koncepcji “release train” i funkcji preview: preview features mogą być w porządku do wypróbowania w produkcji, o ile są używane w mikroserwisie1 i uruchamiasz je w środowisku, które w pełni kontrolujesz - ale nie jeżeli nie możesz kontrolować, której wersji JDK ludzie używają do uruchamiania twoich artefaktów.
Więc ogólnie rzecz biorąc, w Javie 25 blok ze Structured Concurrency - gdzie pojedyncza metoda jest odpowiedzialna w swoim zakresie za obsługę współbieżnych podzadań w sposób, który jest czytelny i zrozumiały - może wyglądać tak:
Wzorzec używany do konfiguracji pojawił się niezależnie w innej części ekosystemu Javy. Co prowadzi nas do nowego klienta Elasticsearch dla Jaby ;-)
Elasticsearch 9 z perspektywy Javy
Po tym, jak zacząłem pracować dla Elastica, konstrukcja klienta Java była, bądźmy szczerzy, jedną z moich zmor. Nie chodzi o to, iż nie nie dało się klienta utworzyć - był to w pełni funkcjonalny, elastyczny kod. W wielu frameworkach choćby nie musisz konstruować klienta manualnie, ponieważ framework zrobiłby to za ciebie na podstawie zmiennych środowiskowych lub podobnej konfiguracji.
Rzecz w tym, iż czasami twój mikroserwis jest napisany poprawnie i naprawdę jest mikro, a nie obciążony tonami zależności. W takich przypadkach kończysz z wieloma obowiązkowymi liniami kodu tylko po to, aby utworzyć klienta ES w czystej Javie, przekazując w rzeczystości tylko dwie rzeczy: URL i klucz API. Niezbyt optymalny stosunek sygnału do szumu, jakby mnie kto pytał.
Na szczęście ludzie, z którymi pracuję, są nie tylko mądrzy, ale także słuchają opinii developer advocatów i wprowadzają poprawki na podstawie otrzymanych informacji zwrotnych. Dlatego Elasticsearch w wersji 9.0 został wyposażony nie tylko w wiele świetnych funkcji wewnętrznych, ale także w nową wersję klienta, która pozwala nam napisać to:
Jestem ciekaw, co o tym myślisz. Moim zdaniem to świetna zmiana:
- ElasticsearchClient może być wreszcie skonstruowany w zaledwie kilku liniach, gdzie każda linia ma faktyczne znaczenie - a nie jest tylko szablonową konkatenacją stringów.
- Cała bazowa maszyneria używa domyślnych implementacji, chyba iż jawnie dostarczysz własne (z uzasadnionych powodów).
- I na koniec: jest również auto-closeable, więc po zakończeniu bloku try klient jest poprawnie zamykany, zasoby są zwalniane, a twój program kończy się czysto - bez potrzeby używania System.exit() (czego i tak naprawdę nie powinniśmy używać).
Jak to nazwać?
Jak mówi stara mądrość, w informatyce są dwa duże problemy:
- nazewnictwo
- czyszczenie pamięci podręcznej
- błędy przesunięcia o jeden.

Okazuje się, iż konstruowanie zamykalnych obiektów dzięki metod fabrycznych z konfiguracją przekazywaną przez wyrażenia lambda zyskuje na popularności. Osobiście uważam to za eleganckie i użyteczne podejście - lepsze niż przekazywanie konfiguracji przez obiekty, implementowanie interfejsów (z wszystkimi tymi metodami default), używanie podklas, a choćby klasyczny wzorzec buildera. Jednak, jak wskazał w tweecie Brian Goetz, możemy nie mieć odpowiedniej nazwy dla tego konkretnego wzorca.
Zatem, jeżeli masz propozycję, może odpowiesz na tweet Briana Goetza?
Co to ma wspólnego z developer advocacy?
Myślę, iż powinienem napisać osobny wpis o byciu developer advocatem - jak to jest, jakie są plusy i minusy, co robimy, a czego nie robimy (zwłaszcza iż pytano mnie o to przy kilku okazjach).
Krótko mówiąc, powiedziałbym: nie sprzedajemy i nie działamy jako oficerowie na odcinku propagandy. To, co robimy (między innymi), to reprezentowanie innych deweloperów z szerokiego świata w organizacjach, dla których pracujemy. Bierzemy to, co najlepsze do zaoferowania, i prezentujemy to dzięki najlepszych dostępnych środków. Innymi słowy, nasza praca to nie tylko nadawanie od wewnątrz na zewnątrz, ale także działanie jako przekaźnik dwukierunkowy: przekazywanie oczekiwań, zdobytych doświadczeń itd. z zewnątrz do wewnątrz - choćby jeżeli jest to często pomijane.
W tym konkretnym przypadku byłem w stanie przynieść opinie “ze społeczności” i wyrazić potrzebę zmian wewnętrznie. Właśnie dlatego powinieneś posłuchać rady Sharata Chandlera, iż trzeba uczestniczyć w Hallway Track na konferencjach (i denerwować developer advocatów - mogą być bardziej skuteczni niż zgłaszanie problemów w publicznych trackerach ;-) )
DevEx!
Często odnosi się to do Developer Experience, czasami skracane jako DevEx. Widzisz, choćby gdy tworzysz wysokowydajną technologię - choćby prawdziwy klejnot inżynierii systemu - jeżeli ludzie nie wiedzą, jak jej używać, lub czują się z nią niezręcznie, twoja praca nie pozostało ukończona. Najlepszy kawałek technologii to taki, który robi to wszystko powyżej i nie wymaga doktoratu z czytania instrukcji ;-)
Podsumowanie
Java wciąż żyje i ma się całkiem dobrze!
-
Mikroserwis to serwis, który może być całkowicie przepisany przez twój zespół w maksymalnie 2 tygodnie; jeżeli przepisanie od zera zajmuje dłużej, to nie jest to mikro serwis, tylko serwis. Definicja moja i moich znajomych. ↩︎