Ai primit vreodată un User Story de migrare Java estimat la 13 puncte Fibonacci — pentru 20-30 de servicii? Știm cu toții cum se termină: zile de erori de compilare, forumuri din anii '90 și promisiunea că "celelalte vor fi copy-paste". În 2026, avem o veste bună: acele 13 puncte pot deveni 3, iar tu poți urmări totul servind o cafea gustoasă.
Dați-ne voie să facem o presupunere: cu siguranță asta nu a fost cea mai plăcută experiență a ta ca programator! A face actualizări de versiuni majore — care vin în mod obișnuit cu actualizări ale altor dependențe secundare și terțiare pe care poate chiar nu le cunoști — nu este cea mai interesantă sarcină. Probabil că ai stat mai mult de două zile pe un singur serviciu, încercând să rezolvi toate acele erori de compilare, efect al deprecierii anumitor API-uri. Apoi, când în sfârșit codul compilează și nu mai ai niciun pic de roșu (preferabil nici galben...), dai drumul aplicației pe local și aceasta îți crapă cu o eroare pe care nu știi de unde să începi să o citești sau cum să o interpretezi. Și atunci începi și cauți ca tot omul pe StackOverflow, dar, nimic. Apoi intri la căutarea avansată pe Google și cauți: Reddit, Medium, Dev.to ș.a.m.d. Când în final nu găsești nimic, după ce ai modificat textul de căutare de câteva ori, după ce ai făcut o pauză de cafea și ai întrebat colegii din birou sau chiar ai dat un @all pe canalul cu toți developerii de Java pe Slack și nu ai primit niciun răspuns favorabil, ajungi la capătul tuturor ideilor. Și atunci zici că mai încerci pentru ultima dată să modifici termenii de căutare, să pui ceva clasă între ghilimele sau să te axezi pe prima eroare care a fost înfășurată în alte excepții de 5-6 ori... Și după ce ai ajuns la capătul internetului (ultima pagină din Google), ajungi pe un forum dubios cu grafică din '90 și acolo cineva spune că a rezolvat problema făcând upgrade la o singură versiune minoră, sau schimbând 1-2 litere dintr-o proprietate. Și o încerci și tu și... VICTORIE, aplicația pornește în sfârșit!!
Ești bucuros. Mâine la daily poți, în sfârșit, să nu mai spui că lucrezi tot pe upgrade-ul de Java la primul serviciu, ci că ai trecut mai departe. Totuși, înainte de a face commit te gândești să rulezi o dată testele pe local. Și ce să vezi? — jumătate din teste crapă, toate cu aceeași eroare că o clasă nu poate fi găsită în classpath, o clasă dintr-un pachet de care de asemenea nu ai auzit niciodată.
Toate acestea sunt la primul serviciu. Unul dintre "doar" 30 de alte servicii pe care tu trebuie să le migrezi la versiunea nouă în două sprinturi. Că doar e o sarcină ușoară: deoarece ai de modificat doar niște numere (versiuni) într-un singur fișier așa cum a zis "X" din echipa cealaltă, User Story-ul tău a primit doar 13 puncte Fibonacci (împărțit în două de 8 puncte fiecare, unul pe fiecare sprint) cu indulgență (adică după ce a fost pus "din burtă" extra ca să acopere orice incertitudine la estimare). Iar tu, la a patra zi din sprint, lucrând la testele primului serviciu, și dându-te de ceasul morții că nu vei reuși să faci la timp, știind că managerul tău care e liniștit acum nu are dreptate când spune că odată ce faci primul, celelalte vor fi copy-paste.
Îți sună puțin cunoscută situația de mai sus? Te râcâie puțin când povestesc? Am reușit să atingem măcar o coardă sensibilă?
Dați-ne voie să răspundem noi: "DA!". Nu credem că vreun developer mid/senior nu s-a întâlnit cu câte ceva asemănător pe parcursul carierei. Da, poate că am exagerat puțin anumite aspecte, ca să facem povestea mai intensă și să aprindem un mic "foc" citind aceste rânduri.
Dar acum venim cu o veste bună! În această zi minunată din 2026 avem toate uneltele necesare pentru a nu mai fi nevoiți să trecem prin tot acel stres. Nu va mai fi nevoie să mergem low level încercând să descoperim ce versiuni ale dependenței "X" merg cu framework-ul "Y", sau care e configurația corectă după migrarea de la un API depreciat la înlocuitorul său. Oare la ce ne referim? Credem că știți cu toții — agenții de inteligență artificială (IA) sau, mai tehnic spus: modelele mari de limbaj (LLM).
Poate că acum urmează să spuneți: "Lasă că am auzit noi de vibe coding și că vin oamenii non-tehnici și fac ei aplicații și că ne iau locurile de muncă. Dar noi știm care este curba de eficiență când faci vibe coding și că va ajunge codul (sau dependențele în situația actuală) de neîntreținut și în final vom pierde mai multe resurse decât dacă facem noi migrarea de mână". Nu am putea decât să vă dăm dreptate: vibe codingul a fost demonstrat că pe termen lung face ravagii și e foarte costisitor la proiecte mari și de lungă durată. Acum vine întrebarea: unde am spus vreodată noi vibe coding? Într-adevăr, vibe coding este o unealtă disponibilă agenților IA, dar nu este singura! Astăzi vrem să explorăm o altă unealtă, și anume: planificarea și executarea folosind agenți specializați, care comunică între ei, fiind coordonați de un agent principal. Sună complicat, nu? Însă odată înțelese aceste aspecte, nu va mai părea atât de dificil.
Dar acum vine și întrebarea: "De ce am vrea să ne batem capul să mergem la ultima versiune de Java LTS (25 la momentul scrierii acestui articol)?" Cel mai bun lucru ar fi să ne uităm la beneficiile aduse de această nouă versiune, plecând de la premisa că suntem cu o versiune LTS în urmă (Java 21). Având acestea în vedere, am dori să punem accentul doar pe câteva dintre noile funcții aduse de Java 25 care ne pot aduce beneficii în mediul enterprise:
Tipuri primitive extinse la instanceof și switch (JEP 507) - este încă în preview, dar deschide ușa pentru o uniformizare și ușurare în scrierea codului Java când folosim tipurile primitive, fără a mai fi forțați să trecem la clasele ce le încapsulează.
Compact Object Headers (JEP 519) - reduce consumul mediu de memorie cu 22% și totodată asigură o pornire a aplicației mult mai rapidă.
Folosirea variabilei nedenumite _ (JEP 456) - beneficiu adus în situațiile când trebuie să declarăm o variabilă datorită sintaxei, deși nu avem nevoie de ea, semnalând compilatorului că variabila este intenționat ignorată și eliminând astfel avertizările de variabilă neutilizată.
Importarea unui întreg modul în loc de a importa pachet cu pachet (JEP 511).
Eficientizarea inițierii unui nou program Java (void main() {...} în loc de a defini o clasă, cu o metodă main statică și publică) (JEP 512) și totodată eliminarea necesității de a importa clasele din java.base (de exemplu Map sau ArrayList), ele funcționând nativ; și nu în ultimul rând, înlocuirea sintagmei System.out/in. cu IO. (de exemplu System.out.println() devine IO.println())
Aplicarea validărilor în constructor înainte de a apela super() sau this() (JEP 513) - rezolvă o problemă existentă de ani de zile, unde super/this trebuiau apelate prima dată în orice constructor.
Agenți AI coordonând migrarea Java
Scrierea comentariilor JavaDoc în Markdown în loc de HTML (JEP 467)
Criptare rezistentă la Computarea Cuantică (JEP 496/497) - pregătește sistemele enterprise pentru momentul în care calculatoarele cuantice vor deveni stabile și comune, acestea putând să treacă cu ușurință peste orice tip de criptografie modernă existentă la ora actuală.
Să o luăm cu începutul: planificarea. Ce înseamnă aceasta oare?
Prin planificare ne referim la faptul că nu vom folosi agenții pentru a modifica direct codul, făcând vreo modificare, ci vom lucra împreună cu agentul ca să scriem un plan foarte bine definit și pus la punct. Acest plan trebuie să explice fiecare modificare pe care dorim să o realizăm, să evidențieze aspectele esențiale și să precizeze ce trebuie realizat la fiecare pas. În final, planul va fi folosit de agent (și de subagenții săi) pentru a implementa toate modificările într-un singur proces.
Care e avantajul acestei abordări? Simplu: faptul că fiecare agent specializat va trece și modifica codul o singură dată asigură că nu vor apărea bucăți de cod uitate, sau aruncate, sau duplicate, ci totul va fi scris da capo al fine având același context. Asta va asigura calitatea finală a codului, corectitudinea rulării și siguranța că la final vom comite ceva complet funcțional și cu șanse minuscule de regresii introduse prin această migrare.
Știm în acest moment că doar abilitățile de programator expert Java nu sunt suficiente, așa că trebuie să apelăm la înțelepciunea internetului, care ne învață despre noua arie din tehnologie: prompt engineering. După cum s-a observat că dă cele mai bune roade, începem cu o cerință generală care descrie ce ne dorim ca agentul IA să scrie în plan. Mai întâi, însă, trebuie să îi transmitem că este un expert care le știe pe toate și că este mai mult decât capabil să facă ceea ce noi cerem:
De acum încolo vei acționa ca un inginer software expert, având aria de expertiză în Java, Spring și alte tehnologii aferente, fiind expert în versiunile cele mai moderne.
Vreau să începi să faci un plan despre ce presupune exact să fac actualizarea acestui serviciu la ultima versiune de Java LTS, și anume Java 25. Vreau să actualizezi absolut toate dependențele din proiect așa încât să aibă compatibilitate 100% cu versiunea de Java și Spring Boot. Vreau să folosești BOM-uri acolo unde este necesar și recomandat, având cel puțin două dependențe care beneficiază de acel BOM.
Dacă există dependențe care s-au mutat în alt pachet, cel curent fiind la capătul vieții, vreau să faci actualizarea la versiunea din noul pachet compatibilă cu codul meu.
De asemenea, vreau să extragi dependențele de teste într-un bloc nou `dependencies {}` și să te asiguri că sunt definite corect, fără să mă bazez în cod pe dependențe tranzitive, ci totul să fie specificat corect în `testImplementation / testRuntimeOnly`.
Te rog nu uita că și versiunea de `gradle wrapper` trebuie actualizată la cea mai nouă versiune potrivită cu versiunea de Java la care migrăm. Totodată, orice imagine Docker ce există trebuie parcursă, așa încât să te asiguri că are versiunea de bază și toate comenzile necesare ca să poată fi construită cu noua versiune de Java.
Nu în ultimul rând, vreau ca toate dependențele să fie verificate în baza de date CVE. Dacă dependențele mele directe sunt vulnerabile, vreau să alegi versiunea recomandată care rezolvă acea vulnerabilitate. Dacă vreuna din dependențele tranzitive ale proiectului sunt vulnerabile, vreau să actualizezi dependența părinte. Dacă acest lucru nu este posibil (vulnerabilitatea nu a fost fixată încă public), vreau ca într-un bloc separat `dependencies {}` să treci versiunea care fixează acea vulnerabilitate a dependenței tranzitive, într-un bloc `implementation {}` în care specifici versiunea strictă ce rezolvă acea problemă și totodată motivul (regula CVE) pentru acea implementare.
Văzând că planul este generat și arată bine, putem trece la următorul pas: să ne asigurăm că tot codul este funcțional în local atât înainte, cât și după, iar testele rulează corect:
Vreau să pornești aplicația în local și să rulezi testele înainte de migrare. După migrare trebuie să te asiguri că aceleași teste rulează corect, iar aplicația pornește și funcționează corect.
Înainte de migrare vreau să verifici dacă testele acoperă majoritatea codului meu, atât ca acoperire a codului, cât și ca testare logică și funcțională a codului atingând majoritatea cazurilor limită. Dacă testele nu acoperă așa cum am cerut, vreau o statistică și un plan de adăugare de noi teste așa încât să respecte cerința mea.
În timp ce agentul procesează, scanează codul și pregătește planul, un gând îmi vine în minte: dacă tot lucrăm cu mai mulți agenți care pot fi specializați, oare nu ar fi mai bine să gândim un proces în care diferiți specialiști să ia unul de la celălalt codul și să facă partea lor specială de îmbunătățire și auditare? De exemplu, am putea să ne axăm pe următoarele dimensiuni:
Testare;
Dependențe și modificări Java;
Calitate a codului & SAST (aici e util să conectezi agentul tău cu toolurile folosite în proiect, gen SonarQube, FOSSA, Checkmarx, PMD etc.);
Fixarea problemelor găsite anterior;
În special, ultimul punct îl considerăm foarte util, pentru că atunci când echipele fac migrarea la o nouă versiune, nimeni nu are timp să treacă peste tot codul așa încât să elimine toate părțile deprecated și să folosească ultimele funcționalități (de exemplu, a converti un String lung de tot, scris pe multe rânduri cu concatenare la un multiline string, sau a folosi .toList() la final de stream în loc de a scrie întreg colectorul).
Urmând aceste idei și bazându-ne pe un set deja existent de agenți (de ex: @ruvnet/ruflo) putem să îi cerem următoarele agentului:
Doresc ca planul pe care îl facem să fie rulat cu un roi de agenți, fiecare specializat pe domeniul său de activitate și coordonat de către tine. Incluzând pașii descriși mai sus, vreau să faci în ordine următoarele:
1. Testarea completă și rularea aplicației în local
2. Actualizarea versiunii de Java și aducerea tuturor celorlalte dependențe la versiunea cea mai nouă care respectă dependențele versiunii de Java și a celorlalte dependențe majore (ca și Spring Boot de exemplu)
3. Pornirea în local a codului și rularea testelor; rezolvarea oricărei probleme apărute datorită migrării.
4. Rularea uneltelor de analiză a versiunilor pentru a descoperi orice vulnerabilitate „CVE" și rezolvarea acestor vulnerabilități
5. Rularea uneltelor de analiză SAST și a celor de securitate, urmată de rezolvarea problemelor descoperite.
6. Pornirea aplicației în local și rularea testelor încă o dată, urmată de rezolvarea oricărei probleme descoperite. Totodată, pe baza logurilor, rezolvarea tuturor notificărilor de depreciere sau a avertizărilor apărute la pornirea aplicației.
7. Scanarea întregului cod și aducerea lui la nivelul nou de Java. În acest pas, agentul expert în Java va trebui să modifice părțile de cod care sunt depreciate, sau care au o variantă mai bună și modernă de scriere, față de cum a fost scris codul original. Ca exemple aici îți pot da conversia de la string-uri concatenate pe multe linii, la a folosi `"""`, transformarea din `.collect(Collectors.toList())` la `.toList()`, folosirea noilor variante de switch-case etc. Nu te limita doar la exemplele date aici, ci ia în considerare toate posibilitățile, așa încât după acest pas, codul să fie la un nivel complet modern.
8. Rularea testelor și pornirea aplicației, urmărirea logurilor și rezolvarea oricăror probleme posibile apărute.
9. Comiterea tuturor modificărilor (aceasta trebuie făcută la fiecare dintre pașii de mai sus), împingerea spre repo folosind un branch nou, finalizată cu deschiderea unui nou pull-request și anunțarea userului despre finalitate.
În acest moment, considerând că am atins toate aspectele necesare producerii unei migrări corecte și elegante, putem da drumul agentului să genereze varianta finală a planului. Odată finalizat, recomandarea noastră este să fie parcurs o dată pentru siguranță și ajustat dacă este nevoie.
Odată ce am ales modelul de LLM cel mai competent, pentru a ne asigura că lucrează corect, îi putem spune pur și simplu:
Lucrând ca un inginer software cu specializare în Java și Spring și bazându-te pe planul pe care l-am conceput împreună, începe să rulezi agenții specializați urmând toate instrucțiunile din plan. La final, vreau o explicație cu tot ce s-a făcut, rezultatul final, și un set extins de statistici.
Și cu asta ar trebui să avem aplicația migrată așa cum niciun programator nu ar avea timpul și răbdarea de a o face. Totuși, cu bune, cu rele, ca să ajungem la acest punct a trebuit să lucrăm câteva ore. Nu a fost tocmai un simplu migrează-mi aplicația asta la Java 21. Oare trebuie să trecem prin acești pași la fiecare aplicație pe care o migrăm? Sau trebuie să facem mereu copiere și lipire la toată această descriere lungă?
Nicidecum! Soluția este simplă — definirea unui skill global și reutilizarea sa. Tot ce trebuie să facem este să creăm un skill care are toate cerințele noastre de mai sus puse la un loc, iar noi va trebui doar să îi spunem folosind skill-ul tău de migrare Java, fă o migrare la Java 21 pentru această aplicație, începând cu planificarea și finalizând cu execuția sa. Partea frumoasă este că un astfel de skill poate fi urcat de către noi în GitHub cu licență MIT, după care mii de alți programatori vor putea face o migrare foarte simplă a codului lor, fără să treacă prin toți pașii noștri de mai sus.
Finalul tuturor lucrurilor este o aplicație migrată elegant și eficient, ocolind stresul unui developer care trebuie să facă o astfel de migrare de câteva ori în carieră. Ba mai mult — un singur developer poate rula mai multe migrări în același timp, lăsând agenții să lucreze în paralel, producând astfel o eficientizare maximă, posibil scurtând acele "13 puncte" inițiale la doar 5 sau poate chiar 3.
Actualizarea versiunilor Java a devenit dintr-odată mai accesibilă, mai interesantă și mai fascinantă. Parcă orizonturile s-au deschis, oferind un mai simplu proces de actualizare. Acum, ca stăpâni ai codului, ne va fi mult mai simplu să fim la zi cu toate versiunile noi de Java care apar mai nou "ca pâinea caldă". Așadar, nu mai suntem nevoiți să așteptăm să apară încă două LTS-uri până să ne luăm inima în dinți și să facem și noi odată actualizarea repede-repede până nu ajunge versiunea noastră în End of Life (EoL).
De la Vibe Coding la Production Engineering
Marți, 30 iunie, ora 18:00
Cognizant (Timișoara)
Facebook Meetup StreamEvent YouTubede Alex Popescu
de Luiza Mihu
de Cosmin Sandu
de Andreea Roșu