Aller au contenu

Docstrings et commentaires

Conventions par langage, quand commenter vs laisser le code parler, et outils de génération automatique de documentation.


Principes généraux

Une docstring documente le contrat d'une fonction ou d'un module : ce qu'elle accepte, ce qu'elle produit, ce qu'elle peut lever comme erreur. Un commentaire inline explique une décision ou un cas non-évident.

Ce qu'il ne faut jamais faire

# Incremente i de 1
i += 1

Ce commentaire ne dit rien que le code ne dit déjà. Il ajoute du bruit sans valeur.

Ce qu'il faut faire

# La spec API impose un delai minimum de 500ms entre deux requetes
# pour eviter le rate-limiting (voir issue #412)
time.sleep(0.5)

Ce commentaire explique le pourquoi, pas le quoi.


Python — docstrings Google style

Le style Google est le plus lisible et le mieux supporte par les outils modernes (Sphinx, mkdocstrings).

def fetch_user(user_id: int, include_deleted: bool = False) -> dict:
    """Recupere un utilisateur par son identifiant.

    Interroge la base de donnees principale. Si l'utilisateur est
    marque comme supprime et que include_deleted est False, leve
    une UserNotFoundError.

    Args:
        user_id: Identifiant unique de l'utilisateur, doit etre positif.
        include_deleted: Si True, retourne aussi les comptes supprimes.

    Returns:
        Dictionnaire contenant les champs : id, email, name, created_at.

    Raises:
        UserNotFoundError: Si l'utilisateur n'existe pas ou est supprime.
        DatabaseError: En cas d'erreur de connexion a la base.

    Example:
        >>> user = fetch_user(42)
        >>> print(user["email"])
        'alice@example.com'
    """

Python — docstrings NumPy style

Préféré dans les projets scientifiques et les librairies de données.

def normalize(array, axis=0):
    """
    Normalise un tableau numpy entre 0 et 1 sur un axe donne.

    Parameters
    ----------
    array : np.ndarray
        Tableau d'entree, de n'importe quelle forme.
    axis : int, optional
        Axe selon lequel normaliser (defaut : 0).

    Returns
    -------
    np.ndarray
        Tableau normalise de meme forme que l'entree.

    Notes
    -----
    Si toutes les valeurs sur un axe sont identiques, retourne 0.5
    pour eviter la division par zero.
    """

TypeScript / JavaScript — JSDoc

/**
 * Calcule le prix TTC a partir du prix HT et du taux de TVA.
 *
 * @param priceHT - Prix hors taxe en euros, doit etre positif.
 * @param vatRate - Taux de TVA en pourcentage (ex: 20 pour 20%).
 * @returns Prix TTC arrondi a deux decimales.
 * @throws {RangeError} Si priceHT est negatif ou vatRate hors [0, 100].
 *
 * @example
 * ```typescript
 * const ttc = calculateTTC(100, 20); // 120.00
 * ```
 */
export function calculateTTC(priceHT: number, vatRate: number): number {
    if (priceHT < 0) throw new RangeError("priceHT doit etre positif");
    if (vatRate < 0 || vatRate > 100) throw new RangeError("vatRate invalide");
    return Math.round(priceHT * (1 + vatRate / 100) * 100) / 100;
}

Java — Javadoc

/**
 * Envoie une notification par email a l'utilisateur specifie.
 *
 * <p>La methode est asynchrone : elle retourne immediatement apres
 * avoir place le message dans la file d'attente. Le resultat de l'envoi
 * est disponible via le {@link CompletableFuture} retourne.
 *
 * @param userId  Identifiant de l'utilisateur destinataire.
 * @param subject Sujet de l'email, 1 a 200 caracteres.
 * @param body    Corps du message au format texte brut ou HTML.
 * @return Future resolue avec {@code true} si l'envoi a reussi.
 * @throws IllegalArgumentException si userId est null ou subject vide.
 * @see EmailTemplate pour les modeles pre-definis.
 */
public CompletableFuture<Boolean> sendNotification(
        String userId, String subject, String body) {
    // ...
}

Go — doc comments

En Go, la convention est simple : la documentation précédé directement la declaration, sans marqueurs speciaux.

// Package auth fournit les primitives d'authentification OAuth2.
// Il supporte les flux authorization_code et client_credentials.
package auth

// Token represente un jeton d'acces OAuth2 avec sa duree de vie.
type Token struct {
    AccessToken string
    ExpiresAt   time.Time
}

// IsExpired retourne true si le jeton a depasse sa date d'expiration.
// Ajoute une marge de 30 secondes pour compenser la latence reseau.
func (t Token) IsExpired() bool {
    return time.Now().After(t.ExpiresAt.Add(-30 * time.Second))
}

// NewClient cree un client OAuth2 avec les identifiants fournis.
// clientID et clientSecret ne doivent jamais etre loggues.
func NewClient(clientID, clientSecret string) (*Client, error) {
    if clientID == "" || clientSecret == "" {
        return nil, errors.New("clientID et clientSecret sont obligatoires")
    }
    // ...
}

Rust — doc comments

/// Calcule le hash SHA-256 d'une chaine de caracteres.
///
/// # Arguments
///
/// * `input` - La chaine a hacher.
///
/// # Returns
///
/// Une chaine hexadecimale de 64 caracteres.
///
/// # Examples
///
/// ```
/// let hash = sha256_hex("hello");
/// assert_eq!(hash.len(), 64);
/// ```
pub fn sha256_hex(input: &str) -> String {
    // ...
}

Tableau recapitulatif

Langage Convention principale Format clé Outil de génération
Python Google style Args: Returns: mkdocstrings, Sphinx
TypeScript JSDoc @param @returns TypeDoc
Java Javadoc @param @return Javadoc (JDK)
Go godoc Commentaire préfixe godoc, pkg.go.dev
Rust rustdoc /// avec # cargo doc

Quand commenter, quand ne pas commenter

Code auto-documentant : pas besoin de commentaire

# Mauvais : le commentaire duplique le code
# Verifie si l'utilisateur est admin
if user.role == "admin":
    ...

# Bon : le nom de la variable rend le commentaire inutile
is_admin = user.role == "admin"
if is_admin:
    ...

Quand le commentaire est indispensable

// La RFC 7231 section 6.5.3 impose ce code 403 (et non 401)
// quand l'utilisateur est authentifie mais non autorise.
// Un 401 forcerait une re-authentification inutile.
w.WriteHeader(http.StatusForbidden)
# Utilise bitwise XOR pour l'echange sans variable temporaire.
# Plus rapide sur les architectures sans registre de scratch.
a ^= b
b ^= a
a ^= b

Règle des trois niveaux

  1. Si le code est clair — pas de commentaire.
  2. Si le code est complexe mais standard — renommez les variables.
  3. Si le pourquoi n'est pas évident — commentez le pourquoi.

Génération automatique de documentation

Python avec mkdocstrings

# mkdocs.yml
plugins:
  - mkdocstrings:
      handlers:
        python:
          options:
            docstring_style: google
            show_source: true
<!-- Dans un fichier .md -->
::: mon_module.MaClasse
    options:
      members: true
      show_docstring_examples: true

TypeScript avec TypeDoc

npx typedoc --entryPoints src/index.ts --out docs/api
// typedoc.json
{
  "entryPoints": ["src/index.ts"],
  "out": "docs/api",
  "excludePrivate": true,
  "includeVersion": true
}

Go avec godoc

# Lancer un serveur de documentation local
godoc -http=:6060

# Ou consulter en ligne sur pkg.go.dev

Java avec Maven Javadoc Plugin

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-javadoc-plugin</artifactId>
    <version>3.6.0</version>
    <configuration>
        <show>public</show>
        <nohelp>true</nohelp>
    </configuration>
</plugin>
mvn javadoc:javadoc
# Genere dans target/site/apidocs/

Prochaine étape

Maintenant que vous savez documenter le code lui-même, voyons comment documenter les décisions d'architecture avec les ADR.

Architecture Decision Records →