Code Smell är ett ytligt tecken i koden som signalerar ett potentiellt problem i applikationens design eller arkitektur. Termen introducerades av Kent Beck och populariserades av Martin Fowler i boken “Refactoring: Improving the Design of Existing Code”. Enligt Martin Fowler betyder kodlukt inte nödvändigtvis en bugg, men indikerar nästan alltid behovet av omfaktorering för att förbättra underhållbarheten.
Huvudsakligt
Code Smell (kodlukt) — är en metafor för symptom i källkoden som med hög sannolikhet indikerar djupare problem. Själva termen har ingen formell definition — det är en heuristik baserad på utvecklares erfarenhet. Martin Fowler och Kent Beck systematiserade 1999 för första gången 22 dofter i boken “Refactoring” och de flesta av dem förblir aktuella efter decennier.
Det är viktigt att förstå skillnaden mellan Code Smell och en bugg. Doften är inte ett fel: koden kompileras, fungerar och ger korrekt resultat. Problemet är att sådan kod är svår att läsa, ändra och testa. Med tiden ökar kostnaden för varje ändring och förtroendet för omfaktoreringens korrekthet minskar. Statiska analysverktyg (SonarQube, Detekt, SwiftLint) upptäcker automatiskt många dofter.
Code Smells heuristiska karaktär innebär att inte varje lång metod måste delas upp och inte varje stor klass kräver omfaktorering. Beslutet fattas av utvecklaren som bedömer sammanhanget: ändringsfrekvens, modulens kritikalitet, utvecklingsplaner. Erfarna ingenjörer känner doften intuitivt — kod “luktar obehagligt”, även om formellt alla regler följs.
Fowler identifierade 22 dofter som delas in i flera kategorier. För mobilutveckling är de mest relevanta strukturella dofter, objektorienterade designdofter och specifika problem relaterade till plattformsbegränsningar. Låt oss undersöka varje grupp med exempel från verklig praxis.
Long Method (lång metod) — den mest utbredda doften i mobilapplikationer. Skärmen med ett registreringsformulär innehåller ofta en enda setupUI-metod på 200+ rader som skapar alla vyer, ställer in begränsningar, prenumererar på händelser och hanterar fel. Lösning: dela upp i metoder efter logiska block — configureEmailField, configurePasswordField, setupConstraints, bindViewModel.
Large Class (stor klass) — Activity eller ViewController som ansvarar för visning, navigering, affärslogik och nätverkskommunikation. En sådan klass bryter mot Single Responsibility Principle och innehåller dussintals fält och metoder. I Android är detta ofta ett Fragment med 1000+ rader som innehåller logik från olika skärmar. Lösning: separera presenter/ViewModel, flytta nätverksarbete till repository, navigering till koordinator.
Duplicate Code (kodduplicering) — kopiering av samma block i olika delar av applikationen. Typiskt exempel: två skärmar som visar en produktkarta — i katalogen och i favoriter. Om visningslogiken är kopierad kommer korrigering av ett fel på ett ställe inte att åtgärda det på ett annat. Lösning: flytta gemensam logik till en återanvändbar komponent eller ett tillägg.
Feature Envy (avundsjuka på en annan klass) — en metods intensiva användning av en annan klass data. I Android visar detta sig när ViewModel direkt kommer åt fälten i User-modellen istället för att anropa en metod i modellen. Signal: om metoden kan flyttas till den klass vars data den använder — flytta den. Switch Statements (villkorskedjor) — switch-konstruktion eller if-else-kedja som kontrollerar objektets typ. Istället bör polymorfism eller strategy-mönster användas.
Data Class — en klass som bara lagrar data men inte innehåller beteende. Data class (i Kotlin) eller strukturer (i Swift) är i sig inte en doft. Problemet uppstår när affärslogik som arbetar med denna data är utspridd över hela kodbasen istället för att vara inkapslad. Refused Bequest — arvtagaren använder inte de flesta av förälderns metoder och åsidosätter dem med tomma implementeringar. Tecken på felaktigt arv: ersätt arv med komposition.
God Activity / God Fragment — Activity eller Fragment som vet allt: om livscykeln, data, navigering, behörigheter, DI. Detta är den dyraste klassen att underhålla i applikationen. Lösning: arkitekturmönster som MVVM, MVI eller Clean Architecture delar på ansvaret. Giant ViewController — analog för iOS, där UIViewController innehåller all logik på skärmen och ofta överstiger 500 rader.
Hardcoded Resources — strängar, färger, storlekar, API-URL:er inbäddade direkt i koden. I Android bryter detta mot användningen av R-resurssystemet, i iOS — NSLocalizedString och Asset Catalog. Åtgärd: flytta alla strängar till strings.xml eller Localizable.strings, URL:er till konfigurationsfil, storlekar till dimens. Leaking Context — att behålla en referens till Activity eller ViewController längre än komponenten själv lever. Ledare till minnesläckor och krascher. Lösning: svaga referenser, Jetpack Lifecycle, RxSwift DisposeBag.
| Doft | Var förekommer | Lösning |
|---|---|---|
| Long Method | Android/iOS | Extract Method, uppdelning |
| Large Class | Activity, ViewController | MVVM, VIPER, Clean Arch |
| Duplicate Code | Vilken skärm som helst | Shared Component, DRY |
| Feature Envy | ViewModel, Presenter | Move Method |
| Leaking Context | Android | Livscykelmedvetna komponenter |
Code review — det mest pålitliga sättet att upptäcka dofter. Det mänskliga ögat ser onaturliga konstruktioner som automatiska analysatorer missar. Effektiviteten av kodgranskning ökar när teamet använder en checklista för typiska dofter. Det rekommenderas att inte granska mer än 200–400 rader kod per session — efter denna tröskel minskar uppmärksamheten och dofter börjar undgå upptäckt.
Statisk analys automatiserar sökandet efter strukturella dofter. För Android är standardverktygen Detekt (Kotlin) och Android Lint, för iOS — SwiftLint och SonarQube. Dessa verktyg hittar långa metoder, stora klasser, kodduplicering och många andra problem. Det är viktigt att konfigurera reglerna för projektet — standardkonfigurationer är ofta för strikta eller tvärtom missar kritiska dofter.
Kodmetriker ger objektiva kriterier: Cyclomatic Complexity (tröskel >10 kräver uppmärksamhet), Lines of Code per Method (tröskel >30), Depth of Inheritance (>3 — anledning att fundera). Verktyg som CodeMetrics (Xcode) och Gradle Metrics Plugin bygger grafer över metrikförändringar över tid. Om en metods komplexitet har ökat från 5 till 15 efter den senaste commiten — är detta en signal för omfaktorering.
// Exempel: metod med Cyclomatic-komplexitet = 7 (över tröskel 5)
fun processOrder(order: Order) {
if (order.status == Status.NEW) { /* 10 rader */ }
else if (order.status == Status.PAID) { /* 15 rader */ }
else if (order.status == Status.SHIPPED) { /* 20 rader */ }
else if (order.status == Status.DELIVERED) { /* 8 rader */ }
else if (order.status == Status.CANCELLED) { /* 5 rader */ }
else { throw IllegalStateException() }
}
// Åtgärd: polymorfism istället för switch
interface OrderHandler {
fun handle(order: Order)
}
Automatisk sökning efter dofter ersätter inte kodgranskning: statiska analysatorer hittar bara strukturella problem men fångar inte semantiska dofter (Feature Envy, Inappropriate Intimacy). Kombinationen av automatiska verktyg och mänsklig kontroll ger bäst resultat. Konfigurera CI/CD-pipelinen så att bygget misslyckas när trösklar för komplexitet eller metodlängd överskrids.
Omfaktorering — den huvudsakliga metoden för att eliminera koddofter. Fowler beskriver dussintals omfaktoreringsmetoder, var och en tillämplig på en specifik doft. Extract Method — för långa metoder, Extract Class — för stora klasser, Move Method — för Feature Envy. Det är viktigt att utföra omfaktorering i små steg och bevara kodens funktionalitet efter varje ändring.
Tester före omfaktorering — ett obligatoriskt villkor. Om kod inte täcks av enhetstester förvandlas omfaktorering till omskrivning med okänt resultat. För äldre kod utan tester, använd Characterisation Tests — skriv tester som registrerar nuvarande beteende, omfaktorera sedan. Testning ger säkerhet att affärslogiken inte har skadats efter omfaktorering.
Gradvishet — nyckeln till framgångsrik eliminering av dofter inom mobilutveckling. Försök inte skriva om God Activity i sin helhet. Separera först navigeringslagret, sedan datalagret, därefter visningslogiken. Varje steg åtföljt av en commit och körning av tester. Använd en feature-toggle för att aktivera omfaktorering för en del av användarna och återställa vid problem.
IDE-verktyg automatiserar många omfaktoreringsmetoder. Android Studio och IntelliJ IDEA erbjuder inbyggda omfaktoreringar: Extract Method, Extract Interface, Pull Members Up, Encapsulate Fields. Xcode (från och med version 14) har förbättrat stödet för omfaktorering i Swift. Användning av automatiska omfaktoreringar minskar risken för fel jämfört med manuell kopiering av kod.
Mobilutveckling lägger till egna specifika dofter relaterade till plattformsbegränsningar. I Android är detta Context-läckor, ej stängd Cursor, felaktig användning av Lifecycle. I iOS — retain cycle genom closures, felaktigt arbete med Auto Layout, gigantiska ViewControllers. Dessa dofter försämrar inte bara underhållbarheten utan påverkar direkt applikationens prestanda och stabilitet.
Callback Hell — en karakteristisk doft för kod som arbetar med asynkrona operationer. Kapslade callback (callback inside callback) gör koden oläslig och svår att felsöka. Lösning: koroutiner (Kotlin), async/await (Swift 5.5+), RxJava/RxSwift eller Combine. Enligt Google I/O 2023 minskar projekt som har bytt från callback-stil till koroutiner antalet buggar med 30% och snabbar upp tillägg av nya funktioner.
Platform Coupling — stel koppling av affärslogik till plattformskomponenter. Testning av sådan logik kräver start av emulator, vilket saktar ner återkopplingsslingan. Åtgärd: Clean Architecture delar upp koden i lager Domain (ren Kotlin/Swift utan plattformsberoenden) och Data/UI (med plattformsberoenden). Affärslogik testas på JVM utan emulator.
Vanliga frågor
Nej — Code Smell är inte ett fel. Kod med doft fungerar korrekt men är svår att underhålla, ändra och testa. En bugg är felaktigt beteende, en doft är en varning om potentiella problem i framtiden.
22 dofter i den andra utgåvan av boken “Refactoring” (2019). Bland dem Long Method, Large Class, Primitive Obsession, Data Clumps, Switch Statements, Speculative Generality och andra. Communityn har lagt till dussintals nya dofter för moderna paradigm och plattformar.
Kombination ger bäst resultat: Detekt (Android/Kotlin), SwiftLint (iOS), SonarQube (båda) för automatisk analys och kodgranskning för semantiska dofter. Inget verktyg hittar 100% av problemen — mänsklig erfarenhet förblir avgörande.
Ja, om koden ändras sällan eller kommer att skrivas om helt inom den närmaste framtiden. Ansamling av dofter omvandlas dock till teknisk skuld: varje ny ändring blir allt svårare och kostnaden för åtgärd ökar exponentiellt.
Ja — deklarativa ramverk har skapat nya dofter: gigantiska @State-block, felaktigt arbete med upprepade renderingar, överdriven recomposition, bristande extraktion till separata vyer. För SwiftUI är den typiska doften Massive View med dussintals @State-variabler.
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å