ADR-011 — Infrastructure de documentation : site arc42 MkDocs sur docs.alpimonitor.fr¶
Date : 2026-04-24 Statut : Acceptée — implémentée Implémentation :
c11d413Phase 1+2 — setup MkDocs Material + squelette arc42 10 sections + migration de 30 fichiers legacy (docs/context/,docs/product/,docs/architecture/,docs/refactor/,docs/runbooks/,docs/ui/,docs/workflow/,docs/STATUS.md) vers la nouvelle structure, archivage sousdocs/_legacy/viagit mv,b6f3881Phase 3 — 4 diagrammes C4 depuisworkspace.dslStructurizr, rendus en SVG viaplantuml/plantumllocal (fallback après écheckroki.iosur la stdlib C4 PlantUML), insertion inline dans §3 / §5,496afbcPhase 4 —apps/docs/Dockerfilemulti-stage (python build → nginx runtime) +apps/docs/nginx-docs.conf+apps/docs/nginx-docs-security-headers.conf, déploiement Coolify surdocs.alpimonitor.fr,67eab2bPhase 4.5 —markdownlint-cli2+ config.markdownlint-cli2.jsonc+ scriptspnpm docs:lint{,:fix}, autofix + 8MD040(fences sans langage) remédiés manuellement,12a3966Phase 4.5 bis — pinpymdown-extensions==10.21.2(bug parser runtime avecPython-Markdown 3.10+, les fencestext/bash/jsonrendaient en<p><code>inline au lieu de<pre><code>bloc).
Contexte¶
À l'issue des deux livraisons majeures v1.0.0-crealp (2026-04-22) et v1.1.0-refactor (2026-04-23), le repo accumulait 30 fichiers documentaires dispersés dans 7 dossiers thématiques (docs/context/, docs/product/, docs/architecture/, docs/refactor/, docs/runbooks/, docs/ui/, docs/workflow/) plus un STATUS.md racine. Chaque dossier suivait ses propres conventions de nommage et de structure ; aucun sommaire ne les articulait ; seul le README racine (125 lignes) faisait office de table des matières — insuffisant pour qu'un relecteur senior se forme une opinion architecturale en moins de 5 min de navigation.
L'enjeu est double :
- Signal entretien — la surface documentaire existe mais elle est effectivement invisible. Un recruteur CREALP qui ouvre le repo GitHub voit le README, clique éventuellement deux liens, et se forme un jugement. Pour un poste Front-End visé avec une composante architecturale forte, donner accès à une doc structurée — reconnue mondialement via le format arc42 — change l'impression donnée.
- Maintenabilité — un contributeur futur qui reprend le projet doit retrouver rapidement où une décision a été tracée. 30 fichiers dans 7 dossiers sans hiérarchie explicite est non-scalable ; arc42 fournit ce cadre, normalisé et durable.
L'idée d'un site de doc autonome docs.alpimonitor.fr — 4ème sous-domaine après alpimonitor.fr, api.alpimonitor.fr, storybook.alpimonitor.fr — s'inscrit dans la cohérence self-hosting du projet (ADR-003, ADR-009). Pas d'hébergement externe, pas de GitHub Pages, pas de Vercel. Le même VPS Hetzner, la même instance Coolify, le même Traefik, les mêmes patterns Dockerfile + nginx que les 3 autres services.
Décision¶
Cadre : arc42 adapté en 10 sections¶
Le format arc42 canonique comporte 12 sections. AlpiMonitor adopte une variante adaptée à 10 sections :
- §2 fusionne arc42 §2 Constraints + §10 Quality Requirements — à notre échelle, distinguer contraintes et exigences qualité produit deux pages anémiques. Une seule section cadre les deux.
- §10 fusionne arc42 §11 Risks + §12 Glossary — mêmes raisons, plus le glossaire (36 termes) est un outil de référence qui gagne à vivre à côté du risque.
Les 8 autres sections (Introduction and Goals, Context, Solution Strategy, Building Block View, Runtime View, Deployment View, Cross-cutting Concepts, Architectural Decisions) sont conservées telles que le template les définit. La numérotation 01-... à 10-... rend l'ordre évident au filesystem et à la navigation.
Stack technique retenue¶
- MkDocs 1.6 + mkdocs-material 9.5 — mature, adopté par des projets TypeScript professionnels (Vue, Vite, Pinia docs), rendu propre, plugins riches (search, nav tabs sticky, dark/light toggle, code copy, edit on GitHub).
pymdownx.superfences+pymdownx.highlight— gestion des fences de code et coloration syntaxique. Requiertpymdown-extensions ≥ 10.21pour compatibilitéPython-Markdown 3.10+(leçon apprise à la dure, cf. §Conséquences.Process learnings).- Diagrammes en 3 formalismes, chacun pour son usage optimal :
- Mermaid inline (via
mkdocs-mermaid2-plugin1.2) — flowcharts, sequence diagrams, deployment topologies — pour les schémas denses qui bénéficient d'être édités directement dans le.md. - Structurizr DSL (
docs/assets/structurizr/workspace.dsl) — les 4 vues C4 canoniques (Context, Containers, Components Frontend, Components Backend). Source de vérité unique pour la décomposition architecturale. - PlantUML local (
plantuml/plantumlDocker image) — rendu des C4 Structurizr en SVG, fallback après écheckroki.iosur la stdlib C4 PlantUML (HTTP 400 systématique sur<C4/C4>). - Kroki.io SaaS (
mkdocs-kroki-plugin0.9) — pour tout PlantUML additionnel qui apparaîtrait post-candidature. Non utilisé aujourd'hui (les 4 C4 passent par plantuml local), mais configuré pour usage futur sans refactor. markdownlint-cli2+.markdownlint-cli2.jsonc— gate discipline : fences avec langage explicite (MD040), blank lines autour des listes (MD032), bare URLs wrappées (MD034). Évite le bug<p><code>des fences sans langage et la dérive éditoriale progressive.- Deploy Coolify —
apps/docs/Dockerfilemulti-stage (python:3.12-alpinebuilder →nginx:1.27-alpineruntime) + vhost nginx avec 6 headers sécurité + CSP tunée pour Mermaid CDN (https://unpkg.com) et Google Fonts. Auto-deploy sur pushmainvia webhook, cohérent avec les 3 autres sous-domaines.
Règles et conventions enforced¶
- Tous les fences de code ont un langage explicite (gate
markdownlint-cli2MD040). Un fence sans langage rend différemment en<p><code>vs<pre><code>selon la version depymdown-extensions, symptôme silencieux. - Dépendances pinnées strictement dans
requirements-docs.txt. Un version range laxiste (ex:pymdown-extensions>=10) laisserait Coolify pull n'importe quelle sous-version au rebuild, risquant la régression silencieuse observée en Phase 4. - Legacy docs/ archivés sous
docs/_legacy/viagit mv— historique git préservé, exclus du build viaexclude_docs: _legacy/dansmkdocs.yml. Permet l'archéologie sans polluer le site public. - Liens ADR internes en relatif MkDocs (
./adr-XXX.md) — les liens GitHub absolus vers le code ou les assets externes restent absolus.
Arbitrages d'implémentation¶
- Fusion §2 + §10 acceptée malgré la déviation du template arc42 canonique. Raison : à l'échelle d'un MVP de 173 tests / 11 ADR, une section dédiée aux Quality Requirements répèterait le contenu des contraintes. Documenté explicitement dans
docs/index.mdet §2 intro. - kroki.io SaaS plutôt que self-host
yuzutech/kroki— dépendance externe assumée en build time uniquement. Self-host coûterait un container supplémentaire (~600 MB RAM sur VPS Hetzner 4 GB) pour un usage non-sensible (diagrammes publics, architecture déjà visible sur le site). Pivot vers self-host possible post-candidature si la souveraineté devient un enjeu. - Commit manuel des 4 SVG C4 plutôt que GitHub Action auto-render — la fréquence de changement de
workspace.dslest très faible (peut-être 1 édition tous les 3-6 mois). Auto-rendering introduit du pipeline qui peut casser plus souvent qu'il n'aide. À revoir si la surface architecturale évolue vraiment. - Documentation française 100 % — cohérent avec le public cible (recruteurs CREALP francophones) et les 10 ADR déjà rédigées en français. Traduire en anglais doublerait la maintenance pour un gain de signal nul vers ce public. Les identifiants de code restent en anglais (
Station,IngestionRun,useStationDrawer) — convention déjà documentée dans §8 Conventions. - Tous les
.mdcomment-blocks de Phase 1 (stubs<!-- TODO Phase 2 -->) ont été remplacés par du contenu rédigé ; aucun stub ne subsiste dans le site servi. Gate strictmkdocs build --strict+markdownlint-cli2refuseraient de toute façon des blocs orphelins.
Conséquences¶
Positives¶
- Signal entretien fort. Un relecteur technique qui ouvre
docs.alpimonitor.frvoit en 30 s le format arc42, les 10 sections numérotées, les diagrammes C4, les 11 ADR indexées. Pattern reconnu mondialement, vocabulaire commun à l'industrie. - Onboarding contributeur accéléré. La carte mentale arc42 (§1 goals → §3 context → §5 blocks → §7 deploy → §9 ADR → §10 risks) est immédiate. Un nouveau venu trouve la section pertinente en moins de 30 s au lieu de fouiller 7 dossiers ad-hoc.
- Traçabilité décisionnelle complète. Les 11 ADR vivent dans
docs/09-architectural-decisions/avec cross-refs internes vers les sections arc42 (§1,§3,§8,§10). Chaque décision pointe son impact. - Discipline éditoriale reproductible.
pnpm docs:lint+mkdocs build --strict+ pins stricts dansrequirements-docs.txtrendent la maintenance prévisible. Un contributeur futur ne subira pas de drift silencieux (fences cassés, liens morts, versions qui dérivent). - Cohérence self-hosting.
docs.alpimonitor.frtourne sur la même stack (Coolify + Traefik + Let's Encrypt + Hetzner) que les 3 autres sous-domaines. Aucune dette d'infra externalisée (Vercel / GitHub Pages / ReadTheDocs).
Négatives¶
- Surface de maintenance accrue. 35 fichiers
.mdà maintenir si le code évolue. Chaque ADR future implique une entrée dans09-architectural-decisions/index.md+ éventuellement un renvoi depuis une section arc42. Tenable sur un projet actif, pénalisant sur un projet abandonné (doc obsolète nuit plus qu'elle n'aide). - Coût de build CI. Le build MkDocs prend ~5 s en CI, négligeable, mais le pipeline Coolify rebuild prend ~60 s à chaque push
maintouchantdocs/. Trivial à l'échelle mensuelle, à surveiller si la fréquence de doc-only commits devient élevée. - Budget candidature consommé. Les 4 phases (environ 5-6 h cumulées sur J18 / 2026-04-24) ne livrent pas de feature applicative. ROI purement signal entretien — assumé vis-à-vis du livrable candidature.
Trade-offs assumés¶
- Dépendance externe
kroki.io(build-time uniquement). Mitigation : pivot possible versyuzutech/krokiself-hosté en un container Docker supplémentaire, à configurer post-candidature si un besoin de souveraineté ou un SLA kroki.io apparaît. Trace dansdocs/README.md. - Commit manuel des 4 SVG Structurizr. Mitigation : GitHub Action auto-render envisageable post-candidature (trigger sur changement de
workspace.dsl, commit auto des SVG). Aujourd'hui, la régénération tient en 2 commandes Docker documentées dansdocs/README.md. - Documentation française uniquement. Délibéré pour le public cible CREALP. Un relecteur international a des traductions automatiques ; un relecteur francophone lit plus naturellement dans sa langue. Le code reste en anglais — la frontière français/anglais est nette et documentée.
- Legacy
docs/_legacy/— redondance filesystem + git. Les 30 fichiers pré-arc42 restent dans l'historique git et à côté de la nouvelle structure. Permet l'archéologie (un lecteur qui suit un ancien lien GitHub absolu trouve encore le fichier), au coût d'une copie redondante. Acceptable — les fichiers ne sont pas servis par MkDocs, et leur maintenance est gelée (pas de backport de correctif). - Pas de versioning multi-version de la doc (mike / mkdocs-monorepo / autre). Une seule version servie, celle de
main. Acceptable pour un livrable unique ; à revoir siv2.0apparaît avec des différences structurantes.
Process learnings¶
Le déploiement Phase 4 a révélé un bug parser runtime silencieux qui mérite sa propre trace : pymdown-extensions 10.11.2 pinné dans requirements-docs.txt s'est révélé incompatible avec Python-Markdown 3.10.2 (transitive de mkdocs-material 9.5.44). Symptôme : tous les fences de code rendaient en <p><code> inline au lieu de <pre><code> bloc — typographiquement illisibles sur le site live.
Ni mkdocs build --strict (0 warnings), ni markdownlint-cli2 (source markdown correct), ni les smoke tests curl -sI (HTTP 200) ne pouvaient détecter le bug. Seule une vérification visuelle humaine sur le site déployé l'a fait surgir, et une répro minimale en Python-Markdown direct l'a confirmé en 30 s :
import markdown
md = markdown.Markdown(extensions=['pymdownx.superfences'])
md.convert("```text\nhi\n```")
# 10.11.2: <p><code>text\nhi</code></p> ← BUG runtime
# 10.21.2: <div class="highlight"><pre>... ← OK
Leçon retenable : un gate --strict à 0 warnings n'est pas une preuve que le rendu HTML est correct. Pour un site de doc, la seule validation certaine est le smoke visuel sur l'URL prod après déploiement. Les gates statiques (lint, build strict) capturent les erreurs structurelles ; ils ne capturent pas les régressions de rendu dues à des incompatibilités runtime entre dépendances. Applicable à tout projet qui dépend d'une chaîne de rendu Markdown → HTML — documenté en mémoire projet pour usage futur.
Alternatives écartées¶
Pas de formalisme — laisser les 30 fichiers dispersés¶
- Bénéfice : 5-6 h économisées sur J18, budget candidature libéré pour polish produit.
- Coût : surface documentaire invisible à un relecteur qui ne clique pas spontanément 7 dossiers ad-hoc. Signal entretien fortement dilué. Maintenance non-scalable si le projet continue.
- Verdict : rejeté. Le ROI signal + onboarding dépasse largement le coût des 5-6 h investies.
Sphinx + reStructuredText¶
- Bénéfice : outil académique mature, utilisé par Python core, Linux kernel, des projets scientifiques. Écosystème riche (intersphinx, directives).
- Coût : reStructuredText vs Markdown — syntaxe plus rigide, onboarding contributeur plus long. Moins adapté à un repo dev-facing TypeScript. Rendu par défaut moins moderne que MkDocs Material.
- Verdict : rejeté. MkDocs + Material correspond mieux au stack Markdown + Vue du projet et au public cible (développeurs, pas scientifiques).
Docusaurus¶
- Bénéfice : solution Meta mature, théme moderne, support versioning intégré (mike-like), blog + docs combinés.
- Coût : stack React — incohérent avec le parti-pris Vue monostack (ADR-001). Ajoute une toolchain Node.js complète côté docs (alors que MkDocs est Python pur, isolé dans un venv). Complexité disproportionnée pour un site de ~35 pages sans versioning multi-version.
- Verdict : rejeté. Incohérence stack + sur-ingénierie.
GitHub Wiki¶
- Bénéfice : zéro deploy, édition web, intégré à GitHub.
- Coût : séparation du repo — la doc vit hors du code, commits non co-localisés, pas de PR review unifiée, pas de CI vérifiant la cohérence. Versioning de la doc désynchronisé des tags du code. Dilue la traçabilité documentation ↔ code.
- Verdict : rejeté. Un refactor qui casse une hypothèse documentée dans le Wiki ne remonte pas dans la CI du repo. Docs-as-code dans le repo est la bonne discipline.
arc42 canonique 12 sections¶
- Bénéfice : conformité stricte au template de référence. Reconnaissance maximale par un lecteur qui connaît arc42 par cœur.
- Coût : §2 Constraints et §10 Quality Requirements auraient chacune ~30 lignes utiles à notre échelle. §11 Risks et §12 Glossary idem. Quatre sections anorexiques n'apportent pas plus de signal que deux sections denses.
- Verdict : rejeté. Fusion explicitée dans
docs/index.mdet §2 intro — un lecteur arc42 expérimenté reconnaît l'adaptation et y voit un choix, pas un oubli.
Self-host yuzutech/kroki plutôt que kroki.io SaaS¶
- Bénéfice : souveraineté, pas de round-trip HTTP externe au build, pas de dépendance à un service tiers.
- Coût : 1 container supplémentaire sur le VPS (~600 MB RAM consommées en permanence), configuration Coolify additionnelle, pas de bénéfice visible côté utilisateur final (le rendu est identique).
- Verdict : rejeté pour la fenêtre candidature. Diagrammes publics sans donnée sensible, build-time uniquement, alternative identifiée = pas de risque de blocage. Pivot possible post-candidature.
GitHub Action auto-render C4 SVG¶
- Bénéfice :
workspace.dsldevient seule source de vérité éditable ; les 4 SVG sont régénérés automatiquement sur changement. - Coût : pipeline à maintenir (image Structurizr + image PlantUML + orchestration GitHub Action + commit auto via bot). Fréquence de changement de
workspace.dsltrès faible (peut-être 1-2 fois/an en maintenance), ROI marginal. - Verdict : rejeté. Commit manuel documenté dans
docs/README.mdsuffit pour la fréquence observée. À revoir si la surface architecturale évolue.
Références¶
- arc42.org — cadre de doc architecturale adopté (version 10 sections adaptées)
docs/index.md— accueil du site, parcours de lecture recruteur 30 s + lecteur approfondidocs/README.md— setup local + regen C4 diagrammes (workflow 2 étapes Docker)mkdocs.yml— configuration site (theme, plugins, nav, extensions)apps/docs/Dockerfile+apps/docs/nginx-docs.conf— stack de déploiement.markdownlint-cli2.jsonc— config lint Markdowndocs/assets/structurizr/workspace.dsl— source C4 des 4 vues- ADR-003 — monolithe Fastify, parti-pris self-hosting qui cadre aussi le choix Coolify pour la doc
- ADR-009 — périmètre Storybook et déploiement sous-domaine
storybook.alpimonitor.fr, pattern comparable repris ici pourdocs.alpimonitor.fr