Tohum

Playbook de développement de produit IoT de bout en bout – Étape 4 : Développement du firmware & du logiciel cloud

Illustration : un microcontrôleur échangeant des données avec un nuage à engrenages

L’Étape 4 fait passer le produit sur le côté droit du V : implémentation et tests. Dans le modèle en V classique du développement produit, le côté gauche concerne la conception (que nous avons traitée dans les étapes précédentes) ; ici, nous développons le firmware (le logiciel qui tourne sur votre appareil IoT) et le logiciel cloud (serveurs, bases de données, APIs, interfaces utilisateur) qui, ensemble, donnent vie à votre produit. Contrairement au développement matériel, relativement rigide une fois imprimé sur une carte électronique, le développement logiciel est hautement itératif et agile. En fait, vous avez peut-être déjà écrit des bouts de firmware à l’Étape 3 en testant votre conception matérielle. Il est maintenant temps d’accélérer. Le logiciel est comme de l’argile – facile à modeler et à changer à la volée – donc nous pouvons itérer vite et affiner en continu. Cette étape se déroulera en parallèle de tout travail matériel restant, chacun informant l’autre au besoin.

L’Étape 4 peut se voir en deux parties principales : d’abord planifier l’architecture et la conception logicielles (pour poser une fondation solide), puis un cycle continu de sprints de développement où nous construisons, testons et recommençons jusqu’à ce que le logiciel soit solide. Décomposons.

4.1 Architecture & conception logicielles

Avant que quiconque saute dans le code, il est sage d’esquisser un plan de jeu pour l’architecture logicielle – à la fois pour le firmware sur l’appareil et pour les composants cloud. Voyez cela comme planifier un road-trip. Vous ne commenceriez pas à conduire sans carte ; de même, nous esquissons les composants majeurs et leurs interactions avant d’écrire des milliers de lignes de code. C’est analogue à l’architecture système faite à l’Étape 2, mais maintenant au niveau du code. Une bonne planification d’architecture logicielle vous aide à éviter le « code spaghetti » plus tard. Voici les points clés à établir dans cette phase :

Architecture firmware (approche en couches et considérations RTOS)

Définissez la structure de votre code embarqué en le stratifiant. Une approche courante en firmware est une architecture en couches, qui aide à organiser le code logiquement et le rend plus facile à maintenir. Par exemple, au niveau le plus bas, vous pourriez avoir un Board Support Package (BSP) ou une couche d’abstraction matérielle – elle contient les drivers bas niveau spécifiques à votre microcontrôleur et à vos périphériques matériels. Au-dessus, vous pourriez avoir des drivers de périphériques ou des modules pour vos capteurs et actionneurs (gérant des choses comme lire un capteur de température ou piloter une LED). Au-dessus des drivers, vous pourriez avoir une couche de services – elle peut implémenter des protocoles de communication, des algorithmes de traitement de données, ou toute autre logique qui traite les données brutes. Enfin, au sommet, vous avez la couche application, qui est le cerveau de votre appareil : elle lie tout ensemble pour remplir l’usage de votre produit (p. ex. lire des valeurs de capteurs, prendre des décisions, envoyer des données au cloud, et réagir aux commandes). Pensez à dessiner un diagramme simple (diagramme de composants UML ou même juste des boîtes et des flèches sur un tableau blanc) pour visualiser ces couches. Cela aide tous les membres de l’équipe à comprendre la séparation des responsabilités – comme construire une maison avec des fondations, une ossature et un toit, chaque couche a son travail.

Autre grande décision d’architecture côté firmware : utiliser ou non un RTOS (Real-Time Operating System). Un RTOS, c’est comme avoir un petit système d’exploitation sur votre appareil, capable d’exécuter plusieurs tâches apparemment en même temps (via un ordonnancement). Si votre appareil ne fait qu’une chose à la fois, vous n’avez peut-être pas besoin d’un RTOS. Mais la plupart des gadgets IoT ont beaucoup de choses qui se passent en concurrence – par exemple, lire des capteurs périodiquement, écouter les messages entrants (du cloud ou d’entrées utilisateur), mettre à jour un écran ou une LED, et gérer la communication réseau. Avec un RTOS, vous pouvez concevoir votre firmware comme un ensemble de tâches ou threads. Par exemple, vous pourriez avoir une tâche dédiée à l’échantillonnage des capteurs, une autre dédiée à la gestion des communications (envoyer des données ou recevoir des commandes), et une autre pour le housekeeping ou les mises à jour de l’interface utilisateur. Dans le cadre de votre conception d’architecture, décrivez ces tâches et comment elles communiqueront entre elles – les mécanismes courants incluent les files de messages, les event flags, ou les données partagées protégées par des mutex (pour éviter les conflits). Décider cela tôt aidera à garantir que votre firmware ne devienne pas un enchevêtrement de code où tout dépend de tout (alias code spaghetti). Au lieu de cela, vous aurez des interfaces claires : peut-être que la tâche capteur met les nouvelles lectures dans une file, et que la tâche communication les récupère pour les envoyer au cloud. Si vous concevez ces interfaces clairement maintenant, le codage sera bien plus fluide et vous vous remercierez plus tard quand viendra le moment de dépanner ou d’étendre le firmware.

Pourquoi tout ce tapage architectural pour le firmware ? Parce que cela vous facilite la vie en aval. Avec une bonne conception en couches et une bonne structure de tâches, vous pouvez modifier une partie (disons, remplacer un driver de capteur ou ajuster un algorithme) sans tout casser. Cela facilite aussi le travail de plusieurs développeurs ensemble sans se marcher sur les pieds – une personne peut développer le driver du capteur pendant qu’une autre travaille sur le code de communication cloud, tant que vous vous êtes accordés sur la façon dont ces parties s’interfacent (p. ex. le format de données d’un message de lecture de capteur). En bref, un peu de planification ici évite beaucoup de douleur plus tard.

Architecture cloud (structure du serveur, de la base de données et de l’application)

Considérons maintenant le côté cloud. L’appareil IoT ne vit pas en isolation – il parle généralement à un service cloud ou un serveur. Une réflexion d’architecture similaire est nécessaire ici : esquissez les composants majeurs de votre logiciel cloud. Par exemple, vous pourriez avoir un serveur API qui reçoit les données des appareils et sert aussi les requêtes des applications utilisateur. Il y aura probablement une base de données où vous stockez les données des appareils (lectures de capteurs, statuts des appareils, infos utilisateurs, etc.). Il peut y avoir des services ou microservices supplémentaires faisant du traitement ou de l’analytique de données – par exemple, analyser les données à la recherche d’anomalies, déclencher des alertes ou des actions quand certaines conditions sont remplies (p. ex. envoyer un email si une lecture de température est trop élevée), ou effectuer des agrégations pour le reporting. Par-dessus tout cela, considérez les applications clientes : peut-être un tableau de bord web ou une application mobile avec laquelle les utilisateurs interagissent. Ces clients communiqueront avec le serveur (souvent via l’API) pour afficher les données et envoyer les commandes utilisateur (comme un utilisateur appuyant sur un bouton pour allumer son ampoule IoT depuis son téléphone).

Il est utile de dessiner un diagramme de cet écosystème – le modèle C4 (Client-Container-Component-Code) est une façon populaire de diagrammer l’architecture logicielle. Dessinez au moins le niveau Container : montrez l’appareil, le serveur, la base de données et l’application cliente, et comment les données circulent entre eux. Par exemple, votre diagramme pourrait avoir une flèche d’Appareil vers API cloud (« envoie les données des capteurs via HTTP/MQTT »), une flèche de Cloud vers Base de données (« stocke les données »), et une flèche d’Application cliente vers Cloud (« l’utilisateur demande des données ou envoie une commande »), et ainsi de suite. Cette carte visuelle garantit que chacun (développeurs firmware, ingénieurs cloud, développeurs frontend, etc.) a un modèle mental clair de la façon dont les choses se connectent. Il est bien plus facile de discuter et d’attraper les problèmes de conception tôt avec un tel diagramme en main. Peut-être découvrirez-vous à cette étape que vous avez besoin d’un composant supplémentaire – par exemple, un message broker ou un service de plateforme IoT – pour gérer le passage à l’échelle vers de nombreux appareils. Mieux vaut y penser maintenant que quand vous serez au fond du code.

Un dernier aspect : pensez à la scalabilité et à la fiabilité dans votre conception cloud. Si vous attendez 100 000 appareils connectés à terme, votre base de données et votre conception de serveur choisies peuvent-elles le gérer ? Si un serveur tombe, que se passe-t-il (avez-vous de la redondance) ? Ces détails pourraient être étoffés à une étape ultérieure, mais la conception de haut niveau devrait tenir compte de préoccupations majeures comme celles-là dès le départ. Par exemple, vous pourriez décider d’utiliser une plateforme IoT cloud (AWS IoT, Azure IoT Hub, etc.) au lieu de tout construire from scratch, ce qui deviendrait partie intégrante de votre décision d’architecture.

Protocoles de communication & formats de données (communication appareil-cloud)

Un produit IoT implique par nature une communication entre l’appareil et le cloud. Une partie de la conception logicielle consiste à décider comment ils se parlent et dans quel langage (protocoles et formats de données). Il y a quelques choix de protocoles courants en IoT : par exemple, votre appareil utilisera-t-il MQTT, HTTP/REST, CoAP, ou peut-être quelque chose comme WebSockets ? Chacun a ses avantages et inconvénients. MQTT est un protocole publish/subscribe léger, excellent pour l’IoT parce qu’il est efficace et fonctionne bien sur des réseaux peu fiables – beaucoup de systèmes IoT l’utilisent pour la messagerie appareil-vers-cloud. HTTP/REST est très courant et facile à tester (puisque vous pouvez l’attaquer avec des outils web standard) mais peut avoir un peu plus d’overhead par message. CoAP est un protocole IoT spécialisé similaire à un HTTP léger, souvent utilisé dans les réseaux contraints. La clé est de choisir celui qui convient aux capacités de votre appareil et à votre infrastructure cloud. Par exemple, sur un appareil fortement contraint (microcontrôleur 8 bits sur batterie), MQTT ou CoAP est souvent un meilleur choix que HTTP. Si votre appareil est plus capable (tournant sous Linux ou similaire), HTTP/REST convient et est plus direct.

Une fois le protocole choisi, concevez le format de données des messages. Beaucoup de projets utilisent JSON parce qu’il est lisible par l’humain et facile à manipuler (la plupart des langages ont des parseurs JSON). Par exemple, un message de lecture de température en JSON pourrait ressembler à :

{ "device_id": "ABC123", "timestamp": 1692124800, "temperature_c": 22.5 }

Cela expose clairement les champs. JSON est un peu verbeux, cependant, donc si votre appareil doit envoyer des données très efficacement (pensez des milliers de messages ou une bande passante très faible), vous pourriez utiliser un format binaire (comme Protocol Buffers, CBOR, ou un schéma binaire personnalisé) pour économiser des octets. Le choix de conception ici affectera à la fois le firmware et le cloud : les deux côtés doivent s’accorder sur le protocole et le format. C’est un peu comme se mettre d’accord sur une langue avant de commencer une conversation – si l’appareil « parle » MQTT+JSON et que le serveur attend autre chose, ils ne se comprendront pas.

Un outil utile à ce stade est de dessiner un diagramme de séquence ou simplement d’écrire un exemple de séquence de communication. Par exemple : « L’appareil prend une lecture de capteur, formate un payload JSON, l’envoie via MQTT au topic X ; le broker cloud le reçoit, le service cloud le traite et l’acquitte, puis le stocke dans la base de données ; plus tard, un utilisateur demande des données via l’application, l’API cloud les récupère de la base et retourne du JSON à l’application. » Écrire ce flux garantit que chacun (le dev firmware et le dev cloud, en particulier) est littéralement sur la même page sur la façon dont les données se déplacent. Cela peut prévenir les malentendus du genre « Oh, je croyais qu’on utilisait un timestamp UNIX en secondes, pas en millisecondes » ou « Est-ce qu’on compressait les données ou pas ? ». Attrapez-les maintenant en conception, cela économisera du temps de débogage plus tard.

Standards de codage et outils d’équipe (la qualité dès le départ)

Parlons maintenant de la façon dont nous allons écrire le code et collaborer, car faire suivre des pratiques communes à toute l’équipe est essentiel. Mettez-vous d’accord sur des standards de codage pour chaque partie du système. Pour le développement C embarqué, beaucoup d’équipes adoptent les directives MISRA-C – un ensemble de règles pour le langage C visant à éviter les constructions dangereuses ou propices aux bugs (issues à l’origine de l’industrie automobile, mais largement utiles dans tout firmware critique). Si vous utilisez C++ en firmware, vous pourriez décider quelles fonctionnalités du C++ moderne sont autorisées ou bannies (pour garder les choses sûres pour les systèmes embarqués – par exemple, peut-être en évitant l’allocation dynamique à l’exécution, etc.). Pour les langages de plus haut niveau côté cloud comme Python, vous suivriez probablement le guide de style PEP 8, et pour JavaScript/TypeScript peut-être un linter avec le style Airbnb ou le standard en vigueur. L’objectif n’est pas d’être pinailleur, c’est d’assurer la cohérence et d’attraper les pièges courants. Quand tout le monde écrit dans un style cohérent, la base de code ressemble à un seul produit plutôt qu’à un patchwork, et il est plus facile de lire et maintenir du code écrit par quelqu’un d’autre dans l’équipe.

En plus du style de code, choisissez les outils et l’infrastructure que l’équipe utilisera. Cela inclut la gestion de version et la configuration du dépôt (presque certainement Git – si vous ne l’utilisez pas déjà, faites-le absolument, c’est la colonne vertébrale du codage collaboratif). Décidez de la stratégie de branches : utiliserez-vous un développement trunk-based simple, ou GitFlow avec des feature branches, etc. ? Mettez aussi en place un système de suivi de tickets si ce n’est pas fait (comme Jira, Trello, Asana, ou GitHub Issues) pour gérer les tâches et user stories de vos sprints. C’est aussi le bon moment pour mettre en place le pipeline de Continuous Integration (CI) : choisissez un service CI (GitHub Actions, GitLab CI, Jenkins, CircleCI, etc.) et configurez-le pour compiler et tester automatiquement votre code à chaque push. Nous parlerons plus de la CI dans la section suivante, mais l’étape d’architecture est celle où vous vous assurez que tous les développeurs ont accès à ces outils et savent les utiliser.

Un autre aspect de la « culture du code » est de décider de choses comme les pratiques de code review – p. ex., chaque pull request exigera-t-elle l’approbation d’au moins un autre développeur ? Fixer ces attentes tôt est sain. Cela peut sembler formel, mais cela vous évite des scénarios où du code non relu de quelqu’un introduit un bug méchant ou un trou de sécurité que les autres n’attrapent que bien plus tard. En convenant que « nous faisons toujours des code reviews, suivons le guide de style, et lançons les tests avant de merger », vous instillez un état d’esprit qualité dès le premier jour. Le développement moderne est autant affaire de collaboration d’équipe et de discipline que de génie du code.

Enfin, rappelez-vous que cette planification de l’Étape 4.1 ne consiste pas à dessiner de jolis diagrammes que personne ne regardera jamais – il s’agit de donner à l’équipe une compréhension partagée et une feuille de route technique. C’est comme planifier l’itinéraire d’un road-trip et emporter une carte : vous savez où sont les grands virages et comment vous naviguerez, mais vous pouvez rester flexible sur les pauses ou les détours. Consacrez un temps raisonnable à cette conception d’architecture, mais ne visez pas la perfection ni le détail de la moindre fonction. Vous voulez juste assez de conception pour que les gens puissent commencer à coder de façon coordonnée. Avec cette fondation posée, vous êtes prêt à sauter dans le développement en confiance.

4.2 Sprints de développement – Itérer pour vaincre

Le plan d’architecture en main, la construction commence – écrire du code, le tester, et regarder votre produit IoT faire réellement des choses. Nous abordons cela par itérations, typiquement avec des sprints comme dans la méthodologie Agile. Un sprint est un cycle court de longueur fixe (couramment 2 semaines, mais cela peut être 1 semaine ou jusqu’à 4 semaines selon les préférences de votre équipe) où l’équipe s’engage à livrer un ensemble de fonctionnalités ou d’améliorations à la fin. L’idée est de découper le travail massif de « développer tout le logiciel » en morceaux digestes que nous pouvons attaquer régulièrement avec des progrès tangibles.

Cette approche itérative a des bénéfices clairs : elle force l’intégration continue des composants (pour que firmware et logiciel cloud soient testés ensemble fréquemment), et elle vous donne de nombreuses occasions d’obtenir du feedback et d’ajuster le cap si nécessaire. C’est bien mieux que de coder pendant 6 mois et d’essayer seulement ensuite de voir si tout fonctionne (recette pour des surprises désagréables). Voyons à quoi un cycle de sprint typique peut ressembler pour notre développement de produit IoT :

Sprint planning & user stories

Chaque sprint commence par une réunion de Sprint Planning. L’équipe regarde le product backlog – la liste maîtresse de toutes les fonctionnalités, corrections et tâches à faire, initialement dérivée des exigences rassemblées à l’Étape 1 (et enrichie à mesure que nous apprenions dans les étapes suivantes). Pendant le planning, l’équipe sélectionne un ensemble de user stories ou de tâches du backlog à traiter dans le sprint, selon la priorité et la capacité de l’équipe.

Une user story est une description digeste d’une fonctionnalité du point de vue de l’utilisateur final, suivant souvent le modèle « En tant que <type d'utilisateur>, je veux <quelque capacité> afin de <bénéfice>. » Par exemple, une story pourrait être : « En tant qu’utilisateur, je veux que l’appareil IoT envoie les données de température au cloud toutes les 10 minutes, pour pouvoir surveiller la température de ma pièce à distance. » À partir de cette story, l’équipe déterminera ce qui doit être fait côté appareil et côté cloud : peut-être que sur le firmware de l’appareil, nous devons lire le capteur de température et envoyer un message MQTT toutes les 600 secondes ; côté cloud, nous avons besoin d’une API ou d’un gestionnaire de messages pour recevoir ces données et d’un endroit pour les stocker, et peut-être d’une interface simple pour les afficher.

Pendant le sprint planning, l’équipe discute chaque story choisie pour verrouiller les critères d’acceptation – essentiellement, comment savons-nous quand cette story est « done » et fonctionne ? Pour l’exemple ci-dessus, les critères pourraient être : « Étant donné un appareil avec un capteur de température, quand il fonctionne pendant 10 minutes, au moins une lecture de température doit être visible dans la base de données cloud et sur le tableau de bord utilisateur. » Être spécifique aide l’équipe à comprendre exactement quoi construire et comment le vérifier. C’est le moment de poser des questions et de clarifier les détails. Il vaut bien mieux aplanir les ambiguïtés maintenant que de découvrir plus tard que les gens du firmware et du cloud avaient des hypothèses différentes (comme un développeur qui pensait que c’était toutes les 10 minutes quand actif, un autre qui pensait que c’était en permanence, etc.). Une fois le planning terminé, chacun devrait savoir pour quel morceau de travail il est responsable dans le sprint à venir.

Implémentation & Continuous Integration (CI)

Passons maintenant à l’implémentation – écrire le code pour donner vie à ces user stories. Les développeurs commenceront à coder les fonctionnalités firmware et cloud identifiées pour le sprint. Une bonne pratique clé ici est d’écrire aussi des tests automatisés pour chaque nouvelle fonctionnalité à mesure qu’elle est développée. Pour le firmware, cela peut inclure l’écriture de tests unitaires pour la logique qui peut tourner sur une machine hôte (par exemple, si vous avez une fonction qui parse une lecture de capteur ou une machine à états pour l’appareil, vous pouvez souvent en écrire un test sur PC). Pour le logiciel cloud, c’est généralement plus facile : vous pouvez écrire des tests unitaires pour vos fonctions, et des tests d’intégration pour vos APIs.

À mesure que les développeurs écrivent du code, ils le commit dans la gestion de version (Git). C’est là que notre configuration de Continuous Integration (CI) brille. Avec la CI, chaque fois que du code est poussé ou qu’une pull request est ouverte, un build automatisé est lancé sur un serveur CI. La CI compilera le code firmware (peut-être dans plusieurs configurations si nécessaire), lancera tous les tests unitaires que vous avez, et peut-être même déploiera le firmware sur un simulateur ou un appareil de test. Elle compilera aussi le logiciel cloud, lancera tous ses tests, vérifiera le style de code (linters), exécutera des outils d’analyse statique, et mesurera même la couverture de code (quelle part du code est exercée par les tests). L’objectif est d’attraper tout problème immédiatement. Si un développeur a accidentellement introduit un bug qui fait échouer un test, le système CI le signalera tout de suite – quelques minutes après l’écriture du code – pour que l’équipe puisse le corriger avant qu’il ne devienne un problème plus gros. Cette boucle de feedback rapide est cruciale. Il est bien plus facile de corriger un bug que vous avez introduit il y a une heure (vous vous souvenez encore de ce que vous faisiez) qu’un bug du mois dernier (qui entre-temps s’est enchevêtré dans beaucoup d’autres changements).

Par exemple, imaginez qu’un développeur modifie le code de lecture de capteur et introduise accidentellement un bug de débordement pour les valeurs très élevées. Si vous avez un test unitaire qui pousse une valeur de capteur à l’extrême, la CI attrapera cet échec et alertera l’équipe. Le développeur peut alors y remédier immédiatement. Sans ce test, le bug pourrait rôder jusqu’à ce qu’une canicule frappe, que de vrais capteurs rapportent des températures élevées, et que l’appareil plante sur le terrain. La CI et les tests existent pour attraper exactement ces moments – automatiquement, et quelques minutes après l’écriture du code.

La Continuous Integration ne concerne pas que les tests ; les équipes intègrent souvent aussi des outils d’analyse statique dans la CI. Les outils d’analyse statique examinent votre code source à la recherche de bugs potentiels, de vulnérabilités de sécurité ou de violations de style sans l’exécuter. Par exemple, ils peuvent attraper des choses comme « déréférencement possible de pointeur nul » ou « variables inutilisées » ou d’autres code smells. Beaucoup d’équipes embarquées utilisent des outils comme PC-Lint ou Cppcheck ou clang-tidy pour le C/C++, et les équipes cloud utilisent des linters (ESLint pour JavaScript, Pylint pour Python, etc.) et peut-être des scanners de sécurité. La CI peut être configurée pour faire échouer le build si ces outils trouvent des problèmes sérieux. Cela fixe une barre de qualité – p. ex., « le code firmware doit avoir 0 nouveau warning et tous les tests passants pour qu’un merge soit autorisé ». Cela peut sembler strict, mais cela garde la base de code saine.

De plus, la CI peut suivre la couverture de code – quel pourcentage de votre code est exercé par les tests automatisés. Bien que 100 % de couverture soit souvent irréaliste (et que tout le code ne soit pas également facile à tester), beaucoup d’équipes fixent une cible (disons, au moins 80 % de couverture) et la suivent. Si un nouveau lot de code fait soudain chuter la couverture de 80 % à 50 %, c’est un signal d’alerte – peut-être que des tests ont été sautés ou que beaucoup de code non testé a été ajouté. En surveillant ces métriques sprint après sprint, vous maintenez une qualité de code élevée tout au long du développement, au lieu d’essayer de la « boulonner » plus tard.

En résumé, l’implémentation dans un contexte itératif n’est pas juste « écrire un tas de code ». C’est écrire du code, intégrer continuellement ce code dans une base partagée, le tester continuellement, et corriger les problèmes au fil de l’eau. Le résultat est un système logiciel qui est toujours dans un état semi-fonctionnel, s’améliorant un peu chaque jour.

Builds incrémentaux et démos de fin de sprint

À la fin de chaque sprint, vous visez un build incrémental de tout le système qui est une amélioration tangible par rapport au précédent. Idéalement, c’est quelque chose que vous pourriez réellement exécuter sur un appareil et un environnement cloud de test pour voir les nouvelles fonctionnalités en action. Un bon mantra est « soyez toujours prêt à démontrer » à la fin du sprint. Pour notre exemple de produit IoT, peut-être qu’à la fin du Sprint 1, nous avons un flux basique de bout en bout qui fonctionne : l’appareil lit une valeur de capteur fictive (peut-être un nombre fixe ou une valeur de test aléatoire) et l’envoie au cloud, le cloud la reçoit et la stocke, et nous pouvons vérifier manuellement que les données sont passées (même si c’est juste en regardant directement la base de données ou les logs). C’est déjà un jalon significatif – il prouve que toute la plomberie de l’appareil au cloud fonctionne !

Au Sprint 2, nous pourrions remplacer la valeur fictive par une vraie lecture de capteur du matériel, et mettre en place une simple page web qui affiche la dernière lecture. Maintenant nous pouvons réellement voir des données en direct. Au Sprint 3, peut-être ajoutons-nous une fonctionnalité d’alerte : si la température dépasse un seuil, l’appareil ou le cloud déclenche une alerte (peut-être qu’une LED clignote ou qu’un email est envoyé). Et ainsi de suite. À chaque sprint, nous superposons des fonctionnalités supplémentaires : peut-être qu’un sprint ajoute le support du contrôle à distance (envoyer des commandes du cloud à l’appareil), un autre ajoute un joli graphique UI des données historiques, etc. Après un certain nombre de sprints, vous aurez un système complet en fonctionnalités. Mais crucialement, à chaque étape, vous aviez un système fonctionnel auquel vous ajoutiez. Cela signifie que si quelque chose tourne mal, vous savez que cela a probablement à voir avec les ajouts les plus récents, ce qui facilite le débogage. Cela signifie aussi que les parties prenantes (comme votre boss, ou le chef de produit, ou même un utilisateur test) peuvent voir les progrès et donner du feedback en continu.

Les démos régulières sont une excellente pratique. À la fin de chaque sprint (ou même plus fréquemment, comme un petit show-and-tell chaque semaine), rassemblez l’équipe et toutes les parties prenantes intéressées et démontrez ce qui est nouveau. Cela peut être aussi simple que « L’appareil envoie maintenant de vraies données de capteur – regardez pendant que je chauffe le capteur avec ma main, et voyez le chiffre sur ce tableau de bord monter en temps réel ! » Ces démos créent un sentiment d’accomplissement et d’élan. Elles forcent aussi la discipline d’intégration : vous ne pouvez pas démontrer quelque chose qui ne fonctionne pas réellement ensemble, donc cela pousse l’équipe à aplanir les problèmes d’intégration au fil de l’eau, plutôt que de reporter l’intégration à la toute fin. Regarder le produit prendre vie petit à petit, sprint après sprint, est bien plus motivant que de travailler dans le vide pendant des mois.

Tests et critères de Definition of Done

Nous avons parlé des tests automatisés pendant le développement, mais ça vaut la peine de souligner à quel point les tests sont tissés dans la définition même du progrès à chaque sprint. Une fonctionnalité ou user story n’est considérée « done » que lorsqu’elle remplit certains critères au-delà du simple « le code a été écrit ». Les équipes ont souvent une checklist Definition of Done (DoD) qui peut inclure des éléments comme :

C’est une longue liste, et au premier regard, elle peut sembler faire beaucoup de cases à cocher. Mais ces pratiques vous épargnent énormément d’ennuis plus tard. Il vaut bien mieux attraper et corriger un bug ou un défaut de conception maintenant, pendant le sprint en cours, que de le laisser se faufiler jusqu’aux tests finaux ou à la production. La Definition of Done signifie essentiellement « cette fonctionnalité n’est pas à moitié cuite ; elle est vraiment intégrée, testée et prête ». Quand chaque partie de votre système remplit ces critères au fil de l’eau, la phase finale d’intégration et de tests (Étape 5) réserve peu de surprises – vous avez vérifié tout du long.

Documentation continue (gardez les cartes à jour)

Vous avez probablement créé des diagrammes et des docs dans la phase d’architecture (Étape 4.1). Maintenant, à mesure que le développement avance, il est important de garder ces documents de conception à jour. Le logiciel a une drôle de façon d’évoluer – peut-être découvrez-vous le besoin d’une nouvelle tâche dans le RTOS pour une fonction watchdog, ou l’architecture cloud change pour ajouter une couche de cache pour la performance. Si vous ne mettez pas à jour les docs d’architecture, elles dériveront de la réalité et finalement plus personne ne leur fera confiance. Pour éviter le problème classique de la documentation périmée, traitez les docs comme une partie vivante du projet. Certaines équipes écrivent même leurs diagrammes en code avec des outils comme PlantUML ou Mermaid. Ainsi, la « source » du diagramme vit dans le dépôt aux côtés du code. Quand un changement est fait qui affecte l’architecture, l’équipe met à jour le code du diagramme et régénère l’image.

Une option pratique (si votre équipe est partante) est d’intégrer aussi la documentation dans le processus CI. Par exemple, vous pourriez avoir un contrôle qui, si certains fichiers cœur ont changé (comme un schéma de message ou une interface de composant) et que le fichier de documentation correspondant n’a pas été mis à jour, la CI vous le rappelle ou fait échouer le build. Cela pousse les développeurs : « Hé, tu as changé la façon dont le système fonctionne – mets à jour le diagramme pour que tout le monde soit au courant. » Cela peut sembler excessif, mais garder les docs en sync paie quand un nouveau développeur rejoint l’équipe ou quand vous revenez au projet dans six mois et essayez de vous rappeler comment les choses sont organisées. Même si vous ne l’automatisez pas, programmez au moins une revue rapide des diagrammes tous les quelques sprints pour vous assurer qu’ils reflètent toujours la réalité.

L’essentiel : une documentation à jour vaut l’effort. Cela signifie que quiconque dans l’équipe (ou en dehors, comme un auditeur de conformité ou un partenaire) peut regarder les docs et leur faire confiance. Vous évitez les situations du genre « Ne lis pas le wiki, il est vieux – Joe a la vraie info dans sa tête (ou dans des commentaires de code). » Au lieu de cela, la connaissance est capturée et mise à jour au fil de l’eau, ce qui est la marque d’une équipe mature et axée qualité.

Démos régulières et boucles de feedback

Nous avons évoqué les démos dans le contexte des sprints, mais soulignons la valeur d’établir une cadence régulière de démonstration des progrès. En Agile, c’est souvent la réunion de Sprint Review : à la fin du sprint, l’équipe montre ce qu’elle a construit aux parties prenantes (chefs de produit, autres équipes, parfois même de vrais utilisateurs ou des dirigeants). Pour un projet IoT, une démo peut être littérale – montrer l’appareil et le système fonctionnant ensemble. Par exemple, vous pourriez projeter le tableau de bord web sur un écran et avoir l’appareil physique sur la table. Puis faire quelque chose comme déclencher l’appareil (appuyer sur un bouton ou créer une condition) et montrer que le cloud se met à jour en temps réel. Ces moments sont réellement validants pour l’équipe et précieux pour les parties prenantes. Cela rend le projet tangible et garde tout le monde engagé. C’est aussi une occasion d’obtenir du feedback : peut-être qu’une partie prenante dit : « C’est bien, mais pourrions-nous aussi afficher les données d’humidité dans la prochaine version ? » ou « L’alerte a mis un peu de temps à apparaître, peut-on accélérer ça ? ». Un feedback précoce comme celui-ci est particulièrement précieux, parce qu’il peut être réinjecté dans les sprints suivants, bien avant que le produit ne soit final, quand les changements sont bien plus faciles à faire.

Ne vous inquiétez pas si dans les premières démos la fonctionnalité est très basique – c’est attendu. La clé est qu’elle s’améliore itérativement. La première démo peut simplement prouver qu’un appareil peut parler à un serveur. La démo suivante montre une UI simple. Les démos ultérieures montrent du polish et des fonctionnalités supplémentaires. Si quelque chose n’a pas fonctionné comme prévu dans une démo, ce n’est pas grave non plus, parce que vous avez découvert un problème d’intégration devant tout le monde et vous pouvez maintenant prioriser sa correction. (En fait, certaines équipes ont un dicton : « Démo tôt, démo souvent – échoue souvent, corrige plus vite ! ») La démo impose aussi un peu de discipline : elle met une pression positive sur l’équipe pour finir le travail dans un état livrable à chaque sprint, plutôt que de procrastiner l’intégration à plus tard. Il vaut bien mieux découvrir maintenant que le JSON de l’appareil ne se parse pas correctement sur le serveur, que de le découvrir pendant une grande phase de test finale. Alors, traitez les démos non pas seulement comme un spectacle, mais comme une partie du processus de développement lui-même.

Enfin, célébrez ces démos. Construire un produit IoT est un travail difficile, impliquant de nombreuses pièces, et chaque pièce fonctionnelle – même une petite fonctionnalité – mérite un moment de reconnaissance. Cela maintient le moral et construit la confiance de l’équipe : l’énergie reste plus haute quand chacun voit régulièrement les résultats tangibles de son travail.


Pour résumer l’Étape 4 : le développement du firmware et du logiciel cloud est entièrement affaire de progrès itératif et d’amélioration continue. En investissant du temps dans l’architecture logicielle en amont, vous donnez à votre équipe un plan solide (et évitez de vous coder dans un coin). En utilisant des sprints de développement, l’intégration continue et une définition de done solide, vous garantissez que la qualité est intégrée à chaque pas – les bugs sont attrapés tôt, et le logiciel est toujours à un pas d’un produit fonctionnel. À la fin de l’Étape 4 (qui englobe probablement plusieurs cycles de sprints), vous devriez avoir un firmware complet en fonctionnalités et testé sur du matériel réel ou représentatif, et un système cloud complet en fonctionnalités et testé dans un environnement réaliste. Autrement dit, tout le côté logiciel de votre produit est prêt à rouler.

Grâce à cette approche itérative, vous avez pu ajuster et améliorer en continu, plutôt que de tout miser sur un big bang à la fin. Cela réduit le risque et rend le processus de développement plus prévisible. C’est aussi simplement une meilleure façon de travailler : au lieu d’une longue période d’incertitude, vous avez regardé votre produit IoT prendre vie étape par étape, d’une LED qui clignote à un appareil intelligent entièrement connecté et intégré au cloud. Le gros du travail côté logiciel est fait, et il a été fait avec rigueur et agilité. En route pour l’étape suivante, où nous prendrons ce matériel et ce logiciel bien conçus et nous préparerons à les lâcher dans le monde.