Frankenstein a programozásban olyan kód, amely különböző technológiák, stílusok és architektúrák összeférhetetlen részeiből épül fel. A ThoughtWorks Technology Radar (2024) kutatása szerint a nagy projektek 28%-a mutatja a Frankenstein-szindróma jeleit — olyan architekturális eklekticizmust, amely egységes technikai vízió hiányában alakul ki. Mary Shelley regényéhez hasonlóan az ilyen kód működik, de a karbantartása rémálommá válik.
Főbb pontok
Frankenstein (Frankenstein code, Frankenstein pattern) egy antipattern, ahol a szoftverrendszer olyan részekből épül fel, amelyeket nem közös munkára terveztek. Akárcsak Frankenstein szörnye, az ilyen kód működhet, de csúnya, kiszámíthatatlan és veszélyes a legkisebb változtatásokra is.
A kifejezés az irodalomból származik: Mary Shelley „Frankenstein, avagy a modern Prométheusz” (1818) című regényében egy tudós élőlényt hozott létre különböző elhunyt emberek testtöredékeiből. A programozásban a hasonlat pontos — a fejlesztők különböző keretrendszerek, könyvtárak, nyelvek darabjait veszik és ragasztják össze „élő cérnával”, ami működő, de szörnyű eredményt ad.
A Frankenstein különbsége a spagetti kódtól a probléma léptékében és természetében van. A spagetti kód egy technológiai vermen belüli összekuszálódott struktúra. A Frankenstein architektúra szintű eklekticizmus: különböző technológiák, összeférhetetlen paradigmák, egymásnak ellentmondó megközelítések egy rendszeren belül.
A mikroszolgáltatás architektúra lehetővé teszi különböző technológiák használatát különböző szolgáltatásokhoz, de egyértelmű határok és szabványosított kommunikációs protokollok feltételével. A Frankenstein határok nélküli kaotikus keveredés: REST és GraphQL egy vezérlőben, két ORM egy modulban, SQL és NoSQL ugyanahhoz az entitáshoz.
Technikai vezető vagy építész hiánya a kiváltó ok. Ha egy projektben nincs senki, aki az architektúra integritásáért felelős, minden fejlesztő „maga számára” választ eszközöket. Az egyik a Spring-et szereti, a másik a Guice-ot, a harmadik a saját DI-t. Az eredmény architekturális zűrzavar.
Projektek összeolvadása a második gyakori ok. Két csapat egymástól függetlenül fejlesztette moduljait különböző stackekkel. Amikor a modulokat egyetlen alkalmazásba kell egyesíteni, egyszerűen „összeragasztják” őket adapterekkel és köztes rétegekkel. Az eredmény Frankenstein.
Vállalati felvásárlások a harmadik forgatókönyv. Az A vállalat megvásárolta a B vállalatot, és integrálni akarja annak termékét a sajátjába. Újraírás helyett — összeragasztás API-n keresztül, megosztott adatbázisokkal és kerülőmegoldásokkal. Egy év múlva a rendszer olyan szörnnyé válik, amelyet senki sem ért.
| Ok | Leírás | Jellemző eredmény |
|---|---|---|
| Nincs építész | Minden fejlesztő a saját stackjét választja | 3 különböző HTTP kliens egy modulban |
| Projekt-összeolvadás | Két termék egyesül eggyé | Két ORM, két naplózási mód |
| M&A | Vállalat felvásárlása a termékével együtt | Különböző architektúrák és stílusok hibridje |
| Kísérletek | Új technológiák bevezetése stratégia nélkül | Java 8 + Java 21 funkciók egy fájlban |
| Politikai döntések | Technológia felülről való erőltetése kontextus nélkül | Vállalati keretrendszer egyszerű szkripthez |
Tapasztalt fejlesztők, akik új technológiákat akarnak kipróbálni élesben, gyakran válnak a Frankenstein forrásává. Ahelyett, hogy a kísérleteket egy elkülönített modulra korlátoznák, kísérleti kódot vezetnek be a rendszer kritikus részébe.
Klasszikus példa több ORM használata egy alkalmazásban. Egyes modulok Hibernate-t, mások MyBatis-t, megint mások közvetlen JDBC lekérdezéseket használnak. A tranzakciók kezelhetetlenné válnak, a gyorsítótár inkonzisztenssé, és az új fejlesztő nem tudja, melyik megközelítést válassza az új funkcióhoz.
Második példa az architekturális stílusok keverése. Egy REST API vezérlőben SOAP szolgáltatáshívások, közvetlen SQL lekérdezések, fájlrendszer-hozzáférés és HTML generálás található. Az ilyen alkalmazást lehetetlen tesztelni, bővíteni vagy dokumentálni.
Harmadik példa egy technológiai stack, ahol Python-t használnak a backendhez, Node.js-t a mikroszolgáltatáshoz, C#-t az asztali klienshez, és Java-t az Android alkalmazáshoz, miközben az összes üzleti logika szét van kenve közöttük egyértelmű felelősségi megosztás nélkül.
// frankenstein — vegyes stílusok és technológiák
// callbacks, Promises és async/await kombinálva
// callbacks
db.query("SELECT * FROM users", function(err, rows) {
if (err) handleError(err);
// Promise callbacken belül
fetch("/api/data").then(function(data) {
// async/await then-en belül
(async () => {
const result = await processData(data);
sendResponse(result);
})();
});
});
// tiszta kód — egységes async/await stílus
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);
}
Egy adatbázist használnak egyszerre SQL relációs (normalizációval) és NoSQL dokumentumorientált (JSON oszlopokkal) módban. A lekérdezések egy része ORM-en keresztül megy, egy része tárolt eljárásokon keresztül, egy része közvetlen SQL-lel a kódból. Az adatbázisséma nincs dokumentálva, a migrációk ütköznek.
A beléptetés összetettsége az első következmény. Egy új fejlesztőnek 5 nyelvet, 3 keretrendszert, 2 architekturális stílust kell ismernie ahhoz, hogy megértse a rendszer működését. A beléptetés hetekről hónapokra nyúlik. A LinkedIn (2023) szerint a technológiai eklekticizmussal rendelkező projektek kétszer gyakrabban veszítik el az új alkalmazottakat.
A viselkedés kiszámíthatatlansága a második következmény. Egy Python mikroszolgáltatásban végzett változtatás váratlanul elronthat egy Java modult, mert közös adatbázist használnak egyértelmű szerződések nélkül. Az ilyen problémák hibakeresése a stack összes technológiájának egyidejű ismeretét igényli.
A biztonság a harmadik következmény. A stack minden technológiája saját biztonsági konfigurációt, saját javításokat, saját monitorozást igényel. A biztonság elfogadható szinten tartása 5–6 különböző technológiához gyakorlatilag lehetetlen. Az egyik elkerülhetetlenül sebezhetőnek bizonyul.
A SonarQube képes mérni a technikai adósságot, de nem tudja mérni az „architekturális adósságot” — a komponensek összeférhetetlenségét. Ez az adósság nem a linter figyelmeztetéseiben nyilvánul meg, hanem abban, hogy lehetetlen új funkciót hozzáadni anélkül, hogy három különböző, különböző technológiákkal írt modult módosítanánk.
Az első és legfontosabb lépés egy építész vagy tech lead kinevezése, aki felelős a technológiai stack integritásáért. Ez a személy vétójoggal rendelkezik az új technológiák bevezetése felett architekturális felülvizsgálat nélkül. Nem demokrácia, hanem felelős egyéni döntés a kulcstechnológiákról.
A második — az Architecture Decision Record (ADR) folyamat bevezetése. Minden jelentős architekturális döntés (adatbázis, keretrendszer, protokoll kiválasztása) rövid szövegként dokumentálódik: kontextus, megfontolt alternatívák, meghozott döntés, következmények. Az ADR-ek a tárolóban vannak, és az egész csapat számára elérhetők.
A harmadik — az „egy feladat — egy eszköz” elv bevezetése. HTTP kérésekhez — egy kliens. ORM-hez — egy könyvtár. Naplózáshoz — egy keretrendszer. Kivételek csak ADR-en keresztül, indoklással engedélyezettek. Ha a projektben már van Axios — ne adj hozzá fetch-et, ha van SLF4J — ne írj System.out-on keresztül.
A kísérletek megengedettek, de elkülönített környezetben. Jelölj ki egy modult vagy szolgáltatást, amely átírható új technológiával anélkül, hogy a rendszer többi részét befolyásolná. Ha a kísérlet sikerül — szabványosítsd ADR-en keresztül. Ha nem — távolítsd el következmények nélkül.
Leltározás az első lépés. Készíts teljes térképet a technológiai stackről: milyen keretrendszerek, könyvtárak, nyelvek, protokollok használatosak, mely modulokban és milyen feladatokhoz. Látni fogod a probléma mértékét: duplikált eszközök, egymásnak ellentmondó technológiák, nem használt függőségek.
Szabványosítás a második lépés. Válassz egy eszközt minden feladathoz. Például: csak Hibernate az ORM-hez, csak SLF4J + Logback a naplózáshoz, csak REST az API-hoz. Dokumentáld a szabványt ADR-ben. Kezdd a cserét azokban a modulokban, ahol az eklekticizmus a legtöbb problémát okozza.
A Parallel Run stratégia a harmadik lépés. A régi és új eszközök párhuzamosan működnek, amíg az új be nem bizonyítja megbízhatóságát. Például a régi HTTP kliens és az új együtt működik, de az új csak a kérések egy részéhez. A stabilizációs időszak után a régi eltávolításra kerül.
// frankenstein — három HTTP megközelítés egy projektben
// A modul: OkHttp
OkHttpClient client = new OkHttpClient().newCall(request);
// B modul: RestTemplate (Spring)
restTemplate.getForObject(url, String.class);
// C modul: java.net.HttpURLConnection
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
// egységes megközelítés: RestTemplate sync-hez, WebClient reactive-hez
@Autowired
private RestTemplate restTemplate;
public String callApi(String url) {
return restTemplate.getForObject(url, String.class);
}
A technikai vezető a legfőbb eszköz a Frankenstein elleni küzdelemben. Nem menedzser, nem építész elefántcsonttoronyban, hanem gyakorló fejlesztő, aki kódot ír, PR-eket review-zik és architekturális döntéseket hoz. Ilyen ember nélkül a projekt elkerülhetetlenül technológiai eklekticizmusba süllyed.
Az RFC (Request for Comments) egy nyílt forráskódú közösségekből kölcsönzött folyamat. Bármely jelentős technológia bevezetése előtt a szerző RFC-t ír: probléma, javasolt megoldás, alternatívák, bevezetési terv. A csapat megvitatja, szavaz, elfogadja vagy elutasítja. Az RFC átláthatóságot teremt és megakadályozza a „csendes” architekturális döntéseket.
A technológiai radar (Technology Radar a ThoughtWorkstől) a technológiák kategorizálásának eszköze: Adopt, Trial, Assess, Hold. A csapat rendszeresen felülvizsgálja a radart és frissíti az állapotokat. Ez segít megkülönböztetni a „divatost” a „hasznostól”, és elkerülni a be nem vált technológiák bevezetését a kritikus kódba.
Az architektúra legfontosabb tulajdonsága az egységesség (consistency). Még a nem legjobb eszköz is, amelyet az egész projektben használnak, jobb, mint a legjobb eszköz, amelyet csak egy modulban használnak. Az egységesség csökkenti a kognitív terhelést, egyszerűsíti a beléptetést és kiszámíthatóvá teszi a kódot.
Gyakran Ismételt Kérdések
A polyglot persistence különböző adatbázisok tudatos használata különböző feladatokhoz (PostgreSQL tranzakciókhoz, Redis gyorsítótárhoz, Elasticsearch kereséshez). A Frankenstein kaotikus keveredés stratégia nélkül. A különbség az architekturális döntés meglétében van: a polyglot egy terv, a Frankenstein annak hiánya.
Igen, és ez gyakori probléma. Amikor minden mikroszolgáltatás saját nyelvet, adatbázist, protokollt és telepítési megközelítést használ központi szabványok nélkül — elosztott Frankenstein jön létre. A mikroszolgáltatásoknál fontosak az általános szabványok: egységes protokoll (REST/gRPC), egységes naplóformátum, központosított megfigyelhetőség.
Ne tiltsd — irányíts. Kérd meg az RFC szerzőjét: írja le, miért nem megfelelő a meglévő megoldás, milyen alternatívákat vettek figyelembe, hogyan fogják végrehajtani a migrációt. Gyakran az RFC írása során a fejlesztő maga is rájön, hogy az új technológiára nincs szükség. Ha az RFC meggyőző — vezesd be, de tervvel és korlátozásokkal.
Először leltározás, aztán szabványosítás. Ne próbálj meg mindent egyszerre átírni. Jelölj ki egy réteget (például HTTP kliensek vagy naplózás), válassz egységes eszközt, írj ADR-t, és fokozatosan migrálj. A Strangler Fig módszer — cseréld le a régi komponenseket egyesével anélkül, hogy az alkalmazást leállítanád.
Minél kevesebb, annál jobb. Ideális esetben — egy nyelv, egy keretrendszer, egy adatbázis, egy naplózási mód. Reálisan — 2–3 nyelv (egyértelmű szétválasztással), 1–2 adatbázis, 1–2 keretrendszer. Minden további technológia növeli a csapat kognitív terhelését és a karbantartás költségeit.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is