Un intranet n’est pas un portail d’actualités internes. C’est une liste, un formulaire, une recherche et un droit. Les quatre primitives se retrouvent dans tous les outils internes que nous construisons, quel que soit le métier : une liste de dossiers, de commandes, de congés ou d’interventions ; un formulaire pour en créer ou en modifier ; une recherche pour retrouver ; et une règle qui décide qui voit quoi.
Les projets d’intranet échouent presque toujours sur deux de ces quatre : la liste met huit secondes à s’afficher, et la recherche répond en plusieurs secondes ou pas du tout. Les salariés retournent alors au tableur partagé, qui est lent aussi mais qu’ils connaissent. Cet article mesure ces deux points sur des bancs montés ici — cinq mille lignes rendues de trois façons, et une recherche plein texte comparée sur des bases de dix mille à cinq cent mille enregistrements — puis en tire les décisions d’architecture, le sujet des droits, et l’arithmétique qui permet de justifier le budget.
Mesure 1 — la liste de cinq mille lignes
Le cas est universel : une table de dossiers, six colonnes, cinq mille lignes. Trois montages possibles, exactement le même rendu à l’écran.
- A — tout dans le HTML. Le serveur envoie les cinq mille lignes. C’est le réflexe du développement classique en PHP ou en Rails.
- B — tout construit par le script. La page arrive vide, récupère les données et fabrique les lignes. C’est le réflexe du développement en React ou en Vue sans précaution.
- C — fenêtrage. Seules les lignes visibles existent dans le document ; une cale donne la bonne hauteur de barre de défilement.
Protocole : processeur ralenti quatre fois pour approcher un portable d’entreprise chargé, cinq chargements par page, médiane retenue. Défilement programmé pendant 1,6 seconde pour observer les images en retard. Tâches longues relevées par PerformanceObserver.
| Mesure | A — dans le HTML | B — par script | C — fenêtrage |
|---|---|---|---|
| Éléments dans le document | 35 021 | 35 022 | 219 |
| Lignes réellement présentes | 5 000 | 5 000 | 28 |
| Premier affichage | 1 668 ms | 92 ms | 96 ms |
| Fin de chargement | 4 264 ms | 76 ms | 82 ms |
| Liste utilisable | 1 668 ms | 1 828 ms | 1 647 ms |
| Tâches de plus de 50 ms | 8 | 2 | 0 |
| La plus longue | 1 316 ms | 2 478 ms | 0 ms |
| Images en retard au défilement | 4 | 5 | 0 |
| Octets transférés | 643 Ko | 708 Ko | 709 Ko |
Quatre lectures, dont deux contre-intuitives.
Le poids transféré ne fait pas la différence. 643, 708 et 709 kilooctets : les trois montages transportent la même chose. Le problème n’est pas la bande passante, c’est le fil d’exécution unique du navigateur. Une réunion de projet qui se termine sur « il faut compresser » a manqué le sujet.
Le montage B est le plus mauvais des trois, et c’est celui qui paraît le plus rapide. Premier affichage à 92 millisecondes : l’en-tête, le titre et les colonnes apparaissent presque instantanément. Puis une tâche de 2 478 millisecondes bloque tout. Pendant deux secondes et demie, la page est peinte mais ne répond à rien : ni clic, ni saisie, ni défilement. C’est la sensation que les utilisateurs décrivent par « ça rame » alors que les mesures d’audience affichent un premier affichage excellent.
Le fenêtrage divise le document par cent soixante. Deux cent dix-neuf éléments au lieu de trente-cinq mille, et surtout aucune tâche longue, aucune image en retard pendant le défilement. C’est la seule des trois qui donne une liste qui glisse.
Et il reste 1,6 seconde à traiter. Honnêteté sur la colonne « liste utilisable » : les 1 647 millisecondes du fenêtrage ne sont pas du rendu, ce sont le transfert et l’analyse des 709 kilooctets de données. Le fenêtrage règle le coût d’affichage, pas le coût de transport. La suite logique est de ne plus envoyer les cinq mille lignes du tout : pagination côté serveur, tri et filtre côté serveur, et le navigateur ne reçoit que ce qu’il montre. C’est là que se gagne la dernière seconde et demie.
Mesure 2 — la recherche, et l’index qui ne sert à rien
Second banc, sur le sujet qui décide de l’adoption : la recherche. Base de cent mille dossiers, avec un client, un objet, un corps de texte d’une quarantaine de mots et une date. Quatre stratégies, la même requête, médiane de neuf exécutions.
| Stratégie | Durée | Coût de l’index |
|---|---|---|
LIKE '%terme%', sans index | 117,9 ms | aucun |
LIKE '%terme%', avec un index sur la colonne | 113,0 ms | 0,35 s de construction, base de 47 à 106 Mo |
LIKE 'préfixe%', avec index | 27,2 ms | le même index, enfin utilisé |
Index plein texte, MATCH | 0,2 ms | 0,93 s de construction |
La deuxième ligne est la plus utile de tout cet article. Ajouter un index sur la colonne n’a rien changé : 113 millisecondes au lieu de 118, soit l’écart de mesure. Un index ordonne les valeurs par leur début ; une recherche qui commence par un joker ne peut pas s’en servir et le moteur relit toute la table. L’index a coûté trois dixièmes de seconde à construire et cinquante-neuf mégaoctets sur le disque pour un gain nul.
C’est le scénario que nous retrouvons dans presque tous les outils internes repris : quelqu’un a constaté que la recherche était lente, a ajouté un index, a constaté que rien n’avait changé, et en a conclu que « la base est trop grosse ». La base n’est pas trop grosse. La requête n’est pas indexable, et il faut un index d’un autre type — un index plein texte, qui range non pas les valeurs mais les mots qu’elles contiennent. Le rapport mesuré est de 748 fois.
Le comportement à l’échelle est encore plus net, parce que les deux stratégies ne grandissent pas de la même façon.
| Dossiers | LIKE '%terme%' | Index plein texte | Rapport | Taille de la base |
|---|---|---|---|---|
| 10 000 | 11,6 ms | 0,09 ms | 135× | 5,5 Mo |
| 50 000 | 51,7 ms | 0,13 ms | 398× | 28,5 Mo |
| 100 000 | 103,5 ms | 0,16 ms | 651× | 57,1 Mo |
| 250 000 | 263,0 ms | 0,17 ms | 1 540× | 136,9 Mo |
| 500 000 | 506,9 ms | 0,19 ms | 2 690× | 276,0 Mo |
La courbe rouge est une droite : chaque dossier ajouté coûte un peu plus de temps à chaque recherche. La courbe verte est plate, et elle reste plate. C’est la différence entre un outil qui tient cinq ans et un outil dont les utilisateurs disent, au bout de dix-huit mois, qu’« il s’est mis à ramer ». Il ne s’est rien mis à ramer : la table a grossi, et la stratégie de recherche était linéaire depuis le premier jour.
Deux précisions techniques, parce qu’un index plein texte n’est pas gratuit. Il faut le tenir à jour à chaque écriture, ce qui se fait par déclencheur ou dans le code applicatif — un index plein texte oublié après six mois d’écritures renvoie des résultats faux, ce qui est pire que lent. Et il travaille sur des mots : il trouve « anodisation », pas « nodisa ». Pour la recherche par fragment — un numéro de série partiel, un code article tronqué — il faut une autre technique, par exemple un index sur les trigrammes. Les deux coexistent très bien dans un même outil, à condition de savoir laquelle répond à quel champ de saisie.
Ce que ces deux mesures décident
Elles décident trois choses, et elles se décident avant la première ligne de code, parce qu’y revenir coûte une réécriture.
- La pagination et le filtrage vivent côté serveur. Le navigateur ne reçoit jamais la table entière. Cela impose que le tri, le filtre et la pagination soient des paramètres de requête, pas du code dans la page.
- Toute liste dépassant quelques centaines de lignes est fenêtrée. Pas « optimisée plus tard » : le fenêtrage change la structure du composant, et le greffer après coup sur une table triable et éditable est un chantier.
- La recherche a son propre index, plein texte, tenu à jour par déclencheur. Et le champ de recherche est unique : un formulaire avec sept filtres déroulants est le symptôme d’une recherche qui ne marche pas.
Les droits : le sujet qui coûte le plus cher quand il arrive en dernier
Les deux mesures précédentes sont des problèmes de performance, donc réparables. Les droits sont un problème de modèle, donc non réparables sans reprendre les données. C’est le vrai risque d’un projet d’intranet, et il se joue dans une seule question : la règle porte-t-elle sur les écrans ou sur les lignes ?
| Droits par écran | Droits par ligne | |
|---|---|---|
| La règle dit | « les commerciaux voient le module Devis » | « chacun voit les devis de son secteur » |
| Mise en œuvre | un test à l’affichage du menu | une condition dans chaque requête, et dans chaque export |
| Coût initial | quelques heures | quelques jours |
| Coût de la migration après coup | — | une reprise de tout l’accès aux données |
| Ce qui fuit si on se trompe | un menu visible de trop | les salaires, les marges, les dossiers d’un autre client |
La quasi-totalité des outils internes que l’on nous demande de reprendre ont été construits sur le premier modèle, puis on a voulu le second. Les symptômes sont toujours les mêmes : un export qui sort tout, une recherche qui remonte des lignes qu’on ne devrait pas voir, une URL de fiche qui s’ouvre si on connaît le numéro. Ce dernier point mérite un test, sur votre outil, aujourd’hui : ouvrez une fiche dont vous n’avez pas le droit, en changeant l’identifiant dans l’adresse. Si elle s’affiche, la règle est sur le menu, pas sur la donnée.
Trois décisions se prennent en même temps que celle-là, et il vaut mieux qu’elles soient écrites avant le développement :
- L’authentification : annuaire d’entreprise et connexion unique, ou comptes propres à l’outil. La connexion unique branchée après coup oblige à réconcilier des identités, ce qui n’est jamais propre.
- La journalisation : qui a vu quoi, qui a modifié quoi, et pendant combien de temps on garde la trace. C’est aussi ce qui permet de répondre à un salarié qui demande l’accès à ses données.
- La sortie : ce que devient un compte quand la personne quitte l’entreprise, et ce qu’il advient de ce qu’elle a créé.
Construire, acheter, ou assembler
La question n’est pas idéologique. Elle se tranche sur un critère unique : vos règles de gestion sont-elles particulières ou standard ?
| Voie | Quand elle est le bon choix | Ce qu’elle coûte vraiment |
|---|---|---|
| Un logiciel du marché en abonnement | vos règles sont celles de tout le monde : congés, notes de frais, base documentaire | l’abonnement par personne, qui devient le poste dominant au-delà de quelques dizaines d’utilisateurs |
| Un assemblage d’outils existants | chaque besoin est standard, mais les outils doivent se parler | les connecteurs, et le jour où l’un des éditeurs change son interface |
| Un développement sur mesure | une règle de gestion vous est propre, et c’est elle qui fait votre métier | le développement initial, puis la maintenance — qui n’est pas optionnelle |
La règle pratique que nous appliquons : on n’écrit pas sur mesure ce qui existe en standard. Un module de congés sur mesure est presque toujours une erreur. En revanche, un outil qui porte la façon particulière dont vous calculez une marge, séquencez un atelier ou qualifiez un dossier ne se trouve pas au catalogue, et un logiciel du marché vous obligera à changer votre métier pour entrer dans son modèle. C’est ce que nous traitons en intranet sur mesure : la partie qui ne se substitue pas.
Le cas fréquent est mixte, et c’est très bien : un logiciel du marché pour les congés et les notes de frais, un outil sur mesure pour le cœur métier, et un point de vérité unique pour les personnes et les droits. La difficulté se déplace alors sur l’interfaçage, qui est un sujet à part entière — le même que celui de connecter un site et un ERP.
L’arithmétique du temps gagné, et sa limite
Le budget d’un outil interne se justifie mal par des arguments qualitatifs. Il se justifie très bien par une multiplication, à condition de ne pas la truquer. Sur deux cent vingt jours ouvrés, une seconde gagnée par consultation donne :
| Effectif | 4 consultations/jour | 8 consultations/jour | 20 consultations/jour |
|---|---|---|---|
| 10 personnes | 2,4 h | 4,9 h | 12,2 h |
| 40 personnes | 9,8 h | 19,6 h | 48,9 h |
| 120 personnes | 29,3 h | 58,7 h | 146,7 h |
Appliquons-le au banc de cet article. Une liste qui passe de douze secondes à trois — ce qui correspond exactement à l’écart entre le montage B sur un portable chargé et le fenêtrage avec pagination — rend neuf secondes par consultation. Pour quarante personnes qui l’ouvrent huit fois par jour : 176 heures par an, soit un neuvième d’équivalent temps plein. Pour cent vingt personnes : 528 heures.
Maintenant la limite, qui est aussi importante que le calcul. Ces heures ne sont récupérables que si l’attente était réellement subie. Trois conditions, et si l’une manque le calcul est faux :
- La tâche était déjà faite. Si l’outil crée un usage nouveau, il n’y a pas de temps gagné, il y a un temps consommé — ce qui peut être très rentable par ailleurs, mais ne se compte pas ainsi.
- L’attente n’était pas mise à profit. Un salarié qui lance une recherche et répond au téléphone pendant les huit secondes ne rend pas ces huit secondes.
- Le gain est perceptible. Deux dixièmes de seconde gagnés sur la même consultation, pour quarante personnes, donnent quatre heures par an : rigoureusement vrai, et sans aucun effet sur l’adoption. Le seuil qui change le comportement se situe autour de la seconde ; en dessous, on optimise pour le tableau de bord.
Ce qui fait échouer un projet d’intranet
Aucun des échecs que nous reprenons n’est technique. Ils sont tous organisationnels, et ils sont prévisibles.
- Personne ne le possède. Un outil interne sans un responsable nommé, qui arbitre les demandes et décide de ce qui n’entre pas, dérive en six mois vers un catalogue de cas particuliers.
- Les données d’avant n’ont pas de plan de reprise. Les cinq tableurs partagés, les huit ans d’historique, les fichiers qui portent la vérité. Ce chantier prend souvent plus de temps que le développement, et il est systématiquement sous-estimé.
- La recherche a été traitée en dernier. C’est la première mesure de cet article : elle décide de l’adoption, et elle impose une décision d’architecture.
- Le mobile a été oublié. Un atelier, un chantier, une tournée : la moitié des consultations se font sur un téléphone, debout, avec une main. Un tableau de six colonnes n’y survit pas, et le dire au moment de la recette est trop tard.
- La connexion unique est arrivée après. Voir plus haut : on ne réconcilie pas proprement des identités créées séparément.
Comment nous le posons
L’ordre que nous suivons est directement dérivé de ce qui précède, et il est fait pour que les décisions irréversibles soient prises en premier.
- Les droits sur les lignes — la matrice « qui voit quoi » écrite, validée, avant le modèle de données.
- L’authentification — annuaire d’entreprise et connexion unique branchés dès la première version.
- La recherche — un index plein texte, un champ unique, tenu à jour par déclencheur.
- Une liste, fenêtrée et paginée côté serveur — le gabarit qui servira à toutes les autres.
- Le premier module métier — celui qui porte la règle qui vous est propre, pas celui qui est le plus facile.
- La reprise des données — en parallèle, avec un rejeu possible, parce qu’elle ne marche jamais du premier coup.
- Le reste — dans l’ordre des demandes réelles, pas du cahier des charges initial.
Le périmètre de la première version est le seul vrai levier de délai : un module, une liste, une recherche, des droits justes. Les projets qui sortent en trois mois sortent avec cela, et les suivants viennent parce que l’outil est utilisé. Les projets qui visent sept modules à la fois sortent en dix-huit mois, et sortent sur un besoin qui a bougé. C’est le même raisonnement que celui du cahier des charges d’un site web : écrire ce qui est décidé, pas ce qui est souhaité.
Ce qu’il faut retenir
Un intranet se joue sur quatre primitives, et deux d’entre elles se mesurent avant de coder. Cinq mille lignes envoyées dans le HTML retardent le premier affichage à 1 668 millisecondes ; construites par un script, elles bloquent le fil d’exécution pendant 2 478 millisecondes en donnant l’illusion d’une page rapide ; fenêtrées, elles laissent deux cent dix-neuf éléments dans le document, aucune tâche longue et aucune image en retard. Le poids transféré, lui, est le même dans les trois cas.
Une recherche par LIKE '%terme%' coûte 118 millisecondes sur cent mille dossiers et 507 sur cinq cent mille : elle croît avec vos données. Un index sur la colonne ne change rien — 113 millisecondes, pour cinquante-neuf mégaoctets de disque. Un index plein texte répond en deux dixièmes de milliseconde et ne bouge plus quand la base grossit.
Le reste — les droits, l’authentification, la reprise des données — n’est pas une question de performance mais de modèle, et se décide avant le premier écran. C’est la seule partie du projet où revenir en arrière coûte une réécriture.


