Frankenstein inom programmering är kod som är sammansatt av inkompatibla delar från olika teknologier, stilar och arkitekturer. Enligt forskning från ThoughtWorks Technology Radar (2024) uppvisar 28 % av stora projekt tecken på Frankensteinsyndrom — arkitektonisk eklekticism som uppstår när en enhetlig teknisk vision saknas. I analogi med Mary Shelleys roman fungerar sådan kod, men underhållet blir en mardröm.
Huvudpunkter
Frankenstein (Frankenstein code, Frankenstein pattern) är ett antipattern där ett mjukvarusystem byggs upp av delar som inte är avsedda att fungera tillsammans. Precis som Frankensteins monster kan sådan kod fungera, men den är ful, oförutsägbar och farlig vid minsta förändring.
Termen kommer från litteraturen: i Mary Shelleys roman ”Frankenstein, eller den moderne Prometheus” (1818) skapade en vetenskapsman en levande varelse av fragment från olika döda människors kroppar. Inom programmering är analogin träffande — utvecklare tar bitar av olika ramverk, bibliotek, språk och limmar ihop dem, vilket ger ett fungerande men monstruöst resultat.
Skillnaden mellan Frankenstein och spagettikod ligger i problemets skala och natur. Spagettikod är en trasslig struktur inom en enda teknikstack. Frankenstein är eklekticism på arkitekturnivå: olika teknologier, inkompatibla paradigm, motstridiga angreppssätt inom ett och samma system.
Mikrotjänst-arkitektur tillåter användning av olika teknologier för olika tjänster, men under förutsättning av tydliga gränser och standardiserade kommunikationsprotokoll. Frankenstein är kaotisk blandning utan gränser: REST och GraphQL i samma kontroller, två ORM i samma modul, SQL och NoSQL för samma entitet.
Avsaknad av en teknisk ledare eller arkitekt är grundorsaken. När det i ett projekt inte finns någon ansvarig för arkitekturens integritet väljer varje utvecklare verktyg ”för sig själv”. En gillar Spring, den andra Guice, den tredje en egen DI. Resultatet blir en arkitektonisk blandning.
Sammanslagning av projekt är den andra vanliga orsaken. Två team utvecklade sina moduler oberoende med olika stackar. När modulerna ska slås samman till en applikation ”limmas” de ihop med adaptrar och mellanlager. Resultatet blir Frankenstein.
Företagsförvärv är det tredje scenariot. Företag A köpte företag B och vill integrera dess produkt i sin egen. Istället för omskrivning — ihoplimning via API, delade databaser och tillfälliga lösningar. Inom ett år förvandlas systemet till ett monster som ingen förstår.
| Orsak | Beskrivning | Typiskt resultat |
|---|---|---|
| Ingen arkitekt | Varje utvecklare väljer sin egen stack | 3 olika HTTP-klienter i en modul |
| Projektsammanslagning | Två produkter slås ihop till en | Två ORM, två sätt att logga |
| M&A | Förvärv av företag med dess produkt | Hybrid av olika arkitekturer och stilar |
| Experiment | Introduktion av ny teknik utan strategi | Java 8 + Java 21-funktioner i en fil |
| Politiska beslut | Påtvingande av teknik uppifrån utan kontext | Enterprise-ramverk för enkelt skript |
Erfarna utvecklare som vill prova ny teknik i produktion blir ofta en källa till Frankenstein. Istället för att begränsa experiment till en isolerad modul inför de experimentell kod i en kritisk del av systemet.
Ett klassiskt exempel är användning av flera ORM i en applikation. Vissa moduler använder Hibernate, andra MyBatis och ytterligare andra direkta JDBC-frågor. Transaktioner blir ohanterliga, cache blir inkonsekvent och en ny utvecklare vet inte vilket angreppssätt som ska väljas för en ny funktion.
Ett andra exempel är blandning av arkitektoniska stilar. I en REST API-kontroller förekommer SOAP-tjänsteanrop, direkta SQL-frågor, filsystemåtkomst och HTML-generering. En sådan applikation går inte att testa, utöka eller dokumentera.
Ett tredje exempel är en teknikstack där Python används för backend, Node.js för en mikrotjänst, C# för en desktopklient och Java för en Android-app, medan all affärslogik är utspridd mellan dem utan tydlig ansvarsfördelning.
// frankenstein — blandade stilar och teknologier
// callbacks, Promises och async/await kombinerade
// callbacks
db.query("SELECT * FROM users", function(err, rows) {
if (err) handleError(err);
// Promise inuti callback
fetch("/api/data").then(function(data) {
// async/await inuti then
(async () => {
const result = await processData(data);
sendResponse(result);
})();
});
});
// ren kod — enhetlig async/await-stil
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);
}
En databas används samtidigt som SQL-relationell (med normalisering) och som NoSQL-dokumentorienterad (med JSON-kolumner). En del av frågorna går via ORM, en del via lagrade procedurer, en del via direkt SQL från koden. Databasschemat är inte dokumenterat, migreringar kolliderar.
Komplexiteten i onboarding är den första konsekvensen. En ny utvecklare måste kunna 5 språk, 3 ramverk, 2 arkitektoniska stilar för att förstå hur systemet fungerar. Onboarding drar ut från veckor till månader. Enligt LinkedIn (2023) förlorar projekt med teknologisk eklekticism nya medarbetare 2 gånger oftare.
Oförutsägbarhet i beteende är den andra konsekvensen. En förändring i en Python-mikrotjänst kan oväntat bryta en Java-modul eftersom de använder en delad databas utan tydliga kontrakt. Felsökning av sådana problem kräver samtidig kunskap om alla teknologier i stacken.
Säkerhet är den tredje konsekvensen. Varje teknik i stacken kräver sin egen säkerhetskonfiguration, sina egna patchar, sin egen övervakning. Att hålla säkerheten på en acceptabel nivå för 5–6 olika tekniker är praktiskt taget omöjligt. En av dem kommer oundvikligen att vara sårbar.
SonarQube kan mäta teknisk skuld, men kan inte mäta ”arkitektonisk skuld” — inkompatibilitet hos komponenter. Denna skuld visar sig inte i lintervarningar, utan i omöjligheten att lägga till en ny funktion utan att ändra tre olika moduler skrivna i olika teknologier.
Det första och viktigaste steget är att utse en arkitekt eller tech lead som är ansvarig för teknikstackens integritet. Denna person har vetorätt mot införande av ny teknik utan arkitektonisk granskning. Inte demokrati, utan ett ansvarsfullt enskilt beslut om nyckelteknik.
Det andra — inför processen Architecture Decision Record (ADR). Varje betydande arkitektoniskt beslut (val av databas, ramverk, protokoll) dokumenteras som en kort text: kontext, övervägda alternativ, fattat beslut, konsekvenser. ADR lagras i repositories och är tillgängliga för hela teamet.
Det tredje — fastställ principen ”en uppgift — ett verktyg”. För HTTP-förfrågningar — en klient. För ORM — ett bibliotek. För loggning — ett ramverk. Undantag tillåts endast via ADR med motivering. Om Axios redan finns i projektet — lägg inte till fetch, om SLF4J finns — skriv inte via System.out.
Experiment är tillåtna, men i en isolerad miljö. Avsätt en modul eller tjänst som kan skrivas om med ny teknik utan att påverka resten av systemet. Om experimentet lyckas — standardisera det via ADR. Om inte — ta bort det utan konsekvenser.
Inventering är det första steget. Skapa en fullständig karta över teknikstacken: vilka ramverk, bibliotek, språk, protokoll som används, i vilka moduler och för vilka uppgifter. Du kommer att se omfattningen av problemet: dubbla verktyg, motstridiga teknologier, oanvända beroenden.
Standardisering är det andra steget. Välj ett verktyg för varje uppgift. Till exempel: endast Hibernate för ORM, endast SLF4J + Logback för loggning, endast REST för API. Dokumentera standarden i ADR. Börja bytet i moduler där eklekticism orsakar flest problem.
Strategin Parallel Run är det tredje steget. Gamla och nya verktyg fungerar parallellt tills det nya bevisar sin tillförlitlighet. Till exempel fungerar den gamla HTTP-klienten och den nya samtidigt, men den nya endast för en del av förfrågningarna. Efter en stabiliseringsperiod tas den gamla bort.
// frankenstein — tre HTTP-metoder i ett projekt
// Modul A: OkHttp
OkHttpClient client = new OkHttpClient().newCall(request);
// Modul B: RestTemplate (Spring)
restTemplate.getForObject(url, String.class);
// Modul C: java.net.HttpURLConnection
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
// enhetlig metod: RestTemplate för sync, WebClient för reactive
@Autowired
private RestTemplate restTemplate;
public String callApi(String url) {
return restTemplate.getForObject(url, String.class);
}
Den tekniska ledaren är det främsta verktyget i kampen mot Frankenstein. Inte en chef, inte en arkitekt i ett elfenbenstorn, utan en praktiserande utvecklare som skriver kod, granskar PR och fattar arkitektoniska beslut. Utan en sådan person glider projektet oundvikligen mot teknologisk eklekticism.
RFC (Request for Comments) är en process lånad från open source-communityn. Innan införande av någon betydande teknik skriver författaren en RFC: problem, föreslagen lösning, alternativ, implementationsplan. Teamet diskuterar, röstar, accepterar eller avslår. RFC skapar transparens och förhindrar ”tysta” arkitektoniska beslut.
Teknikradarn (Technology Radar från ThoughtWorks) är ett verktyg för kategorisering av teknik: Adopt, Trial, Assess, Hold. Teamet granskar regelbundet radarn och uppdaterar statusar. Detta hjälper att skilja ”modernt” från ”nyttigt” och undvika att införa oprövad teknik i kritisk kod.
Den viktigaste egenskapen hos arkitektur är konsekvens (consistency). Även ett inte bästa verktyg som används i hela projektet är bättre än det bästa verktyget som endast används i en modul. Konsekvens minskar kognitiv belastning, förenklar onboarding och gör koden förutsägbar.
Vanliga frågor
Polyglot persistence är medveten användning av olika databaser för olika uppgifter (PostgreSQL för transaktioner, Redis för cache, Elasticsearch för sökning). Frankenstein är kaotisk blandning utan strategi. Skillnaden ligger i förekomsten av ett arkitektoniskt beslut: polyglot är en plan, Frankenstein är frånvaron av en.
Ja, och det är ett vanligt problem. När varje mikrotjänst använder sitt eget språk, sin egen databas, sitt eget protokoll och sin egen metod för driftsättning utan centraliserade standarder — uppstår en distribuerad Frankenstein. För mikrotjänster är gemensamma standarder viktiga: enhetligt protokoll (REST/gRPC), enhetligt loggformat, centraliserad observability.
Förbjud inte — vägled. Be RFC-författaren: beskriv varför den befintliga lösningen inte är lämplig, vilka alternativ som övervägts, hur migreringen kommer att genomföras. Ofta under skrivandet av RFC inser utvecklaren själv att den nya tekniken inte behövs. Om RFC är övertygande — implementera, men med plan och begränsningar.
Först inventering, sedan standardisering. Försök inte skriva om allt på en gång. Välj ett lager (till exempel HTTP-klienter eller loggning), välj ett enhetligt verktyg, skriv en ADR och migrera gradvis. Strangler Fig-metoden — ersätt gamla komponenter en efter en utan att stoppa applikationen.
Ju färre, desto bättre. Idealiskt — ett språk, ett ramverk, en databas, ett sätt att logga. Realistiskt — 2–3 språk (med tydlig separation), 1–2 databaser, 1–2 ramverk. Varje ytterligare teknik ökar teamets kognitiva belastning och underhållskostnader.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också