Disons que vous construisez une app de liste de tâches en discussion depuis quelques semaines. Pour l'instant, c'est un site web — React, une base de données, rien de compliqué. Vous tapez « fais-moi une version Android » et appuyez sur Entrée. Voici ce qui se passe réellement entre cette frappe et un fichier .aab qui atterrit dans votre dossier de téléchargements, car la plupart des plateformes ne vous montrent pas cette partie, et c'est justement là que résidait toute la douleur autrefois.
Le .aab Android
Gradle prend le relais en premier. Les modules natifs de votre app — accès caméra, stockage local, tous les plugins intégrés au build — déclarent chacun la version NDK avec laquelle ils ont été compilés, et ces déclarations ne concordent pas toujours. J'ai vu un module compilé avec NDK r25 refuser de se lier à un autre supposant r26, et l'erreur générée ne dit pas « incompatibilité de version », elle mentionne un symbole manquant trois niveaux plus bas dans un fichier .so. Les versions de Kotlin font quelque chose de plus sournois encore : une version épinglée dans un module Gradle peut masquer silencieusement celle déclarée en haut de votre script de build, et le build réussit — il produit simplement un binaire qui plante sur certaines versions d'Android une fois en production. Rien d'exotique là-dedans. C'est la taxe standard de l'envoi natif sur Android, et c'est pourquoi les équipes embauchent une personne dont le travail consiste entièrement à savoir quel flag fait disparaître l'erreur de la semaine.
Le build ici exécute la véritable chaîne d'outils et absorbe lui-même ce travail de résolution :
- Conflits de dépendances détectés avant qu'ils ne deviennent un plantage en production
- Une configuration de chaîne d'outils qui s'améliore à chaque nouvel échec dont elle tire une leçon
Une version plus rapide et factice de tout ça — qui façonnerait un .aab sans exécuter les vraies tâches Gradle — sortirait en moins d'une minute. Elle mourrait aussi dès que votre app aurait besoin d'un service en arrière-plan ou d'une bibliothèque cryptographique native, et la revue du Play Store la signalerait en une journée. Nous préférons passer les vingt minutes.
Le .dmg macOS
Deux builds entrent dans ce seul fichier. Les outils en ligne de commande de Xcode compilent séparément un binaire Apple Silicon et un binaire Intel, puis lipo les assemblent en un seul exécutable universel.
C'est tentant — livrer un seul binaire et considérer l'affaire close — jusqu'à ce qu'on se rappelle que beaucoup de gens utilisent l'ordinateur portable de leur employeur, pas leur propre choix de matériel, et que cet ordinateur peut avoir trois ans et être en Intel. Plutôt que de demander à un utilisateur de déterminer quelle puce il possède (la plupart ne peuvent pas vous le dire), nous livrons les deux et laissons l'OS choisir silencieusement. L'alternative, que nous avons essayée au début, consiste à tout cross-compiler depuis une machine Linux avec des chaînes d'outils émulées. C'est plus rapide. C'est aussi comme ça qu'on se retrouve avec un cas limite de signature de code qui n'apparaît que sur du vrai matériel macOS 12, six semaines après le lancement, signalé par un utilisateur perplexe qui ne comprend pas pourquoi son app ne s'ouvre pas.
L'installateur Windows
C'est là que l'expérience de premier lancement décide si votre utilisateur fait confiance à l'app ou non. Windows SmartScreen ne connaît pas encore votre installateur — il n'a pas encore bâti de réputation auprès des serveurs de Microsoft — il affiche donc un écran bleu « Windows a protégé votre PC » avec un bouton « Ne pas exécuter » en gras et un lien à peine visible « Plus d'infos » qui, une fois cliqué, révèle « Exécuter quand même ». macOS fait sa propre version de cette chorégraphie : clic droit, Ouvrir, confirmer, car les apps hors App Store ne sont pas non plus approuvées par défaut. Au début, nous renvoyions ces deux cas vers une FAQ générique. Les tickets de support nous ont montré que ça ne fonctionne pas — quelqu'un qui fixe un écran disant que son téléchargement pourrait être un malware ne va pas lire de la documentation, il fait une capture d'écran et demande s'il s'est fait pirater. Le flux d'installation détecte donc l'OS et affiche les trois clics exacts nécessaires, sans FAQ requise. Petit détail, mais le fichier lui-même compte aussi : le téléchargement porte le nom de votre produit, pas celui d'un artefact de build. Personne ne devrait avoir à expliquer par chat qu'il a téléchargé « app-release-signed-v2-final.exe » et ne peut pas dire si c'est le bon fichier.
Le manifeste de l'extension navigateur
C'est l'exception dans tout le pipeline — pas de Gradle, pas de NDK, pas d'étape de compilation au sens habituel. Ce qu'il y a à la place, c'est un manifeste, et le manifeste est une négociation avec un examinateur du Chrome Web Store à qui vous ne parlerez jamais directement.
| Autorisation demandée | Résultat de la revue |
|---|---|
| <all_urls> (plus large que ce qu'exige la fonctionnalité) | Échanges de deux semaines avec quelqu'un qui ne précise pas exactement ce qui posait problème |
| activeTab (limité au besoin réel) | Validé le jour même |
MV3 complique aussi quelque chose que MV2 rendait facile : les service workers en arrière-plan sont déchargés en plein milieu d'une tâche par conception, une décision de politique de Google visant l'autonomie de la batterie, et une fonctionnalité qui doit y survivre doit être conçue autour de cette contrainte plutôt que contre elle. Nous configurons par défaut chaque extension avec l'ensemble d'autorisations le plus restreint dont sa fonctionnalité réelle a besoin, et nous l'élargissons uniquement quand une fonctionnalité spécifique l'exige.
Le keystore
Sous le build Android se trouve l'artefact que vous ne voyez jamais et que vous ne pouvez pas vous permettre de perdre : la clé de signature. La perdre ne signifie pas seulement perdre la capacité de mettre à jour votre app — vous perdez la capacité de la mettre à jour sous son identité existante, définitivement, sans aucun recours que Google vous accordera, jamais. Ce n'est pas une infrastructure glamour. C'est un fichier. Mais c'est la différence entre livrer la version six dans six mois comme une mise à jour transparente et la livrer comme une toute nouvelle fiche qui repart de zéro installation et zéro avis. Nous générons une clé par projet et la conservons pour que chaque build futur signe avec la même clé que le premier jour.
Le fil de discussion en dessous de tout ça
Rien de tout ça ne vit dans un « projet mobile » séparé. C'est la même conversation qui a construit l'app web. Demandez un changement d'UI, le build web se met à jour ; demandez ensuite un bundle Android, et il compile à partir de ce même état actuel, pas d'une bifurcation qui aurait dérivé trois semaines plus tôt. La plupart des équipes que j'ai vues essayer de greffer du natif après coup finissent par maintenir deux bases de code qui divergent l'une de l'autre — une app web qui est livrée chaque jour et une enveloppe native que quelqu'un doit se souvenir de mettre à jour avant chaque sortie. C'est dans cet écart que vit l'obsolescence, et c'est exactement ce qu'un historique de build unique supprime. Cela dit, ça marche dans les deux sens : si la discussion a été bâclée ces derniers temps, le build Android en hérite aussi. Ce n'est pas une passe de finition séparée, c'est une compilation directe de ce qui existe réellement — ce qui, en pratique, garde tout le monde honnête, car il n'y a pas de quête annexe « on nettoiera avant la soumission » à sauter.
Quand c'est prêt pour une boutique d'applications, le parcours de mise en ligne passe le relais à votre propre fiche Play Store et à votre propre compte Apple Developer. Pas les nôtres. Nous ne voulions pas nous placer entre vous et votre propre distribution.



