DataGyver #1 : Les 4 piliers d'un projet robuste
Structure, standard, documentation et qualité : La recette d’un projet data d’exception
Sommaire ⏱️
L'organisation du code
UV : le nouveau standard
La documentation
Les bonnes pratiques
Préambule 🎉
Avant toute chose, merci aux lecteurs de ce premier numéro. A l’heure ou j’écris ses lignes, vous êtes 150 inscrits !
Pourquoi une Newsletter ?
Je partage du contenu depuis 1 an sur LinkedIn et je suis quelque fois frustré de ne pas pouvoir aller plus loin sur certains sujets. Il y a principalement deux limites :
les 3000 caractères
la baisse de la portée de votre post dès qu’on met des liens externes
Ici, je vous partagerai des tutoriels, des ressources, du code, des éléments de veille.
Je vais aussi profiter de cette Newsletter pour vous parler de mes futurs projets 2025, je vais avoir besoin de vous pour choisir les plus intéressants !
Quelle fréquence ?
Pour le moment, j’ai prévu une fréquence mensuelle, avec à chaque fois des thèmes différents. J’ai pour chaque thème, deux axes différents, je vous demanderai ce que vous préférez via un sondage LinkedIn.
Programme
Maintenant, passons au vif du sujet. Sur ce premier numéro, on va s’attarder sur des sujets pas très sexy comme la doc ou les environnements de développement. Je pense que ça peut servir à beaucoup de personnes, juniors ou reconvertis.
Prenons l’exemple de Mathieu, reconverti après un bootcamp data. En 3 mois, il aura appris python et ses librairies (pandas, numpy, etc.), le machine learning, une petite dose de deep learning (tensorflow/pytorch), SQL et les bases de données, Git, Docker et il aura mettre tout ça en pratique dans des projets.
Bien souvent, ce que je considère comme les bases ne sont pas abordées : comment structurer son projet, comme mettre en place un environnement de développement, comment documenter son projet, etc. Bref, comment travailler proprement pour pouvoir, par la suite, mieux collaborer avec ses collègues.
1. L'organisation du code : la clé de voûte
Je suis passionné d’horlogerie. Mon truc, c’est de prendre une montre mécanique HS, de la démonter, de diagnostiquer la panne, de la réparer, de la remonter. Il peut y avoir plusieurs dizaines de pièces. J’utilise des boites compartimentées pour regrouper les pièces par fonction. Votre projet Python mérite la même rigueur d'organisation.
Voyons l'évolution naturelle d'un projet data, du plus simple au plus sophistiqué :
Pattern "Script Simple" (Débutant)
projet_data/
├── analyse.py # Script principal
├── données.csv # Données brutes
└── README.md # Documentation basique
Ce pattern convient uniquement pour des analyses ponctuelles ou des scripts très simples. Il devient rapidement problématique dès que le projet grandit.
Pattern "Notebook" (Exploration)
projet_analyse/
├── notebooks/
│ ├── 01_exploration.ipynb # Analyse exploratoire
│ ├── 02_nettoyage.ipynb # Nettoyage des données
│ └── 03_modélisation.ipynb # Création du modèle
├── data/
│ ├── raw/ # Données brutes
│ └── processed/ # Données transformées
└── README.md
Idéal pour la phase exploratoire et l'analyse de données, mais insuffisant pour du code de production.
Pattern "src/" (Production)
projet_production/
├── src/
│ └── monpackage/
│ ├── __init__.py # Définit le package
│ ├── data/
│ │ ├── __init__.py
│ │ ├── loader.py # Chargement des données
│ │ └── cleaner.py # Nettoyage
│ ├── features/
│ │ ├── __init__.py
│ │ └── builder.py # Création de features
│ └── models/
│ ├── __init__.py
│ └── trainer.py # Entraînement
├── notebooks/
│ └── exploration/ # Notebooks d'exploration
├── tests/
│ ├── __init__.py
│ ├── test_data.py # Tests unitaires
│ └── test_models.py
├── docs/
│ ├── index.md # Documentation
│ └── api.md # Documentation API
├── data/
│ ├── raw/ # Données brutes
│ └── processed/ # Données transformées
├── pyproject.toml # Configuration du projet
├── setup.cfg # Configuration setuptools
└── README.md
Le pattern src/
est devenu le standard de l'industrie pour une raison simple : il crée une séparation claire entre votre code de production et vos expérimentations. Cette structure évite le fameux "notebook spaghetti" où exploration et production se mélangent dangereusement.
Mais comment démarrer rapidement avec une structure professionnelle ? C'est là qu'intervient cookiecutter. Pensez-y comme à un plan d'architecte prêt à l'emploi. En une seule commande, vous générez un projet complet avec tous les fichiers nécessaires au bon endroit. Plus besoin de vous demander où placer vos tests ou comment structurer votre documentation - cookiecutter s'en charge pour vous.
# Installation
pip install cookiecutter
# Création d'un projet data science
cookiecutter gh:drivendata/cookiecutter-data-science
# Alternative pour un package Python
cookiecutter gh:audreyr/cookiecutter-pypackage
Les templates Cookiecutter les plus populaires, comme cookiecutter-data-science ou cookiecutter-pypackage, intègrent des années de bonnes pratiques de la communauté Python. Ils vous offrent une structure éprouvée qui peut évoluer avec votre projet, de la simple analyse exploratoire jusqu'au déploiement en production.
Vous pouvez créer votre propre structure en fonction de vos besoins. J’ai par exemple créé un template pour les projets BI : cookiecutter-BI
J’en parle dans mon livre :
Pour aller plus loin :
Le repo cookiecutter : https://github.com/cookiecutter/cookiecutter
La documentation cookiecutter : https://cookiecutter.readthedocs.io/en/latest/usage.html
Le guide "Python Project Structure and Packaging" de Real Python : https://realpython.com/python-application-layouts/
2. uv : le nouveau standard pour la gestion des dépendances
La gestion des dépendances en Python peut sembler intimidante au premier abord. C'est comme si vous deviez gérer une cuisine où les ingrédients interagissent de manière complexe et où chaque recette nécessite une version précise de chaque ingrédient. uv transforme cette complexité en un processus fluide et intuitif.
Les environnements virtuels : votre espace de travail isolé
Imaginez que vous deviez préparer simultanément un plat traditionnel qui nécessite une ancienne version d'un ingrédient, et un plat moderne qui requiert la dernière version. Dans une cuisine traditionnelle, ce serait un casse-tête. C'est exactement le problème que résolvent les environnements virtuels en Python.
Avec uv, la création et la gestion des environnements virtuels devient remarquablement simple :
# Création d'un nouvel environnement
uv venv .venv
# Activation de l'environnement
source .venv/bin/activate # Sur Unix/macOS
.venv\Scripts\activate # Sur Windows
Cette commande crée un environnement isolé, comme une cuisine dédiée où vous pouvez installer exactement les versions des packages dont vous avez besoin, sans interférer avec d'autres projets.
uv run : l'exécution intelligente de vos scripts
La commande uv run
est l'une des innovations les plus élégantes de uv. Au lieu d'activer manuellement votre environnement virtuel à chaque fois, uv run
s'en charge automatiquement :
# Au lieu de :
source .venv/bin/activate
python script.py
# Utilisez simplement :
uv run script.py
C'est comme avoir un assistant qui préparerait automatiquement votre espace de travail avant chaque session. Cette commande assure que votre script s'exécute toujours dans le bon environnement, avec toutes les dépendances nécessaires.
UV Tools : vos utilitaires toujours prêts
UV Tools résout un problème courant : la gestion des outils de développement comme black, flake8, ou pytest. Traditionnellement, ces outils devaient être installés dans chaque environnement virtuel, ce qui alourdissait inutilement vos projets.
UV Tools adopte une approche plus intelligente. Prenons un exemple concret :
# Utilisation traditionnelle
pip install black
black mon_script.py
# Avec UV Tools
uvx black mon_script.py
ette commande fait plusieurs choses intelligemment :
Crée un environnement temporaire dédié
Y installe black à la volée
Exécute la commande
Nettoie automatiquement après utilisation
C'est comme avoir une boîte à outils magique qui fournit instantanément l'outil dont vous avez besoin, puis le range automatiquement. Plus besoin de surcharger votre environnement principal avec des outils de développement.
Installation en mode développement : le lien vivant
L'installation en mode développement avec uv pip install -e .
est un concept crucial pour tout développeur Python. Imaginons que vous développiez une bibliothèque ou un package :
mon_projet/
├── src/
│ └── mon_package/
│ ├── __init__.py
│ └── fonctions.py
└── setup.py
L'installation classique (pip install .
) crée une copie figée de votre code dans l'environnement virtuel. En revanche, l'installation en mode éditable (uv pip install -e .
) crée un lien dynamique vers votre code source. Ainsi :
Chaque modification de votre code est immédiatement prise en compte
Vous pouvez déboguer votre package comme s'il était installé
Les imports fonctionnent exactement comme en production
C'est comme avoir un lien direct entre votre cuisine de développement et votre restaurant : chaque amélioration de recette est instantanément disponible aux clients, sans nécessiter de nouvelle installation.
Mise en pratique : un workflow complet
Voici comment ces éléments s'assemblent dans un workflow de développement typique :
# 1. Initialisation du projet
uv init mon_projet
cd mon_projet
# 2. Configuration de l'environnement de développement
uv venv .venv
source .venv/bin/activate
# 3. Installation des dépendances du projet
uv add pandas scikit-learn
# 4. Installation en mode développement
uv pip install -e .
# 5. Utilisation des outils de développement
uvx black src/
uvx pytest tests/
Pour aller plus loin :
La documentation de uv : https://docs.astral.sh/uv/
Un tutoriel très complet : https://www.datacamp.com/tutorial/python-uv
3. La documentation : votre meilleur allié
La documentation n'est pas un luxe, c'est une nécessité. Au-delà des docstrings et des README, une bonne documentation raconte l'histoire de votre projet.
Une bonne documentation répond à trois questions essentielles :
Pourquoi ce code existe-t-il ? (La vision et le contexte)
Comment l'utiliser ? (Les guides pratiques et API docs)
Quelles décisions techniques ont été prises et pourquoi ? (Architecture Decision Records)
Deux outils modernes se distinguent pour créer une documentation professionnelle : MkDocs et Docusaurus. MkDocs, avec son thème Material, transforme vos fichiers Markdown en une documentation élégante et navigable. Son approche "convention over configuration" vous permet de vous concentrer sur le contenu plutôt que sur la mise en forme.
# Installation
uvx pip install mkdocs-material
# Création d'un nouveau projet de documentation
mkdocs new mon-projet-docs
# Prévisualisation en temps réel
mkdocs serve
# Déploiement
mkdocs gh-deploy
Pour la structure, tout se passe dans le fichier de configuration mkdocs.yml
. Pensez-le comme la recette maîtresse de votre documentation, où les sections principales (site_name
, theme
et nav
) définissent respectivement l'identité de votre projet, son apparence et sa structure de navigation.
Pour vous lancer rapidement, copiez cette configuration de base qui intègre les fonctionnalités les plus utiles du thème Material, notamment la recherche améliorée, les onglets de navigation et la copie de code :
site_name: Nom de Votre Projet
theme:
name: material
features:
- navigation.tabs # Ajoute des onglets en haut
- navigation.indexes # Permet d'avoir des sections cliquables
- navigation.sections # Sections repliables dans la navigation
- search.suggest # Suggestions pendant la recherche
- content.code.copy # Bouton de copie sur les blocs de code
palette:
- scheme: default # Thème clair/sombre
primary: indigo # Couleur principale
accent: indigo # Couleur d'accent
markdown_extensions: # Extensions pour enrichir le Markdown
- pymdownx.highlight # Coloration syntaxique du code
- pymdownx.superfences # Blocs de code améliorés
- admonition # Notes, warnings, etc.
nav: # Structure de votre documentation
- Accueil: index.md
- Guide:
- Installation: guide/installation.md
- Utilisation: guide/usage.md
- API: api.md
Docusaurus, développé par Meta, va encore plus loin en proposant un véritable site de documentation avec recherche intégrée, versioning de la documentation, et support multilingue. C'est le choix idéal pour les projets qui grandissent et nécessitent une documentation évolutive. Je vous invite à aller voir la documentation, je ne couvrirais pas en détail ici.
Pour aller plus loin :
La documentation officielle Mkdocs : https://www.mkdocs.org/getting-started/
La documentation officielle Docusaurus : https://docusaurus.io/
4. Les bonnes pratiques qui font la différence
Quand on démarre en Python, il est facile de se concentrer uniquement sur le code qui "fonctionne". Mais la différence entre un code qui fonctionne et un code professionnel réside dans l'application systématique de bonnes pratiques. Explorons ensemble quelques pratiques essentielles qui transformeront vos projets.
Tests Automatisés : Votre filet de sécurité
Les tests unitaires ne sont pas une contrainte mais une libération : ils vous permettent de modifier votre code en toute confiance. Avec pytest, tester devient presque aussi naturel. Ci joint un exemple basique :
# tests/test_preprocessing.py
import pytest
import pandas as pd
from src.monpackage.preprocessing import nettoyer_donnees
def test_nettoyer_donnees_gere_valeurs_manquantes():
# Préparation des données de test
donnees_test = pd.DataFrame({
'age': [25, None, 30],
'revenu': [50000, 60000, None]
})
# Exécution de la fonction à tester
resultat = nettoyer_donnees(donnees_test)
# Vérifications
assert not resultat.isna().any().any(), "Le résultat contient des valeurs manquantes"
assert len(resultat) == 3, "Des lignes ont été perdues pendant le nettoyage"
def test_nettoyer_donnees_rejette_donnees_invalides():
with pytest.raises(ValueError, match="Les colonnes requises sont manquantes"):
nettoyer_donnees(pd.DataFrame({'colonne_invalide': [1, 2, 3]}))
Lancez vos tests régulièrement :
# Lancer tous les tests
pytest tests/
# Lancer les tests avec plus de détails
pytest -v tests/
# Voir la couverture de code
pytest --cov=src tests/
# Lancer les tests à chaque modification
pytest-watch
Type Hints : Prévenir plutôt que guérir
Le typage statique avec MyPy est comme un assistant qui vérifie votre code avant même son exécution. Il capture les erreurs évidentes comme passer une chaîne de caractères là où un nombre est attendu, mais aussi des problèmes plus subtils dans la logique de votre code. C'est particulièrement précieux dans les projets data où les transformations de données sont complexes. Voici comment l'intégrer progressivement :
from typing import Dict, List
import pandas as pd
def analyser_ventes(
ventes: pd.DataFrame,
colonnes_analyse: List[str]
) -> Dict[str, float]:
"""Calcule les statistiques de base pour les colonnes spécifiées.
Exemple d'utilisation:
stats = analyser_ventes(df, ['montant', 'quantite'])
"""
return {col: ventes[col].mean() for col in colonnes_analyse}
Vérifiez votre code avec MyPy :
# Vérification basique
mypy src/
# Mode strict recommandé
mypy --strict src/
# Vérification continue
watchmedo shell-command --patterns="*.py" --command='mypy src/'
Formatage automatique : la cohérence sans effort
Black et Ruff ne sont pas de simples outils de formatage : ils éliminent les débats stériles sur le style du code. Plus besoin de se demander où placer les espaces ou comment aligner les paramètres. Ces outils appliquent automatiquement un style cohérent, vous permettant de vous concentrer sur ce qui compte vraiment : la logique de votre code.
La première étape consiste à paramétrer votre fichier pyproject.toml
:
# pyproject.toml
[tool.black]
line-length = 88
target-version = ['py39']
include = '\.pyx?$'
[tool.ruff]
line-length = 88
select = ["E", "F", "I", "UP"]
ignore = ["E501"]
Vous pouvez ensuite facilement intégrer ces outils dans votre workflow :
# Formatage du code avec Black
black src/ tests/
# Vérification du style sans modification
black --check src/
# Utilisation de Ruff pour le linting
ruff check src/
# Correction automatique avec Ruff
ruff check --fix src/
Pre-commit : la qualité dès le départ
Les hooks pre-commit sont comme un contrôle qualité automatisé. Avant chaque commit, ils vérifient que votre code respecte les standards définis : formatage correct, absence de secrets exposés, tests qui passent. C'est votre première ligne de défense contre les régressions.
Les hooks pre-commit garantissent que chaque commit respecte vos standards de qualité. Créez un fichier .pre-commit-config.yaml
:
repos:
- repo: https://github.com/psf/black
rev: 23.3.0
hooks:
- id: black
language_version: python3.9
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.261
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.3.0
hooks:
- id: mypy
additional_dependencies: [types-all]
- repo: local
hooks:
- id: pytest-check
name: pytest-check
entry: pytest
language: system
pass_filenames: false
always_run: true
Le fichier .pre-commit-config.yaml
est votre gardien de la qualité du code. Il définit une série d'outils qui vérifient automatiquement votre code avant chaque commit : black pour le formatage, ruff pour les bonnes pratiques Python, mypy pour la vérification des types, et pytest pour exécuter les tests. C'est comme avoir un assistant qui s'assure que tout est parfait avant que votre code ne soit partagé avec l'équipe.
Pour l’installation et l’utilisation :
# Installation des hooks
pre-commit install
# Vérification manuelle
pre-commit run --all-files
# Vérification d'un fichier spécifique
pre-commit run --files src/monpackage/models.py
Ces pratiques forment un cercle vertueux : chaque outil renforce l'efficacité des autres. Ensemble, ils créent un environnement de développement professionnel où la qualité n'est plus une option mais une habitude.
Pour aller plus loin :
Type hints cheat sheet : https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html
Documentation de pre-commit : https://pre-commit.com/
Conclusion
Ces pratiques peuvent sembler superflues au début, mais elles constituent la différence entre un projet qui reste maintenable et évolutif, et un projet qui devient progressivement impossible à gérer.
N'hésitez pas à me faire part de vos questions et suggestions pour les prochains numéros !
À bientôt,
Gaël