Frankenstein în programare este codul format din părți incompatibile ale diferitelor tehnologii, stiluri și arhitecturi. Conform cercetării ThoughtWorks Technology Radar (2024), 28% dintre proiectele mari conțin semne ale sindromului Frankenstein — un eclectism arhitectural care apare în absența unei viziuni tehnice unitare. Prin analogie cu romanul lui Mary Shelley, un astfel de cod funcționează, dar întreținerea sa devine un coșmar.
Principalele
Frankenstein (Frankenstein code, Frankenstein pattern) — este un antipattern în care un sistem software este asamblat din părți care nu sunt destinate să lucreze împreună. Ca monstrul lui Frankenstein, un astfel de cod poate funcționa, dar este urât, imprevizibil și periculos la cele mai mici modificări.
Termenul provine din literatură: în romanul Mary Shelley „Frankenstein sau Prometeu modern” (1818), un savant a creat o ființă vie din fragmente ale trupurilor diferiților morți. În programare, analogia este exactă — dezvoltatorii iau fragmente din diferite framework-uri, biblioteci, limbaje și le „lipesc pe viu”, obținând un rezultat funcțional, dar monstruos.
Diferența dintre Frankenstein și codul spaghete constă în amploarea și natura problemei. Codul spaghete — este o structură încurcată în cadrul unei singure stive tehnologice. Frankenstein — este un eclectism la nivel de arhitectură: tehnologii diferite, paradigme incompatibile, abordări conflictuale în cadrul unui singur sistem.
Arhitectura microserviciilor permite utilizarea unor tehnologii diferite pentru servicii diferite, dar cu condiția unor limite clare și a unor protocoale de comunicare standardizate. Frankenstein — este o amestecătură haotică fără limite: REST și GraphQL în același controler, două ORM-uri în același modul, SQL și NoSQL pentru aceeași entitate.
Lipsa unui lider tehnic sau arhitect — cauza principală. Când în proiect nu există o persoană responsabilă pentru integritatea arhitecturii, fiecare dezvoltator alege instrumentele „după sine”. Unul preferă Spring, altul — Guice, al treilea — propriul DI scris de mână. Rezultatul — un vinegret arhitectural.
Fuziunea proiectelor — a doua cauză frecventă. Două echipe și-au dezvoltat modulele independent, folosind stive tehnologice diferite. Când modulele trebuie să fie unite într-o singură aplicație, ele sunt pur și simplu „lipite” cu adaptoare și straturi intermediare. Rezultă Frankenstein.
Achizițiile corporative — al treilea scenariu. Compania A a cumpărat compania B și dorește să integreze produsul său în propriul său produs. În loc de rescriere — lipire prin API, baze de date comune și proteze. După un an, sistemul se transformă într-un monstru pe care nimeni nu îl înțelege.
| Cauză | Descriere | Rezultat tipic |
|---|---|---|
| Fără arhitect | Fiecare dezvoltator alege propria stivă | 3 clienți HTTP diferiți în același modul |
| Fuziunea proiectelor | Două produse sunt lipite într-unul | Două ORM-uri, două metode de logare |
| M&A | Achiziția companiei cu produsul său | Hibrid de arhitecturi și stiluri diferite |
| Experimente | Implementarea de noi tehnologii fără strategie | Java 8 + Java 21 în același fișier |
| Decizii politice | Impunerea tehnologiei de sus fără a ține cont de context | Framework enterprise pentru un script simplu |
Dezvoltatorii experimentați care doresc să încearnce tehnologii noi în producție devin adesea sursa lui Frankenstein. În loc să limiteze experimentele la un modul izolat, ei introduc cod experimental în partea critică a sistemului.
Exemplul clasic — utilizarea mai multor ORM-uri într-o singură aplicație. O parte din module folosesc Hibernate, o parte — MyBatis, iar o parte — interogări directe JDBC. Tranzacțiile devin imposibil de gestionat, memoria cache — inconsistentă, iar un nou dezvoltator nu știe ce abordare să aleagă pentru o funcție nouă.
Al doilea exemplu — amestecarea stilurilor arhitecturale. Într-un controler REST API apar apeluri către servicii SOAP, interogări SQL directe, acces la sistemul de fișiere și generare HTML. O astfel de aplicație nu poate fi testată, extinsă sau documentată.
Al treilea exemplu — o stivă tehnologică în care Python este folosit pentru backend, Node.js pentru microserviciu, C# pentru clientul desktop, iar Java pentru aplicația Android, în timp ce toată logica de afaceri este dispersată între ele fără o separare clară a responsabilităților.
// frankenstein — stiluri și tehnologii mixte
// callbacks, Promises și async/await combinate
// callbacks
db.query("SELECT * FROM users", function(err, rows) {
if (err) handleError(err);
// Promise în interiorul callback
fetch("/api/data").then(function(data) {
// async/await în interiorul then
(async () => {
const result = await processData(data);
sendResponse(result);
})();
});
});
// cod curat — stil unificat async/await
async function getUserData(userId) {
const user = await db.query("SELECT * FROM users WHERE id = ?", [userId]);
const data = await fetch("/api/data/" + userId);
return await processData(user, data);
}
O singură bază de date este utilizată simultan atât ca SQL relațional (cu normalizare), cât și ca NoSQL orientat pe documente (cu coloane JSON). O parte din interogări trec prin ORM, o parte — prin proceduri stocate, o parte — prin SQL direct din cod. Schema bazei de date nu este documentată, migrațiile sunt conflictuale.
Dificultatea onboardingu — prima consecință. Un nou dezvoltator trebuie să cunoască 5 limbi, 3 framework-uri, 2 stiluri arhitecturale pentru a înțelege cum funcționează sistemul. Onboardingul se prelungește de la săptămâni la luni. Conform LinkedIn (2023), proiectele cu eclectism tehnologic pierd noii angajați de 2 ori mai des.
Imprevizibilitatea comportamentului — a doua consecință. O modificare într-un microserviciu Python poate strica neașteptat un modul Java, deoarece acestea folosesc o bază de date comună fără contracte clare. Depanarea unor astfel de probleme necesită cunoașterea simultană a tuturor tehnologiilor din stivă.
Securitatea — a treia consecință. Fiecare tehnologie din stivă necesită propria configurație de securitate, propriile patch-uri, propriul monitorizare. Menținerea securității la un nivel acceptabil pentru 5–6 tehnologii eterogene este practic imposibilă. Una dintre ele va fi inevitabil vulnerabilă.
SonarQube poate măsura datoria tehnică, dar nu poate măsura „datoria arhitecturală” — incompatibilitatea componentelor. Această datorie nu se manifestă în avertismentele linter-ului, ci în imposibilitatea de a adăuga o nouă funcție fără a modifica trei module diferite scrise în tehnologii diferite.
Primul și cel mai important pas — numirea unui arhitect sau tech lead responsabil pentru integritatea stivei tehnologice. Această persoană are drept de veto asupra implementării noilor tehnologii fără o revizuire arhitecturală. Nu democrație, ci o decizie responsabilă individuală în privința tehnologiilor cheie.
Al doilea — implementarea procesului Architecture Decision Record (ADR). Orice decizie arhitecturală semnificativă (alegerea bazei de date, framework-ului, protocolului) este documentată sub forma unui text scurt: context, alternativele luate în considerare, decizia luată, consecințele. ADR-urile sunt stocate în repository și accesibile întregii echipe.
Al treilea — stabilirea principiului „o sarcină — un instrument”. Pentru cereri HTTP — un client. Pentru ORM — o bibliotecă. Pentru logare — un framework. Excepțiile sunt permise doar prin ADR cu justificare. Dacă în proiect există deja Axios — nu adăugați fetch, dacă există SLF4J — nu scrieți prin System.out.
Experimentele sunt permise, dar într-un mediu izolat. Alocați un modul sau un serviciu care poate fi rescris într-o tehnologie nouă fără a afecta restul sistemului. Dacă experimentul reușește — standardizați-l prin ADR. Dacă nu — ștergeți-l fără consecințe.
Inventarierea — primul pas. Întocmiți o hartă completă a stivei tehnologice: ce framework-uri, biblioteci, limbaje, protocoale sunt utilizate, în ce module și pentru ce sarcini. Veți vedea amploarea problemei: duplicarea instrumentelor, tehnologii conflictuale, dependențe neutilizate.
Standardizarea — al doilea pas. Alegeți un instrument pentru fiecare sarcină. De exemplu: doar Hibernate pentru ORM, doar SLF4J + Logback pentru logare, doar REST pentru API. Documentați standardul în ADR. Începeți înlocuirea de la modulele unde eclectismul cauzează cele mai multe probleme.
Strategia Parallel Run — al treilea pas. Instrumentele vechi și noi funcționează în paralel până când cel nou își dovedește fiabilitatea. De exemplu, clientul HTTP vechi și cel nou funcționează simultan, dar cel nou — doar pentru o parte din cereri. După o perioadă de stabilizare, cel vechi este eliminat.
// frankenstein — trei abordări HTTP într-un singur proiect
// Modulul A: OkHttp
OkHttpClient client = new OkHttpClient().newCall(request);
// Modulul B: RestTemplate (Spring)
restTemplate.getForObject(url, String.class);
// Modulul C: java.net.HttpURLConnection
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
// abordare unificată: RestTemplate pentru sincron, WebClient pentru reactiv
@Autowired
private RestTemplate restTemplate;
public String callApi(String url) {
return restTemplate.getForObject(url, String.class);
}
Liderul tehnic — principalul instrument de luptă împotriva lui Frankenstein. Nu un manager, nu un arhitect în turnul de fildeș, ci un dezvoltator practicant care scrie cod, revizuiește PR-uri și ia decizii arhitecturale. Fără o astfel de persoană, proiectul alunecă inevitabil spre eclectism tehnologic.
RFC (Request for Comments) — un proces preluat din comunitățile Open Source. Înainte de implementarea oricărei tehnologii semnificative, autorul scrie un RFC: problema, soluția propusă, alternativele, planul de implementare. Echipa discută, votează, acceptă sau respinge. RFC creează transparență și previne deciziile arhitecturale „tăcute”.
Radarul tehnologic (Technology Radar de la ThoughtWorks) — un instrument de categorizare a tehnologiilor: Adopt, Trial, Assess, Hold. Echipa revizuiește periodic radarul și actualizează statusurile. Acest lucru ajută la deosebirea „modului” de „util” și la evitarea implementării tehnologiilor netestate în codul critic.
Cea mai importantă calitate a arhitecturii este coerența (consistency). Chiar și un instrument nu cel mai bun, utilizat în întregul proiect, este mai bun decât cel mai bun instrument utilizat doar într-un singur modul. Coerența reduce sarcina cognitivă, simplifică onboardingul și face codul previzibil.
Întrebări frecvente
Polyglot persistence — utilizarea conștientă a diferitelor baze de date pentru diferite sarcini (PostgreSQL pentru tranzacții, Redis pentru cache, Elasticsearch pentru căutare). Frankenstein — o amestecătură haotică fără strategie. Diferența constă în existența unei decizii arhitecturale: polyglot — este un plan, Frankenstein — absența acestuia.
Da, și aceasta este o problemă frecventă. Când fiecare microserviciu folosește propriul limbaj, propria bază de date, propriul protocol și propria abordare de implementare fără standarde centralizate, se obține un Frankenstein distribuit. Pentru microservicii sunt importante standardele comune: un protocol unic (REST/gRPC), un format comun de loguri, un observability centralizat.
Nu interziceți — ghidați. Propuneți autorului să scrie un RFC: să descrie de ce soluția existentă nu este potrivită, ce alternative a luat în considerare, cum va migra. Adesea în procesul de scriere a RFC-ului, dezvoltatorul înțelege singur că noua tehnologie nu este necesară. Dacă RFC este convingător — implementați, dar cu un plan și limitări.
Mai întâi inventarierea, apoi standardizarea. Nu încem să rescrieți totul deodată. Separați un strat (de exemplu, clienții HTTP sau logarea), alegeți un instrument unic, scrieți un ADR și migrați treptat. Metoda Strangler Fig — înlocuiți componentele vechi cu cele noi una câte una, fără a opri funcționarea aplicației.
Cu cât mai puține, cu atât mai bine. Ideal — un limbaj, un framework, o bază de date, o metodă de logare. Realist — 2–3 limbaje (cu separare clară), 1–2 baze de date, 1–2 framework-uri. Fiecare tehnologie suplimentară crește sarcina cognitivă a echipei și costul de întreținere.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și