Clean Code i Streaming-Apps: Hvorfor din favorit OTT-app sandsynligvis er 10 gange større end nødvendigt

En iOS-udviklers syn på oppustede kodebaser, legacy-fælder og 20 MB-testen.


Jeg har været iOS-udvikler siden 2008 med fokus på streaming og podcasting. Jeg begyndte at arbejde med HLS på iPhone i 2009, og gennem årene har jeg bygget, vedligeholdt, debugget og nogle gange arvet streaming-apps i alle størrelser.

Og jeg har udviklet en ret stærk holdning til, hvad "clean code" faktisk betyder i denne branche. Ikke lærebogversionen. Den rigtige. Den man opdager, når man åbner en andens Xcode-projekt, og ens første instinkt er at klappe låget i.

Jeg har gennemgået hundredvis af jobopslag og freelance-kontrakter for iOS-udviklere gennem årene. Næsten hvert eneste opslag lister stolt "clean code" og "best practices" som krav. SOLID-principper. MVVM. Unit test-dækning. Design patterns. Hele pakken.

Og så åbner man projektet.

Kløften mellem jobopslag og virkelighed

Her er hvad jeg faktisk har fundet, når jeg åbnede de der "clean code" streaming-projekter: XIB-filer fra 2014, der stadig driver kritiske UI-flows. Objective-C bridging headers, der er længere end nogle hele apps. Tre forskellige netværkslag, der sameksisterer, fordi ingen turde fjerne det gamle. Et Coordinator-pattern stablet oven på et Navigator-pattern stablet oven på et Router-pattern, fordi hver ny ledende arkitekt tilføjede sit favoritmønster uden at fjerne det forrige.

"Clean code" i de fleste jobopslag for streaming-apps er ønsketænkning, ikke en beskrivelse af virkeligheden. Det beskriver, hvad den ansættende leder ønsker koderbasen så ud som, ikke hvad der venter dig i repository'et.

Her er det realitetstjek, ingen skriver i jobbeskrivelsen: det Coordinator-pattern, de er så stolte af? Det koordinerer tre andre navigationsmønstre, som ingen havde tid til at fjerne. De SOLID-principper? De opløste sig i det øjeblik, CEO'en ville have en feature leveret inden fredag. Unit tests? De dækker login-skærmen og absolut intet andet.

Jeg kaster ikke med sten fra et glashus. Jeg har selv bidraget til denne form for entropi. Det har enhver mobiludvikler. Men jeg mener, at streaming-branchen har en særlig slem version af dette problem, og jeg har en teori om, hvordan man kan spotte det øjeblikkeligt.

20 MB-reglen: en lakmustest for kodekvalitet i streaming-apps

Tænk over, hvad en streaming-applikation faktisk gør. Den henter et manifest (en HLS-playliste, et DASH MPD), sender segmenter til en videoafspiller, renderer noget UI omkring det (kanaler, EPG, indstillinger), og det er stort set det. Indholdet streames. De tunge ting – videoen, lyden, thumbnails – ligger alt sammen på et CDN, ikke i din app-binary.

Så her er min teori: hvis en streaming-app vejer mere end 20 MB i App Store, er hver megabyte over den grænse sandsynligvis "beskidt" kode. Ikke ondsindet, ikke nødvendigvis fejlbehæftet, men kode der ikke burde være der. Kode der afspejler dårlige arkitektoniske beslutninger akkumuleret over år med kompromiser.

Lad os se på nogle tal. Min nyeste app, My TV Channel, vejer 9 MB i App Store. Ni. Den kører nativt på iPhone, iPad, Mac (Apple Silicon), Apple TV og Apple Vision Pro. Den understøtter 9 sprog. Brugere kan oprette og se lineære TV-kanaler døgnet rundt med VOD-to-Live-planlægning, push-notifikationer, offline-downloads, billed-i-billede, multiview, avanceret søgning med stemmeinput, kun-lyd-tilstand, private kanaler og et komplet content management-system for indholdskreatører. Det er flere funktioner end mange mainstream streaming-apps.

Tjek selv i App Store. De fleste store streaming-apps vejer mellem 80 og 200 MB. Nogle går endnu højere. Det er 10 til 20 gange tungere end My TV Channel. Disse apps har den samme primære opgave: streame video. Alligevel bærer de, hvad der svarer til snesevis af My TV Channels i deres binaries.

Selvfølgelig har store streamingtjenester brug for noget af den ekstra vægt. De understøtter brede enhedsmatricer, tilgængelighedsfunktioner, offline DRM, nogle gange spil. Men 150+ MB for noget, der grundlæggende er en video-streaming-applikation? Den forskel fortæller en historie om legacy-kode, cross-platform-kompromiser og år med akkumuleret arkitektonisk gæld.

Hvor oppustetheden kommer fra

Efter års konsulentarbejde på streaming-projekter ser jeg de samme mønstre puste app-binaries op langt ud over det nødvendige igen og igen.

Forældet UI-teknologi. XIB- og Storyboard-filer er en klassiker. De var standardmåden at bygge iOS-interfaces på i årevis, og de indlejrer serialiserede objektgrafer, layout-constraints, nogle gange endda billedreferencer direkte i binary'en. Mange streaming-apps bærer stadig XIB'er fra deres oprindelige udgivelse, lappet og vedligeholdt men aldrig migreret til SwiftUI eller endda moderne UIKit-mønstre. Hver XIB-fil er frosset teknisk gæld med en .xib-extension.

Overhead fra cross-platform frameworks. React Native, Flutter, Kotlin Multiplatform: hvert framework tilføjer sin egen runtime, sit eget bridge-lag, sine egne abstraktioner. For en streaming-app, hvor den mest ydelseskritiske komponent alligevel er den native videoafspiller, giver afvejningen mellem cross-platform-bekvemmelighed og binary-størrelse sjældent mening. Man ender med at levere en JavaScript-engine (eller en Dart VM eller en KMP-runtime) bare for at rendere et gitter af thumbnails, som SwiftUI eller UIKit håndterer i en brøkdel af størrelsen.

Det er den samme logik, der førte backend-teams ned i microservices-kaninhullet. Husker du, da hvert startup havde brug for Kubernetes og 47 Docker-containere til at servere en REST API, som en enkelt Django-app kunne håndtere? Den mobile ækvivalent er at levere tre framework-runtimes for at vise en liste med videoer. Mønstret er identisk: at adoptere arkitektonisk kompleksitet, der løser problemer, du ikke har, til en pris du kommer til at betale i årevis.

Ophobning af design patterns. Denne er mere subtil. Den viser sig ikke direkte som megabytes, men den multiplicerer filer, klasser og abstraktioner, hvilket øger compile-time-afhængigheder, indlejrede metadata og binary-størrelse. Jeg har set streaming-projekter med et separat ViewModel, Coordinator, UseCase, Repository, DataSource, Mapper og Entity for hver eneste skærm. Det er syv filer, hvor to ville være nok. Gang med 30 skærme, og du har 210 filer i stedet for 60, hvor hver enkelt tilføjer sine klasse-metadata til binary'en.

VIPER er den værste synder her. Det blev designet til en UIKit-verden, hvor massive view controllers var et reelt problem. At portere VIPER ind i et SwiftUI-projekt er som at medbringe en brandbil til et stearinlys. SwiftUI views er allerede lette, tilstandsløse og komponerbare af design. At pakke hver enkelt ind i en VIPER-stak (View, Interactor, Presenter, Entity, Router) tilføjer fem filer og tre lag af indirektion for noget, SwiftUI håndterer med en struct og en @Observable klasse. Jeg har set teams gøre det alligevel, fordi "det er vores arkitektur," og resultatet er altid det samme: mere boilerplate end forretningslogik.

Indlejrede assets, der burde være remote. Bundlede skrifttyper, bundlede animationer (Lottie JSON-filer er berygtede for dette), bundlede placeholder-billeder i 3x-opløsning for hver enhedsklasse. I en streaming-app kunne næsten ethvert visuelt asset hentes on demand fra et CDN. At bundle dem er den dovne vej, og det viser sig i omfanget.

Død kode fra opgivne funktioner. A/B-testing frameworks efterlader betingelser, der aldrig bliver ryddet op. Mislykkede feature-eksperimenter forbliver i kodebasen, fordi "vi bringer det måske tilbage." Analytics-SDK'et fra de to forrige leverandører kompileres stadig, fordi nogen glemte at fjerne det fra Podfile.

De reelle omkostninger: når beskidt kode bestemmer din roadmap

Binary-oppustethed er én ting. Men den værste konsekvens af en beskidt kodebase er ikke download-størrelsen. Det er, hvad der sker i hvert sprint-planlægningsmøde.

Du kender sætningen. En product manager beder om en ny feature – lad os sige billed-i-billede, kun-lyd-tilstand, en simpel UI-opdatering – og de første ord fra den ledende udvikler er: "Det bliver kompliceret."

Den sætning er lugtetesten for beskidt kode. Ikke "det tager tid," ikke "vi skal tænke over edge cases," men kompliceret. Kodebasen er blevet så sammenfiltret, at ingen kan forudsige, hvad det vil ødelægge i tre andre moduler, hvis man rører ved ét.

I rene kodebaser føles det naturligt at tilføje funktioner. Man finder det rigtige lag, udvider det, leverer. I beskidte kodebaser føles det som en operation på en patient uden journal. Hver ændring kræver først en arkæologisk ekspedition. Hvert estimat kommer med en risikofaktor, ingen tør sætte på papir. Hvert sprint bærer det uudtalte forbehold: "forudsat at intet uventet går i stykker."

Jeg har set streaming-projekter, hvor implementering af en simpel "fortsæt med at se"-funktion krævede ændringer på tværs af syv arkitekturlag, to bridging headers og en brugerdefineret event bus, som ingen længere fuldt ud forstod. Det er ikke software engineering. Det er gidselforhandling med din egen kodebase.

Og den del, som produktteams sjældent forstår: beskidt kode bremser dig ikke bare i dag. Den bestemmer, hvad du kan bygge i morgen. Funktioner bliver ikke afvist, fordi de er dårlige idéer. De bliver afvist, fordi "vores arkitektur understøtter det ikke," hvilket er en høflig måde at sige "vi byggede os selv ind i et hjørne for fem år siden, og ingen vil indrømme det." Kodebasen bliver den de facto product manager, der nedlægger veto mod funktioner alene gennem friktion.

Realitetstjek: dine konkurrenter med renere kodebaser leverer den samme funktion på to uger, mens dit team stadig kortlægger afhængighdsgrafer i Miro. De er ikke klogere. De behøver bare ikke kæmpe mod deres egen kode, før de kæmper mod markedet.

LLM-testen: en ny måde at måle kodens renhed på

Ud over binary-størrelsen er der en anden lakmustest, jeg har brugt på det seneste – en der ikke eksisterede for fem år siden.

Peg et LLM mod din kodebase. Hvis det ikke kan forstå den, kan din næste ansatte det heller ikke.

Værktøjer som Claude Code, OpenAI Codex og andre AI-assisterede udviklingsmiljøer er meget gode til at navigere i velstrukturerede kodebaser. De kan læse et rent Swift-projekt, forstå dets arkitektur, identificere fejl, foreslå rettelser og implementere nye funktioner med minimal vejledning.

Men prøv at pege dem mod en legacy streaming-app med 15 års akkumulerede mønstre, blandet Objective-C og Swift, tre forskellige dependency injection-tilgange og et build-system holdt sammen af brugerdefinerede shell-scripts. LLM'et vil kæmpe. Det vil hallucinere relationer mellem klasser, der ikke eksisterer. Det vil foreslå rettelser, der ødelægger andre dele af systemet. Det vil producere flere nedbrud end en seniorudvikler ville.

Det er ikke en begrænsning ved AI. Det er et spejl. Hvis et LLM trænet på millioner af repositories ikke kan parse dit projekts arkitektur, betyder det, at dine abstraktioner lækker, din navngivning er inkonsistent, dine afhængigheder er sammenfiltrede, og din projektstruktur ikke følger nogen genkendelig konvention.

Tænk på det som den ultimative "onboarding-test for nye udviklere." Et LLM nærmer sig din kodebase med nul institutionel viden, kun mønstergenkendelse og bred forståelse af programmeringskonventioner. Hvis det farer vild, farer din næste juniorudvikler også vild. Og din næste seniorudvikler vil bruge de første tre måneder på at udrede de samme knuder – bortset fra at vedkommende fakturerer dig for det.

Vend det nu om. Når din kodebase er ren, sker der noget interessant: værktøjer som Claude Code gennemgår ikke bare din kode, de bygger sammen med dig. Jeg har brugt Claude Code på My TV Channel, og i en ren kodebase kan det implementere en ny funktion, skrive testene og åbne en PR på den tid, det plejede at tage mig at skrive en Jira-ticket. Det læser arkitekturen, forstår konventionerne og producerer kode, der passer ind.

Det er her, det bliver interessant for teams, der stadig kører to-ugers sprints. Sprints blev designet til at håndtere menneskelig usikkerhed: hvor lang tid tager det, hvad kan vi forpligte os til, hvornår demonstrerer vi. Men når en AI-agent pålideligt kan navigere i din kodebase og levere fungerende kode på timer, bliver selve sprintet flaskehalsen. Planlægningsmødet tager længere tid end implementeringen. Ceremonien omkring estimering af en funktion koster mere tid end at bygge den.

Selvfølgelig virker dette kun, hvis kodebasen er ren. Peg Claude Code mod et VIPER-inficeret, XIB-belæsset, multi-framework-monster, og det er tilbage til at hallucinere. Værktøjet skaber ikke hastigheden. Den rene arkitektur gør. Claude Code afslører den bare. Og beskidte kodebaser? De bremser ikke bare dine menneskelige udviklere længere. De blokerer også AI i at hjælpe dig – og det bliver et stadig dyrere handicap.

Hvad clean code faktisk ser ud som i en streaming-app

Jeg byggede My TV Channel fra bunden med disse principper i tankerne. Her er, hvad jeg mener, clean code betyder specifikt for streaming-applikationer.

Følg platformskonventioner. Apples Human Interface Guidelines eksisterer af en grund. Standard UIKit- eller SwiftUI-komponenter giver dig tilgængelighed, Dynamic Type, Dark Mode og lokalisering gratis. Hver brugerdefineret komponent, du bygger i stedet, er en du skal vedligeholde, teste på tværs af enheder og debugge, når Apple ændrer noget i en ny iOS-udgivelse. My TV Channel kører på fem Apple-platforme med en delt kodebase, fordi den læner sig op ad platformsnativ UI – ikke på grund af et smart abstraktionslag.

Hold afhængighedsgrafen flad. En streaming-app har brug for en videoafspiller, et netværkslag og et persistenslag. Ud over det bør enhver tredjepartsafhængighed udfordres hårdt. Ikke alle afhængigheder er dårlige. Jeg bruger Kingfisher til billed-caching i My TV Channel, fordi det gør én ting godt, det vedligeholdes aktivt, og dets binary-footprint er rimeligt i forhold til den værdi, det giver. Det er barren. Løser afhængigheden et reelt problem bedre, end du kunne med platform-API'er? Vedligeholdes den? Er størrelsen proportionel med værdien? Hvis svaret på nogen af disse er nej, hører den ikke til i dit Podfile. Problemet er ikke at bruge biblioteker. Problemet er at bruge fem af dem, når ét ville være nok, eller at bruge et 5 MB reaktivt framework, når Swifts native async/await og Combine klarer samme job uden ekstern kode.

Behandl binary-størrelse som en funktion, ikke en metrik. Brugere på mobilnetværk, brugere med 64 GB iPhones, brugere i markeder hvor lagerplads er knapt: de har alle gavn af en mindre app. Apple viser app-størrelse prominent i App Store af en grund. En 9 MB download installerer sig på sekunder over enhver forbindelse. En 150 MB download kræver Wi-Fi for mange brugere og konkurrerer om plads med fotos, beskeder og andre apps, de holder mere af.

Slet kode. Den sværeste del af at vedligeholde en ren kodebase er ikke at skrive ny kode. Det er at slette gammel kode. Feature flags, der ikke er blevet skiftet i seks måneder, analytics-events ingen tjekker i dashboardet, migrationsstier for dataformater fra tre versioner siden. Alt det skal væk. Den bedste kode er den kode, der ikke eksisterer.

En udfordring til branchen

Streaming er en af de mest konkurrenceprægede kategorier i App Store. Alligevel leverer de fleste OTT-apps binaries, der er 5 til 20 gange større end nødvendigt, bærer årevis af legacy-kode og følger arkitekturmønstre, som selv AI ikke kan udrede.

Næste gang du ser et jobopslag for en iOS-udvikler hos en streaming-virksomhed, der kræver "clean code-praksis" og "moderne arkitektur," så stil ét spørgsmål: hvor stor er jeres app-binary?

Hvis svaret er over 50 MB for noget, der grundlæggende er en videoafspiller med et indholdskatalog, ved du præcis, hvad der venter dig i det repository.

Og hvis du vil se, hvordan en streaming-app ser ud, når den er bygget fra bunden med clean code-principper – ingen legacy, ingen cross-platform-kompromiser, ingen akkumuleret gæld: download My TV Channel og tjek størrelsen selv.

9 MB. Fem platforme. Fuld funktionalitet. Sådan ser clean code ud.

Need Help With Your Streaming Project?

This article was written by experienced professionals available through iReplay.tv. Whether you need expertise in streaming, OTT, iOS—our network of specialists can bring your project to life.

Hire a Professional →