Die Sicht eines iOS-Entwicklers auf aufgeblähte Codebasen, Legacy-Fallen und den 20-MB-Test.
Ich bin seit 2008 iOS-Entwickler mit Schwerpunkt auf Streaming und Podcasting. Ich habe 2009 mit HLS auf dem iPhone angefangen, und im Laufe der Jahre habe ich Streaming-Apps jeder Größe gebaut, gewartet, debuggt und manchmal auch geerbt.
Und ich habe eine ziemlich klare Meinung darüber entwickelt, was „Clean Code“ in dieser Branche wirklich bedeutet. Nicht die Lehrbuchversion. Die echte. Die, die man entdeckt, wenn man das Xcode-Projekt eines anderen öffnet und der erste Instinkt ist, den Laptop zuzuklappen.
Ich habe im Laufe der Jahre Hunderte von Stellenausschreibungen und Freelance-Verträge für iOS-Entwickler durchgesehen. Fast jede einzelne listet stolz „Clean Code“ und „Best Practices“ als Anforderungen auf. SOLID-Prinzipien. MVVM. Unit-Test-Abdeckung. Design Patterns. Das volle Programm.
Und dann öffnet man das Projekt.
Die Kluft zwischen Stellenausschreibungen und Realität
Hier ist, was ich tatsächlich vorgefunden habe, als ich diese „Clean Code“-Streaming-Projekte geöffnet habe: XIB-Dateien aus dem Jahr 2014, die immer noch kritische UI-Abläufe steuern. Objective-C-Bridging-Header, die länger sind als manche komplette Apps. Drei verschiedene Netzwerkschichten, die nebeneinander existieren, weil sich niemand getraut hat, die alte zu entfernen. Ein Coordinator-Pattern, das auf einem Navigator-Pattern aufgesetzt ist, das wiederum auf einem Router-Pattern sitzt, weil jeder neue leitende Architekt sein Lieblingsmuster hinzugefügt hat, ohne das vorherige zu entfernen.
„Clean Code“ in den meisten Stellenausschreibungen für Streaming-Apps ist ein Wunschdenken, keine Zustandsbeschreibung. Es beschreibt, wie sich der Hiring Manager die Codebasis wünscht, nicht das, was im Repository auf einen wartet.
Hier ist der Realitätscheck, den niemand in die Stellenbeschreibung schreibt: Das Coordinator-Pattern, auf das sie so stolz sind? Es koordiniert drei weitere Navigationsmuster, für deren Entfernung niemand Zeit hatte. Die SOLID-Prinzipien? Die haben sich aufgelöst, als der CEO ein Feature bis Freitag ausgeliefert haben wollte. Die Unit-Tests? Sie decken den Login-Bildschirm ab und absolut nichts anderes.
Ich werfe hier keine Steine aus dem Glashaus. Ich habe selbst zu dieser Art von Entropie beigetragen. Jeder Mobile-Entwickler hat das. Aber ich denke, die Streaming-Branche hat eine besonders schlimme Version dieses Problems, und ich habe eine Theorie, wie man es sofort erkennen kann.
Die 20-MB-Regel: ein Lackmustest für die Codequalität von Streaming-Apps
Überlegen Sie, was eine Streaming-Anwendung tatsächlich tut. Sie ruft ein Manifest ab (eine HLS-Playlist, ein DASH MPD), füttert Segmente an einen Videoplayer, rendert etwas UI drumherum (Kanäle, EPG, Einstellungen), und das war es im Wesentlichen. Der Inhalt wird gestreamt. Die schweren Sachen – das Video, das Audio, die Thumbnails – liegen alle auf einem CDN, nicht in Ihrem App-Binary.
Hier ist also meine Theorie: Wenn eine Streaming-App im App Store mehr als 20 MB wiegt, ist jedes Megabyte über diesem Schwellenwert wahrscheinlich „schmutziger“ Code. Nicht bösartig, nicht unbedingt fehlerhaft, aber Code, der dort nicht sein sollte. Code, der schlechte architektonische Entscheidungen widerspiegelt, die sich über Jahre von Kompromissen angesammelt haben.
Schauen wir uns einige Zahlen an. Meine neueste App, My TV Channel, wiegt 9 MB im App Store. Neun. Sie läuft nativ auf iPhone, iPad, Mac (Apple Silicon), Apple TV und Apple Vision Pro. Sie unterstützt 9 Sprachen. Benutzer können lineare TV-Kanäle rund um die Uhr erstellen und ansehen, mit VOD-to-Live-Planung, Push-Benachrichtigungen, Offline-Downloads, Bild-in-Bild, Multiview, erweiterter Suche mit Spracheingabe, Nur-Audio-Modus, privaten Kanälen und einem vollständigen Content-Management-System für Creator. Das sind mehr Features als viele Mainstream-Streaming-Apps bieten.
Prüfen Sie es selbst im App Store. Die meisten großen Streaming-Apps wiegen zwischen 80 und 200 MB. Manche gehen sogar noch höher. Das ist 10- bis 20-mal schwerer als My TV Channel. Diese Apps haben dieselbe Hauptaufgabe: Video streamen. Dennoch tragen sie das Äquivalent von Dutzenden My TV Channels in ihren Binaries.
Sicher, große Streaming-Dienste brauchen etwas von diesem Zusatzgewicht. Sie unterstützen breite Gerätematrizen, Barrierefreiheitsfunktionen, Offline-DRM, manchmal Spiele. Aber 150+ MB für etwas, das im Kern eine Video-Streaming-Anwendung ist? Diese Lücke erzählt eine Geschichte über Legacy-Code, plattformübergreifende Kompromisse und Jahre angesammelter architektonischer Schulden.
Woher der Ballast kommt
Nach Jahren der Beratung bei Streaming-Projekten sehe ich immer wieder dieselben Muster, die App-Binaries weit über das Notwendige hinaus aufblähen.
Veraltete UI-Technologien. XIB- und Storyboard-Dateien sind ein Klassiker. Sie waren jahrelang der Standardweg, um iOS-Interfaces zu bauen, und sie betten serialisierte Objektgraphen, Layout-Constraints, manchmal sogar Bildreferenzen direkt ins Binary ein. Viele Streaming-Apps tragen immer noch XIBs aus ihrer Erstveröffentlichung mit sich, geflickt und gewartet, aber nie auf SwiftUI oder auch nur moderne UIKit-Patterns migriert. Jede XIB-Datei ist eingefrorene technische Schuld mit einer .xib-Endung.
Overhead durch plattformübergreifende Frameworks. React Native, Flutter, Kotlin Multiplatform: Jedes bringt seine eigene Runtime, seine eigene Bridge-Schicht, seine eigenen Abstraktionen mit. Für eine Streaming-App, bei der die leistungskritischste Komponente sowieso der native Videoplayer ist, ergibt der Kompromiss zwischen plattformübergreifender Bequemlichkeit und Binary-Größe selten Sinn. Am Ende liefert man eine JavaScript-Engine (oder eine Dart-VM oder eine KMP-Runtime) aus, nur um ein Raster von Thumbnails zu rendern, das SwiftUI oder UIKit in einem Bruchteil der Größe bewerkstelligt.
Es ist dieselbe Logik, die Backend-Teams in den Microservices-Kaninchenbau geführt hat. Erinnern Sie sich, als jedes Startup Kubernetes und 47 Docker-Container brauchte, um eine REST-API zu bedienen, die eine einzige Django-App hätte bewerkstelligen können? Das mobile Äquivalent ist, drei Framework-Runtimes auszuliefern, um eine Liste von Videos anzuzeigen. Das Muster ist identisch: architektonische Komplexität übernehmen, die Probleme löst, die man nicht hat, zu Kosten, die man jahrelang bezahlen wird.
Anhäufung von Design Patterns. Dieser Punkt ist subtiler. Er zeigt sich nicht direkt in Megabytes, aber er vervielfacht Dateien, Klassen und Abstraktionen, was Compile-Time-Abhängigkeiten, eingebettete Metadaten und die Binary-Größe erhöht. Ich habe Streaming-Projekte gesehen, die für jeden einzelnen Bildschirm ein separates ViewModel, Coordinator, UseCase, Repository, DataSource, Mapper und Entity haben. Das sind sieben Dateien, wo zwei reichen würden. Multiplizieren Sie das mit 30 Bildschirmen und Sie haben 210 Dateien statt 60, jede einzelne fügt ihre Klassen-Metadaten zum Binary hinzu.
VIPER ist hier der schlimmste Übeltäter. Es wurde für eine UIKit-Welt entworfen, in der massive View Controller ein echtes Problem waren. VIPER in ein SwiftUI-Projekt zu portieren ist wie ein Löschfahrzeug zu einer Kerze mitzubringen. SwiftUI-Views sind von Haus aus leichtgewichtig, zustandslos und komponierbar. Jedes einzelne in einen VIPER-Stack (View, Interactor, Presenter, Entity, Router) einzuwickeln fügt fünf Dateien und drei Schichten Indirektion hinzu, für etwas, das SwiftUI mit einem Struct und einer @Observable-Klasse erledigt. Ich habe Teams gesehen, die es trotzdem machen, weil „das unsere Architektur ist“, und das Ergebnis ist immer dasselbe: mehr Boilerplate als Geschäftslogik.
Eingebettete Assets, die remote sein sollten. Gebundelte Schriftarten, gebundelte Animationen (Lottie-JSON-Dateien sind dafür berüchtigt), gebundelte Platzhalterbilder in 3x-Auflösung für jede Geräteklasse. In einer Streaming-App könnte fast jedes visuelle Asset bei Bedarf von einem CDN abgerufen werden. Sie einzubetten ist der bequeme Weg, und das zeigt sich im Umfang.
Toter Code aus aufgegebenen Features. A/B-Testing-Frameworks hinterlassen Bedingungen, die nie aufgeräumt werden. Gescheiterte Feature-Experimente bleiben in der Codebasis, weil „wir es vielleicht wieder einführen“. Das Analytics-SDK des vorletzten Anbieters wird immer noch kompiliert, weil jemand vergessen hat, es aus dem Podfile zu entfernen.
Die wahren Kosten: wenn schmutziger Code Ihre Roadmap bestimmt
Binary-Ballast ist eine Sache. Aber die schlimmste Konsequenz einer schmutzigen Codebasis ist nicht die Download-Größe. Es ist das, was in jeder Sprint-Planungssitzung passiert.
Sie kennen den Satz. Ein Product Manager fragt nach einem neuen Feature – sagen wir Bild-in-Bild, Nur-Audio-Modus, ein einfaches UI-Refresh – und die ersten Worte des Lead-Entwicklers sind: „Das wird kompliziert.“
Dieser Satz ist der Geruchstest für schmutzigen Code. Nicht „es wird Zeit brauchen“, nicht „wir müssen über Randfälle nachdenken“, sondern kompliziert. Die Codebasis ist so verworren geworden, dass niemand vorhersagen kann, was das Berühren eines Moduls in drei anderen kaputt machen wird.
In sauberen Codebasen fühlt sich das Hinzufügen von Features natürlich an. Man findet die richtige Schicht, erweitert sie, liefert aus. In schmutzigen Codebasen fühlt es sich an wie eine Operation an einem Patienten ohne Krankenakte. Jede Änderung erfordert zuerst eine archäologische Expedition. Jede Schätzung kommt mit einem Risikofaktor, den niemand auf Papier zu bringen wagt. Jeder Sprint trägt den unausgesprochenen Vorbehalt: „vorausgesetzt, es geht nichts Unerwartetes kaputt.“
Ich habe Streaming-Projekte gesehen, bei denen die Implementierung eines einfachen „Weiterschauen“-Features Modifikationen über sieben Architekturschichten, zwei Bridging-Header und einen benutzerdefinierten Event-Bus erforderte, den niemand mehr vollständig verstand. Das ist kein Software Engineering. Das ist eine Geiselverhandlung mit der eigenen Codebasis.
Und der Teil, den Produktteams selten begreifen: Schmutziger Code bremst Sie nicht nur heute aus. Er bestimmt, was Sie morgen bauen können. Features werden nicht abgelehnt, weil sie schlechte Ideen sind. Sie werden abgelehnt, weil „unsere Architektur das nicht unterstützt“, was eine höfliche Art ist zu sagen: „Wir haben uns vor fünf Jahren in eine Ecke gebaut und niemand will es zugeben.“ Die Codebasis wird zum De-facto-Product-Manager, der Features allein durch Reibung ablehnt.
Realitätscheck: Ihre Wettbewerber mit saubereren Codebasen werden dasselbe Feature in zwei Wochen ausliefern, während Ihr Team noch Abhängigkeitsgraphen in Miro erstellt. Sie sind nicht schlauer. Sie müssen nur nicht gegen ihren eigenen Code kämpfen, bevor sie gegen den Markt kämpfen.
Der LLM-Test: eine neue Methode zur Messung der Code-Sauberkeit
Über die Binary-Größe hinaus gibt es einen weiteren Lackmustest, den ich in letzter Zeit verwende – einen, den es vor fünf Jahren noch nicht gab.
Richten Sie ein LLM auf Ihre Codebasis. Wenn es sie nicht versteht, wird es Ihr nächster Mitarbeiter auch nicht.
Tools wie Claude Code, OpenAI Codex und andere KI-gestützte Entwicklungsumgebungen sind sehr gut darin, gut strukturierte Codebasen zu navigieren. Sie können ein sauberes Swift-Projekt lesen, seine Architektur verstehen, Fehler identifizieren, Korrekturen vorschlagen und neue Features mit minimaler Anleitung implementieren.
Aber versuchen Sie, sie auf eine Legacy-Streaming-App mit 15 Jahren angesammelter Patterns zu richten, gemischtem Objective-C und Swift, drei verschiedenen Dependency-Injection-Ansätzen und einem Build-System, das durch benutzerdefinierte Shell-Skripte zusammengehalten wird. Das LLM wird Schwierigkeiten haben. Es wird Beziehungen zwischen Klassen halluzinieren, die nicht existieren. Es wird Korrekturen vorschlagen, die andere Teile des Systems kaputt machen. Es wird mehr Abstürze produzieren als ein Senior-Entwickler.
Das ist keine Einschränkung der KI. Es ist ein Spiegel. Wenn ein LLM, das auf Millionen von Repositories trainiert wurde, die Architektur Ihres Projekts nicht parsen kann, bedeutet das, dass Ihre Abstraktionen undicht sind, Ihre Benennung inkonsistent ist, Ihre Abhängigkeiten verwickelt sind und Ihre Projektstruktur keiner erkennbaren Konvention folgt.
Betrachten Sie es als den ultimativen „Onboarding-Test für neue Entwickler“. Ein LLM nähert sich Ihrer Codebasis ohne institutionelles Wissen, nur mit Mustererkennung und einem breiten Verständnis von Programmierkonventionen. Wenn es sich verirrt, wird sich Ihr nächster Junior-Entwickler auch verirren. Und Ihr nächster Senior-Entwickler wird die ersten drei Monate damit verbringen, dieselben Knoten zu entwirren – nur dass er Ihnen dafür eine Rechnung stellt.
Drehen Sie es jetzt um. Wenn Ihre Codebasis sauber ist, passiert etwas Interessantes: Tools wie Claude Code überprüfen nicht nur Ihren Code, sie bauen mit Ihnen. Ich verwende Claude Code für My TV Channel, und in einer sauberen Codebasis kann es ein neues Feature implementieren, die Tests schreiben und einen PR öffnen in der Zeit, die ich früher brauchte, um ein Jira-Ticket zu schreiben. Es liest die Architektur, versteht die Konventionen und produziert Code, der passt.
Hier wird es interessant für Teams, die noch Zwei-Wochen-Sprints fahren. Sprints wurden entworfen, um menschliche Unsicherheit zu managen: Wie lange wird das dauern, was können wir zusagen, wann präsentieren wir. Aber wenn ein KI-Agent zuverlässig durch Ihre Codebasis navigieren und funktionierenden Code in Stunden liefern kann, wird der Sprint selbst zum Flaschenhals. Das Planungsmeeting dauert länger als die Implementierung. Die Zeremonie rund um die Schätzung eines Features kostet mehr Zeit als es zu bauen.
Natürlich funktioniert das nur, wenn die Codebasis sauber ist. Richten Sie Claude Code auf ein VIPER-verseuchtes, XIB-beladenes, Multi-Framework-Monster und es halluziniert wieder. Das Tool erzeugt nicht die Geschwindigkeit. Die saubere Architektur tut es. Claude Code macht sie nur sichtbar. Und schmutzige Codebasen? Sie bremsen nicht mehr nur Ihre menschlichen Entwickler aus. Sie blockieren auch die KI dabei, Ihnen zu helfen – und das wird ein zunehmend teures Handicap.
Wie Clean Code in einer Streaming-App wirklich aussieht
Ich habe My TV Channel von Grund auf mit diesen Prinzipien im Sinn gebaut. Hier ist, was Clean Code meiner Meinung nach speziell für Streaming-Anwendungen bedeutet.
Plattformkonventionen folgen. Apples Human Interface Guidelines existieren aus gutem Grund. Standard-UIKit- oder SwiftUI-Komponenten bieten Barrierefreiheit, Dynamic Type, Dark Mode und Lokalisierung kostenlos. Jede benutzerdefinierte Komponente, die Sie stattdessen bauen, müssen Sie warten, über Geräte hinweg testen und debuggen, wenn Apple in einem neuen iOS-Release etwas ändert. My TV Channel läuft auf fünf Apple-Plattformen mit einer gemeinsamen Codebasis, weil es auf plattformnative UI setzt – nicht wegen einer cleveren Abstraktionsschicht.
Den Abhängigkeitsgraphen flach halten. Eine Streaming-App braucht einen Videoplayer, eine Netzwerkschicht und eine Persistenzschicht. Darüber hinaus sollte jede Drittanbieter-Abhängigkeit hart hinterfragt werden. Nicht alle Abhängigkeiten sind schlecht. Ich verwende Kingfisher für Bild-Caching in My TV Channel, weil es eine Sache gut macht, aktiv gewartet wird und sein Binary-Footprint im Verhältnis zum gebotenen Wert angemessen ist. Das ist die Messlatte. Löst die Abhängigkeit ein echtes Problem besser als Sie es mit Plattform-APIs könnten? Wird sie gewartet? Ist die Größe verhältnismäßig zum Wert? Wenn die Antwort auf eine dieser Fragen Nein ist, gehört sie nicht in Ihr Podfile. Das Problem ist nicht, Bibliotheken zu verwenden. Das Problem ist, fünf davon zu verwenden, wenn eine reichen würde, oder ein 5-MB-Reactive-Framework zu nutzen, wenn Swifts natives async/await und Combine denselben Job ohne externen Code erledigen.
Binary-Größe als Feature behandeln, nicht als Metrik. Nutzer in Mobilfunknetzen, Nutzer mit 64-GB-iPhones, Nutzer in Märkten, in denen Speicherplatz knapp ist: Sie alle profitieren von einer kleineren App. Apple zeigt die App-Größe im App Store aus gutem Grund prominent an. Ein 9-MB-Download installiert sich in Sekunden über jede Verbindung. Ein 150-MB-Download benötigt für viele Nutzer WLAN und konkurriert um Speicherplatz mit Fotos, Nachrichten und anderen Apps, die ihnen wichtiger sind.
Code löschen. Der schwierigste Teil bei der Pflege einer sauberen Codebasis ist nicht das Schreiben von neuem Code. Es ist das Löschen von altem Code. Feature-Flags, die seit sechs Monaten nicht umgeschaltet wurden, Analytics-Events, die niemand im Dashboard prüft, Migrationspfade für Datenformate von vor drei Versionen. All das muss weg. Der beste Code ist der Code, der nicht existiert.
Eine Herausforderung an die Branche
Streaming ist eine der wettbewerbsintensivsten Kategorien im App Store. Dennoch liefern die meisten OTT-Apps Binaries aus, die 5- bis 20-mal größer sind als nötig, tragen Jahre von Legacy-Code mit sich und folgen Architekturmustern, die selbst KI nicht entwirren kann.
Wenn Sie das nächste Mal eine Stellenausschreibung für einen iOS-Entwickler bei einem Streaming-Unternehmen sehen, die „Clean-Code-Praktiken“ und „moderne Architektur“ verlangt, stellen Sie eine Frage: Wie groß ist Ihr App-Binary?
Wenn die Antwort über 50 MB liegt für etwas, das im Grunde ein Videoplayer mit Inhaltskatalog ist, wissen Sie genau, was Sie in diesem Repository erwartet.
Und wenn Sie sehen möchten, wie eine Streaming-App aussieht, die von Grund auf mit Clean-Code-Prinzipien gebaut wurde – ohne Legacy, ohne plattformübergreifende Kompromisse, ohne angesammelte Schulden: laden Sie My TV Channel herunter und prüfen Sie die Größe selbst.
9 MB. Fünf Plattformen. Voller Funktionsumfang. So sieht Clean Code aus.