Clean Code dans les apps de streaming : pourquoi votre app OTT préférée est probablement 10 fois plus grosse que nécessaire

Le point de vue d'un développeur iOS sur les codebases obèses, les pièges du legacy et le test des 20 Mo.


Je suis développeur iOS depuis 2008, spécialisé dans le streaming et le podcasting. J'ai commencé à travailler avec HLS sur iPhone dès 2009, et au fil des années, j'ai construit, maintenu, débogué et parfois hérité d'applications de streaming de toutes tailles.

Et j'ai développé une opinion assez tranchée sur ce que signifie réellement le "clean code" dans cette industrie. Pas la version des manuels. La vraie. Celle que vous découvrez quand vous ouvrez le projet Xcode de quelqu'un d'autre et que votre premier réflexe est de refermer l'écran.

J'ai examiné des centaines d'offres d'emploi et de contrats freelance pour développeurs iOS au fil des ans. Presque chacune d'entre elles affiche fièrement "clean code" et "bonnes pratiques" dans ses exigences. Principes SOLID. MVVM. Couverture de tests unitaires. Design patterns. Tout le programme.

Et puis vous ouvrez le projet.

Le fossé entre les offres d'emploi et la réalité

Voici ce que j'ai réellement trouvé en ouvrant ces projets de streaming prétendument "clean code" : des fichiers XIB de 2014 qui pilotent encore des flux d'interface critiques. Des bridging headers Objective-C plus longs que certaines applications entières. Trois couches réseau différentes qui coexistent parce que personne n'a osé supprimer l'ancienne. Un pattern Coordinator empilé sur un pattern Navigator empilé sur un pattern Router, parce que chaque nouveau lead architecte a ajouté son favori sans retirer le précédent.

Le "clean code" dans la plupart des offres d'emploi pour les apps de streaming est un souhait, pas une description. Il décrit ce que le responsable du recrutement aimerait que la codebase ressemble, pas ce qui vous attend dans le dépôt.

Voici le constat que personne n'écrit dans la fiche de poste : ce pattern Coordinator dont ils sont si fiers ? Il coordonne trois autres patterns de navigation que personne n'a eu le temps de supprimer. Ces principes SOLID ? Ils se sont dissous à l'instant où le CEO a voulu une fonctionnalité livrée pour vendredi. Les tests unitaires ? Ils couvrent l'écran de connexion et absolument rien d'autre.

Je ne jette pas la pierre depuis une maison de verre. J'ai moi-même contribué à ce type d'entropie. Tous les développeurs mobiles l'ont fait. Mais je pense que l'industrie du streaming a une version particulièrement aiguë de ce problème, et j'ai une théorie pour le détecter instantanément.

La règle des 20 Mo : un test décisif pour la qualité du code des apps de streaming

Réfléchissez à ce que fait réellement une application de streaming. Elle récupère un manifeste (une playlist HLS, un MPD DASH), envoie des segments à un lecteur vidéo, affiche une interface autour (chaînes, EPG, paramètres), et c'est à peu près tout. Le contenu est streamé. Le gros du travail — la vidéo, l'audio, les vignettes — vit sur un CDN, pas dans le binaire de votre app.

Voici donc ma théorie : si une application de streaming pèse plus de 20 Mo sur l'App Store, chaque mégaoctet au-dessus de ce seuil est probablement du code "sale". Pas malveillant, pas nécessairement bogué, mais du code qui ne devrait pas être là. Du code qui reflète de mauvaises décisions architecturales accumulées au fil d'années de compromis.

Regardons quelques chiffres. Ma dernière application, My TV Channel, pèse 9 Mo sur l'App Store. Neuf. Elle tourne nativement sur iPhone, iPad, Mac (Apple Silicon), Apple TV et Apple Vision Pro. Elle supporte 9 langues. Les utilisateurs peuvent créer et regarder des chaînes TV linéaires 24h/24, 7j/7, avec de la programmation VOD-to-Live, des notifications push, des téléchargements hors ligne, le picture-in-picture, le multiview, la recherche avancée avec saisie vocale, le mode audio uniquement, les chaînes privées et un système complet de gestion de contenu pour les créateurs. C'est plus de fonctionnalités que beaucoup d'applications de streaming grand public.

Allez vérifier sur l'App Store par vous-même. La plupart des grandes applications de streaming pèsent entre 80 et 200 Mo. Certaines vont encore plus haut. C'est 10 à 20 fois plus lourd que My TV Channel. Ces applications ont le même travail principal : streamer de la vidéo. Pourtant, elles embarquent l'équivalent de dizaines de My TV Channel dans leurs binaires.

Bien sûr, les grands services de streaming ont besoin d'un peu de ce poids supplémentaire. Ils supportent de larges matrices d'appareils, des fonctionnalités d'accessibilité, le DRM hors ligne, parfois des jeux. Mais plus de 150 Mo pour ce qui est, fondamentalement, une application de streaming vidéo ? Cet écart raconte une histoire de code legacy, de compromis cross-platform et d'années de dette architecturale accumulée.

D'où vient le gonflement

Après des années de conseil sur des projets de streaming, je retrouve toujours les mêmes schémas qui gonflent les binaires d'applications bien au-delà du nécessaire.

Les technologies d'interface legacy. Les fichiers XIB et Storyboard sont un grand classique. Ils étaient la manière standard de construire des interfaces iOS pendant des années, et ils embarquent des graphes d'objets sérialisés, des contraintes de mise en page, parfois même des références d'images directement dans le binaire. Beaucoup d'applications de streaming transportent encore des XIB datant de leur lancement initial, patchés et maintenus mais jamais migrés vers SwiftUI ou même des patterns UIKit modernes. Chaque fichier XIB est de la dette technique congelée avec une extension .xib.

Le surcoût des frameworks cross-platform. React Native, Flutter, Kotlin Multiplatform : chacun ajoute son propre runtime, sa propre couche de liaison, ses propres abstractions. Pour une application de streaming où le composant le plus critique en termes de performance est de toute façon le lecteur vidéo natif, le compromis entre commodité cross-platform et taille du binaire est rarement justifié. Vous finissez par livrer un moteur JavaScript (ou une VM Dart, ou un runtime KMP) juste pour afficher une grille de vignettes que SwiftUI ou UIKit gère dans une fraction de la taille.

C'est la même logique qui a entraîné les équipes backend dans le terrier des microservices. Vous vous souvenez quand chaque startup avait besoin de Kubernetes et de 47 conteneurs Docker pour servir une API REST qu'une seule application Django pouvait gérer ? L'équivalent mobile, c'est livrer trois runtimes de frameworks pour afficher une liste de vidéos. Le schéma est identique : adopter une complexité architecturale qui résout des problèmes que vous n'avez pas, à un coût que vous paierez pendant des années.

L'accumulation de design patterns. Celui-ci est plus subtil. Il ne se traduit pas directement en mégaoctets, mais il multiplie les fichiers, les classes et les abstractions, ce qui augmente les dépendances à la compilation, les métadonnées embarquées et la taille du binaire. J'ai vu des projets de streaming avec un ViewModel, un Coordinator, un UseCase, un Repository, un DataSource, un Mapper et une Entity séparés pour chaque écran. C'est sept fichiers là où deux suffiraient. Multipliez par 30 écrans et vous obtenez 210 fichiers au lieu de 60, chacun ajoutant ses métadonnées de classe au binaire.

VIPER est le pire contrevenant ici. Il a été conçu pour un monde UIKit où les view controllers massifs étaient un vrai problème. Porter VIPER dans un projet SwiftUI, c'est comme amener un camion de pompiers pour une bougie. Les vues SwiftUI sont déjà légères, sans état et composables par conception. Envelopper chacune d'elles dans une pile VIPER (View, Interactor, Presenter, Entity, Router) ajoute cinq fichiers et trois niveaux d'indirection pour ce que SwiftUI gère avec une struct et une classe @Observable. J'ai vu des équipes le faire quand même parce que "c'est notre architecture", et le résultat est toujours le même : plus de boilerplate que de logique métier.

Des assets embarqués qui devraient être distants. Des polices embarquées, des animations embarquées (les fichiers JSON Lottie sont tristement célèbres pour ça), des images placeholder embarquées en résolution 3x pour chaque classe d'appareil. Dans une application de streaming, presque chaque ressource visuelle pourrait être chargée à la demande depuis un CDN. Les embarquer, c'est la voie de la facilité, et ça se voit sur la balance.

Du code mort issu de fonctionnalités abandonnées. Les frameworks d'A/B testing laissent derrière eux des conditionnelles qui ne sont jamais nettoyées. Les expérimentations de fonctionnalités échouées restent dans la codebase parce qu'"on pourrait la ramener". Le SDK d'analytics de l'avant-dernier prestataire est encore compilé parce que quelqu'un a oublié de le retirer du Podfile.

Le vrai coût : quand le code sale décide de votre roadmap

Le gonflement du binaire, c'est une chose. Mais la pire conséquence d'une codebase sale, ce n'est pas la taille du téléchargement. C'est ce qui se passe à chaque réunion de planification de sprint.

Vous connaissez la phrase. Un product manager demande une nouvelle fonctionnalité — disons le picture-in-picture, le mode audio uniquement, un simple rafraîchissement de l'interface — et les premiers mots qui sortent de la bouche du lead développeur sont : "Ça va être compliqué."

Cette phrase est le test olfactif du code sale. Pas "ça prendra du temps", pas "il faut réfléchir aux cas limites", mais compliqué. La codebase est devenue tellement emmêlée que personne ne peut prédire ce que le fait de toucher un module va casser dans trois autres.

Dans les codebases propres, ajouter des fonctionnalités semble naturel. Vous trouvez la bonne couche, vous l'étendez, vous livrez. Dans les codebases sales, c'est comme une chirurgie sur un patient sans dossier médical. Chaque modification nécessite d'abord une expédition archéologique. Chaque estimation vient avec un multiplicateur de risque que personne n'ose mettre sur le papier. Chaque sprint porte la réserve tacite : "en supposant que rien d'inattendu ne casse."

J'ai vu des projets de streaming où l'implémentation d'une simple fonctionnalité "continuer à regarder" nécessitait des modifications à travers sept couches architecturales, deux bridging headers et un bus d'événements custom que plus personne ne comprenait pleinement. Ce n'est pas de l'ingénierie logicielle. C'est de la négociation de prise d'otages avec votre propre codebase.

Et la partie que les équipes produit saisissent rarement : le code sale ne vous ralentit pas seulement aujourd'hui. Il décide de ce que vous pourrez construire demain. Les fonctionnalités ne sont pas rejetées parce que ce sont de mauvaises idées. Elles sont rejetées parce que "notre architecture ne le supporte pas", ce qui est une manière polie de dire "on s'est enfermés dans une impasse il y a cinq ans et personne ne veut l'admettre". La codebase devient le product manager de fait, mettant son veto aux fonctionnalités par la friction seule.

Retour à la réalité : vos concurrents avec des codebases plus propres livreront la même fonctionnalité en deux semaines pendant que votre équipe cartographie encore des graphes de dépendances dans Miro. Ils ne sont pas plus intelligents. Ils n'ont simplement pas à se battre contre leur propre code avant de se battre contre le marché.

Le test LLM : une nouvelle façon de mesurer la propreté du code

Au-delà de la taille du binaire, il y a un autre test décisif que j'utilise dernièrement, un test qui n'existait pas il y a cinq ans.

Pointez un LLM sur votre codebase. S'il ne peut pas la comprendre, votre prochain recrutement non plus.

Des outils comme Claude Code, OpenAI Codex et d'autres environnements de développement assistés par l'IA sont très bons pour naviguer dans des codebases bien structurées. Ils peuvent lire un projet Swift propre, comprendre son architecture, identifier des bugs, suggérer des correctifs et implémenter de nouvelles fonctionnalités avec un minimum de guidage.

Mais essayez de les pointer sur une application de streaming legacy avec 15 ans de patterns accumulés, du Objective-C et du Swift mélangés, trois approches différentes d'injection de dépendances et un système de build maintenu par des scripts shell custom. Le LLM va galérer. Il va halluciner des relations entre des classes qui n'existent pas. Il va suggérer des correctifs qui cassent d'autres parties du système. Il produira plus de crashs qu'un développeur senior.

Ce n'est pas une limitation de l'IA. C'est un miroir. Si un LLM entraîné sur des millions de dépôts ne peut pas parser l'architecture de votre projet, cela signifie que vos abstractions fuient, que votre nommage est incohérent, que vos dépendances sont emmêlées et que la structure de votre projet ne suit aucune convention reconnaissable.

Voyez-le comme le test ultime d'"intégration d'un nouveau développeur". Un LLM aborde votre codebase avec zéro connaissance institutionnelle, juste de la reconnaissance de patterns et une compréhension large des conventions de programmation. S'il se perd, votre prochain développeur junior se perdra aussi. Et votre prochain développeur senior passera ses trois premiers mois à démêler les mêmes noeuds, sauf qu'il vous les facturera.

Maintenant, inversez la perspective. Quand votre codebase est propre, quelque chose d'intéressant se produit : des outils comme Claude Code ne se contentent pas de relire votre code, ils construisent avec vous. J'utilise Claude Code sur My TV Channel, et dans une codebase propre, il peut implémenter une nouvelle fonctionnalité, écrire les tests et ouvrir une PR dans le temps qu'il me fallait pour rédiger un ticket Jira. Il lit l'architecture, comprend les conventions et produit du code qui s'intègre.

C'est là que les choses deviennent intéressantes pour les équipes qui fonctionnent encore en sprints de deux semaines. Les sprints ont été conçus pour gérer l'incertitude humaine : combien de temps ça prendra, sur quoi on s'engage, quand fait-on la démo. Mais quand un agent IA peut naviguer de manière fiable dans votre codebase et livrer du code fonctionnel en quelques heures, c'est le sprint lui-même qui devient le goulot d'étranglement. La réunion de planification prend plus de temps que l'implémentation. Le cérémonial autour de l'estimation d'une fonctionnalité coûte plus de temps que sa réalisation.

Bien sûr, cela ne fonctionne que si la codebase est propre. Pointez Claude Code sur un monstre infesté de VIPER, truffé de XIB et multi-frameworks, et il recommence à halluciner. L'outil ne crée pas la vélocité. C'est l'architecture propre qui la crée. Claude Code ne fait que la révéler. Et les codebases sales ? Elles ne ralentissent plus seulement vos développeurs humains. Elles empêchent aussi l'IA de vous aider, ce qui va devenir un handicap de plus en plus coûteux.

A quoi ressemble vraiment le clean code dans une app de streaming

J'ai construit My TV Channel de zéro avec ces principes en tête. Voici ce que je pense que le clean code signifie pour les applications de streaming spécifiquement.

Suivre les conventions de la plateforme. Les Human Interface Guidelines d'Apple existent pour une raison. Les composants standard UIKit ou SwiftUI vous offrent l'accessibilité, le Dynamic Type, le Dark Mode et la localisation gratuitement. Chaque composant custom que vous construisez à la place est un composant que vous devez maintenir, tester sur différents appareils et déboguer quand Apple change quelque chose dans une nouvelle version d'iOS. My TV Channel tourne sur cinq plateformes Apple avec une codebase partagée parce qu'elle s'appuie sur l'interface native de la plateforme, pas sur une couche d'abstraction astucieuse.

Garder un graphe de dépendances peu profond. Une application de streaming a besoin d'un lecteur vidéo, d'une couche réseau et d'une couche de persistance. Au-delà, chaque dépendance tierce devrait être sérieusement remise en question. Toutes les dépendances ne sont pas mauvaises. J'utilise Kingfisher pour la mise en cache d'images dans My TV Channel parce qu'il fait une chose bien, qu'il est activement maintenu et que son empreinte binaire est raisonnable par rapport à la valeur qu'il apporte. C'est la barre à atteindre. La dépendance résout-elle un vrai problème mieux que vous ne pourriez le faire avec les API de la plateforme ? Est-elle maintenue ? La taille est-elle proportionnelle à la valeur ? Si la réponse à l'une de ces questions est non, elle ne devrait pas être dans votre Podfile. Le problème n'est pas d'utiliser des bibliothèques. Le problème, c'est d'en utiliser cinq quand une seule suffirait, ou d'utiliser un framework réactif de 5 Mo quand l'async/await natif de Swift et Combine font le même travail sans code externe.

Traiter la taille du binaire comme une fonctionnalité, pas comme une métrique. Les utilisateurs sur réseaux cellulaires, les utilisateurs avec des iPhones de 64 Go, les utilisateurs dans des marchés où le stockage est limité : ils bénéficient tous d'une application plus légère. Apple affiche la taille de l'application de manière proéminente sur l'App Store pour une raison. Un téléchargement de 9 Mo s'installe en quelques secondes sur n'importe quelle connexion. Un téléchargement de 150 Mo nécessite le Wi-Fi pour beaucoup d'utilisateurs et entre en concurrence pour l'espace avec les photos, les messages et d'autres applications qui leur importent davantage.

Supprimer du code. La partie la plus difficile de la maintenance d'une codebase propre, ce n'est pas d'écrire du nouveau code. C'est de supprimer l'ancien. Les feature flags qui n'ont pas été basculés depuis six mois, les événements analytics que personne ne consulte dans le tableau de bord, les chemins de migration pour des formats de données d'il y a trois versions. Tout ça doit partir. Le meilleur code, c'est le code qui n'existe pas.

Un défi à l'industrie

Le streaming est l'une des catégories les plus compétitives de l'App Store. Pourtant, la plupart des applications OTT livrent des binaires 5 à 20 fois plus gros que nécessaire, transportent des années de code legacy et suivent des patterns architecturaux que même l'IA ne peut pas démêler.

La prochaine fois que vous verrez une offre d'emploi pour un développeur iOS dans une entreprise de streaming qui exige des "pratiques de clean code" et une "architecture moderne", posez-leur une seule question : quelle est la taille de votre binaire ?

Si la réponse dépasse les 50 Mo pour ce qui est fondamentalement un lecteur vidéo avec un catalogue de contenus, vous savez exactement ce qui vous attend dans ce dépôt.

Et si vous voulez voir à quoi ressemble une application de streaming construite de zéro avec des principes de clean code, sans legacy, sans compromis cross-platform, sans dette accumulée : téléchargez My TV Channel et vérifiez la taille par vous-même.

9 Mo. Cinq plateformes. Toutes les fonctionnalités. Voilà à quoi ressemble le clean code.

Besoin d'aide pour votre projet streaming ?

Cet article a été rédigé par des professionnels expérimentés disponibles sur iReplay.tv. Que vous ayez besoin d'expertise en streaming, OTT, iOS—notre réseau de spécialistes peut concrétiser votre projet.

Recruter un professionnel →