Introduction
Ce document se veut une compilation d’un ensemble de conseils pour développer en donnant votre potentiel maximum, en Python, SQL et dans le développement d’application cloud.
De plus une partie s’arrêtera sur la posture du développeur, avec des astuces et des conseils sur l’ergonomie du poste de travail.
Écrire du code propre
Se limiter au commentaire utile
Il y a les pros et les opposants aux commentaires, mais dans l’ensemble, lorsque vous êtes nouveaux sur un projet la lecture des commentaires facilite la compréhension du code et est moteur dans l’intégration dans l’équipe.
Le dosage du commentaire reste un élément essentiel de son utilité. En effet, le commentaire sert à compenser la complexité du code. Par contre, il devient pesant de lire des commentaires verbeux, si la lecture du code est suffisamment claire.
Bien qu’il y ait une différence d’appréciation de l’utilité du commentaire, entre un junior et un sénior, une règle de base peut servir à décider quand insérer un commentaire, mais c’est à vous de la définir, bien que nous donnions quelques pistes ci-dessous.
Outre le fait qu’ajouter une description à chaque fonction (en Python c’est une docstring), ajouter un commentaire ne doit pas venir pallier un déficit de sens dans le nommage des variables. Sachez seulement que vos commentaires peuvent intégrer vos tests unitaires et donc être particulièrement utiles dans les évolutions/maintenances de votre code, surtout si ce n’est pas vous qui le modifiez.
Enfin, la construction du code, le choix d’un design-pattern, le bon nommage des fonctions, des paramètres et des variables pourraient rendre inutiles les commentaires, exception faite que les commentaires vous aiderons souvent à écrire une documentation étayée, etc.
Une fonction ne fait qu’une chose, mais bien
Voilà une règle qui élimine la complexité et permet de calibrer sa fonction à son action.
Dans l’exemple suivant, le code est présenté soit dans une forme développé, soit dans une forme factorisé.
Si l’appel à la fonction reste identique, chaque étape du programme fait appel à une seule fonction.
Bien que le code comporte plus de ligne quand on le factorise, il sera plus facile à debuguer et à maintenir.
import string
def encryption(message, cypher):
"""Renvoie un message chiffré"""
char_list = string.printable
char_dict = {y:x for x,y in enumerate(char_list)}
modulo = len(char_list)
crypted_message = []
for char in message:
token = char_list[(char_dict[char] + cypher) % modulo]
crypted_message.append(token)
return ''.join(crypted_message)
>>> encryption("J'adore Python", 123456789)
'yV\x0c2dg3@Eni6dc'
import string
def get_tables():
"""Retourne les tables de valeurs des caractères"""
ordered_chars = string.printable
chars_dictionary = {y:x for x,y in enumerate(ordered_chars)}
length_of_chars_list = len(ordered_chars)
return ordered_chars, chars_dictionary, length_of_chars_list
def encoder(character_to_encrypt, shift):
"""Retourne le caractère encodé"""
chars_list, chars_dict, length = get_tables()
crypted_char = chars_list[(chars_dict[character_to_encrypt] + shift) % length]
return crypted_char
def encryption(message, cypher):
"""Retourne un message en le chiffrant"""
crypted_message = []
for char in message:
token = encoder(char, cypher)
crypted_message.append(token)
return ''.join(crypted_message)
>>> encryption("J'adore Python", 123456789)
'yV\x0c2dg3@Eni6dc'
Le code s’appelle de la même façon et retourne le même résultat, mais la lecture est facilitée par la simplicité de chaque fonction.
Si une fonction devient trop complexe, réduisez-la en factorisant. Factoriser revient souvent à ajouter des fonctions ou de nouveaux paramètres aux fonctions.
La programmation objet permet d’encapsuler la complexité
Quand un programme regroupe plusieurs corps, on peut alors parler du concept d’objets. Les objets sont des instanciations de classes ou des interfaces, présentant des caractéristiques qu’ils peuvent hériter et partager avec un autre objet mère ou fille.
Il existe également des classes abstraites, dont les valeurs ne peuvent pas être instantiées mais elles servent à l’implémentation de fonctionnalités.
Les objets peuvent intéragir les uns avec les autres, suivant deux types d’actions, soit l’agrégation, soit la compositon.
Des objets sont nées les design pattern (ou DP), sorte de modèle encapsulant la complexité du code informatique.
Il existe un DP pour chaque problématique de programmation objet. Ils peuvent être combinés et apportent beaucoup d’élégance à l’écriture du code en permettant d’en masquer la complexité.
Ils sont classés en trois grandes familles :
-
Les Creational Patterns
-
Les Structural Patterns
-
Les Behavioral Patterns
Si vous souhaitez commencer d’utiliser les DP en Python, je vous conseille ce site web.
Donner des noms sensés aux variables et aux paramètres
Sans renverser la table, tout le monde s’accordera à dire que la lecture du code est simplifié lorsque les variables ne sont pas justes des lettres, à plus forte raison si les noms couvrent le rôle de la variable dans l’application.
Bien qu’en python, l’interpréteur soit fortement typé (nul besoin de définir les types de vos variables), il peut être une bonne idée de terminer le nom de votre variable par son type.
Bien sûr, vous pourrez aller plus loin en déclarant le type de la variable, comme dans l’exemple ci-dessous, présentant un mix des solutions possibles en Python :
from io import TextIOWrapper
filename_str: str = "my_file.csv"
file: TextIOWrapper = open(filename_str, "r")
L’intérêt de la déclaration du type n’est pas dénué de tout sens, même si cela représente un effort supplémentaire, cela peut permettre de contrôler la présence d’un éventuel bug, notamment avec la bibliothèque mypy en Python.
Indenter votre code
Une des forces du langage Python est de rendre superflue les points virgules de fin de ligne ou les accolades des fonctions, lesquelles sont partout dans d’autres langages, tels que le C ou Java.
C’est par l’indentation du code que l’interpréteur Python comprend ce qu’il doit faire.
Pour nous autre codeur, l’indentation est souvent le paradigme d’une meilleure compréhension du code.
Aujourd’hui, il y a des outils pour indenter le code automatiquement, dont le principal avantage est d’éviter au codeur de retenir toutes les règles du langage, lesquelles peuvent être fastidieuses à apprendre.
Dans le code python
Supprimer les variables non-utilisées
Durant le développement, le codeur teste beaucoup d’élément et tout le monde n’utilise pas un debugger dans son IDE. En fait, les raisons sont nombreuses, pour lesquelles peuvent apparaître des variables inutiles quand le code évolue, même normalement.
On imagine aisément que les variables inutilisés, sont, comme le code mort, un frein à la compréhension du code. Par conséquent, tout codeur doit passer un peu de temps pour passer le code en revue et éliminer ce qui n’est plus utile au programme.
Ne pas écrire des exceptions sans un type d’Erreur
Attraper des exceptions, en Python, représente la voie royale pour qu’un programme s’exécute malgré tout ce qui peut arriver au moment de l’exécution.
La problématique des exceptions offre un spectre large de cas. Il suffit pour s’en convaincre de jeter un œil dans la documentation de Python, Le logiciel Zeal vous sera, en ce sens, un précieux atout et vous permettra de l’avoir toujours avec vous, hors-ligne.
try:
with open("file.txt", "r") as file:
content = file.read()
except FileNotFoundError as e:
print("Le fichier n'existe pas : ", e)
finally:
with open("file.txt", "w") as file:
file.write("Hello World!")
Rappelez-vous que vous écrivez du code, aussi pour qu’il soit lisible et compris par d’autres codeurs. Choisir la bonne exception ajoute un élément de compréhension sur l’objectif du code.
Éviter d’écrire des boucles infinies
Bien que nos systèmes soient multi-tâches, les boucles infinies sont toujours considérées comme des bugs par les programmeurs.
Des boucles while telles que :
while True:
(action code)
ou même encore :
while 1 == 1:
(action code)
Sont détestables aux yeux de tout codeur avec un minimum d’expérience. Il est donc important de toujours prendre la peine d’écrire des tests cohérents et sensés, même lors de la phase de développement d’un programme, où de nombreux tests sont réalisés. Cela réduira la possibilité d’oublier une boucle infinie dans le code final.
Écrire des tests unitaires
Selon mon propre avis, les tests unitaires sont la partie la moins amusante de l’écriture d’un programme. Non seulement, ils demandent un certain effort pour être trouvés, mais en plus, choisir la bonne solution technique parmi les différents produits existants est une gageure.
Je pense que ce n’est pas pour rien si testeur de programme est un métier. Toutefois, n’oublions pas que leur automatisation représente un gage de plus grande stabilité du programme, face aux défis que représente le développement en équipe, Agile la plupart du temps.
Parmi les différentes solutions techniques, en Python, vous trouverez une implémentation dans la librairie standard, à travers la fonctionnalité doctest, que vous pourrez également utiliser, et plus encore avec la bibliothèque standard unittest ou la bibliothèque tierce pytest.
Nous vous avons présenté les plus importantes, mais un tour d’horizon des solutions existantes est disponibles en cliquant sur ce lien.
Utiliser mypy pour typer les variables
Bien que Python possède un interprétateur muni du duck typing, cela aurait pu le conditionner au rang des langages idéaux pour le prototypage, mais pas assez sûrs pour des phases de production.
Toutefois, il est possible d’utiliser les types, explicitement dans le code. Bien sûr, l’interpréteur n’est pas directement influencé par vos annotations, mais une bibliothèque vous aidera à déterminer la cohérence des types, à travers les fonctions du code : il s’agit de mypy.
Mypy analyse les annotations sur les variables et permet de les contrôler. Écrire du code en typant vos variables vous permettra d’écrire du code de meilleure qualité et plus robuste aux pannes.
Toutefois, pour aller plus loin que mypy, il sera nécessaire de conditionner l’interpréteur, pour qu’il tienne compte des annotations. Cela peut passer par le test du type des variables au moment de l’exécution.
Python sera donc, plus verbeux, mais le programme ne s’exécutera que dans des cas précis, ce qui permet d’exclure les erreurs en bornant le programme.
Pour démarrer avec mypy, vous pouvez tester le type de la variable en suivant la fonction comme décrit en suivant :
# reveal.py
import math
reveal_type(math.pi)
mypy reveal.py
/home/user/project/.venv/bin/python -m mypy reveal.py
reveal.py:3: note: Revealed type is "builtins.float"
La qualité de vos programmes sera bien meilleure grâce à mypy et vous pourrez ainsi augmenter la complexité des processus, sans être à la peine sur la gestion des bugs.
Indenter son code avec la biblio black
Python est un langage dont la syntaxe est épurée, grâce à des règles sur l’indentation du code. L’interpréteur comprend le retour à la ligne et l’indentation, là où un langage comme le C exige des points-virgules et des accolades.
Toutefois, les règles de l’interpréteur ne suffisent pas pour qu’un code en Python soit lisible. C’est pour y parvenir que la règle PEP8 a été écrite par des développeurs de Python : Guido van Rossum, Barry Warsaw, Alyssa Coghlan.
De nombreux IDE intègre déjà les règles PEP8 et vous permettront de formatter votre code en un clic. Si vous n’avez pas d’IDE sous la main, vous pourrez utiliser la bibliothèque Black.
Il suffit de lancer la commande suivante pour le fichier.
pip install black
python -m black {source_file_or_directory}
Cette bibliothèque étant compatible avec jupyter, vous pourrez l’utiliser dans tous vos usages du code Python.
Ne pas coder une shallow copy, si une deep copy est nécessaire.
La copie d’un objet, en informatique, peut signifier la copie d’une référence ou d’une valeur. Le terme exact est une shallow copy (copie peu profonde) pour la copie de la référence en mémoire, et deep copy (copie profonde) pour une copie de la valeur de l’objet.
Concrètement, cette différence peut entraîner des comportements anormaux dans un programme, où l’on déplace des données avant de les modifier. En effet, la copie peu profonde attribut la position en mémoire d’une variable à une autre variable. Le comportement est comme suit :
>>> a = [1, 2, 3]
>>> b = a
>>> a.pop()
>>> print(b)
[1, 2]
Ce comportement est dû à la copie des références de a dans b et non pas des valeurs de a.
Toutefois, une copie peu profonde pourrait être utilisée pour mettre à jour une variable à partir d’une seconde variable. Pour choisir entre les deux modes de copie, le module copy de Python, contient une fonction copy, laquelle retourne une copie peu profonde. De plus ce module contient une fonction deepcopy, laquelle retourne une copie profonde d’un objet.
>>> import copy
>>> a = [1, 2, 3]
>>> b = copy.deepcopy(a)
>>> a.pop()
>>> print(b)
[1, 2, 3]
Dans le code SQL
Bien typer les colonnes en fonction de la data
Il existe beaucoup de types différents pour catégoriser les données suivant les usages qu’elles nécessitent. Le stockage de certaines données consomme plus d’octet que pour d’autres données. Cela peut avoir un impact plus grand lorsque l’on stocke des blobs ou simplement quand le nombre de lignes dépasse le million.
Dès lors que le prix du stockage dépend de la taille des tables, il est bon pour le portefeuille de typer les champs au plus près du type de data représenté par les champs. Cela veut dire typer un booléen en booléen et non en integer, entre autre. Les économies dans les big datas peuvent être colossaux, à cette échelle de valeur le nombre de ligne peut atteindre le milliard et le gain de poids d’un booléen à un integer est d’environ 800%.
Ne pas insérer d’Order By si ce n’est pas utile
Lorsqu’une requête est réalisée dans un test, il est souvent pratique d’ordonner les résultats suivant une colonne, ou pour mettre des valeurs nulles en évidence. Ce tri s’effectue à la fin de la requête, mais il rajoute une quantité non négligeable de calcul au cours de l’exécution.
En règle générale, les données ne sont qu’en vrac dans les tables et dans les vues également. Il n’y a que très peu souvent une nécessité de les ordonner.
Compte-tenu que la facturation s’applique en comptant la quantité de data traitées par l’utilisateur, la commande order by est à utiliser en bonne intelligence avec les besoins réels de votre ingénierie des données.
Dans le hardware
Mesure de la consommation du programme
Après l’optimisation d’un programme, l’équilibre doit-être trouvé avec les ressources allouées. Cependant, les consommables d’un programme sont mesurables donc, trouver une concordance reste un objectif atteignable.
Les paramètres d’un programme sont :
-
Le temps d’exécution
-
La mémoire utilisée
-
L’espace disque utilisé
-
Le nombre de cœurs
Si l’influence du code est grande, le type de processus peut à lui seul définir une ressource nécessaire. Toutefois, il peut y avoir des paramètres incompressibles suivant les cas, comme le nombre maximum d’appels simultanés, à un serveur, lequel peut ralentir l’exécution du programme à une durée que l’on ne peut réduire.
Réduire le temps d’exécution
Les pistes pour réduire le temps d’exécution sont :
-
Simplifier la complexité du programme
-
Analyser et optimiser le processus de chaque fonction
-
Opter pour du multithreading ou du multiprocessing
-
Prendre des bibliothèques déjà optimisées pour vos opérations
Réduire la consommation de mémoire
Les pistes pour réduire la consommation de mémoire sont :
-
L’écriture sur le disque
-
La destruction des variables inutiles
-
La compression des données
-
Le format des données
-
Le nombre de bibliothèques
Localiser les serveurs au plus des clients
Déployer un serveur de service cloud au plus près des utilisateurs du service a beaucoup d’avantage en termes de résistance aux pannes ou pour la mise en conformité réglementaire.
Notons simplement que, sous un axe d’optimisation, le temps de latence sera réduit, donc la proximité du serveur améliorera sensiblement l’expérience utilisateur.
Réserver au flux uniquement les data utilisées par le client
Le transfert de données se comptant en multiple d’octet, un levier d’amélioration, qui compte pour beaucoup est de s’assurer que les données envoyées au client lui sont vraiment nécessaires.
De la même façon, il sera sans doute utile de bien choisir les opérations faites sur le terminal du client et les opérations calculées par le serveur.
Cela entre en résonance avec l’architecture de votre programme et devrait être pensé au moment de sa conception, car les impacts sont nombreux en terme d’usage.
La posture du développeur sans courbature
Éviter de décoller les mains du clavier
Pour bien coder, une posture détendue est primordiale. La posture idéale est celle qui entraîne le moins de mouvement des épaules. De la sorte, elles pourront rester détendues.
Garder vos coudes en appuie sur votre bureau, ou sur les appuie-coudes de votre fauteuil, vous permettra de détendre vos épaules.
Toutefois, pour protéger vos poignets, vous aurez besoin, eux-aussi de les reposer, mais en utilisant un repose poignet.
VIM
Utiliser un plugin VIM dans votre IDE et apprendre quelques raccourcis clavier, vous permettront de vous passer de la souris pour vous déplacer dans votre environnement de développement intégré (IDE) tout en réalisant des tâches diverses comme le copier/coller, la recherche/remplacement de suite de caractère, etc.
Vous pourrez l’installer sur PyCharm en suivant cette documentation, ou sur VSCode avec cette autre documentation.
Apprendre une disposition clavier ergonomique
BÉPO
Pour aller plus loin dans l’ergonomie du clavier, il est avantageux de changer pour une disposition clavier plus ergonomique, car l’AZERTY n’est pas conçue pour être ergonomique.
En effet, l’AZERTY date de l’époque des machines à écrire et permettait d’éviter que les marteaux de la machine ne s’emmêlent lorsque des professionnels s’en servaient à toute allure.
Depuis plus de 20 ans, il existe une disposition clavier, ergonomique et adaptée à la langue française : elle a été appelée la disposition BÉPO.
Le BÉPO vous permet de limiter le mouvement du bras, de l’épaule, des poignets et des doigts, notamment en disposant les lettres les plus courantes du français au centre du clavier et en plaçant les moins utilisées en périphérie du clavier.
5 minutes par jour pendant 2 semaines permettent de taper en BÉPO rapidement. De nombreux logiciels de dactylographie vous permettront de vous entraîner gratuitement : All the touch typing tutors
Puis, pour utiliser votre clavier, vous n’aurez plus besoin de regarder vos mains, ce qui détendra votre vision, de plus, vous utiliserez tous vos doigts, ce qui augmentera votre vitesse de frappe jusqu’à 45 mots par minute.
Pour installer la disposition BÉPO sur Windows, ce site vous indiquera la méthode à suivre.
Utilisation d’un outil d’analyse statique du code
Qodana
Pour réaliser un vrai travail de fond sur votre code, si vous utilisez un IDE JetBrains, il vous sera possible d’utiliser Qodana, l’outil d’analyse de code statique.
Qodana s’appuie sur les inspections natives de JetBrains, en proposant plus de 2500 inspections de code. Il vous permettra de détecter une grande variété de problèmes et peut les présenter sous forme de rapport.
Conclusion
Le but de cet ouvrage est de vous délivrer l’essentiel à connaître pour écrire du code propre.
Le principe a été étendu à la posture, car elle influence grandement la qualité du code, bien que ce soit de manière indirecte.
J’espère vraiment que ce document vous aura plu et qu’il ne vous aura pas été pénible de le lire.
Si vous avez des remarques ou des commentaires, vous pouvez me les envoyer à l’adresse suivante romain@boyrie.email.
Il ne me reste plus qu’à vous souhaiter « Bon Code ! »