Lorsque vous construisez des pipelines de données, vous réalisez rapidement à quel point ils sont dépendants des paramètres, des chemins d'accès aux sources, des seuils, des mappages de colonnes, et plus encore. Cela commence généralement de manière simple : vous faites des prototypes dans un carnet, en essayant d'éviter de coder en dur trop de choses directement dans votre logique en ajoutant quelques paramètres de base comme l'environnement, les noms de tables, les détails de connexion, ... Plus tard viennent des conditions de filtrage, peut-être un peu de logique conditionnelle, ou même des schémas de stratégie si vous êtes dans le génie logiciel.
Très vite, votre pipeline est opérationnel en local ou en dev, et vos paramètres sont éparpillés dans le notebook. Certains paramètres sont encore codés en dur, d'autres sont dupliqués ou appliqués de manière incohérente, et maintenant vous faites défiler le carnet pour trouver un paramètre que vous voulez ajuster pour les tests.
Cela vous rappelle quelque chose ?
Le problème des paramètres codés en dur et comment le résoudre
Au début, il peut sembler inoffensif de définir ces paramètres directement dans votre carnet de notes ou votre fichier principal, tant qu'il ne s'agit pas de données sensibles, bien sûr. Mais au fur et à mesure que votre pipeline se complexifie ou que vous commencez à prendre en charge plusieurs environnements (comme le développement, la mise en scène et la production), le codage en dur des paramètres n'est plus le gain rapide qu'il était au début :
- Lorsque la complexité des pipelines augmente, le nombre de paramètres augmente également. Plutôt que d'avoir une longue liste de paramètres (s'ils ne sont pas éparpillés dans votre notebook bien sûr), il est plus facile de regrouper les valeurs en fonction, par exemple, de l'ensemble de données qui doit être traité par votre pipeline. Une classe de données ou un objet pydantique de classe de données peut joliment encapsuler votre configuration et rendre l'accès à ses attributs facile et lisible.
- Les valeurs codées en dur sont difficiles à réutiliser d'un environnement à l'autre. Lorsque vous passez de l'environnement de développement à l'environnement de production, vous aurez souvent besoin d'informations d'identification, de chemins de fichiers ou de paramètres de réglage différents. Bien que vous puissiez rendre ces valeurs conditionnelles en fonction de l'environnement, vous savez déjà qu'il est plus facile d'avoir, par exemple, un fichier de configuration YAML par environnement.
Étape 1 : Encapsuler votre configuration dans des classes de données
Lorsque vous créez votre carnet ou fichier de pipeline, commencez par créer une classe de données pydantique dans laquelle vous encapsulez vos paramètres de pipeline sous forme d'attributs. Ces classes de données vous permettent de définir facilement votre objet de configuration et d'ajouter des attributs tout au long de votre développement. Vous trouverez ci-dessous un extrait d'une classe de données simple avec l'instanciation de l'objet et la manière dont vous pouvez accéder aux attributs de configuration.
Vous disposez à présent d'un endroit centralisé où placer vos paramètres et y accéder, c'est simple, n'est-ce pas ? L'étape suivante, qui consiste à déplacer vos valeurs de configuration dans un fichier de configuration séparé, sera ainsi beaucoup plus simple.
Étape 2 : Déplacer les paramètres vers un fichier de configuration dédié (YAML)
Pour simplifier la gestion des paramètres, il est plus facile de les externaliser dans un fichier de configuration distinct. Cela vous permet de modifier le comportement de votre pipeline sans modifier votre code, ce qui le rend plus propre et plus adaptable.
Évitez de mettre des secrets tels que des mots de passe ou des clés d'API dans vos fichiers de configuration. Ceux-ci devraient se trouver dans des variables d'environnement ou dans un magasin de secrets sécurisé.
Si JSON est l'un des premiers formats auxquels vous pensez pour un fichier de configuration, pensez aussi à YAML (YAML Ain't Markup Language), qui offre quelques fonctionnalités intéressantes :
- Plus lisible
- Support pour les commentaires, ce qui est utile pour les collègues (et votre futur moi)
- Structure flexible qui peut facilement gérer des objets imbriqués, des listes, des chaînes de caractères multi-lignes, ...
Les chaînes multi-lignes sont une fonctionnalité sous-estimée qui peut s'avérer très utile pour l'ingénierie des données. Considérons un pipeline qui utilise PySpark où le même pipeline peut être utilisé pour traiter différents ensembles de données. Supposons que vous souhaitiez appliquer un filtre à l'aide de SQL et que, pour certains ensembles de données, vous disposiez d'un filtre assez complexe sur plusieurs colonnes. En utilisant YAML, vous pouvez formater l'instruction SQL de manière agréable et facilement lisible, comme indiqué ci-dessous. Ci-dessous, nous avons un extrait d'une instruction SQL provenant d'une configuration YAML et comment elle pourrait être utilisée dans un pipeline PySpark pour une condition de fusion.
Étape 3 : Validez vos configurations
Bien que ces fichiers de configuration soient vraiment excellents, ils doivent être valides. Malheureusement, une petite faute de frappe ou un mauvais type de données peut entraîner des bogues subtils.
Par exemple, en utilisant
enable_feature : "false"
dans votre fichier de configuration semble correct à première vue, mais en Python, une chaîne non vide (même "false") est évaluée à True. Python est un langage à typage dynamique et ne sait pas que vous vouliez un Bool au lieu d'une chaîne. Si vous ne validez pas les types et formats d'entrée, vous risquez d'activer une fonctionnalité inattendue et de configurer votre pipeline différemment de ce qui était prévu.
C'est exactement la raison pour laquelle nous conseillons d'utiliser la bibliothèque pydantic, puisqu'elle vous permet de définir un schéma et de valider la configuration avant même de lancer le pipeline. Votre validation peut être aussi complexe et élaborée que vous le souhaitez, alors n'hésitez pas à consulter le package pydantic! Comme vous pouvez le voir, le changement de la valeur de max_nb_files de 100 à dix entraîne une erreur de validation, ce qui permet d'éviter la chasse aux bugs du vendredi après-midi :
En bloquant les configurations erronées avant l'exécution de votre pipeline, vous savez exactement ce que vous attendez de votre instance PipelineConfig. Vous pouvez même ajouter la validation de vos configurations au pipeline de déploiement afin de vous assurer que seules les configurations valides sont déployées dans la phase d'essai et, plus important encore, dans la phase de production.
Dernières réflexions
La gestion de la configuration est loin d'être la partie la plus sexy de la construction de pipelines de données, mais c'est une question qui revient tôt ou tard. Dans votre prochain projet, assurez-vous de commencer par encapsuler vos paramètres de configuration pour en faciliter l'accès. Une fois que votre POC fonctionne, déplacez les valeurs de configuration dans des fichiers YAML dédiés et validez-les avec pydantic. Ainsi, vous saurez que votre pipeline fait ce que vous avez prévu, même si vos collègues commencent à s'amuser avec les configurations !



