Ce blog que vous lisez en ce moment a été construit en une seule conversation avec Claude Code, le CLI d'Anthropic, en environ 30 minutes. Pas de nuit blanche, pas de template acheté, pas de WordPress. Une session de travail dans le terminal. Voici exactement comment ça s'est passé — le vrai code, les vraies commandes, et ce qui a failli merder.
Pourquoi un blog sur un CV ?
Mon portfolio web-developpeur.com fait son travail : présenter mon parcours et mes projets. Mais un site statique a ses limites côté référencement. Google adore le contenu frais, les pages qui répondent à des vraies questions, les sites qui évoluent.
L'objectif du blog : créer un cocon sémantique autour du portfolio.
- Des articles sur les technos que j'utilise (Golang, PHP, Vue.js, DevOps)
- Des retours d'expérience concrets sur des projets réels
- Du contenu que j'aurais envie d'écrire de toute façon
Chaque article = une vraie URL indexable, avec ses propres balises meta et Open Graph. Sans base de données, sans CMS, sans maintenance.
Pourquoi pas WordPress ?
Réflexe légitime. Mais pour ce cas d'usage, c'est un bazooka pour tuer une mouche. Mes contraintes :
- Pas de BDD à gérer sur le serveur
- Pas de plugins à mettre à jour (surface d'attaque)
- Intégration propre au site existant (Apache + PHP)
- Maintenable seul avec Claude Code en deux minutes
La solution : des fichiers PHP pour les articles, un manifeste JSON pour l'index, et un template partagé. Chaque article est une vraie page avec son URL propre, ses balises SEO, et sa coloration syntaxique. Aucune couche d'abstraction inutile.
L'architecture complète
Voici la structure finale du projet :
blog/
├── index.php # Page listing avec recherche + filtres JS
├── posts.json # Manifeste JSON : métadonnées de tous les articles
├── template.php # Fonctions blog_header() et blog_footer()
├── .htaccess # URL propres : /blog/mon-slug → posts/mon-slug.php
├── assets/
│ └── blog.css # Styles : nav, cards, article, code blocks
└── posts/
└── mon-article.php # Un fichier PHP par article
Le fichier posts.json
C'est le cœur du système. Chaque article y est référencé avec ses métadonnées.
C'est ce fichier que la page d'index charge via fetch() pour alimenter
la recherche et les filtres catégories en temps réel.
[
{
"slug": "creer-un-blog-avec-claude-code",
"title": "Créer un blog sans CMS avec Claude Code",
"date": "2026-02-22",
"category": "Retour d'expérience",
"tags": ["claude-code", "ia", "php", "no-cms", "seo"],
"excerpt": "Comment j'ai construit ce blog en 30 minutes..."
}
]
Pour ajouter un article : créer le fichier PHP + ajouter une ligne dans ce JSON. C'est tout.
Le template.php
Deux fonctions PHP qui wrappent chaque article : blog_header() génère
le <head> complet avec les balises meta, Open Graph, Twitter Card,
le chargement de Bootstrap, Font Awesome, Prism.js (CDN). blog_footer()
ferme la page et charge les scripts.
<?php
// Dans chaque article :
require_once '../template.php';
blog_header(
'Titre de l\'article',
'Description meta pour Google et Open Graph',
'https://www.web-developpeur.com/blog/mon-slug'
);
?>
<article class="blog-article">
<h1>Mon titre</h1>
<div class="article-content">
<p>Contenu...</p>
<!-- Coloration syntaxique automatique via Prism.js -->
<pre><code class="language-go">
func main() {
fmt.Println("Hello, blog!")
}
</code></pre>
</div>
</article>
<?php blog_footer([]); ?>
Le .htaccess pour les URLs propres
Sans rewriting, les URLs seraient /blog/posts/mon-slug.php. Pas terrible.
Deux règles dans le .htaccess racine suffisent :
# Dans le .htaccess racine du site
RewriteRule ^blog/?$ blog/index.php [L]
RewriteRule ^blog/([a-z0-9-]+)$ blog/posts/$1.php [L]
Résultat : /blog/ pointe sur blog/index.php,
et /blog/creer-un-blog-avec-claude-code pointe sur
blog/posts/creer-un-blog-avec-claude-code.php.
La recherche côté client
Pas de requête serveur pour la recherche. La page d'index charge posts.json
une seule fois, et le filtrage se fait en JS pur :
fetch('posts.json')
.then(res => res.json())
.then(posts => {
// Trier par date décroissante
allPosts = posts.sort((a, b) => new Date(b.date) - new Date(a.date));
render(allPosts);
});
function filterPosts() {
return allPosts.filter(post => {
const matchCat = activeCategory === 'Tous' || post.category === activeCategory;
if (!matchCat) return false;
if (!searchTerm) return true;
const term = searchTerm.toLowerCase();
return post.title.toLowerCase().includes(term)
|| post.excerpt.toLowerCase().includes(term)
|| post.tags.some(tag => tag.toLowerCase().includes(term));
});
}
Claude Code : comment ça marche concrètement
Claude Code est un CLI qui tourne dans le terminal. Il a accès à votre système de fichiers, peut lire, écrire, modifier des fichiers, et exécuter des commandes bash. Ce n'est pas un chat où vous copiez-collez du code : c'est un agent qui travaille directement sur le codebase, comme un développeur qui aurait accès à votre machine.
Pour l'installer :
npm install -g @anthropic-ai/claude-code
claude # Lance le CLI dans le terminal
Mon workflow : tech lead + agent
Je joue le rôle de tech lead. Je décris l'architecture et prends les décisions. Claude exécute. Pour les tâches lourdes (écriture de plusieurs fichiers en parallèle), le modèle Opus délègue à des agents Sonnet qui travaillent simultanément.
Pour ce blog, j'ai commencé par décrire l'architecture complète dans un message : quels fichiers créer, le rôle de chacun, les contraintes de design (mêmes couleurs que le CV, Bootstrap 3, Prism.js pour la coloration), les fichiers existants à modifier. Claude a rédigé un plan détaillé, je l'ai validé, et il a tout créé en une passe.
Durée totale : ~30 minutes. Répartition :
- ~10 min — décrire le plan + validation
- ~10 min — création de tous les fichiers (agents Sonnet en parallèle)
- ~10 min — corrections design (voir section suivante)
Pour tester pendant le développement, j'utilisais le serveur PHP intégré :
# À la racine du projet CV
php -S localhost:8000
# Puis dans le navigateur :
# http://localhost:8000/blog/
# http://localhost:8000/blog/posts/creer-un-blog-avec-claude-code.php
Note : les URLs propres (
/blog/mon-slugsans.php) ne fonctionnent qu'avec Apache et mod_rewrite actif. En local avec le serveur PHP intégré, il faut passer un fichier router qui simule les rewrites :php -S localhost:8000 router.php.
Ce qui a failli merder : classes CSS désynchronisées
Le premier rendu visuel était cassé. Navigation en liste à puces, boutons sans style, tags collés. En inspectant le source, le problème était évident :
/* CSS généré — sélecteurs qui ne matchent rien */
.blog-listing .post-card { ... }
.blog-nav .navbar-brand { ... }
<!-- HTML généré en parallèle — classes différentes -->
<article class="blog-card">...</article>
<a class="blog-nav-brand">Odilon Hugonnot</a>
Deux agents Sonnet avaient écrit le CSS et le HTML en parallèle. Résultat : des noms de classes qui divergent silencieusement. Pas d'erreur PHP, pas d'erreur JS — juste du CSS qui ne s'applique nulle part.
La correction a illustré quelque chose d'important : avec un outil IA, le feedback visuel est plus efficace que la description. J'ai partagé une capture d'écran. Claude a comparé les classes CSS aux classes HTML et réécrit le CSS pour correspondre. Deux minutes. Sans la screenshot, on aurait pu tourner en rond longtemps.
Conclusion
Seul, sans outil IA, ce blog m'aurait pris une demi-journée. Pas parce que c'est complexe, mais à cause des frictions habituelles : retrouver les bons fichiers, écrire le CSS depuis zéro, tester les rewrites Apache, déboguer les includes PHP.
Avec Claude Code : 30 minutes.
Ce n'est pas de la magie. Ça demande de savoir précisément ce qu'on veut, de le décrire techniquement, et de relire ce qui est produit. Mais pour un développeur senior qui connaît son stack, c'est un vrai multiplicateur de productivité.
Le blog est là. Il va accueillir des retours d'expérience réels sur des projets concrets. C'est exactement ce pour quoi il a été conçu.