---
title: "Julia Evans redécouvre pourquoi les frameworks existent : supprimer cent petites frictions"
locale: "fr"
url: "https://irz.fr/fr/articles/django-julia-evans-small-frictions-fr"
markdown_url: "https://irz.fr/fr/articles/django-julia-evans-small-frictions-fr.md"
category: "tech"
tags: ["Django", "Python", "framework", "web", "SQLite"]
published_at: "2026-08-24T09:52:00.000Z"
author: "Léa Perrin"
translation: "https://irz.fr/en/articles/django-julia-evans-small-frictions-en.md"
---

# Julia Evans redécouvre pourquoi les frameworks existent : supprimer cent petites frictions

QuerySets, filtres de template, migrations : Julia Evans aime Django là où le framework enlève du travail répétitif, jusqu’au jour où un cache oublié expose le prix des conventions cachées.

Julia Evans s'est mise à fabriquer un site qu'elle décrit elle-même comme « façon 2010 » : base SQL, HTML rendu sur le serveur, très peu de JavaScript.[1](https://jvns.ca/blog/2026/07/21/more-nice-django-things/) Ce choix arrive après des années où son outillage confortable ressemblait plutôt à un générateur statique, une page avec quelques expériences JavaScript, ou une petite SPA Vue reliée à Lambda ou à un backend Go. Tant que l'application tenait sur une page, cette organisation lui convenait très bien ; avec beaucoup de pages différentes, elle avait moins envie de reconstruire toute la logique côté navigateur.[1](https://jvns.ca/blog/2026/07/21/more-nice-django-things/)

Elle cherche donc à faire côté serveur ce qu'elle appréciait déjà dans ses petites SPA : garder l'essentiel de la logique au même endroit. Django l'intéresse à ce moment-là pour une raison assez terre-à-terre. Evans connaît SQL, sait écrire un backend et n'attend pas du framework qu'il invente des capacités nouvelles ; elle découvre plutôt le confort d'un système qui a déjà décidé comment traiter une centaine de petites tâches récurrentes.

## Petites économies

Dans son billet du 21 juillet 2026, Evans passe en revue des choses modestes : `QuerySet` personnalisés, filtres de template, tag `querystring`, migrations automatiques. Pris séparément, rien de tout cela ne justifierait de changer de stack ; mis bout à bout, ces outils retirent une série d'opérations faciles à réécrire de travers, à renommer d'une page à l'autre ou simplement à oublier.[1](https://jvns.ca/blog/2026/07/21/more-nice-django-things/)

> **Le framework gagne par accumulation**
> - migrations déjà créées sur le projet d’Evans: 19
> - test léger avant de restaurer le cache des templates: 2–3 req/s
> - test léger après restauration du cache: ≈12 req/s
> Les mesures de performance sont celles d’Evans sur un VM à environ 10 $/mois ; elle précise ne pas avoir fait un benchmark avant/après rigoureux.

Quelques lignes de sa vue montrent bien le résultat. Evans a défini un `EventQuerySet` avec `approved()`, `for_tab()`, `with_festivals()`, `is_free()` et `is_outdoors()` ; au moment de construire la page, elle enchaîne ces noms et lit directement quelles sélections sont appliquées, sans devoir relire à chaque fois les colonnes et opérateurs utilisés dessous.[1](https://jvns.ca/blog/2026/07/21/more-nice-django-things/)

Django prévoit précisément ce genre d’extension. Un `QuerySet` personnalisé peut recevoir ses propres méthodes, puis être exposé par un manager ; la documentation propose notamment `as_manager()` et `Manager.from_queryset()` pour faire remonter ces méthodes vers l’API du modèle.[2](https://docs.djangoproject.com/en/6.0/topics/db/managers/)

Evans raconte qu'elle pensait auparavant, en substance, connaître SQL et ne pas avoir besoin d'un query builder.[1](https://jvns.ca/blog/2026/07/21/more-nice-django-things/) L'expérience la fait changer d'avis sur un point précis : **un vocabulaire local** comme `approved()` annonce immédiatement l'intention, là où `approved_at__isnull=False` oblige chaque lecteur à reconstruire ce choix.

## Requête lisible

Tant qu'une requête tient sur une ligne, la différence paraît surtout cosmétique ; dès qu'une page combine plusieurs filtres facultatifs, elle commence à organiser la lecture du programme.

> **Même requête, deux niveaux**
> - Chaîner directement les conditions, noms de colonnes et cas optionnels dans chaque vue.: Mécanique
> - Events.objects.approved().for_tab(...).is_free(...).is_outdoors(...).: Intention
> Le SQL n’a pas disparu. Le choix est de déplacer ses motifs répétés vers un vocabulaire partagé par l’application.

Le `QuerySet` reste paresseux : on peut le construire, ajouter des filtres et le transmettre avant l'accès effectif à la base, qui arrive lors de l'évaluation.[2](https://docs.djangoproject.com/en/6.0/topics/db/managers/) Le chaînage ne déclenche donc pas une requête pour chaque helper. Evans n'aime d'ailleurs pas spécialement la syntaxe servant à définir certains filtres ; ce qu'elle apprécie, une fois ces méthodes écrites, c'est de pouvoir les relire simplement dans les vues.[1](https://jvns.ca/blog/2026/07/21/more-nice-django-things/)

Cela sépare deux questions que les discussions sur les ORM mélangent souvent : « est-ce plus joli que SQL ? » et « où ranger les décisions de filtrage répétées par l'application ? ». Le retour d'Evans dit surtout quelque chose sur la seconde.

## HTML ordinaire

La même logique apparaît dans les templates avec `urlize`, `linebreaksbr`, le formatage de dates et `json_script`. Transformer une URL textuelle en lien, afficher des retours à la ligne, formater une date ou convertir proprement un dictionnaire Python en JSON paraît presque trop banal pour être mentionné ; précisément pour cette raison, ce travail finit facilement dispersé partout.[1](https://jvns.ca/blog/2026/07/21/more-nice-django-things/)

Le coût vient rarement de la difficulté technique. Ces détails sont trop petits pour mériter une architecture à eux seuls, mais assez nombreux pour grignoter du temps ; Django les range donc dans l'outillage standard de son moteur de templates.[3](https://docs.djangoproject.com/en/6.0/ref/templates/builtins/)

Evans met particulièrement en avant `querystring`. Son site utilise des paramètres comme `?date=2026-06-01` ou `?outdoors=...` pour représenter l’état de filtres. Le tag permet de créer un lien vers **la même query string avec une modification**, ou de retirer un paramètre en lui passant `None`.[1](https://jvns.ca/blog/2026/07/21/more-nice-django-things/)

> **Un filtre sans plomberie**
> - La page porte son état dans l’URL : date, gratuit, extérieur…: 1
> - Le template conserve les paramètres existants.: 2
> - querystring modifie seulement la valeur ciblée.: 3
> - Le lien obtenu reste partageable et fonctionne sans état frontend séparé.: 4
> Le gain est petit à chaque lien ; il devient sensible lorsque toute l’interface de filtrage repose sur cette convention.

Bien sûr, le comportement existe toujours quelque part. Ici il vit dans le framework, ce qui évite à l'application de maintenir son propre utilitaire de fusion des paramètres, avec l'encodage, la conservation des autres filtres et la suppression des valeurs vides à vérifier elle-même.

## Dix-neuf migrations

Une partie beaucoup moins visible du site lui procure le même genre de confort : **19 migrations** ont déjà été créées, et Evans s'attend à en ajouter d'autres.[1](https://jvns.ca/blog/2026/07/21/more-nice-django-things/) Elle ne présente pas ce nombre comme une dette ; il mesure plutôt combien de fois le schéma a pu suivre le projet pendant que sa vision du problème se précisait.

Django traite les migrations comme un historique versionné du schéma. `makemigrations` compare les modèles actuels à l’état reconstruit depuis les migrations précédentes et écrit de nouvelles opérations déclaratives ; `migrate` applique ensuite ces opérations à la base.[4](https://docs.djangoproject.com/en/6.0/topics/migrations/) La documentation recommande malgré tout de lire ce que l’autodétecteur produit, notamment pour les changements complexes.[4](https://docs.djangoproject.com/en/6.0/topics/migrations/)

> **Ce que 19 migrations veulent dire**
> - Modifier le modèle impose de réfléchir immédiatement à la transformation du schéma et à sa reproduction ailleurs.: Sans historique
> - Le changement du modèle produit un artefact versionné qui voyage avec le code et peut être rejoué.: Avec migrations
> L’automatisation ne supprime pas les migrations difficiles ; elle rend surtout les changements ordinaires beaucoup moins coûteux.

QuerySets, templates et migrations racontent finalement la même histoire : Django industrialise **le cas ordinaire**. Les migrations délicates existent toujours, mais l'ajout banal d'un champ ou l'évolution d'une table ne commence plus par une procédure inventée pour ce seul projet.

## Mauvais réglage

La contrepartie apparaît quand le framework a déjà installé tant de comportements utiles qu'une partie du travail devient invisible, au point qu'il faut savoir reconnaître le jour où l'on en désactive un sans le vouloir.

Evans tombe sur ce problème quand des scrapers liés aux LLM commencent à envoyer environ dix requêtes par seconde. Un test léger avec ApacheBench, sur un VM à une dizaine de dollars par mois, lui montre alors un site qui sert seulement **2 à 3 requêtes par seconde**.[1](https://jvns.ca/blog/2026/07/21/more-nice-django-things/)

Elle profile le CPU et remarque beaucoup de temps passé dans le rendu des templates. La documentation Django rappelle qu’activer le loader de templates en cache peut améliorer fortement les performances en évitant de recompiler chaque template à chaque rendu.[5](https://docs.djangoproject.com/en/2.0/topics/performance/) Problème : ce cache devait être actif par défaut dans sa configuration, mais Evans l’avait désactivé par accident en modifiant autre chose.[1](https://jvns.ca/blog/2026/07/21/more-nice-django-things/)

Après restauration du cache, Evans observe **environ 12 requêtes par seconde** sans saturation complète du CPU.[1](https://jvns.ca/blog/2026/07/21/more-nice-django-things/) Elle précise elle-même qu'il ne s'agit pas d'un benchmark avant/après rigoureux, donc le chiffre ne prouve aucune multiplication universelle des performances ; il montre plutôt à quel point **une convention invisible peut accomplir beaucoup de travail jusqu'au jour où un réglage la retire**.

> **Le contrat caché**
> - Le loader mis en cache évite de recompiler les templates à chaque rendu.: Default utile
> - Evans le désactive accidentellement en changeant ses réglages.: Configuration
> - Le rendu des templates apparaît comme une dépense importante.: Profil CPU
> - Le simple retour au comportement attendu change nettement son test de charge.: Correction
> Plus un framework fournit de conventions, plus comprendre son fichier de settings devient une compétence en soi.

## Pas tout aimer

Le billet échappe heureusement au format « dix raisons d'utiliser Django », car Evans décrit aussi les conventions qui l'agacent.

Elle tente par exemple de partager la logique de quatre class-based views grâce à l'héritage, trouve le résultat pénible à suivre et revient à des fonctions plus directes.[1](https://jvns.ca/blog/2026/07/21/more-nice-django-things/) L'héritage ne la dérange pas lorsqu'il sert simplement à implémenter une interface Django, comme avec `EventQuerySet` ; sa préférence porte donc moins sur les classes en général que sur **la manière d'organiser la logique propre à l'application**.

Plusieurs questions restent ouvertes autour de la vitesse et de la capacité du site : faut-il préparer les pointes de trafic, rendre davantage de choses cachables, essayer Jinja, ou se soucier vraiment de la différence entre blocs et includes ? Evans les énumère sans prétendre avoir déjà choisi une architecture finale.[1](https://jvns.ca/blog/2026/07/21/more-nice-django-things/)

Cette incertitude rend le retour plus utile : le framework n'annule pas les décisions, il choisit seulement lesquelles sont assez fréquentes pour disposer d'un chemin déjà pavé.

## Une seule pile

Evans explique que ses anciennes petites applications frontend fonctionnaient bien lorsqu’elles concentraient presque toute la logique dans le navigateur. Pour un site composé de nombreuses pages, elle cherche aujourd’hui la même qualité dans l’autre direction : HTML côté serveur, peu de JavaScript, logique concentrée dans le backend.[1](https://jvns.ca/blog/2026/07/21/more-nice-django-things/)

Vu depuis ce projet, Django ressemble moins à un bloc monolithique qu'à une série de continuités entre base, URL, vue et HTML : les migrations gardent l'histoire du schéma, les QuerySets portent le vocabulaire du domaine, les templates manipulent dates et query strings, tandis que l'échappement et le chargement des templates suivent déjà des conventions communes.

Au bout du compte, **la complexité change surtout d'adresse**. Evans écrit moins de plomberie locale, en échange d'un ensemble plus vaste de conventions communes qu'il faut connaître assez bien pour repérer un réglage qui s'écarte du comportement attendu.

Son enthousiasme n'a donc pas grand-chose à voir avec une capacité impossible à reproduire en Go, Flask ou quelques fonctions maison. Le billet raconte plutôt le moment où une développeuse qui sait déjà construire ces briques commence à trouver rationnel de ne plus les reconstruire : si les mêmes petits problèmes reviennent sur des milliers de sites, autant profiter d'un framework qui les a déjà rencontrés.

## References

1. [Julia Evans, Some more things about Django I've been enjoying, 21 juillet 2026](https://jvns.ca/blog/2026/07/21/more-nice-django-things/)
2. [Django 6.0 — Managers et QuerySets personnalisés](https://docs.djangoproject.com/en/6.0/topics/db/managers/)
3. [Django 6.0 — Built-in template tags and filters](https://docs.djangoproject.com/en/6.0/ref/templates/builtins/)
4. [Django 6.0 — Migrations](https://docs.djangoproject.com/en/6.0/topics/migrations/)
5. [Django — Performance and optimization](https://docs.djangoproject.com/en/2.0/topics/performance/)
