Clean Code i streaming-apper: Hvorfor favoritt-OTT-appen din sannsynligvis er 10 ganger større enn nødvendig

En iOS-utviklers syn på oppblåste kodebaser, legacy-feller og 20 MB-testen.


Jeg har vært iOS-utvikler siden 2008, med fokus på streaming og podcasting. Jeg begynte å jobbe med HLS på iPhone i 2009, og gjennom årene har jeg bygget, vedlikeholdt, feilsøkt og noen ganger arvet streaming-apper i alle størrelser.

Og jeg har utviklet en ganske sterk mening om hva "clean code" faktisk betyr i denne bransjen. Ikke lærebokversjonen. Den virkelige. Den du oppdager når du åpner noen andres Xcode-prosjekt og det første instinktet ditt er å lukke lokket.

Jeg har gjennomgått hundrevis av stillingsannonser og frilanskontrakter for iOS-utviklere gjennom årene. Nesten hver eneste en lister stolt "clean code" og "best practices" som krav. SOLID-prinsipper. MVVM. Enhetstestdekning. Design patterns. Hele pakken.

Og så åpner du prosjektet.

Gapet mellom stillingsannonser og virkelighet

Her er hva jeg faktisk har funnet når jeg åpnet de "clean code" streaming-prosjektene: XIB-filer fra 2014 som fortsatt driver kritiske UI-flyter. Objective-C bridging headers som er lengre enn noen hele apper. Tre forskjellige nettverkslag som sameksisterer fordi ingen turte fjerne det gamle. Et Coordinator-pattern stablet oppå et Navigator-pattern stablet oppå et Router-pattern, fordi hver ny ledende arkitekt la til sitt favorittmønster uten å fjerne det forrige.

"Clean code" i de fleste stillingsannonser for streaming-apper er ønsketenkning, ikke en beskrivelse av virkeligheten. Det beskriver hva den ansettende lederen ønsker at kodebasen så ut som, ikke hva som venter deg i repositoryet.

Her er realitetssjekken ingen skriver i stillingsbeskrivelsen: det Coordinator-patternet de er så stolte av? Det koordinerer tre andre navigasjonsmønstre som ingen hadde tid til å fjerne. De SOLID-prinsippene? De løste seg opp i det øyeblikket CEO-en ville ha en funksjon levert innen fredag. Enhetstestene? De dekker innloggingsskjermen og absolutt ingenting annet.

Jeg kaster ikke stein fra et glasshus. Jeg har selv bidratt til denne typen entropi. Det har enhver mobilutvikler. Men jeg mener at streaming-bransjen har en spesielt ille versjon av dette problemet, og jeg har en teori om hvordan man kan oppdage det umiddelbart.

20 MB-regelen: en lakmustest for kodekvalitet i streaming-apper

Tenk over hva en streaming-applikasjon faktisk gjør. Den henter et manifest (en HLS-spilleliste, et DASH MPD), sender segmenter til en videoavspiller, rendrer litt UI rundt det (kanaler, EPG, innstillinger), og det er stort sett det. Innholdet strømmes. De tunge tingene – videoen, lyden, miniatyrbildene – alt ligger på et CDN, ikke i app-binaryen din.

Så her er teorien min: hvis en streaming-app veier mer enn 20 MB i App Store, er hver megabyte over den grensen sannsynligvis "skitten" kode. Ikke ondsinnet, ikke nødvendigvis feilaktig, men kode som ikke burde være der. Kode som gjenspeiler dårlige arkitektoniske beslutninger akkumulert over år med kompromisser.

La oss se på noen tall. Min nyeste app, My TV Channel, veier 9 MB i App Store. Ni. Den kjører nativt på iPhone, iPad, Mac (Apple Silicon), Apple TV og Apple Vision Pro. Den støtter 9 språk. Brukere kan opprette og se lineære TV-kanaler døgnet rundt, med VOD-to-Live-planlegging, push-varsler, nedlastinger for frakoblet bruk, bilde-i-bilde, multiview, avansert søk med stemmeinndata, kun-lyd-modus, private kanaler og et komplett innholdsstyringssystem for skapere. Det er flere funksjoner enn mange mainstream streaming-apper.

Sjekk App Store selv. De fleste store streaming-apper veier mellom 80 og 200 MB. Noen går enda høyere. Det er 10 til 20 ganger tyngre enn My TV Channel. Disse appene har den samme primære oppgaven: strømme video. Likevel bærer de tilsvarende dusinvis av My TV Channels i binariene sine.

Selvfølgelig trenger store strømmetjenester noe av den ekstra vekten. De støtter brede enhetsmatriser, tilgjengelighetsfunksjoner, offline DRM, noen ganger spill. Men 150+ MB for det som i bunn og grunn er en videostrømmeapplikasjon? Det gapet forteller en historie om legacy-kode, kompromisser på tvers av plattformer og år med akkumulert arkitektonisk gjeld.

Hvor oppblåstheten kommer fra

Etter års konsulentarbeid på strømmeprosjekter ser jeg de samme mønstrene blåse opp app-binaries langt utover det nødvendige, gang på gang.

Utdatert UI-teknologi. XIB- og Storyboard-filer er en klassiker. De var standardmåten å bygge iOS-grensesnitt på i årevis, og de bygger inn serialiserte objektgrafer, layout-begrensninger, noen ganger til og med bildereferanser direkte i binaryen. Mange streaming-apper bærer fortsatt XIB-er fra den opprinnelige utgivelsen, lappet og vedlikeholdt men aldri migrert til SwiftUI eller engang moderne UIKit-mønstre. Hver XIB-fil er frosset teknisk gjeld med en .xib-utvidelse.

Overhead fra cross-platform-rammeverk. React Native, Flutter, Kotlin Multiplatform: hvert rammeverk legger til sin egen kjøretid, sitt eget bro-lag, sine egne abstraksjoner. For en streaming-app der den mest ytelseskritiske komponenten uansett er den native videoavspilleren, gir avveiningen mellom cross-platform-bekvemmelighet og binarystørrelse sjelden mening. Du ender opp med å levere en JavaScript-motor (eller en Dart VM, eller en KMP-kjøretid) bare for å rendere et rutenett av miniatyrbilder som SwiftUI eller UIKit håndterer i en brøkdel av størrelsen.

Det er den samme logikken som førte backend-team ned i mikrojeneste-kaninhullet. Husker du da hvert startup trengte Kubernetes og 47 Docker-containere for å servere en REST API som en enkelt Django-app kunne håndtere? Den mobile ekvivalenten er å levere tre rammeverk-kjøretider for å vise en liste med videoer. Mønsteret er identisk: å adoptere arkitektonisk kompleksitet som løser problemer du ikke har, til en pris du kommer til å betale i årevis.

Opphopning av design patterns. Denne er mer subtil. Den viser seg ikke direkte som megabytes, men den multipliserer filer, klasser og abstraksjoner, noe som øker kompileringstidsavhengigheter, innebygde metadata og binarystørrelse. Jeg har sett strømmeprosjekter med en separat ViewModel, Coordinator, UseCase, Repository, DataSource, Mapper og Entity for hver eneste skjerm. Det er syv filer der to ville vært nok. Gang med 30 skjermer, og du har 210 filer i stedet for 60, der hver eneste legger til sine klassemetadata i binaryen.

VIPER er den verste synderen her. Det ble designet for en UIKit-verden der massive view controllers var et reelt problem. Å portere VIPER inn i et SwiftUI-prosjekt er som å ta med en brannbil til et stearinlys. SwiftUI-views er allerede lette, tilstandsløse og komponerbare av design. Å pakke hver enkelt inn i en VIPER-stabel (View, Interactor, Presenter, Entity, Router) legger til fem filer og tre lag med indireksjon for noe SwiftUI håndterer med en struct og en @Observable-klasse. Jeg har sett team gjøre det likevel fordi "det er arkitekturen vår," og resultatet er alltid det samme: mer boilerplate enn forretningslogikk.

Innebygde ressurser som burde vært eksterne. Medfølgende skrifttyper, medfølgende animasjoner (Lottie JSON-filer er beryktede for dette), medfølgende plassholder-bilder i 3x-oppløsning for hver enhetsklasse. I en streaming-app kunne nesten alle visuelle ressurser blitt hentet på forespørsel fra et CDN. Å pakke dem med er den late veien, og det viser seg i omfanget.

Død kode fra forlatte funksjoner. A/B-testing-rammeverk etterlater betingelser som aldri blir ryddet opp. Mislykkede funksjonseksperimenter forblir i kodebasen fordi "vi tar det kanskje tilbake." Analytics-SDK-et fra de to forrige leverandørene kompileres fortsatt fordi noen glemte å fjerne det fra Podfile.

Den virkelige kostnaden: når skitten kode bestemmer veiviseren din

Oppblåst binary er én ting. Men den verste konsekvensen av en skitten kodebase er ikke nedlastingsstørrelsen. Det er hva som skjer i hvert sprintplanleggingsmøte.

Du kjenner setningen. En produktsjef ber om en ny funksjon – la oss si bilde-i-bilde, kun-lyd-modus, en enkel UI-oppdatering – og de første ordene fra den ledende utvikleren er: "Det blir komplisert."

Den setningen er luktetesten for skitten kode. Ikke "det tar tid," ikke "vi må tenke gjennom kanttilfeller," men komplisert. Kodebasen har blitt så sammenfiltret at ingen kan forutsi hva det vil ødelegge i tre andre moduler hvis man berører én.

I rene kodebaser føles det naturlig å legge til funksjoner. Du finner det riktige laget, utvider det, leverer. I skitne kodebaser føles det som kirurgi på en pasient uten journal. Hver endring krever en arkeologisk ekspedisjon først. Hvert estimat kommer med en risikofaktor ingen tør sette på papiret. Hvert sprint bærer det usagte forbeholdet: "forutsatt at ingenting uventet går i stykker."

Jeg har sett strømmeprosjekter der implementering av en enkel "fortsett å se"-funksjon krevde endringer på tvers av syv arkitekturlag, to bridging headers og en egendefinert hendelsebuss som ingen lenger fullt ut forsto. Det er ikke programvareutvikling. Det er gisselforhandling med din egen kodebase.

Og den delen som produktteam sjelden forstår: skitten kode bremser deg ikke bare i dag. Den bestemmer hva du kan bygge i morgen. Funksjoner blir ikke avvist fordi de er dårlige idéer. De blir avvist fordi "arkitekturen vår støtter det ikke," som er en høflig måte å si "vi bygde oss selv inn i et hjørne for fem år siden, og ingen vil innrømme det." Kodebasen blir den de facto produktsjefen som legger ned veto mot funksjoner gjennom friksjon alene.

Realitetssjekk: konkurrentene dine med renere kodebaser leverer den samme funksjonen på to uker mens teamet ditt fortsatt kartlegger avhengighetsgrafer i Miro. De er ikke smartere. De trenger bare ikke å kjempe mot sin egen kode før de kjemper mot markedet.

LLM-testen: en ny måte å måle kodens renhet på

Utover binarystørrelse finnes det en annen lakmustest jeg har brukt i det siste – en som ikke fantes for fem år siden.

Pek et LLM mot kodebasen din. Hvis det ikke kan forstå den, kan heller ikke din neste ansatte det.

Verktøy som Claude Code, OpenAI Codex og andre AI-assisterte utviklingsmiljøer er veldig gode til å navigere i velstrukturerte kodebaser. De kan lese et rent Swift-prosjekt, forstå arkitekturen, identifisere feil, foreslå rettelser og implementere nye funksjoner med minimal veiledning.

Men prøv å peke dem mot en legacy streaming-app med 15 års akkumulerte mønstre, blandet Objective-C og Swift, tre forskjellige tilnærminger til dependency injection og et byggesystem holdt sammen av egendefinerte shell-skript. LLM-en vil slite. Den vil hallusinere relasjoner mellom klasser som ikke eksisterer. Den vil foreslå rettelser som ødelegger andre deler av systemet. Den vil produsere flere krasj enn en seniorutvikler ville.

Det er ikke en begrensning ved AI. Det er et speil. Hvis et LLM trent på millioner av repositorier ikke kan analysere prosjektets arkitektur, betyr det at abstraksjonene dine lekker, navngivningen er inkonsistent, avhengighetene er sammenfiltret, og prosjektstrukturen ikke følger noen gjenkjennelig konvensjon.

Tenk på det som den ultimate "onboarding-testen for nye utviklere." Et LLM nærmer seg kodebasen din med null institusjonell kunnskap, bare mønstergjenkjenning og bred forståelse av programmeringskonvensjoner. Hvis det går seg vill, vil din neste juniorutvikler også gå seg vill. Og din neste seniorutvikler vil bruke de første tre månedene på å nøste opp de samme flokene – bortsett fra at vedkommende fakturerer deg for det.

Snu det nå. Når kodebasen din er ren, skjer det noe interessant: verktøy som Claude Code gjennomgår ikke bare koden din, de bygger sammen med deg. Jeg har brukt Claude Code på My TV Channel, og i en ren kodebase kan det implementere en ny funksjon, skrive testene og åpne en PR på den tiden det pleide å ta meg å skrive en Jira-sak. Det leser arkitekturen, forstår konvensjonene og produserer kode som passer inn.

Det er her det blir interessant for team som fortsatt kjører toukers sprinter. Sprinter ble designet for å håndtere menneskelig usikkerhet: hvor lang tid tar det, hva kan vi forplikte oss til, når demonstrerer vi. Men når en AI-agent pålitelig kan navigere i kodebasen din og levere fungerende kode på timer, blir selve sprinten flaskehalsen. Planleggingsmøtet tar lengre tid enn implementeringen. Seremonien rundt estimering av en funksjon koster mer tid enn å bygge den.

Selvfølgelig fungerer dette bare hvis kodebasen er ren. Pek Claude Code mot et VIPER-infisert, XIB-belastet, multi-rammeverk-monster, og det er tilbake til å hallusinere. Verktøyet skaper ikke hastigheten. Den rene arkitekturen gjør det. Claude Code avslører den bare. Og skitne kodebaser? De bremser ikke bare de menneskelige utviklerne dine lenger. De blokkerer også AI fra å hjelpe deg – og det blir et stadig dyrere handikap.

Hva clean code faktisk ser ut som i en streaming-app

Jeg bygde My TV Channel fra bunnen av med disse prinsippene i tankene. Her er hva jeg mener clean code betyr spesifikt for strømmeapplikasjoner.

Følg plattformkonvensjoner. Apples Human Interface Guidelines eksisterer av en grunn. Standard UIKit- eller SwiftUI-komponenter gir deg tilgjengelighet, Dynamic Type, Dark Mode og lokalisering gratis. Hver egendefinerte komponent du bygger i stedet, er en du må vedlikeholde, teste på tvers av enheter og feilsøke når Apple endrer noe i en ny iOS-utgivelse. My TV Channel kjører på fem Apple-plattformer med en delt kodebase fordi den lener seg på plattformnativ UI – ikke på grunn av et smart abstraksjonslag.

Hold avhengighetsgrafen grunn. En streaming-app trenger en videoavspiller, et nettverkslag og et persistenslag. Utover det bør enhver tredjepartsavhengighet utfordres grundig. Ikke alle avhengigheter er dårlige. Jeg bruker Kingfisher for bildebufring i My TV Channel fordi det gjør én ting godt, det vedlikeholdes aktivt, og dets binære fotavtrykk er rimelig i forhold til verdien det gir. Det er listen. Løser avhengigheten et reelt problem bedre enn du kunne med plattform-API-er? Vedlikeholdes den? Er størrelsen proporsjonal med verdien? Hvis svaret på noen av disse er nei, hører den ikke hjemme i Podfile. Problemet er ikke å bruke biblioteker. Problemet er å bruke fem av dem når ett ville vært nok, eller å bruke et 5 MB reaktivt rammeverk når Swifts native async/await og Combine gjør den samme jobben uten ekstern kode.

Behandle binarystørrelse som en funksjon, ikke en metrikk. Brukere på mobilnett, brukere med 64 GB iPhones, brukere i markeder der lagringsplass er knapt: alle har nytte av en mindre app. Apple viser appstørrelse fremtredende i App Store av en grunn. En 9 MB nedlasting installeres på sekunder over enhver tilkobling. En 150 MB nedlasting krever Wi-Fi for mange brukere og konkurrerer om plass med bilder, meldinger og andre apper de bryr seg mer om.

Slett kode. Den vanskeligste delen av å vedlikeholde en ren kodebase er ikke å skrive ny kode. Det er å slette gammel kode. Feature-flagg som ikke har blitt endret på seks måneder, analysehendelser ingen sjekker i dashbordet, migreringsstier for dataformater fra tre versjoner siden. Alt dette må bort. Den beste koden er koden som ikke eksisterer.

En utfordring til bransjen

Strømming er en av de mest konkurranseutsatte kategoriene i App Store. Likevel leverer de fleste OTT-apper binaries som er 5 til 20 ganger større enn nødvendig, bærer årevis med legacy-kode og følger arkitekturmønstre som selv AI ikke kan nøste opp.

Neste gang du ser en stillingsannonse for en iOS-utvikler hos et strømmeselskap som krever "clean code-praksis" og "moderne arkitektur," still ett spørsmål: hvor stor er app-binaryen deres?

Hvis svaret er over 50 MB for noe som i bunn og grunn er en videoavspiller med en innholdskatalog, vet du nøyaktig hva som venter deg i det repositoryet.

Og hvis du vil se hvordan en streaming-app ser ut når den er bygget fra bunnen av med clean code-prinsipper – ingen legacy, ingen cross-platform-kompromisser, ingen akkumulert gjeld: last ned My TV Channel og sjekk størrelsen selv.

9 MB. Fem plattformer. Full funksjonalitet. Det er slik clean code ser ut.

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 →