Je profite de cette anecdote pour rédiger un long journal afin de partager mon expérience et mon apprentissage en espérant t’apprendre de nouveaux points de vue.
Peut-être cela vaut le coup d'en faire une dépêche… Dis-le moi dans les commentaires. 😉
Ma carrière professionnelle de développeur C a débuté en 1994 et j’ai naturellement adopté avec enthousiasme le C++ en 1999.J’étais un développeur C++ heureux et j'avais considéré la nouvelle version du standard C++11 juste comme une évolution tant attendue.
Mais en 2015, je réalise enfin à quel point C++11 a révolutionné l’écosystème C++ et ses bouleversements dans l’état d’esprit de la communauté C++.Je me passionne alors pour C++11, C++14 puis C++17, je m’implique beaucoup, je deviens un référent pour mes collègues, je donne des conférences sur le C++, j’organise des Meetups à Paris, je publie de nombreux articles C++ sur LinuxFr.org…
Cependant, en 2018, une expérience change radicalement ma façon d’appréhender le développement logiciel.À cette époque, une équipe C++ se retrouve surchargée et ne peut implémenter toutes les fonctionnalités attendues.On réfléchit à une solution de secours, et me voilà chargé de développer une application en Python en intégrant des briques sous licence libre.Je travaille alors en étroite collaboration avec les utilisateurs finaux et on sort l’application en quelques mois.
Ma nouvelle vision :
L’application bricolée en Python donne entièrement satisfaction.Les utilisateurs sont contents d’avoir eu très tôt leur outil, et cela a coûté moins cher par rapport à toute une équipe C++ dans sa tour d’ivoire.
Attention, je ne dénigre pas le C++, celui-ci peut être une bonne solution face à de nombreuses problématiques.Mais avant de courir tête baissée, s’assurer que la technologie choisie répondra bien aux attentes non exprimées de l’utilisateur final.J’écris « attentes non exprimées" car bien souvent le développeur se base sur sa propre interprétationd’un document présenté comme LA spécification qui a en plus été rédigée par un intermédiaire (MOA, MOE, Business Analyste, Product Owner, Architecte…)
Nous l’avons peut-être déjà tous constaté, l’utilisateur final ne sait pas vraiment ce qu’il veut, et a beaucoup de difficulté à exprimer clairement par écrit ses attentes.Un intermédiaire (MOA, MOE, BA, PO, Archi) est nécessaire pour lui permettre de prendre du reculet pour traduire une demande fonctionnelle en exigence technique.
Mais l’intermédiaire rajoute une couche intermédiaire !C’est humain, l’intermédiaire aura tendance à se rendre indispensable.Et sans le faire exprès, l’intermédiaire évitera que développeurs et utilisateurs échangent en direct.
Le top est d’avoir l’utilisateur final dans son équipe de développement, même si ce n’est pas dans le même bureau. Avec les habitudes de travail à distance, développeurs et utilisateur peuvent clavarder (chat) régulièrement.
La durée idéale d’un sprint c’est la journée.Le matin on échange rapidement avec le client de ce que l’on pourrait faire, on s’attelle à la tâche, on livre, l’utilisateur peut tester, on re-livre…Et, en fin de journée, on débriefe très rapidement.
L’intérêt du Python (par rapport au C++) dans ce mode de fonctionnement c’est la livraison :on peut se permettre de livrer directement le code source et hop l’utilisateur exécute l’application !Ainsi, dans mon cas, quand l’utilisateur lançait le démarrage de l’application, la branche master du dépôt Git était automatiquement mise à jour.J’essayais quand même de le prévenir quand une nouvelle version était sur la branche master.
La joie de livrer souvent, rapidement, et d’avoir du feed-back dans la foulée :-)
Nous pourrions caricaturer :
¹ Utiliser Pythran/PyPy/Cython/Numba/… accélère l’exécution, mais cela ralentit le développement : compilation plus lente, développement plus complexe, bug difficile à investiguer…
Bien souvent, le client final a besoin rapidement d’une fonctionnalité, même si l'exécution n'est pas super optimisée.En livrant rapidement cette fonctionnalité, le client gagne en maturité et a de nouvelles idées, de nouvelles façons pour optimiser son travail… et ainsi de suite avec des itérations courtes.
Pour une application existante, nous avons besoin de fournir une interface WebSocket basique afin que nos clients puissent accéder à un service de Souscription/Publication.
Je développe la fonctionnalité en utilisant Python et Socket.io.Les tests JavaScript sont concluants.Cependant, nous nous apercevons que Socket.io rajoute son protocole par-dessus le protocole WebSocket.Nous allons donc forcer les clients à devoir utiliser Socket.io ce qui n’est pas acceptable.
Nous voulons offrir une simple WebSocket afin que le client ne soit pas enfermé dans une technologie et soit libre d’implémenter son logiciel comme il le souhaite.
Je m’oriente alors vers WAMP avec Autobahn…Mais rebelote le P dans WAMP signifie Protocol.
Pour ne pas réinventer la roue, je cherche alors une solution qui implémente déjà la fonctionnalité Souscription/Publication et idéalement avec des coroutines (async
et await
).
C’est alors que je découvre uWebSockets (µWS), une implémentation C++17 à couper le souffle qui intègre la fonctionnalité Souscription/Publication tout en conservant le protocole WebSocket de base. \o/
Mon équipe est enthousiaste. Je clone le projet, j’adapte un exemple C++ à nos besoins et je présente un résultat convainquant. C’est agréable de compiler un projet C++ qui ne tire pas des dizaines de dépendances :-)
Je précise que pour ce nouveau projet, je viens de rejoindre une autre organisation, et j’ai été embauché, en partie, pour mes connaissances pointues en C++17.
En Python, Node.js, Ruby, Go, Rust on a pip
, npm
, gem
, go-get
, cargo
qui simplifie le téléchargement des dépendances, et l’intégration au projet avec un import xxxx
. De plus, certains IDE prennent en charge cette gestion des dépendances.
En C++, la gestion des dépendances et leur compilation ne sont pas standardisées. En fait, cela n’a pas beaucoup évolué depuis les origines : le C++ a 40 ans et se compile toujours dans le même esprit que le C qui a lui 60 ans.
Pour cela, nous avons le choix du compilateur, dont voici une sélection avec leurs dates de naissance :
Pour la construction logicielle (build), nous avons une dizaine d’outils pour nous abstraire du compilateur, en voici une petite sélection sous licence libre :
Et pour nous abstraire de ces outils de construction logicielle, nous utilisons des générateurs de configuration de build, dont voici des projets toujours actifs :
Avec les outils ci-dessus, le build et les tests de non régression d’une importante application C++ peut parfois prendre une demi-journée.C’est beaucoup trop pour attendre si ce que l’on a implémenté est correct !
D’autres outils de construction logicielle ont donc été développées dans le but de builder/tester en un minimum de temps :
Ces projets ne réutilisent pas les outils de build cités plus haut, et ne génèrent pas les fichiers projet des IDE.
Par contre, ces outils analysent finement le graphe de dépendance, gèrent de gros dépôts de code source,parallélisent la construction logicielle sur tous les cœurs (CPU) de nombreuses machines (cloud).Les étapes du build sont mémoïsées pour éviter de refaire la même opération N fois (par exemple, pour éviter de compiler un fichier non modifié, ou de linker une bibliothèque inchangée).
Sans avoir à recourir à ces outils, on peut améliorer les temps de compilation C++ et d’édition de liens (link) avec ces deux bons vieux outils :
Ces outils ccache
et distcc
sont pris en charge par CMake, SCons…
Mais au fait, comment gérer les dépendances C++ ?Quel est le gestionnaire de paquets C++ officiel ? (package manager)Quel est l’équivalent C++ pour les commandes pip
, npm
, gem
, go-get
et cargo
?
Eh bien… disons que nous avons des initiatives encourageantes :
Attention à ne pas confondre l’exécutable b
du projet build2 avec l’exécutable b2
du projet Boost.build cité plus haut.
Une autre façon de gérer très simplement ses dépendances C++ est de carrément copier le code source de celles-ci (et aussi le code source des dépendances des dépendances) avec le code source de l’application. Ça compile toujours.
Mais ce n’est pas une bonne pratique pour, au moins, deux raisons :
Je teste différents moyens pour obtenir le paquet uWebSocket : le package manager de ma distribution, Conan, Hunter… et finalement, le paquet uWebSocket a fraîchement été intégré au dépôt vcpkg. Je teste, et, au miracle, cela télécharge le code source et me l’installe sur ma distribution GNU/Linux !
L’outil vcpkg fonctionne avec CMake, donc ce sera CMake qui gérera la construction logicielle.Le temps de se documenter et bien prendre en main vcpkg, de tenter une intégration dans mon CMakeLists.txt
, d’échouer, de recommencer…
Pourtant l’exemple avec SQLite3 fonctionne chez moi… Arg… En fait, c’est mal empaqueté, le find_package()
ne pourra jamais trouver la bibliothèque uWebSocket fournie par vcpkg.
J’essaye de le faire moi-même. Puis, je me rends compte au second jour que vcpkg est un outil amateur, a ne surtout pas utiliser pour aller en production : pas de version des dépendances, impossible de décider des options de compilation des dépendances…
Bon, je connais bien C++, alors empaqueter proprement une bibliothèque qui ne dépend de rien ne devrait pas poser problème.
Je retrousse mes manches et je commence à chercher si quelqu’un a déjà empaqueté uWebSockets…
Je trouve surtout que le mainteneur principal a supprimé les fichiers CMakeLists.txt
et meson.build
en 2017.Et celui-ci semble envoyer bouler les contributeurs proposant la compatibilité avec CMake, supprime le titre des Pull Request et on trouve même des commentaires supprimés.
On trouve une explication dans la FAQ :
En gros, le mainteneur principal refuse tout système de construction logicielle, car les développeurs veulent utiliser différents outils et que pour un même outil, les développeurs ne se mettent pas d’accord de la bonne façon de faire.
Un commit très étonnant est le Not my problem qui change la licence et remplace dans tous les fichiers la ligne : Copyright 2018 Alex Hultman and contributors.
par: Authored by Alex Hultman, 2018-2019.
Intellectual property of third-party.
Mon IDE préféré pour le C++ est QtCreator, mais celui-ci ne prend pas encore en charge Meson. Et je n’ai plus beaucoup de temps pour prendre en main GNOME Builder.
Et j’obtiens un joli CMakeLists.txt
qui fonctionne sur deux différentes distributions GNU/Linux et aussi sur GitLab.
Je me rends aussi compte que GCC-8 ne compile pas cette bibliothèque, et que nous devons utiliser seulement Clang.
Mais, mon plus gros problème est que je n’arrive plus à faire compiler cette bibliothèque par QtCreator, alors qu’avec la ligne de commande cela fonctionne parfaiement… Arg… J’investigue encore du temps…
Mes collègues ne comprennent pas pourquoi je mets autant de temps pour faire l’équivalent d’un pip install
en C++. De plus, je vais devoir refaire ce même travail dès que j’intégrerai les autres bibliothèques : base de donnée, file d’attente de message, journalisation…
Nous décidons ensemble d’arrêter de s’obstiner, de relever la tête, et de lister les possibilités :
J’ai plusieurs collègues très compétents en Node.js, alors c’est parti. Effectivement, l’installation de la dépendance est très simple et on implémente rapidement l’application.
Le grand avantage de Node.js (et de JavaScript en général) est la taille de la communauté et l’esprit de partage et d’innovation incroyables. Alors qu’il est nécessaire d’une décénie en C++ pour se décider, la communauté JavaScript décide en quelques mois. Les projets, les concepts de programmation, les méthodes de travailler ne cessent d’évoluer.
J’entre dans un vaisseau qui se déplace à la vitesse de la lumière. En C++, la durée de vie d’un projet peut être d’une dizaine d’années. En Node.js, c’est plutôt 10 mois. C’est aussi l’inconvénient, il faut s’adapter vite.
Mais, nous nous apercevons que le projet uWebSockets.js ne prend pas en charge la Souscription/Publication de la bibliothèque sous-jacente uWebSockets. Ça ne marche pas.
Stop, nous venons de gagner en maturité, voyons voir les possibilités :
Nous prennons concience que la partie WebSocket risque d’être un point sensible au niveau performance. Finalement, nous mettons entre parenthèses notre tentative Node.js.
Le Rust est encore trop jeune (peu de développeurs maîtrisant Rust sur le marché). Et nous avons un collègue devops fan du Go.
Notre collègue maîtrisant Go n’étant pas très disponible, on apprend se débrouille seul. Finalement, le principal du langage Go s’acquiert en quelques heures de programmation. Le test WebSocket est un succès et nous implémentons une première version basique de la Publication/souscription en Go.
Nous mettrons en place des tests de performance reproductibles, et seulement après nous pourrons décider quelles sont les parties qui nécessitent d’être optimisées.
Mais, non, je n’abandonne pas le C++ !
J’apprécie d’acquérir de nouvelles cordes à mon arc,de m’ouvrir l’esprit sur de nouvelles pratiques.
Certaines technologies sont plus adaptées à certains contextes.N’ayons pas de dogme, choisissons la bonne techno selon la situation.
Cependant, je ne sais pas quand je coderai à nouveau en C++…Peut-être quand on aura un C++ package manager standardisé… :-)
Un peu de publicité : Tu apprécies cet état d’esprit et que tu aimes coder en Python, Node.js, JavaScript/TypeScript/Angular ou Go ? N’hésite pas à me contacter sur oli (à) Lmap (point) org
. Nous avons des postes à Paris et à Metz. On peut s’arranger pour le télétravail. Pour le moment, on ne publie rien sous licence libre. Mais je pousse pour libérer quelques briques intéressantes…
DIRECT. Assassinat de Razia Askari à Besançon : "Pas de culpabilité, ni de remords, manque d'empathie", le profil de Rashid Askari, accusé de l'assassinat de sa femme, disséqué
[Vidéo] The Amazing Spider-Man 2 : la bande-annonce ultime
Nantes. Il avait agressé un chauffeur de tram : condamné à 6 mois, il évite la prison
Test du Samsung AU9000 | TechRadar