On entend souvent qu'une fonction ne devait faire qu'une seule chose à la fois.
Ce principe, proche du principe de responsabilité unique (SRP), est plein de bon sens mais pourrait vous pousser à réduire arbitrairement la taille de vos fonctions, comme si une fonction de 3 lignes valaient mieux qu'une fonction de 15 et garantissait la lisibilité du code.
En réalité, ce n’est pas tant la taille d’une fonction qui importe mais son respect d'un seul niveau d’abstraction.
Quand plusieurs niveaux d'abstraction sont présents au sein d'une même méthode, on passe sans transition d’une intention métier à des détails techniques : cette rupture oblige le lecteur à changer sans cesse de perspective ce qui nuit au confort de lecture.
Une fonction devient donc difficile à lire quand elle dit à la fois ce qu’elle veut faire et comment elle s’y prend.
Pour fluidifier la lecture, une fonction devrait donc toujours se limiter à un seul niveau d'abstraction : soit exprimer une intention soit contenir une implémentation technique.
Une corrélation existe entre l'intention et le niveau d'abstraction que nous pouvons représenter de la manière suivante :

Lorsqu’elle exprime une intention, une fonction opère à un niveau d’abstraction élevé : elle décrit ce qui doit être accompli sans dire comment le faire, le fonction détient le "quoi".
À l’inverse, une fonction située à un niveau d’abstraction plus faible se concentre sur l’exécution concrète des choses : elle ne porte plus une intention globale mais déroule une mécanique interne, le "comment".
La fonction sendWelcomeEmailToNewcomers
suivante est de haut niveau, elle ne s’embarrasse pas des détails techniques et raconte une histoire simple, lisible en diagonale et composée d’actions explicites : on comprend immédiatement le besoin métier qu’elle cherche à accomplir, le "quoi".
function sendWelcomeEmailToNewcomers()
{
$users = $this->getNewUsers();
$emails = $this->retrieveEmails($users);
$this->sendWelcomeEmail($emails);
}
À l’inverse, une fonction comme retrieveEmails
opère à un niveau bien plus bas, elle parcourt un tableau, extrait des valeurs, assemble des éléments ... elle s’intéresse au "comment". Elle ne raconte plus une intention métier mais contient une série d’instructions techniques au service d’une tâche plus vaste.
function retrieveEmails(array $users): array
{
$emails = [];
foreach($users as $user)
{
$emails[] = $user->email;
}
return $emails;
}
Afin de faciliter la lecture, une fonction devrait rester sur un seul niveau d’abstraction du début à la fin, c’est cette cohérence de style qui permet au code de rester clair et compréhensible.
Une fonction de quelques lignes peut être confuse si elle juxtapose des idées trop éloignées alors qu’une fonction de plusieurs dizaine de lignes peut être parfaitement limpide si elle reste homogène et conserve un seul niveau d'abstraction.
En respectant ce principe, vous commencerez naturellement à structurer votre code différemment, cela vous amènera à découper les fonctions, non pas pour les raccourcir à tout prix, mais pour séparer les idées par niveau : certaines fonctions orchestrent des intentions tandis que d’autres exécutent les détails techniques.
Ce découpage vous amènera à créer une multitude de petites fonctions, cela n'est pas grave car vous obtiendrez un enchaînement de petites briques lisibles, faciles à tester, à réutiliser ... et chacune concentrée sur un seul niveau d’abstraction.
« If code requires effort to understand, extract it into a function named after its purpose »
— Robert C. Martin