Je rêve
Je rêve de participer à un projet dès son inception, pour pouvoir décider de chaque aspect à inclure.
Être capable de choisir les technologies, l'architecture, les pratiques, les outils, les processus, les méthodologies, l'équipe même.
À l'époque où j'ai commencé à programmer pour gagner ma vie, beaucoup des technologies que nous considérons comme acquises aujourd'hui n'existaient même pas.
C'est comme si j'avais grandi avec l'évolution de certaines technologies, appris de mes erreurs.
J'ai essayé différentes alternatives pour le même besoin et j'ai expérimenté où certaines sont meilleures que d'autres.
Depuis longtemps, je suis principalement un développeur Java.
Quand j'ai commencé, je savais programmer, mais pas selon la Programmation Orientée Objet.
Ma première expérience en Java était d'aider un projet dans l'université ou j'avais fait mes études en chimie.
Je ne savais pas vraiment ce qu'était une classe, ni comment la compiler, mais je comprenais comment afin d'encoder les caractères spéciaux afin qu'un servlet puisse créer du JavaScript qui créerait lui-même du HTML qui se compilerait et fonctionnerait.
J'ai beaucoup appris depuis.
Je dois reconnaître que ce bouquin, Big Java, ma permis de bien entreprendre ce voyage.
Alors plongeons directement dans le vif du sujet de cet article.
Je vais faire un inventaire non exhaustif des technologies, pratiques, outils, processus, méthodologies, membres de l'équipe, etc. que j'aimerais avoir dans mon projet de terrain vierge.
Ne pas réinventer la roue
En tant que principe directeur, je m'appuierais sur des technologies, pratiques, processus, méthodologies et outils existants plutôt que de réinventer la roue..
Rares sont les cas où nous devons inventer quelque chose de nouveau.
La plupart du temps, nous pouvons utiliser quelque chose qui existe déjà.
C'est plus rapide, moins cher et plus fiable.
J'appelle cela: Se tenir sur les épaules de géants.
Du code, des bugs
Souvent, j'aime provoquer un peu les gestionnaires en leur disant que "des développeurs, ce sont des générateurs de bugs".
Ça part du principe que chaque ligne de code que nous écrivons peut avoir un bug.
Mais, si nous n'écrivons pas de code, nous n'avons pas de fonctionnalités.
L'autre principe, c'est que le plus tard nous trouvons un bug, le plus cher ce sera de le corriger.
Imaginez trouver un bug dans le code du véhicule Mars Rover après qu'il ait été lancé.
Comme corollaire, le plus près de sa création nous trouvons un bug, le moins cher ce sera de le corriger.
Donc, nous devrions viser à trouver les bugs le plus rapidement possible. Voici quelques pratiques qui permettent d'aider à produire du code de qualité :
- Utiliser un IDE. Ce sont plus que de simples éditeurs de textes. Ils peuvent nous aider à trouver des bugs, des erreurs de syntaxe, de logique, etc. avant même que nous compilions le code. Portons attention aux avertissements qu'ils nous donnent.
- Écrivons des tests. Même si nous n'appliquons pas la méthode TDD, nous devrions écrire des tests. Ils nous aident à trouver des bugs. Et, quand un bug est rapporté, nous devrions écrire un test pour le reproduire, réparer le bug, et rouler le test pour nous assurer que le bug est corrigé.
- Faire rouler les tests, tant localement que dans notre pipeline. Nous devrions avoir un pipeline qui roule les tests et bloque le merge si les tests ne passent pas.
- Faire de la revue de code (voir plus bas)
- Utiliser l'analyse statique de code. Elle permet de trouver des bugs qui ne sont pas évidents, comme des variables non utilisées, des imports inutiles, des erreurs de syntaxe, etc.
- Planifier du temps pour appliquer ce que nous avons découvert dans la revue de code et l'analyse statique.
- Déployer en environnement QA le plus tôt possible. Le plus tôt nous testons le code en environnement réel, le plus tôt nous trouvons les bugs.
- Avoir des QA qui testent le code. Ils peuvent trouver des bugs ou situations auxquels nous n'avons pas pensé.
- Faire du monitoring de l'application. Ça permet de déceler des bugs qui ne nous ont pas été rapportés par les utilisateurs.
- Déployer à un sous-ensemble d'utilisateur en premier. Ça permet de trouver des bugs que nous n'avons pas vu en QA.
Conventions et pratiques
Conventional Commits
J'ai découvert il y a un certain temps déjà la spécification des Conventional Commits.
C'est une convention simple sur la façon d'écrire des messages de commit.
C'est simple, mais c'est puissant.
Cela nous permet de générer des journaux de modifications, des versions des numéros, etc. automatiquement.
Avec une convention définie, il est plus facile pour chacun dans l'équipe de comprendre ce qu'est un commit à propos.
Cela facilite également la génération de notes de version.
Un message de commit typique ressemblerait à ceci :
feat: permettre à l'objet de configuration fourni d'étendre d'autres configurations
Ferme: JIRA-1234
forme des commits
<type>[portée optionnelle]: <description>
[corps optionnel]
[pied(s) de page optionnel(s)]
Le type peut être l'un des suivants :
- feat : Une nouvelle fonctionnalité
- fix : Un correctif
- build : Changements qui affectent le système de construction ou les dépendances externes
- chore : Changements qui ne modifient pas les fichiers src ou test
- ci : Changements apportés à nos fichiers de configuration CI et scripts
- docs: Changements uniquement dans la documentation
- style: Changements qui n'affectent pas le sens du code (espaces, formatage, point-virgule manquant deux-points, etc)
- refactor: Un changement de code qui ne corrige ni un bug ni n'ajoute une fonctionnalité
- perf: Amélioration des performances
- test: Ajout de tests manquants ou correction de tests existants
Dans le pied de page, j'ajouterais une référence au ticket JIRA (ou tout autre système de tickets) auquel le commit est lié.
En allant un peu plus loin, je pense que le type devrait également être le préfixe pour le nom de la branche.
Suivi du numéro de ticket, et enfin quelques mots sur la fonctionnalité ou le problème.
De cette façon, nous pouvons facilement voir de quoi il s'agit dans la branche.
exemple
feat/JIRA-1234_permettre-objet-config-fourni-d'étendre-autres-configs
Semantic versioning
J'adopterais le versionnement sémantique pour gérer les versions projet.
C'est une convention simple qui nous permet de savoir quel type de changements se trouvent dans une version rien qu'en regardant le numéro de version.
Traduction de la définition depuis celle du site web semver :
Étant donné un numéro de version MAJOR.MINOR.PATCH, incrémentez :
la version MAJOR lorsque vous apportez des modifications API incompatibles
la version MINOR lorsque vous ajoutez des fonctionnalités de manière rétrocompatible
la version PATCH lorsque vous effectuez des corrections de bogues rétrocompatibles
Des étiquettes supplémentaires pour les métadonnées de pré-version et de construction sont disponibles en tant qu'extensions au format MAJOR.MINOR.PATCH.
exemples
1.0.0
2.1.3
4.1.3ALPHA
En ce qui concerne la version, ce ne sont que des chiffres, nous ne devrions pas hésiter à les incrémenter, ils ne coûtent rien.
Et nous ne devrions pas essayer de garder toutes les parties d'un projet synchronisées avec le numéro de version.
Il est acceptable d'avoir une version 1.0.0 d'une bibliothèque et une version 2.0.0 de l'application qui l'utilise.
Mais, lorsque nous déployons, nous devons garder une trace des versions des différentes parties du projet.
Cela nous permet de voir facilement ce qui est déployé où.
Conquérir le monde (i18n) dès le départ
Nous devons intégrer l'internationalisation (i18n) dès le début du projet.
Nous ne pouvons pas simplement écrire les chaines de caractères pour les boutons, menus, descriptions, etc.
Nous utiliserons une bibliothèque appropriée pour les frameworks retenus (frontend et backend).
C'est beaucoup plus facile à mettre en place dès le départ que de tenter de réintégrer le tout une fois le projet démarré.
Aussi, si nous enregistrons de l'information dans le backend, comme des configurations, nous devrions retourner toutes les langues comme réponses aux interrogations, et laisser le frontend décider de ce qu'il affiche.
C'est particulièrement vrai quand on crée des apis.
Dates standard (ISO8601) dès le départ
La plupart des projets vont devoir gérer des dates à un moment ou un autre.
Nous adopterons le format de date ISO8601 pour toutes les communications dès le début.
De plus, les dates, c'est difficile.
Il suffit de demander à Google ou de jeter un oeil à Falsehoods programmers believe about time.
Nous utiliserons donc des bibliothèques reconnues pour manipuler les données temporelles.
Ça va nous sauver du temps à long terme.
Securité dès le début
La sécurité doit être une priorité dès le début.
Nous devons l'avoir à l'esprit dès le début du projet.
Nous devrions prendre le temps de réfléchir aux permissions et aux groupes, de déterminer quels points d'accès doivent être sécurisés, quels demandent des authorisations particulières et quels doivent être public.
Nous devrions aussi utiliser les fonctionnalités de sécurité des frameworks retenus, pas seulement pour l'accès, mais aussi pour éviter l'injection SQL, la reprise de session, etc.
OWASP Top Ten est un bon point de départ.
L'équipe, ou les rôles
Certains rôles et responsabilités sont essentiels et doivent être attribués à des membres de l'équipe, même s'ils peuvent être combinés.
- Développeur : C'est la personne qui écrit le code.
- QA : C'est la personne qui teste le code.
- Architecte : C'est la personne qui conçoit l'architecture du projet.
- Product Owner : C'est la personne qui définit les fonctionnalités du projet.
- Gestionnaire de projet : C'est la personne qui s'assure que le projet est livré à temps et dans le budget.
- Agile Methodology Master : C'est la personne qui s'assure que l'équipe respecte les principes de la méthodologie retenue.
- DevOps : C'est la personne qui s'assure que le code est déployé correctement.
Documentation
Nous devons documenter divers aspects de notre projet de manière organisée.
Toute la documentation n'a pas besoin d'être entreposée au même endroit.
Il est souvent préférable de garder la documentation près du code pour s'assurer qu'elle reste à jour.
Cependant, nous avons également besoin d'un endroit central pour indexer toute la documentation.
Un wiki est une bonne solution pour cet aspect. [antora] est une autre solution possible.
Diataxis
J'ai récemment été introduit au concept de Diataxis (https://dev.to/onepoint/documentation-chaotique-diataxis-a-la-rescousse—3e9o).
C'est une façon de catégoriser et d'organiser la documentation d'un projet.
On peut le voir comme une matrice avec deux axes : le contenu et la forme.
si le contenu décrit | et permet au lecteur de | alors cela devrait être une forme de |
---|---|---|
actions |
acquérir des compétences |
tutoriel |
actions |
appliquer des compétences |
guide pratique |
connaissances |
acquérir des connaissances |
explication de concepts |
connaissances |
appliquer les connaissances |
références |
Je n'ai pas encore utilisé cette méthode, mais je pense que c'est une bonne idée pour organiser et de réfléchir la documentation.
Format asciidoctor
Il existe de nombreuses façons et formats pour documenter notre futur projet.
Très souvent, nous verrons markdown comme format.
Malheureusement, markdown est plus limité, et il existe une variété de saveurs en compétition pour markdown.
Par exemple, il est difficile de numéroter les titres dans markdown, il faut le faire à la main, et s'assurer de faire suivre la numérotation quand on déplace des sections.
AsciiDoc est un format plus puissant qui peut être utilisé pour rédiger de la documentation.
Il permet de faire plus de choses que markdown.
Il est relativement facile à lire dans sa forme brute.
Donc, nous devrions utiliser Asciidoc comme format.
Il peut être utilisé pour générer de la documentation dans de nombreux formats, comme html, pdf, etc.
La documentation peut être pour différentes sorties, comme un livre, un article, etc.
Si nous devons un jour le convertir à nouveau en markdown, nous pouvons utiliser la commande suivante :
Conversion d'asciidoctor à markdown
asciidoctor -b docbook -a leveloffset=+1 -o - green-field.adoc| pandoc --wrap=preserve-t markdown_strict -f docbook - > green-field2.md
Documentation de projet antora
Antora est défini comme le générateur de site de documentation mono ou multi dépôt pour les rédacteurs techniques qui aiment rédiger en Asciidoc.
Antora permet de rédiger de la documentation en asciidoc dans plusieurs dépôts de codes (penser frontend, multiples modules backend) et de créer un dépôt pour centraliser toute la documentation de vos dépôts et de publier un site statique pour votre organisation.
C'est une façon très intéressante de faire en sorte d'avoir un point de départ pour toute la documentation du projet tout en la maintenant à jour.
Enregistrement de décisions architecturales (Architectural Decision Records ADR)
Dès le début d'un projet, nous prenons des décisions d'architectures.
Cet article en suggère plusieurs.
Avec le temps qui passe, les personnes peuvent changer de projet et la mémoire de ces décisions et des raisons qui les ont appuyées peuvent se perdre.
Les ADR sont une façon d'enregistrer ces décisions et de les garder au même endroit.
Quelques projets existent pour faciliter la création d'ADR, mais la plupart utilisent du markdown comme format.
Je suis encore à la recherche d'un bon projet qui supporte le asciidoc.
Pour l'instant, adr-j semble un bon candidat qui supporte à la fois le markdown et le asciidoc.
Pour des articles ou de la documentation, voir aussi Hugo
Se prétendant être le framework le plus rapide pour construire un site statique, Hugo est un framework qui prend un ensemble de documents rédigés en markdown ou asciidoc et les convertis en site statique avec la possibilité d'appliquer des thèmes et d'autres fonctionnalités intéressantes comme les mots clés.
J'ai commencé à l'utiliser pour générer mon blog personnel. Pour l'instant, je suis satisfait.
Développement
IDE (Environnement de Développement Intégré)
J'utilise IntelliJ IDEA de JetBrains depuis décembre 2012 et je l'apprécie beaucoup.
Mais en fait, chaque personne devrait utiliser n'importe quel IDE qu'elle aime, à une condition : Ils devraient le maîtriser.
Ils devraient savoir comment l'utiliser à son plein potentiel.
Si nous avons une personne junior dans notre équipe, assurons-nous qu'elle prenne le temps d'apprendre son IDE.
Projet de services auxiliaires (docker-compose)
Dans de nombreux projets, nous aurons besoin de certains services auxiliaires.
J'utiliserais docker-compose pour définir ces services auxiliaires et les rassembler.
Et envelopper les actions (démarrage, arrêt, réinitalisation de la base de données, etc.) dans un script shell qui offre une aide et des valeurs par défaut raisonnables.
De cette façon, nous pouvons démarrer les services auxiliaires avec une seule commande.
Nous pouvons également arrêter les services avec une seule commande.
Nous pouvons aussi redémarrer les services auxiliaires avec une seule commande.
Dans nos projets, le script d'aide comprend des profils.
Ainsi, un développeur front-end commencerait par l'aide des services comme la base de données et le backend, tandis qu'un développeur backend commencerait par la base de données et le front-end.
Et un QA commencerait tout.
Page d'aide en libre-service.
C'est une simple page html qui est servie par les services d'aide.
Elle contient des informations sur les services d'aide, comme la version, les points de terminaison, la documentation, etc.
Nous utilisons caddy pour cela, et un volume local pour servir la page html.-
traefik comme un proxy inverse pour toutes nos applications
- Nous pouvons le configurer avec un basculement. De cette façon, même si nous avons commencé avec un profil spécifique, disons dites backend, nous pouvons toujours démarrer le backend localement et cela prendra le pas sur celui dans le fichier docker-compose.
- https: traefik nous permet d'utiliser https avec une configuration simple. Cela peut provenir d'un certificat let's encrypt, ou d'un certificat auto-signé, ou en utilisant le projet localhost.direct.
portainer pour gérer nos conteneurs sans se soucier de la plateforme que nos développeurs ou qa utilisent
-
traduction des jetons JWT avec jwt.io
- Si nous utilisons un jeton JWT, nous devrons souvent extraire les informations de ceux-ci. Nous pouvons utiliser jwt.io pour cela. C'est un outil simple qui peut être utilisé pour extraire les informations d'un JWT token. Mais, si nous avons peur de la fuite d'informations, nous pouvons également utiliser une version locale de jwt.io.
postgresql ou autre base de données
serveur keycloak si nécessaire
grafana : dans notre cas, nous utilisons grafana pour afficher aux utilisateurs
rabbitmq : dans notre cas, nous utilisons rabbitmq pour gérer les messages et les files d'attente entre les différents services
wiremock : dans notre cas, nous utilisons wiremock pour simuler des services externes
dozzle, pour voir les journaux des conteneurs
mailhog pour voir les e-mails envoyés par l'application, c'est un simple serveur smtp qui peut être utilisé pour voir les e-mails envoyés par l'application
une sorte de service de ??? pour surveiller les services auxiliaires et expérimenter avec le monitoring de l'application
Nous pouvons également ajouter tout autre service d'assistance qui peut être dockerisé.
Et bien sûr, tous les projets, modules ou microservices qui font partie du projet.
- front end
- back end
- passerelle api
- etc.
Langages
Backend : Java
Comme je l'ai dit au début, je suis développeur Java de métier et d'expérience.
J'utiliserais Java pour construire le backend du projet.
C'est un langage mature.
C'est un langage puissant qui a de nombreuses fonctionnalités comme la programmation orientée objet, la programmation fonctionnelle, etc.
Il existe également de nombreux frameworks et bibliothèques matures qui ont été développés par des experts dans leurs domaines.
Bien sûr, d'autres langages pourraient être utilisés, comme Kotlin, Scala, Groovy, etc.
Mais je resterais avec Java.
Frontend
Pour le frontend, j'aurais du mal à choisir entre React et Angular.
React a beaucoup de momentum en ce moment, mais je n'ai pas beaucoup d'expérience avec.
D'un autre côté, on me dit qu'il y a beaucoup d'extensions qui servent le même but, donc il n'est pas facile de savoir quelle est la bonne stratégie à adopter pour un projet donné.
Angular est un framework complet qui comporte tout ce dont nous avons besoin pour construire une application frontend. Il est également maintenu par Google, donc il est probable qu'il sera maintenu à long terme.
Le jury est toujours en délibération sur ce choix.
Formatage du code
La simple réalité est qu'il faut choisir un format pour le code, n'importe lequel et de s'y tenir.
Mais, d'après mon expérience, j'ajouterais d'autres critères pour le sélectionner :
- Défini par une entité bien connue (ne perdez pas de temps à débattre si vous devez mettre des accolades à la fin de la ligne ou sur la ligne suivante)
- Facile à utiliser (vous ne devriez pas avoir à y penser)
- Peut être vérifié automatiquement par vos pipelines, donc instrumenté
- Peut être appliqué automatiquement par votre IDE
- Est fortement prescriptif (il ne devrait pas y avoir beaucoup de configurations que vous pouvez lui appliquer)
Base de code Java : Google java format
Pour le code Java, j'utiliserais Google Java Format.
C'est défini par Google, donc c'est une entité bien connue.
C'est facile à utiliser, et cela formatera notre code.
Il peut être vérifié automatiquement par nos pipelines et appliqué automatiquement par notre IDE.
Formatage du code Javascript/Typescript : Prettier
Je ne sais pas grand-chose sur le formatage du code Javascript.
J'utiliserais les mêmes critères que pour le formatage du code Java.
Prettier semble être un bon candidat.
Système de tickets et de problèmes
Dès qu'il y a (ou pourrait y avoir) plus d'une personne travaillant sur un projet, nous aurons besoin d'un moyen pour gérer notre travail, notez les tâches à accomplir, leur état d'avancement, etc.
Nous devrions utiliser le système de tickets qui est déjà en place dans l'organisation où le projet a commencé.
S'il n'y en a pas, de nombreuses options sont disponibles.
Messages d'erreur : utiliser l'API des problèmes RFC 9457
Lorsque nous construisons une API, nous devrons renvoyer des messages d'erreur.
Il est préférable de prédéfinir le format des messages d'erreur et être cohérents dans toutes les APIs que nous exposons, même si elles sont exposées seulement en interne.
J'utiliserais le Problem Details for HTTP APIs (RFC 9457) pour renvoyer des messages d'erreur.
C'est une simple convention qui peut être utilisée pour renvoyer des messages d'erreur.
Elle peut être utilisée pour renvoyer des messages d'erreur dans de nombreux formats, comme json, xml, etc.
Elle peut être utilisée pour renvoyer des messages d'erreur dans de nombreux langages, comme java, javascript, etc.
exemple d'API de problèmes
{
"statut": 500,
"titre": "Erreur interne du serveur",
"uuid": "d79f8cfa-ef5b-4501-a2c4-8f537c08ec0c",
"application": "super-microservice",
"version": "1.0"
}
Une caractéristique à noter est que nous pouvons faire en sorte que l'erreur dans les journaux ait un UUID unique qui est également renvoyé au client.
De cette façon, nous pouvons tracer l'erreur dans les journaux et dans le client.
Voici un article plus long par A java geek qui explique https://blog.frankel.ch/problem-details-http-apis/
Il existe une implémentation prête pour Quarkus : https://github.com/quarkiverse/quarkus-resteasy-problem
Système de chat
La communication est essentielle dans un projet.
Que ce soit pour une question rapide, pour partager un extrait de code, pour demander de l'aide, etc.
Nous avons besoin d'un système de chat.
Ici encore, j'utiliserais le système de chat qui est déjà en place dans l'organisation où le projet est lancé.
S'il n'y en a pas, de nombreuses options comme MS Teams, Slack, etc. sont disponibles.
S'assurer que nous créons des canaux dédiés pour différents aspects (code, révision, déploiements/devops, communication ludique) du projet.
De cette façon, nous pouvons garder la conversation ciblée sur le bon sujet.
Exemples de code sélectionnés
J'identifierais dans la base de code des exemples de bon code.
De cette façon, lorsqu'un nouveau développeur rejoint l'équipe, il peut voir ce qui est considéré comme un bon code.
Cela peut être une classe simple, une méthode, un modèle, etc.
Tests unitaires et d'intégration
Adopter dès le début la pratique de rédiger des tests pour chaque fonctionnalité développée.
Premièrement des tests unitaires pour tester le code et les cas limites, et ensuite, des tests d'intégration pour tester les interactions entre les différentes parties du système là où c'est nécessaires.
Éviter de tester les bibliothèques de code utilisées.
Les tests doivent être exécutés automatiquement à chaque fois que le code est modifié et avant qu'il ne soit fusionné.
Qualité du code
Si nous écrivons du code, nous devrions viser à faire le meilleur code possible. Quelques bonnes pratiques suivent.
Analyse statique
L'analyse statique du code est une bonne pratique pour attraper des bugs avant qu'ils n'arrivent.
Votre IDE est la première ligne de défense, gardons un oeil sur les avertissements qu'il prodigue.
Idéalement, nous devrions relier notre IDE à un outil plus robuste, comme (#sonarqube) afin qu'il vérifie le code avec la même configuration que ce que le pipeline fera.
Ça doit être fait pendant que l'on code, ou, minimalement avant de commettre le code.
Revue de code
Une autre façon d'augmenter la qualité du code, c'est de le réviser.
Ça permet d'attraper les bugs, mais aussi de partager la connaissance sur le code, les pratiques et le projet.
Ça permet aussi d'avoir une base de code cohérente et facilement lisible pour l'ensemble de l'équipe et des futurs développeurs.
Même si l'équipe est petite, c'est une bonne pratique que de faire de la revue de code.
Il devrait y avoir une étape dans le pipeline qui bloque le merge si le code n'a pas été révisé.
Pipeline de construction
Nous devrions avoir un pipeline de construction qui roule les tests, l'analyse statique et s'assure que le code a été révisé.
Il pourra attraper les erreurs qui n'arrivent pas sur notre propre poste et aider à faire un build plus robuste.
Cadres (framework) et bibliothèques
Quarkus
J'utiliserais Quarkus comme framework pour construire le backend du projet.
C'est un framework Java moderne qui est assez mature.
On dirait qu'il a été construit dès le départ avec le développeur en tête.
Et il peut créer des artefacts qui sont natifs, rapides et adaptés aux conteneurs.
Il existe un excellent tutoriel pour nous donner un aperçu du framework et des fonctionnalités associées. https://quarkus.io/quarkus-workshops/super-heroes/
Mapstruct
Très souvent, lors de la construction d'un backend robuste, nous aurons besoin de différents modèles (DTO, pojo, entités) pour différentes parties de l'application.
À mesure que l'information passe d'une partie de l'application à une autre (de la base de données au service, du service au contrôleur, du contrôleur au client), nous devrons mapper les informations d'un modèle à un autre.
J'utiliserais Mapstruct.
C'est un produit puissant qui peut être utilisé pour mapper des objets d'un type à un autre.
Le mapping se fait à la compilation, donc c'est rapide.
C'est assez utile quand nous devons mapper d'un DTO à une entité et vice versa.
Il peut faire correspondre les propriétés par nom, ou nous pouvons définir le mapping nous-mêmes.
Nous pouvons également facilement définir des transformations personnalisées au besoin.
Lombok
L'une des plaintes que les gens ont sur Java est de devoir écrire beaucoup de code répétitif.
J'utiliserais Lombok pour alléger cela.
C'est un produit puissant qui peut être utilisé pour générer le code répétitif pour nous.
Il peut être utilisé pour générer le code de base pour nous de plusieurs manières, comme les accesseurs, les mutateurs, les constructeurs, y compris certains modèles comme les constructeurs, equals et hashcode, etc.
Pour certaines constructions, utiliser les Records de java pourrait être une bonne alternative.
Liquibase
À un certain moment, nous aurons probablement besoin d'une base de données relationnelle pour stocker nos données (voir (#postgresql) plus tard à ce sujet).
Et ensuite, nous aurons besoin d'un moyen de gérer le schéma de cette base de données.
J'utiliserais Liquibase pour cela.
C'est un produit mature qui peut être utilisé pour gérer le schéma de la base de données.
Il peut être utilisé pour créer le schéma, mettre à jour le schéma, etc.
Il peut également être utilisé pour créer des données dans la base de données.
Il prend également en charge le concept de contextes.
Ainsi, nous pouvons stocker dans le même système différents ensembles de modifications pour différents environnements (comme des données pour les QAs), besoins ou fonctionnalités.
C'est une fonctionnalité puissante.
Il y a même un certain support pour certaines bases de données non relationnelles/sql, comme MongoDB, Noe4j, Databricks Data Lakehouses, etc.
OpenTelemetry
Mettre en place ce qui est nécessaire pour surveiller notre application est souvent une tâche qui est repoussée à plus tard, après que les fonctionnalités n'aient étés mises en oeuvre.
Mais il est important de commencer à y penser tôt.
J'utiliserais OpenTelemetry pour surveiller l'application.
C'est un projet framework implémenté par plusieurs bibliothèques de code.
Il peut être utilisé pour surveiller l'application en production, mais aussi en développement.
Il peut être utilisé pour surveiller l'application dans un conteneur, mais aussi dans un environnement natif.
Et nous pouvons également ajouter nos propres métriques.
Disons que nous voulons surveiller le nombre de fois qu'une fonctionnalité spécifique est utilisée.
Nous pouvons ajouter une métrique pour cela.
Ou si nous voulons nous assurer qu'un job cron est complété correctement au taux attendu, nous pouvons ajouter une métrique pour cela.
Un exemple de la documentation quarkus :
https://quarkus.io/guides/opentelemetry-metrics
package org.acme;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.Meter;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.jboss.logging.Logger;
@Path("/hello-metrics")
public class MetricResource {
private static final Logger LOG = Logger.getLogger(MetricResource.class);
private final LongCounter counter;
public MetricResource(Meter meter) {
counter = meter.counterBuilder("hello-metrics")
.setDescription("hello-metrics")
.setUnit("invocations")
.build();
}
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
counter.add(1);
LOG.info("hello-metrics");
return "hello-metrics";
}
}
Nous aurons besoin de commutateurs de fonctionnalités (feature flags)
Que diriez-vous si je vous disais "vous pouvez tout mettre dans des commutateurs de fonctionnalité" ?
Dès que le coeur de notre application existe, nous devrions envisager d'encapsuler chaque fonctionnalité supplémentaire dans un commutateur de fonctionnalité.
Il y a deux raisons principales à cela :
Nous pouvons publier une fonctionnalité sans la rendre disponible aux utilisateurs, ce qui facilite la livraison continue
Nous pouvons publier une fonctionnalité à un sous-ensemble d'utilisateurs, afin de pouvoir la tester avec de vrais utilisateurs avant de la publier pour tout le monde.
Nous pouvons également rendre la fonctionnalité disponible sur différents plans d'abonnement, etc.
Nous pouvons également utiliser des commutateurs de fonctionnalité pour désactiver une fonctionnalité si elle ne fonctionne pas comme prévu.
OpenFeature
En préparant cet article, je suis tombé sur le projet OpenFeature.
C'est une spécification de service de commutateurs de fonctionnalités qui peut être implémentée par n'importe quel service.
En utilisant les SDK openfeature, nous pouvons éviter le verrouillage des fournisseurs et avoir une manière cohérente de gérer nos drapeaux de fonctionnalités.
Unleash
Unleash propose une version gratuite que nous pouvons utiliser pour commencer.
Nous pouvons le déployer sur notre propre infrastructure.
Il y a une discussion sur le fait de faire en sorte qu'unleash prenne en charge la spécification openfeature, mais ce n'est pas encore implémenté.
Outils et services
Postgresql
Si notre projet nécessite une base de données relationnelle, j'utiliserais Postgresql.
C'est un produit mature qui peut être utilisé pour stocker les données du projet.
C'est un produit puissant qui a de nombreuses fonctionnalités comme les transactions, les contraintes, les déclencheurs, etc.
Il a de nombreuses capacités intégrées, comme le stockage d'objets en json format, la recherche en texte intégral, etc.
Il a également de nombreuses extensions, comme Postgis, qui peuvent être utilisés pour stocker et interroger des données géospatiales, Timescale, qui peut être utilisé pour stocker et interroger des données de séries temporelles, etc.
Il est très stable et a une grande communauté.
Timescale Données de séries temporelles
Si jamais nous rencontrons une situation où nous devons stocker des données de séries temporelles, j'utiliserais Timescale.
C'est une extension de Postgresql qui peut être utilisée pour stocker et interroger des données de séries temporelles.
C'est un produit puissant et performant qui possède de nombreuses fonctionnalités comme le regroupement temporel, les agrégats continus, etc.
C'est un produit puissant qui peut être utilisé pour stocker et interroger des données de séries temporelles.
Il existe une version gratuite et une version cloud géré par la compagnie.
Keycloak
À un moment donné, nous devrons gérer les utilisateurs et leur accès à l'application.
J'utiliserais Keycloak pour cela.
C'est un produit mature qui peut être utilisé pour gérer les utilisateurs, les rôles, les permissions, etc.
Nous pouvons également le configurer pour déférer l'authentification à un système externe en utilisant des fournisseurs d'identité.
Il existe même un moyen de migrer nos utilisateurs d'un système externe vers Keycloak.
Wiremock
Il est tout à fait possible que notre projet doive interagir avec des services externes.
Nous voudrons tester notre code sans avoir à dépendre de l'appel réel à ces services externes.
Nous pouvons utiliser la documentation du service pour obtenir le format du payload attendu.
J'utiliserais Wiremock pour remplacer ces services pendant le développement.
C'est un produit mature qui peut simuler les services externes.
Nous pouvons définir les réponses que nous voulons obtenir des services externes et utiliser Wiremock pour simuler les services externes.
Il prend même en charge la randomisation du résultat ou le retour de timestamps qui sont toujours une période définie dans le passé ou le futur de l'appel.
Gestion des mots de passe
Nous avons des mots de passe, beaucoup trop probablement.
Et nous ne devrions pas les stocker en texte clair.
J'utiliserais un gestionnaire de mots de passe pour stocker les mots de passe.
Il existe de nombreux gestionnaires de mots de passe disponibles, comme 1Password, LastPass, Bitwarden, etc.
Certains, comme 1Password, sont plus qu'un simple coffre-fort de mots de passe, ils viennent avec des outils qui nous permettent d'utiliser en toute sécurité les mots de passe dans nos applications ou sur la ligne de commande.
https: Let's Encrypt ou localhost.direct
De nos jours, le web est censé être sécurisé.
Nous devrions utiliser https.
Utiliser https dès le départ nous aide avec la sécurité du projet. Certains outils pour valider le frontend ne fonctionnent pas bien sans https.
Déployer dans un environnement avec https n'est pas vraiment difficile dans le cloud.
Même avec votre propre infrastructure, ce n'est pas si difficile.
Nous pouvons utiliser Let's Encrypt pour obtenir un certificat gratuit et mettre en place une mécanique de renouvellement automatique.
Mais, si nous travaillons dans un environnement local, le défi est plus important.
Nous pouvons encore utiliser Let's Encrypt pour obtenir un certificat gratuit.
Cependant, c'est plus difficile de faire en sorte que chaque développeur ait son propre certificat localement.
Pour les environnements locaux, nous pouvons nous inspirer du projet localhost.direct pour obtenir un certificat gratuit pour notre environnement local et gérer les noms de domaines en local.
Commit
Git and repository
Puisque nous parlons finalement d'écrire du code en équipe, nous avons besoin d'un moyen de gérer notre code.
Je choisirais Git comme système de contrôle de version.
Ensuite, nous aurions besoin d'un endroit pour stocker ce code.
Les suspects habituels sont Github, Gitlab, Bitbucket, etc.
Je serais pragmatique et choisirais ce qui est déjà utilisé dans l'organisation où le projet est commencé.
Tant que nous pouvons également avoir des pipelines pour vérifier, construire et empaqueter le code, ça me va.
Git Credential Manager
Nous travaillerons probablement sur plus d'un projet à un moment donné, et nous devrons gérer nos identifiants.
J'utiliserais Git Credential Manager pour gérer mes identifiants.
C'est un outil puissant qui peut être utilisé pour gérer nos identifiants.
Il peut être utilisé pour gérer nos identifiants de plusieurs manières, comme les stocker de manière sécurisée, les partager avec notre équipe, etc.
Il peut également être utilisé pour gérer nos identifiants dans de nombreux environnements, comme le développement, la qa, la mise en scène, l'uat, la production.
Sops
À un moment donné, c'est sûr, nous devrons gérer des secrets dans notre dépôt.
Nous utiliserons Sops pour chiffrer ces secrets.
De cette façon, nous pouvons les stocker dans le dépôt git sans craindre qu'ils ne soient lu par des personnes qui ne devraient pas y avoir accès.
Assurez-vous que nous mettons cette pratique en place tôt dans le processus, afin qu'aucun secret ne soit jamais stocké en texte clair dans notre dépôt. (Voir l'article que j'ai rédigé à ce sujet)
Gitlab ou autre dépôt de code
Certaines organisations utilisent Gitlab, d'autres utilisent Github, Bitbucket ou même AWS CodeCommit.
Peu importe ce que votre organisation utilise, assurez-vous que votre organisation dispose d'un système pipeline capable de :
- vérifier,
- construire,
- empaqueter,
- déployer,
- surveiller et
- revenir en arrière sur le code.
CI (Intégration continue)
Gitlab CI / Pipelines
Comme nous utilisons Gitlab, nous utiliserons les pipelines qui peuvent s'exécuter dans gitlab.
C'est un outil puissant qui peut être utilisé pour vérifier, construire et empaqueter le code.
Il peut également être utilisé pour déployer, surveiller la qualité du code.
Il peut être utilisé pour revenir en arrière sur le code si un problème était découvert.
Voici quelques étapes typiques que nous mettons dans nos pipelines :
- pré-valider : utilisé pour vérifier les messages de commit et s'assurer qu'ils respectent les conventions que nous avons établies avec l'équipe.
vérifier le format : assurez-vous que le code est correctement formaté.
Comme nous ne voulons pas donner les droits de commit du pipeline, nous ne formatons pas le code, mais nous vérifions qu'il est correctement formaté.compiler : assurez-vous que le code se compile correctement.
C'est une étape simple qui peut être effectuée rapidement.test unitaire : exécutez des tests unitaires pour le code
installer : installez le code java dans le dépôt maven
test d'intégration : s'ils existent, exécutez le test d'intégration.
rapport de couverture de code : générez le rapport de couverture de code.
Cela peut être fait avec JaCoCo, ou tout autre outil de couverture de code.analyse statique : exécutez une analyse statique sur le code.
Cela peut être fait avec (#Sonarqube), ou tout autre outil d'analyse statique.scan sat : exécutez l'outil satscan sur le code.
Cela peut être fait avec l'outil satscan.image(s) docker : créez l'image docker de l'application ou du module.
Si nous utilisons le modèle mono-repo, il peut y avoir plusieurs images docker à construire ici.post-validation : encore avec le framework danger.
Typiquement ici, nous vérifions si le nombre approprié d'approbations existe.
Danger
Traduction libre du site web de danger :
Danger s'exécute pendant votre processus CI, et donne aux équipes la chance d'automatiser les tâches de révision de code.
Cela fournit une autre étape logique dans votre construction, à travers cela Danger peut aider à appliquer vos tâches répétitives dans la révision quotidienne du code.
Vous pouvez utiliser Danger pour codifier les normes de vos équipes. Laisser les humains réfléchir à des problèmes plus difficiles .
Cela se produit par Danger laissant des messages dans vos PRs basés sur des règles que vous créez avec JavaScript ou TypeScript.
Au fil du temps, à mesure que les règles sont respectées, le message est modifié pour refléter l'état actuel de la révision du code.
Nous devrions utiliser Danger pour valider et imposer les normes de notre équipe.
Sonarqube
Nous voudrons vérifier la qualité de notre code.
L'analyse statique de notre code permet de détecter de nombreux problèmes comme des mauvaises habitudes de programmation, des bugs ou des problèmes de sécurité.
J'utiliserais Sonarqube pour cela.
C'est un produit mature qui peut vérifier notre code pour des bugs, vulnérabilités, mauvaises pratiques de code, etc.
Il peut également vérifier notre code pour la couverture, les duplications, etc.
La plupart des IDE devraient avoir un plugin afin que nous puissions voir les résultats de l'analyse directement dans notre IDE ou avant de valider.
Déploiement
Images et conteneurs Docker
Il est raisonnable de prévoir le déploiement de notre application dans des conteneurs.
D'autant plus si notre application n'est pas un gros monolithe, mais un ensemble de modules ou de microservices.
Pensez à un frontend en React, un backend en Quarkus, une base de données en Postgresql, etc.
Nous pouvons utiliser Docker pour créer les images de notre application.
Nous pouvons également utiliser Docker pour exécuter les conteneurs de notre application.
Et, si le besoin se présente, nous pouvons utiliser Kubernetes pour déployer l'ensemble de notre pile d'application.
Donc, tôt dans le projet, assurons-nous que nous avons un pipeline qui peut construire les images de notre application.
Nous devons prendre en considération les étapes pour construire les images, et quelles sont les configurations que nous voudrons passer à ces images.
Et testons autant le pipeline que les images résultantes.
Idéalement, nous devrions avoir un pipeline qui construit les images et les pousse vers un dépôt de conteneurs.
Cela nous permet d'utiliser la même image dans tous nos environnements.
Je pense que faire une image différente pour chaque environnement est une mauvaise idée.
Nous devrions être capables de déployer la même image dans tous nos environnements.
La seule différence devrait être la configuration.
Nous nous épargnerons beaucoup de stress si nous commençons tôt avec cela au lieu d'attendre de le faire quand nous sommes près du Test d'Acceptation Utilisateur ou pire, de la date de Production.
Terraform pour l'infrastructure en tant que code
Nous allons déployer notre application dans une infrastructure, qu'elle soit chez un fournisseur cloud, ou déployée sur l'infra de l'organisation.
Et nous aurons très probablement besoin de la même infrastructure dans différents environnements, comme le développement, la qa, la pré-production, l'uat, la production.
Le meilleur moyen de s'assurer que chaque environnement est aussi proche que possible du précédent est de le rendre reproductible.
J'utiliserais Terraform pour définir l'infrastructure en tant que code.
De cette façon, nous pouvons déployer la même infrastructure dans chaque environnement.
Un autre avantage de Terraform est qu'il nous permet de synchroniser des parties de l'infrastructure qui sont définies chez différents fournisseurs cloud.
Prenons pour exemple que nous utilisons Github comme dépôt de code, Amazon Pipelines pour nos pipelines de construction et que nous voulons aussi configurer Keycloak et Grafana. Nous pouvons définir tout cela dans Terraform et le déployer en une seule commande.
Ce qui est, je pense, plus simple que d'utiliser la configuration propre à chaque fournisseur et de tenter de créer une orcherstration par dessus.
Terragrunt pour aider à rendre Terraform un peu plus gérable
Terragrunt est un wrapper mince pour Terraform qui fournit des outils supplémentaires pour garder vos configurations DRY, travailler avec plusieurs modules Terraform, et gérer l'état distant.
Gérer une grande infrastructure avec Terraform est un peu douloureux.
Nous avons probablement un gros fichier d'état sur le bucket AWS S3, plusieurs modules et plusieurs environnements.
Terragrunt peut nous aider à gérer tout cela.
Surveillance des projets
Nous devrons mettre en place une surveillance pour notre application.
Je suis actuellement en train d'évaluer Signoz, mais je n'ai pas vraiment d'option préférée ou recommandée pour le moment.
plausible pour les données analytiques
Je considère cela comme un sous-ensemble de la surveillance.
Nous voudrons savoir si, quand et d'où nos utilisateurs utilisent notre application.
J'utiliserais Plausible pour cela.
C'est un produit simple qui peut être utilisé pour surveiller notre application.
Il peut être utilisé pour surveiller notre application en production, mais aussi en développement, dans un conteneur ou dans un environnement natif.
Autres projets à explorer
- Debezium pour la capture de données de changement
- Javers pour l'audit des changements de ligne
- Hibernate Envers pour l'audit des changements
- Pitest Mutation Testing pour évaluer la résistance des tests à la mutation
Top comments (0)