Publié le

Une fonction doit faire une seule chose

5 min lecture - 856 mots
Auteurs
  • avatar
    Nom
    Cédric RIBALTA
    Twitter

Introduction

Sais-tu quel est le point commun entre le cerveau de l'homme et une fonction parfait ?

Ils font tous les deux une seule chose à la fois.

Pourquoi une fonction devrait-elle faire une seule chose ?

Dans le développement logiciel, on recherche avant tout la simplicité.
En tant que développeurs, on veut écrire du code :

  1. Efficace
  2. Lisible
  3. Maintenable
  4. Et surtout facile à comprendre

C'est là qu'entre en jeu l'un des principes les plus fondamentaux de Clean Code de Robert C. Martin (aka Oncle Bob) : une fonction doit faire une seule chose, et la faire bien.

Une question de clarté et de lisibilité

Lorsque nous écrivons une fonction qui se concentre sur une seule tâche, cela rend le code beaucoup plus facile à lire. La clarté est l'une des premières victimes quand une fonction commence à "faire trop de choses".

Imagine une fonction qui ouvre un fichier, le lit, le parse, et enregistre une copie modifiée ailleurs. À première vue, ce n'est pas si mal. Mais au fil du temps, avec des ajouts et des modifications, cette fonction peut devenir un véritable cauchemar de maintenance.

En revanche, en divisant ces étapes en plusieurs fonctions dédiées, chacune focalisée sur une seule tâche, tu obtiens un code bien plus compréhensible.

La réutilisabilité

Une fonction qui fait une seule chose est aussi plus réutilisable.

Par exemple, si tu as une fonction qui ne fait qu'ouvrir un fichier, tu peux la réutiliser dans différents contextes sans avoir à copier/coller du code.

C’est beaucoup plus difficile si la fonction fait tout un ensemble de choses, car elle sera souvent trop spécifique à un cas d’usage particulier.

Une fonction, un niveau d’abstraction

Un autre point clé que Oncle Bob met en avant est qu’une fonction ne devrait pas mélanger plusieurs niveaux d’abstraction.

Si tu appelles une fonction, elle devrait travailler sur un seul niveau :

  • Traiter des données brutes
    ou
  • Orchestrer des opérations de haut niveau

Mais pas les deux en même temps.

Mélanger les niveaux d’abstraction dans une seule fonction complique la lecture et la compréhension du code, car cela force le lecteur à jongler entre différents concepts à des niveaux différents.

Un code plus facile à tester

Les fonctions focalisées sur une seule tâche sont également beaucoup plus faciles à tester. En isolant chaque comportement, tu peux écrire des tests unitaires précis, ciblés et robustes.

Plus la fonction est concise et spécifique, plus tu es sûr que chaque test est valide et maintenable sur le long terme.

La tentation de tout faire

Il peut être tentant de regrouper plusieurs petites actions dans une seule fonction pour "gagner du temps" ou par souci de commodité.

Cependant, cela finit par être contre-productif. Ce genre de pratique complexifie le code et le rend plus difficile à maintenir, et peut aussi introduire des bugs cachés.

Une fonction bien pensée qui fait une seule chose est beaucoup plus facile à corriger, à ajuster et à améliorer.

Un exemple concret

// Mauvais exemple : une fonction qui fait trop de choses à la fois
function processFile(filePath: string): void {
  const fs = require('fs')

  // Lire le fichier
  const fileContent = fs.readFileSync(filePath, 'utf-8')

  // Parser le contenu (imaginons que c'est du JSON)
  const parsedContent = JSON.parse(fileContent)

  // Transformer les données (par exemple, ajouter une propriété à chaque objet)
  const transformedContent = parsedContent.map((item: any) => {
    item.newProperty = 'valeur'
    return item
  })

  // Sauvegarder les nouvelles données
  fs.writeFileSync(filePath, JSON.stringify(transformedContent, null, 2))
}

Dans cet exemple, la fonction processFile fait plusieurs choses :

  1. Lire le fichier
  2. Parser le contenu
  3. Transformer les données
  4. Ecrire le fichier.

Ce mélange de responsabilités la rend difficile à tester, à maintenir, et à réutiliser.

Voyons maintenant comment améliorer cela en suivant le principe de "faire une seule chose" :

const fs = require('fs')

// Fonction qui lit un fichier
function readFile(filePath: string): string {
  return fs.readFileSync(filePath, 'utf-8')
}

// Fonction qui parse le contenu d'un fichier
function parseFileContent(content: string): any[] {
  return JSON.parse(content)
}

// Fonction qui transforme les données
function transformData(data: any[]): any[] {
  return data.map((item: any) => {
    item.newProperty = 'valeur'
    return item
  })
}

// Fonction qui écrit dans un fichier
function writeFile(filePath: string, content: any): void {
  fs.writeFileSync(filePath, JSON.stringify(content, null, 2))
}

// Fonction principale qui orchestre les opérations
function processFile(filePath: string): void {
  const content = readFile(filePath)
  const parsedData = parseFileContent(content)
  const transformedData = transformData(parsedData)
  writeFile(filePath, transformedData)
}

Explication :

  • readFile : Cette fonction fait une seule chose : elle lit le fichier et retourne son contenu sous forme de chaîne de caractères.
  • parseFileContent : Elle ne fait qu'une seule tâche, parser le contenu JSON.
  • transformData : Elle transforme les données et ajoute une propriété à chaque élément.
  • writeFile : Cette fonction écrit les données transformées dans le fichier.
  • processFile : Elle orchestre l'ensemble des opérations, mais chaque étape est déléguée à une fonction qui se concentre sur une seule tâche.

Conclusion

L'idée qu'une fonction doit faire une seule chose est un principe fondamental de la propreté du code. Il encourage à écrire du code simple, lisible, maintenable et réutilisable.

Robert Martin ne cesse de rappeler que "Clean code is simple and direct. Clean code reads like well-written prose."

En appliquant ce principe, tu améliores non seulement la qualité de ton code, mais aussi la qualité de vie des développeurs qui auront à travailler dessus après toi — y compris ton futur toi-même !