<?xml version="1.0"?>
<rss version="2.0"><channel><title>Plan&#xE8;te PHP</title><description>Agr&#xE9;gateur de flux RSS sur le PHP francophone</description><link>http://www.planete-php.fr/rss.php</link><language>fr-fr</language><generator>AFUP</generator><managingEditor>planetephpfr@afup.org</managingEditor><item><title>Comment utiliser les attributs PHP sur un contr&#xF4;leur Symfony ?</title><link>https://jolicode.com/blog/comment-utiliser-les-attributs-php-sur-un-controleur-symfony</link><author>JoliCode Team</author><date>Wed, 10 Jun 2026 14:42:00 +0200</date><description><![CDATA[<p>PHP 8.0 a introduit les attributs, et c'est une excellente fonctionnalité ! Le code devient plus lisible, plus simple à écrire, et bénéficie pleinement de la coloration syntaxique et du <em>linting</em> de notre IDE.</p>
<p>Symfony a très vite adopté cette nouveauté. On les utilise aujourd'hui partout : pour configurer les routes, ajouter des contraintes de validation ou déclarer des <em>listeners</em>.</p>
<p>Dans cet article, nous allons voir <strong>comment créer et ajouter un attribut PHP personnalisé</strong> sur un contrôleur pour lui injecter un comportement automatique, comme du logging.</p>

<div class="c-alert c-alert--note">
    <p class="c-alert__title">
                    <span class="c-icon c-icon--monospace">
                <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="c-icon__svg" focusable="false" viewBox="0 0 70 71"><path fill-rule="nonzero" d="M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9"/></svg>
            </span>
                        <strong>Info</strong>
    </p>
    <div class="c-alert__content">
                <p>
Nous avions déjà parlé des attributs <a href="https://jolicode.com/blog/rate-limit-your-symfony-apis">en 2021 pour limiter le débit de vos API (Rate Limit)</a>. Cependant, Symfony a bien évolué depuis, et leur utilisation est devenue encore plus simple.</p>
        </div>
</div>

<h2>1. Créer un attribut</h2>
<p>La première étape consiste à créer une classe PHP classique. Pour indiquer qu'elle servira d'attribut, on lui ajoute elle-même l'attribut natif <code>#[\Attribute]</code> :</p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-4">namespace</span><span> </span><span class="syntax-6">App\AuditLog</span><span class="syntax-2">;</span></span>
<span class="line"></span>
<span class="line"><span class="syntax-4">use</span><span class="syntax-2"> Psr\Log\</span><span class="syntax-5">LogLevel</span><span class="syntax-2">;</span></span>
<span class="line"></span>
<span class="line"><span class="syntax-2">#[\Attribute(</span><span class="syntax-5">\Attribute</span><span class="syntax-4">::</span><span class="syntax-3">TARGET_METHOD</span><span class="syntax-4"> |</span><span class="syntax-5"> \Attribute</span><span class="syntax-4">::</span><span class="syntax-3">TARGET_CLASS</span><span class="syntax-2">)]</span></span>
<span class="line"><span class="syntax-5">class</span><span> </span><span class="syntax-6">Loggable</span></span>
<span class="line"><span class="syntax-2">{</span></span>
<span class="line"><span class="syntax-4">    public</span><span class="syntax-5"> function</span><span class="syntax-9"> __construct</span><span class="syntax-2">(</span></span>
<span class="line"><span class="syntax-4">        public</span><span class="syntax-4"> string</span><span class="syntax-2"> $level </span><span class="syntax-4">=</span><span class="syntax-5"> LogLevel</span><span class="syntax-4">::</span><span class="syntax-3">INFO</span><span class="syntax-2">,</span></span>
<span class="line"><span class="syntax-2">    ) {</span></span>
<span class="line"><span class="syntax-2">    }</span></span>
<span class="line"><span class="syntax-2">}</span></span></code></pre>
<p>Les paramètres du constructeur sont optionnels. Ils permettent de passer des options au moment où on utilise l'attribut. Ici, notre propriété <code>$level</code> sert à définir le niveau de log souhaité.</p>
<h2>2. Utiliser l'attribut sur un contrôleur</h2>
<p>Une fois notre classe créée, nous pouvons l'appliquer directement sur un contrôleur, soit sur la classe entière, soit sur une méthode (action) spécifique :</p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-4">namespace</span><span> </span><span class="syntax-6">App\Controller</span><span class="syntax-2">;</span></span>
<span class="line"></span>
<span class="line"><span class="syntax-4">use</span><span class="syntax-2"> App\AuditLog\</span><span class="syntax-5">Loggable</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-4">use</span><span class="syntax-2"> Psr\Log\</span><span class="syntax-5">LogLevel</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-4">use</span><span class="syntax-2"> Symfony\Bridge\Twig\Attribute\</span><span class="syntax-5">Template</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-4">use</span><span class="syntax-2"> Symfony\Bundle\FrameworkBundle\Controller\</span><span class="syntax-5">AbstractController</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-4">use</span><span class="syntax-2"> Symfony\Component\Routing\Attribute\</span><span class="syntax-5">Route</span><span class="syntax-2">;</span></span>
<span class="line"></span>
<span class="line"><span class="syntax-2">#[Loggable()]</span></span>
<span class="line"><span class="syntax-4">final</span><span class="syntax-5"> class</span><span> </span><span class="syntax-6">HomepageController</span><span class="syntax-4"> extends</span><span> </span><span class="syntax-7">AbstractController</span></span>
<span class="line"><span class="syntax-2">{</span></span>
<span class="line"><span class="syntax-2">    #[Loggable(</span><span class="syntax-5">LogLevel</span><span class="syntax-4">::</span><span class="syntax-3">CRITICAL</span><span class="syntax-2">)]</span></span>
<span class="line"><span class="syntax-2">    #[Route(</span><span class="syntax-1">'/'</span><span class="syntax-2">, name: </span><span class="syntax-1">'app_homepage'</span><span class="syntax-2">)]</span></span>
<span class="line"><span class="syntax-2">    #[Template(</span><span class="syntax-1">'homepage/index.html.twig'</span><span class="syntax-2">)]</span></span>
<span class="line"><span class="syntax-4">    public</span><span class="syntax-5"> function</span><span class="syntax-8"> index</span><span class="syntax-2">()</span><span class="syntax-4">:</span><span class="syntax-4"> void</span></span>
<span class="line"><span class="syntax-2">    {</span></span>
<span class="line"><span class="syntax-2">    }</span></span>
<span class="line"><span class="syntax-2">}</span></span></code></pre>
<p>Ici, la méthode <code>index</code> hérite de la configuration par défaut de la classe, et ajoute un nouveau niveau de log plus critique (<code>CRITICAL</code>).</p>
<h2>3. Créer le listener pour activer le comportement</h2>
<p>Pour que notre attribut serve à quelque chose, il faut intercepter l'appel du contrôleur. Symfony déclenche l'événement <code>ControllerEvent</code> juste avant d'exécuter l'action d'un contrôleur. C'est le moment idéal pour vérifier la présence de notre attribut.</p>
<p>Selon votre version de Symfony, l'implémentation est devenue de plus en plus simple.</p>
<h3>Avec Symfony 6.2+</h3>
<p>On utilise la méthode <code>getAttributes()</code> de l'événement pour récupérer notre attribut et appliquer notre logique :</p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-4">namespace</span><span> </span><span class="syntax-6">App\AuditLog</span><span class="syntax-2">;</span></span>
<span class="line"></span>
<span class="line"><span class="syntax-4">use</span><span class="syntax-2"> Psr\Log\</span><span class="syntax-5">LoggerInterface</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-4">use</span><span class="syntax-2"> Psr\Log\</span><span class="syntax-5">NullLogger</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-4">use</span><span class="syntax-2"> Symfony\Component\EventDispatcher\Attribute\</span><span class="syntax-5">AsEventListener</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-4">use</span><span class="syntax-2"> Symfony\Component\HttpKernel\Event\</span><span class="syntax-5">ControllerEvent</span><span class="syntax-2">;</span></span>
<span class="line"></span>
<span class="line"><span class="syntax-5">class</span><span> </span><span class="syntax-6">LoggerListener</span></span>
<span class="line"><span class="syntax-2">{</span></span>
<span class="line"><span class="syntax-4">    public</span><span class="syntax-5"> function</span><span class="syntax-9"> __construct</span><span class="syntax-2">(</span></span>
<span class="line"><span class="syntax-4">        private</span><span class="syntax-4"> readonly</span><span class="syntax-5"> LoggerInterface</span><span class="syntax-2"> $logger </span><span class="syntax-4">=</span><span class="syntax-4"> new</span><span class="syntax-5"> NullLogger</span><span class="syntax-2">(),</span></span>
<span class="line"><span class="syntax-2">    ) {</span></span>
<span class="line"><span class="syntax-2">    }</span></span>
<span class="line"></span>
<span class="line"><span class="syntax-2">    #[AsEventListener(priority: </span><span class="syntax-4">-</span><span class="syntax-3">100</span><span class="syntax-2">)]</span></span>
<span class="line"><span class="syntax-4">    public</span><span class="syntax-5"> function</span><span class="syntax-8"> logSymfony62</span><span class="syntax-2">(</span><span class="syntax-5">ControllerEvent</span><span class="syntax-2"> $event)</span><span class="syntax-4">:</span><span class="syntax-4"> void</span></span>
<span class="line"><span class="syntax-2">    {</span></span>
<span class="line"><span class="syntax-4">        foreach</span><span class="syntax-2"> ($event</span><span class="syntax-4">-></span><span class="syntax-8">getAttributes</span><span class="syntax-2">()[</span><span class="syntax-5">Loggable</span><span class="syntax-4">::class</span><span class="syntax-2">] </span><span class="syntax-4">??</span><span class="syntax-2"> [] </span><span class="syntax-4">as</span><span class="syntax-2"> $attribute) {</span></span>
<span class="line"><span class="syntax-11">            $this</span><span class="syntax-4">-></span><span class="syntax-2">logger</span><span class="syntax-4">-></span><span class="syntax-8">log</span><span class="syntax-2">($attribute</span><span class="syntax-4">-></span><span class="syntax-2">level, </span><span class="syntax-1">'Controller is loggable'</span><span class="syntax-2">, [</span></span>
<span class="line"><span class="syntax-1">                'controller'</span><span class="syntax-4"> =></span><span class="syntax-2"> $event</span><span class="syntax-4">-></span><span class="syntax-8">getController</span><span class="syntax-2">(),</span></span>
<span class="line"><span class="syntax-2">            ]);</span></span>
<span class="line"><span class="syntax-2">        }</span></span>
<span class="line"><span class="syntax-2">    }</span></span>
<span class="line"><span class="syntax-2">}</span></span></code></pre>
<h3>Avec Symfony 8.1+</h3>
<p>Symfony 8.1 a introduit des événements spécifiques aux attributs. Plus besoin de boucler manuellement, l'événement contient directement l'attribut ciblé :</p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-4">use</span><span class="syntax-2"> Symfony\Component\HttpKernel\Event\</span><span class="syntax-5">ControllerAttributeEvent</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-4">use</span><span class="syntax-2"> Symfony\Component\HttpKernel\</span><span class="syntax-5">KernelEvents</span><span class="syntax-2">;</span></span>
<span class="line"></span>
<span class="line"><span class="syntax-5">class</span><span> </span><span class="syntax-6">LoggerListener</span></span>
<span class="line"><span class="syntax-2">{</span></span>
<span class="line"><span class="syntax-2">    #[AsEventListener(</span><span class="syntax-5">KernelEvents</span><span class="syntax-4">::</span><span class="syntax-3">CONTROLLER</span><span class="syntax-4"> .</span><span class="syntax-1"> '.'</span><span class="syntax-4"> .</span><span class="syntax-5"> Loggable</span><span class="syntax-4">::class</span><span class="syntax-2">, priority: </span><span class="syntax-4">-</span><span class="syntax-3">100</span><span class="syntax-2">)]</span></span>
<span class="line"><span class="syntax-4">    public</span><span class="syntax-5"> function</span><span class="syntax-8"> logSymfony81</span><span class="syntax-2">(</span><span class="syntax-5">ControllerAttributeEvent</span><span class="syntax-2"> $event)</span><span class="syntax-4">:</span><span class="syntax-4"> void</span></span>
<span class="line"><span class="syntax-2">    {</span></span>
<span class="line"><span class="syntax-11">        $this</span><span class="syntax-4">-></span><span class="syntax-2">logger</span><span class="syntax-4">-></span><span class="syntax-8">log</span><span class="syntax-2">($event</span><span class="syntax-4">-></span><span class="syntax-2">attribute</span><span class="syntax-4">-></span><span class="syntax-2">level, </span><span class="syntax-1">'Controller is loggable'</span><span class="syntax-2">, [</span></span>
<span class="line"><span class="syntax-1">            'controller'</span><span class="syntax-4"> =></span><span class="syntax-2"> $event</span><span class="syntax-4">-></span><span class="syntax-2">kernelEvent</span><span class="syntax-4">-></span><span class="syntax-8">getController</span><span class="syntax-2">(),</span></span>
<span class="line"><span class="syntax-2">        ]);</span></span>
<span class="line"><span class="syntax-2">    }</span></span>
<span class="line"><span class="syntax-2">}</span></span></code></pre>
<h3>Avec Symfony 8.2+</h3>
<p>La version 8.2 simplifira encore la syntaxe grâce à un attribut dédié (<code>AsControllerAttributeListener</code>) qui cible directement notre classe :</p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-4">use</span><span class="syntax-2"> Symfony\Component\HttpKernel\Attribute\</span><span class="syntax-5">AsControllerAttributeListener</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-4">use</span><span class="syntax-2"> Symfony\Component\HttpKernel\Event\</span><span class="syntax-5">ControllerAttributeEvent</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-4">use</span><span class="syntax-2"> Symfony\Component\HttpKernel\Event\</span><span class="syntax-5">ControllerEvent</span><span class="syntax-2">;</span></span>
<span class="line"></span>
<span class="line"><span class="syntax-5">class</span><span> </span><span class="syntax-6">LoggerListener</span></span>
<span class="line"><span class="syntax-2">{</span></span>
<span class="line"><span class="syntax-2">    #[AsControllerAttributeListener(</span><span class="syntax-5">ControllerEvent</span><span class="syntax-4">::class</span><span class="syntax-2">, </span><span class="syntax-5">Loggable</span><span class="syntax-4">::class</span><span class="syntax-2">)]</span></span>
<span class="line"><span class="syntax-4">    public</span><span class="syntax-5"> function</span><span class="syntax-8"> logSymfony82</span><span class="syntax-2">(</span><span class="syntax-5">ControllerAttributeEvent</span><span class="syntax-2"> $event)</span><span class="syntax-4">:</span><span class="syntax-4"> void</span></span>
<span class="line"><span class="syntax-2">    {</span></span>
<span class="line"><span class="syntax-11">        $this</span><span class="syntax-4">-></span><span class="syntax-2">logger</span><span class="syntax-4">-></span><span class="syntax-8">log</span><span class="syntax-2">($event</span><span class="syntax-4">-></span><span class="syntax-2">attribute</span><span class="syntax-4">-></span><span class="syntax-2">level, </span><span class="syntax-1">'Controller is loggable'</span><span class="syntax-2">, [</span></span>
<span class="line"><span class="syntax-1">            'controller'</span><span class="syntax-4"> =></span><span class="syntax-2"> $event</span><span class="syntax-4">-></span><span class="syntax-2">kernelEvent</span><span class="syntax-4">-></span><span class="syntax-8">getController</span><span class="syntax-2">(),</span></span>
<span class="line"><span class="syntax-2">        ]);</span></span>
<span class="line"><span class="syntax-2">    }</span></span>
<span class="line"><span class="syntax-2">}</span></span></code></pre>
<h2>Conclusion</h2>
<p>Les attributs apportent une grande souplesse à l'écosystème PHP, et Symfony propose des outils parfaits pour les exploiter.</p>
<p>Grâce à eux, vous pouvez ajouter des comportements à vos contrôleurs de manière propre et déclarative, sans polluer vos méthodes avec du code répétitif.</p>
<p>Bien sûr, Symfony intègre déjà nativement des attributs puissants pour gérer la sécurité (<code>#[IsGranted]</code>), le cache (<code>#[Cache]</code>), ou encore le rate limiting (<code>#[RateLimit]</code>). Mais créer vos propres attributs ouvre la porte à des cas d'usage métiers très intéressants :</p>
<ul>
<li><strong>Le Feature Flipping (<code>#[FeatureFlag('new-dashboard')]</code>)</strong> : pour activer ou désactiver l'accès à une route selon le déploiement progressif d'une fonctionnalité ;</li>
<li><strong>La télémétrie et l'observabilité (<code>#[TrackActivity('checkout')]</code>)</strong> : pour envoyer des statistiques précises à des outils tiers comme OpenTelemetry ou Plausible dès qu'un utilisateur visite une page clé ;</li>
<li><strong>La transformation de réponse (<code>#[Serialize]</code>)</strong> : pour intercepter le retour de vos contrôleurs et le formater automatiquement selon un standard précis (comme le format JSON d'une API spécifique).</li>
</ul>
<p>Les possibilités n'ont de limite que votre imagination !</p>]]></description></item><item><title>Am&#xE9;liorer la maintenance de vos workflows GitHub</title><link>https://jolicode.com/blog/ameliorer-la-maintenance-de-vos-workflows-github</link><author>JoliCode Team</author><date>Tue, 09 Jun 2026 11:42:00 +0200</date><description><![CDATA[<p>Les failles de sécurité liées à la <em>supply chain</em> sont de plus en plus courantes. En tant que mainteneur d'un projet open source populaire, l'enjeu est de taille. Votre code est déployé sur de nombreux serveurs de production et postes de développeurs. Il est donc crucial de garder vos pipelines CI à jour et sécurisés.</p>
<p>Le projet open source qui nous importe aujourd'hui est Castor 🦫, que nous vous invitons <a href="https://jolicode.com/blog/le-task-runner-castor-est-maintenant-disponible-en-version-1">à découvrir</a>.</p>
<p>Pour nous aider dans cette tâche, nous avons choisi <a rel="nofollow noopener noreferrer" href="https://github.com/zizmorcore/zizmor">zizmor</a>. C'est un outil d'analyse statique de code. Il détecte les vulnérabilités dans vos fichiers de configuration de CI et propose souvent des correctifs. Pour faire simple, c'est l'équivalent de PHPStan, mais pour vos workflows.</p>
<p>Une fois installé, une simple exécution (<code>zizmor .</code>) permet de détecter les erreurs de configuration et de les corriger rapidement. Nous avons ainsi amélioré notre sécurité et facilité la maintenance à long terme.</p>
<p>Voici un exemple de rapport généré par l'outil :</p>
<pre><code>error[unpinned-uses]: unpinned action reference
  --&gt; ./.github/actions/cache/action.yaml:48:13
   |
48 |       uses: actions/cache@v5
   |             ^^^^^^^^^^^^^^^^ action is not pinned to a hash (required by blanket policy)
   |
   = note: audit confidence → High
   = note: this finding has an auto-fix
   = help: audit documentation → https://docs.zizmor.sh/audits/#unpinned-uses

help[artipacked]: credential persistence through GitHub Actions artifacts
  --&gt; ./.github/workflows/artifacts.yml:16:9
   |
16 |         - name: Checkout
   |  _________^
17 | |         uses: actions/checkout@v6
   | |_________________________________^ does not set persist-credentials: false
   |
   = note: audit confidence → Low
   = note: this finding has an auto-fix
   = help: audit documentation → https://docs.zizmor.sh/audits/#artipacked

error[github-env]: dangerous use of environment file
  --&gt; ./.github/actions/cache/action.yaml:14:7
   |
14 | /       run: |
15 | |         set -e
16 | |
17 | |         # Should be the same command as the one in tools/static/castor.php
...  |
44 | |         echo cache_dirname_test=$cache_dirname_test &gt;&gt; $GITHUB_ENV
45 | |         echo cache_key_test=$cache_key_test &gt;&gt; $GITHUB_ENV
   | |__________________________________________________________^ write to GITHUB_ENV may allow code execution
   |
   = note: audit confidence → Low
   = help: audit documentation → https://docs.zizmor.sh/audits/#github-env

error[template-injection]: code injection via template expansion
  --&gt; ./.github/actions/install/action.yaml:28:108
   |
28 |       run: composer install --prefer-dist --no-progress --optimize-autoloader --classmap-authoritative ${{ inputs.composer-flags }}
   |       --- this run block                                                                                   ^^^^^^^^^^^^^^^^^^^^^ may expand into attacker-controllable code
   |
   = note: audit confidence → High
   = note: this finding has an auto-fix
   = help: audit documentation → https://docs.zizmor.sh/audits/#template-injection
</code></pre>
<p>Un grand nombre d'erreurs peuvent être corrigées automatiquement :</p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-8">zizmor</span><span class="syntax-1"> .</span><span class="syntax-3"> --fix=all</span></span></code></pre>
<p>Cependant, pour trouver le hash de commit correspondant à un tag, l'outil a besoin d'un token GitHub avec les bonnes permissions. Il convient donc de le relancer avec la variable configurée :</p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-2">GITHUB_TOKEN</span><span class="syntax-4">=</span><span class="syntax-1">ghp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</span><span class="syntax-8"> zizmor</span><span class="syntax-3"> --fix=all</span></span></code></pre>
<p>Ou, si vous utilisez la CLI <code>gh</code> au quotidien :</p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-2">GITHUB_TOKEN</span><span class="syntax-4">=</span><span class="syntax-1">`</span><span class="syntax-8">gh</span><span class="syntax-1"> auth token`</span><span class="syntax-8"> zizmor</span><span class="syntax-3"> --fix=all</span></span></code></pre>
<p>En revanche, zizmor ne gère pas les montées de version majeures des actions. Pour cela, nous utilisons deux autres méthodes.</p>
<p>En local, nous exécutons <a rel="nofollow noopener noreferrer" href="https://github.com/azat-io/actions-up">actions-up</a>. Cet outil détecte les actions utilisées et propose des mises à jour. C'est le <code>composer update</code> de vos workflows.</p>
<p>En CI, nous faisons confiance à <a rel="nofollow noopener noreferrer" href="https://dependabot.com/">Dependabot</a>. Il vérifie régulièrement vos dépendances et ouvre des Pull Requests automatiquement. Voici notre configuration :</p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-4">version</span><span class="syntax-2">: </span><span class="syntax-3">2</span></span>
<span class="line"><span class="syntax-4">updates</span><span class="syntax-2">:</span></span>
<span class="line"><span class="syntax-2">    - </span><span class="syntax-4">package-ecosystem</span><span class="syntax-2">: </span><span class="syntax-1">'github-actions'</span></span>
<span class="line"><span class="syntax-4">      directory</span><span class="syntax-2">: </span><span class="syntax-1">'/'</span></span>
<span class="line"><span class="syntax-4">      target-branch</span><span class="syntax-2">: </span><span class="syntax-1">'main'</span></span>
<span class="line"><span class="syntax-4">      schedule</span><span class="syntax-2">:</span></span>
<span class="line"><span class="syntax-4">          interval</span><span class="syntax-2">: </span><span class="syntax-1">'monthly'</span></span>
<span class="line"><span class="syntax-4">      groups</span><span class="syntax-2">:</span></span>
<span class="line"><span class="syntax-4">          github-actions</span><span class="syntax-2">:</span></span>
<span class="line"><span class="syntax-4">              patterns</span><span class="syntax-2">:</span></span>
<span class="line"><span class="syntax-2">                  - </span><span class="syntax-1">'*'</span></span>
<span class="line"><span class="syntax-4">      cooldown</span><span class="syntax-2">:</span></span>
<span class="line"><span class="syntax-4">          default-days</span><span class="syntax-2">: </span><span class="syntax-3">7</span></span></code></pre>
<p>En conclusion, nous vous recommandons vivement d'adopter zizmor. C'est un outil puissant, rapide à prendre en main, qui sécurise vos pipelines et vous fait gagner un temps précieux au quotidien.</p>]]></description></item><item><title>Satellites, Horloge &#xE0; air et cimeti&#xE8;re des IA</title><link>https://www.synolia.com/synolab/front-office/satellites-horloge-a-air-et-cimetiere-des-ia/</link><author>Estelle M.</author><date>Tue, 09 Jun 2026 09:30:15 +0200</date><description><![CDATA[<p><span style="font-weight: 400;">Découvrez notre veille technique du mois de mai entre satellites, horloge à air et cimetière des IA.</span></p>
<h2><b>Tout savoir sur les satellites</b></h2>
<p><span style="font-weight: 400;">Vous êtes-vous déjà demandé comment Internet arrivait jusqu’à vos téléphones ? Comment, même en pleine montagne, vous pouvez regarder vos vidéos préférées ? La réponse est simple ; les satellites.</span></p>
<p><span style="font-weight: 400;">Mais savez-vous comment fonctionne un satellite ? Quels en sont les types, et quels sont les grands acteurs qui envoient chaque année des petits vaisseaux tourner au-dessus de nos têtes ?</span><span style="font-weight: 400;"><br />
</span><span style="font-weight: 400;"> Beaucoup de questions auxquelles cet article de</span><a href="https://buttondown.com/LimitesNumeriques/archive/limites-numeriques-23/"> <span style="font-weight: 400;">buttondown.com</span></a><span style="font-weight: 400;"> répond ce mois-ci.</span></p>
<p><span style="font-weight: 400;">Composé avec l’aide de Tom Nico, de l’ARCEP* et du chercheur Irénée Régnauld, cet article s’exprime dans un discours extrêmement simple à comprendre et très plaisant à lire. Les informations mises en avant, comme les différents impacts de ces appareils et le fait que l’on a envoyé autant de satellites dans l’espace ces trois dernières années que depuis 1957, sont réellement enrichissantes.</span></p>
<p><span style="font-weight: 400;">Une bonne morale sur l’exploration spatiale, ses promesses de partage et une réalité bien plus contrastée…</span></p>
<p><span style="font-weight: 400;"> </span></p>
<p><span style="font-weight: 400;">* Autorité indépendante française en charge de la régulation des réseaux télécoms</span><b></b></p>
<h2><b>Quand l’air donne l’heure</b></h2>
<p><span style="font-weight: 400;">Depuis plusieurs décennies, l’électronique s’est emparée d’une part très importante de la vie moderne. Quand l’on regarde les outils précurseurs à nos horloges, nos appareils électroménagers, etc., on les juge d’archaïque, historique, même.</span></p>
<p><span style="font-weight: 400;">Mais s’il existait des alternatives à l’électronique ? C’est la question à laquelle répond cette</span><a href="https://www.youtube.com/watch?v=E1BLGpE5zH0"> <span style="font-weight: 400;">vidéo de soiboi soft</span></a><span style="font-weight: 400;">, qui nous présente ce mois-ci une horloge à quatre chiffres et qui fonctionne… à l’air.</span></p>
<p><span style="font-weight: 400;">Avec l’aide de la logique microfluidique, de membranes en silicone et de la pression de l’air, le créateur nous démontre une véritable ingénierie et une exploration fascinante des formes alternatives de calcul.</span></p>
<p><span style="font-weight: 400;">Ce qui rend cette vidéo particulièrement intéressante, au-delà du fait que le système est très satisfaisant à regarder, c’est qu’elle montre comment les concepts fondamentaux de l’informatique – comme la logique, la mémoire et le stockage des données – peuvent exister sans électronique.</span></p>
<p>&nbsp;</p>
<h2><b>Choisir l’enseignement à la haine </b></h2>
<p><span style="font-weight: 400;">Perdre son travail, ce n’est pas une mince affaire. Dans une situation pareille, il est compréhensible de laisser exploser sa colère, d’avoir un discours amer et de critiquer ses anciens employeurs.</span></p>
<p><span style="font-weight: 400;">Et si nous transformions cette colère en quelque chose de plus concret… de plus utile ? Voici l’histoire du licenciement de Vasilios Syrakis, racontée ce mois-ci dans</span><a href="https://www.financialexpress.com/trending/i-built-a-lot-of-things-laid-off-engineer-shares-38-minute-what-i-built-video-instead-of-a-rant/4243251/"> <span style="font-weight: 400;">cet article du financianexpress</span></a><span style="font-weight: 400;">.</span></p>
<p><span style="font-weight: 400;">Après son licenciement, Syrakis a choisi de publier une vidéo de 38 minutes pour expliquer les systèmes d’infrastructure qu’il a conçus pendant huit ans ; routage réseau, équilibrage de charge, plateformes cloud et outils permettant à des milliers de développeurs de déployer leurs services à grande échelle. L’objectif est simple ; documenter son travail et partager son expérience avec d’autres ingénieurs. Plutôt que de se concentrer sur la perte de son emploi, Syrakis a choisi de transformer son histoire de licenciement en une démonstration de savoir-faire et de transmission de connaissances.</span></p>
<h2><b>“Vous gaspillez 90% de vos tokens sans le savoir”</b></h2>
<p><span style="font-weight: 400;">Si vous utilisez des IAs de manière quotidienne et gratuite, vous avez sans doute été confronté au blocage journalier imposé par ces dernières.</span></p>
<p><span style="font-weight: 400;">Avec cette</span><a href="https://www.youtube.com/watch?v=Q3VqYvsFo84"> <span style="font-weight: 400;">vidéo de Shubham SHARMA</span></a><span style="font-weight: 400;">, vous serez désormais incollable sur l’utilisation des IAs et la compréhension de leur limite.</span></p>
<p><span style="font-weight: 400;">En bon instructeur, Shubham commence d’abord ses explications par la définition de ce que sont des « tokens » d’IA, pour ensuite faire la différence entre la limite de message et de token, et expliquer pourquoi les IA deviennent plus lentes, voire même moins intelligentes à l’utilisation.</span></p>
<p><span style="font-weight: 400;">Shubham surligne ensuite 9 pièges communs classés parmi 3 grandes catégories. Parmi ces pièges, on retiendra surtout que :</span></p>
<p><span style="font-weight: 400;">&#8211; Plus la conversation est longue, plus chaque message coûte cher (car l’IA relit tout l’historique),</span></p>
<p><span style="font-weight: 400;">&#8211; Les réponses verbeuses coûtent plus cher, et qu’il existe des outils, comme</span><a href="https://github.com/JuliusBrussee/caveman/blob/main/README.md"> <span style="font-weight: 400;">Caveman</span></a><span style="font-weight: 400;">, qui permettent de réduire ces longues réponses,</span></p>
<p><span style="font-weight: 400;">&#8211; Le choix du modèle est important (ne pas utiliser une Ferrari pour aller chercher du pain).</span></p>
<p><span style="font-weight: 400;">&#8211; L’utilisation de fichiers lourds, comme des images ou des PDFs, fait exploser le quota de token. Il est donc préférable d’utiliser du texte.</span></p>
<p><span style="font-weight: 400;">Et vous, connaissiez-vous ces pièges ?</span></p>
<h2><b>Le cimetière des IA </b></h2>
<p><a href="https://vibegraveyard.ai/"><span style="font-weight: 400;">Vibe Graveyard</span></a><span style="font-weight: 400;"> est un site qui recense des incidents, des échecs et des « oopsies » liés à l’utilisation excessive, voire imprudence, des intelligences artificielles dans le développement logiciel, les produits numériques, les médias, la santé et même le droit. Il se présente comme un cimetière de projets développés en grande partie par l’IA et avec peu de compréhension ou de validation humaine.</span></p>
<p><span style="font-weight: 400;">De la petite bourde aux erreurs catastrophiques, vous pourrez y lire des cas réels d’utilisation d’IA ayant parfois entraîné des erreurs juridiques, voire médicales. Un des cas récents concerne par exemple un problème de gestion de stock au sein des enseignes Starbucks, dont l’IA comptait et étiquetait souvent de manière erronée des boissons de base, notamment des types de lait similaires, et omettait parfois complètement certains produits.</span></p>
<p><span style="font-weight: 400;">L’objectif d’un tel site n’est pas d’offrir une analyse neutre de l’IA, mais de rappeler que l’automatisation ne remplace pas l’expertise technique, la validation et la maintenance logicielle. Une riche archive et un contrepoids très concret au discours enthousiaste entourant l’IA générative.</span></p>
<p>&nbsp;</p>
<h2><b>Pour aller plus loin</b></h2>
<p><span style="font-weight: 400;">Voici les autres liens que vous avez partagés ce mois-ci, bonne lecture !</span></p>
<ul>
<li><a href="https://actu.fr/societe/pourquoi-il-faut-donner-son-sang-juste-avant-les-ponts-du-mois-de-mai_60965603.html"><span style="font-weight: 400;">Pourquoi il faut donner son sang juste avant les ponts du mois de mai</span></a><span style="font-weight: 400;"> </span></li>
<li><a href="https://wiki.php.net/rfc/bound_erased_generic_types"><span style="font-weight: 400;">https://wiki.php.net/rfc/bound_erased_generic_types</span></a><span style="font-weight: 400;"> </span></li>
<li><a href="https://thehackernews.com/2026/05/windows-zero-days-expose-bitlocker.html?m=1"><span style="font-weight: 400;">Windows Zero-Days Expose BitLocker Bypasses And CTFMON Privilege Escalation</span></a><span style="font-weight: 400;"> </span></li>
<li><a href="https://grimlink.com/blog/2026/grimlink-com-finally-live/"><span style="font-weight: 400;">GrimLink.com Finally Live!</span></a><span style="font-weight: 400;"> </span></li>
<li><a href="https://learn.microsoft.com/en-us/windows/powertoys/"><span style="font-weight: 400;">Microsoft PowerToys &#8211; Windows Utilities for Power Users</span></a><span style="font-weight: 400;"> </span></li>
<li><a href="https://www.frandroid.com/marques/google/3105147_trafic-divise-par-trois-agents-qui-ne-cliquent-plus-la-google-i-o-2026-sonne-la-fin-dune-epoque"><span style="font-weight: 400;">Trafic divisé par trois, agents qui ne cliquent plus : la Google I/O 2026 sonne la fin d&rsquo;une époque</span></a><span style="font-weight: 400;"> </span></li>
<li><a href="https://www.frenchweb.fr/google-i-o-2026-google-veut-reconstruire-internet-autour-de-lia/461888"><span style="font-weight: 400;">Google I/O 2026 : Google veut reconstruire Internet autour de l’IA</span></a><span style="font-weight: 400;"> </span></li>
<li><a href="https://www.linkedin.com/news/story/la-guerre-affecte-62-des-pme-tpe-8875618/"><span style="font-weight: 400;">La guerre affecte 62% des PME-TPE | LinkedIn</span></a><span style="font-weight: 400;"> </span></li>
<li><a href="https://www.europesays.com/ai/28009/"><span style="font-weight: 400;">https://www.europesays.com/ai/28009/</span></a><span style="font-weight: 400;"> </span></li>
</ul>
<p>&nbsp;</p>
<p>Cet article <a href="https://www.synolia.com/synolab/front-office/satellites-horloge-a-air-et-cimetiere-des-ia/">Satellites, Horloge à air et cimetière des IA</a> est apparu en premier sur <a href="https://www.synolia.com">Synolia, agence e-commerce, CRM, Data, PIM/DAM, OMS</a>.</p>
]]></description></item><item><title>Contre-performance pour la campagne de collecte du barom&#xE8;tre des salaires en PHP 2026</title><link>https://afup.org/news/1259-contreperformance-barometre-salaires-2026</link><author/><date>Mon, 08 Jun 2026 20:37:00 +0200</date><description><![CDATA[<h3>Une participation en baisse</h3>
<p>La quatorzième édition du baromètre des salaires en PHP vient de clore sa campagne de récolte de données. 509 professionnel•le•s ont pris le temps de répondre à l'enquête cette année : qu'ils et elles en soient sincèrement remercié·e·s ! Ces 509 voix permettront, comme chaque année, de publier en fin d'année une photographie précise et utile des rémunérations et des conditions de travail dans l'écosystème PHP. <br>
Mais un constat s'impose déjà : il s'agit là de notre score le plus bas depuis 2019. Après 721 réponses en 2025, la baisse est significative, de près de 30%. </p>
<h3>De premières pistes d'explications</h3>
<p>Le marché de l'emploi tech en France traverse depuis deux ans une période difficile : gels de recrutement, restructurations, fermetures de structures, incertitude sur les trajectoires de carrière. Dans ce contexte, participer à une enquête collective suppose un certain sentiment d'appartenance à un écosystème dans lequel on se projette. Or quand les devs traversent une période d'instabilité professionnelle, ce type de démarche passe naturellement au second plan. <p><p>
Nous étudions également un autre phénomène avec la multiplication des enquêtes en ligne. Les devs sont sollicité·e·s de toutes parts. La multiplication des sondages, questionnaires et formulaires de feedback a progressivement érodé la disponibilité pour ce type de démarche.<p><p>
Cette contre-performance est un signal que nous prenons au sérieux en vue des prochaines éditions : sans avoir de solution à l'heure actuelle, nous allons réfléchir à des pistes pour rebooster cette enquête essentielle pour notre secteur. </p>
<h3>Le pôle baromètre passe à l'action !</h3>
<p>Le travail continue : les données collectées vont maintenant être analysées avec le soin et la rigueur qui ont fait la réputation du baromètre depuis quatorze ans. Les résultats seront publiés en fin d'année, comme toujours, au moment où ils sont le plus utiles : à l'approche des entretiens annuels, quand il s'agit d'engager des conversations concrètes sur la rémunération.<p><p>
<strong>Les résultats vous seront partagés cet automne</strong>, vous donnant ainsi les informations les plus fraîches en vue de ce moment important pour votre carrière.</p>
<h3>Merci à nos partenaires Human Coders et le Journal du Net</h3>
<p>Un grand merci à l'équipe de <a href="https://www.humancoders.com/">Human Coders</a>, centre de formation pour développeurs·euses, de nous avoirà nouveau accompagnés pour cette édition 2026. Merci également au <a href="https://www.journaldunet.com/">Journal du Net</a> pour son soutien.
<br><br>
<strong>Merci aux 509 personnes qui ont choisi de contribuer à ce bien commun. Leurs réponses éclaireront des centaines de professionnel·le·s du secteur : c'est pour ça que le baromètre existe, et c'est pour ça qu'il continuera ! Rendez-vous à l'automne pour la publication des résultats.</strong></p>]]></description></item><item><title>Qu'est-ce qu'un &#xAB; bon code &#xBB; &#xE0; l'&#xE8;re de l'IA ?</title><link>https://www.jdecool.fr/blog/2026/06/06/qu-est-ce-qu-un-bon-code-a-l-ere-de-l-ia.html</link><author/><date>Sat, 06 Jun 2026 00:00:00 +0200</date><description><![CDATA[<p>La semaine dernière, je faisais référence au mantra “code is cheap”. Depuis, j’ai trouvé le projet “<a href="https://simonwillison.net/guides/agentic-engineering-patterns/ " target="_blank" rel="noopener noreferrer">Agentic Engineering Patterns</a>” de Simon Willison qui documente les patterns pratiques pour travailler et obtenir les meilleurs résultats possibles avec l’IA.</p>

<p>On y retrouve une section “Writing code is cheap now” avec toute fois une nuance que je partage: “Good code still has a cost”. Ce qu’il y explique a pour moi toujours était vrai même avant l’IA. Écrire du code est facile, mais écrire du bon code, ça l’est moins et c’est ce point qui représente un coût.</p>

<!--more-->

<p>Pour lui, un “bon code” a les caractéristiques suivantes:</p>

<ul>
  <li>il fonctionne réellement: il fait ce qu’il est censé faire, sans bugs majeurs.</li>
  <li>on sait qu’il fonctionne: il existe des preuves (tests, validation, vérifications) qui inspirent confiance.</li>
  <li>il résout le bon problème: produire une solution techniquement correcte, mais inutile reste un échec.</li>
  <li>il gère les erreurs proprement: pas seulement le scénario idéal, mais aussi les cas d’échec, avec des messages exploitables pour le futur debugging.</li>
  <li>il reste simple et minimal: éviter la complexité inutile et l’“over-engineering”.</li>
  <li>il est protégé par des tests: pour éviter les régressions quand le code évolue.</li>
  <li>il est documenté: et la documentation doit rester synchronisée avec le comportement réel du système.</li>
  <li>il reste évolutif: il ne doit pas compliquer inutilement les changements futurs, tout en évitant d’anticiper des besoins hypothétiques (principe YAGNI).</li>
  <li>il respecte les qualités non fonctionnelles: sécurité, maintenabilité, observabilité, accessibilité, fiabilité, scalabilité, etc., selon le contexte du projet.</li>
</ul>

<p>Je vous recommande vivement d’aller jeter un coup d’oeil au projet: <a href="https://simonwillison.net/guides/agentic-engineering-patterns/ " target="_blank" rel="noopener noreferrer">https://simonwillison.net/guides/agentic-engineering-patterns/</a></p>
]]></description></item><item><title>Correcteur orthographique pour Chrome via Gemini nano</title><link>https://www.gameandme.fr/divers/correcteur-orthographique-pour-chrome-via-gemini-nano/</link><author>Yohann Nizon</author><date>Tue, 02 Jun 2026 09:59:53 +0200</date><description><![CDATA[<p>Il y a quelques temps, j&#8217;ai entendu parler d&#8217;un sujet à débat quant à l&#8217;introduction silencieuse du LLM Gemini nano dans le navigateur Chrome. Au delà de l&#8217;aspect polémique du sujet, je me suis penché dessus pour voir ce qu&#8217;on pouvait en tirer, et j&#8217;ai réalisé un correcteur orthographique qui j&#8217;espère vous plaira. Un LLM ... <a title="Correcteur orthographique pour Chrome via Gemini nano" class="read-more" href="https://www.gameandme.fr/divers/correcteur-orthographique-pour-chrome-via-gemini-nano/" aria-label="En savoir plus sur Correcteur orthographique pour Chrome via Gemini nano">Lire la suite</a></p>
<p>Cet article <a href="https://www.gameandme.fr/divers/correcteur-orthographique-pour-chrome-via-gemini-nano/">Correcteur orthographique pour Chrome via Gemini nano</a> est apparu en premier sur <a href="https://www.gameandme.fr">Game And Me</a>.</p>
]]></description></item><item><title>Des nouvelles de la situation de l'AFUP</title><link>https://afup.org/news/1256-des-nouvelles-situation-afup-mai2026</link><author/><date>Tue, 02 Jun 2026 06:59:00 +0200</date><description><![CDATA[<p>Nous vous avions partagé nos <a href="https://afup.org/news/1254-signal-dalarme-tire-a-lafup">difficultés</a> sur notre site.</p>
<h3>Un AFUP Day 2026 qui manque à ses objectifs</h3>
<p>L'AFUP Day est l'événement printanier multi-destinations de l'AFUP depuis 2019. Chaque année, le cycle de conférences a annoncé complet sur chaque édition des semaines voire des mois avant le jour J. <br><br>
Sauf en 2026. <br><br>
Cette année, alors que les jauges lyonnaises et lilloises étaient les mêmes que précédemment, et que les jauges bordelaises et parisiennes étaient raisonnables pour une première édition, seul l'AFUP Day 2026 Paris a annoncé le sold-out.  L'événement a affiché un taux de remplissage de 90% à Bordeaux, 74% à Lille et 95% à Lyon.  L'objectif de billetterie n'a donc pas été atteint.<br>
<br>Côté sponsoring, l'événement a plafonné à 75% de l'objectif, malgré des soutiens remarquables suite à notre appel à l'aide. Merci à ces entités de s'être mobilisées à nos côtés, cela nous a donné chaud au cœur !<br>
Le résultat de l'AFUP Day 2026 est donc décevant, d'autant plus qu'il est habituellement l'événement le plus lucratif de l'année pour l'association. </p>
<h3>Un appel qui trouve peu d'écho dans un contexte tendu</h3>
<p>Dans le même temps, la campagne de sponsoring pour le Forum PHP 2026 continue. Malgré nos multiples prises de contact, malgré les appels à la communauté, la recherche de soutiens est difficile. De multiples entreprises nous indiquent ne pas avoir de budget marketing en 2026. La grande incertitude qui règne dans le secteur, que cela soit lié au contexte mondial, à l'arrivée de l'IA ou d'autres raisons économiques, n'invite pas les acteurs de l'écosystème à s'impliquer auprès de l'association. <br>
La campagne de récolte d'informations pour le <a href="https://barometre.afup.org">baromètre des salaires en PHP 2026</a> connait elle aussi un revers, signe supplémentaire que la communauté est préoccupée et a peu de temps à investir sur le plan associatif.</p>
<h3>Impact pour le deuxième semestre 2026</h3>
<p>L'équipe AFUP reste mobilisée et à la recherche de solutions ! Hors de question pour nous de nous avouer vaincus, notre mission auprès des devs PHP est trop importante pour baisser les bras. Cependant, certaines actions vont être mises en place pour passer ce cap difficile.<br>
<strong>Le Forum PHP 2026 revoit sa copie côté budgets :</strong> les conditions d'accueil, notamment des speakers du Forum PHP 2026, vont être revues à la baisse. L'amplitude horaire des 3 salles de conférences va également être modifiée, pour limiter les coûts liés à leur exploitation. <br>
<strong>Le Forum PHP 2026 va également adapter sa ligne éditoriale</strong> vers des sujets qui vous permettront de rester des devs pertinent•e•s face aux changements que connaît le milieu : si la dimension technique restera importante, un axe approfondi dédié à l'IA, à la souveraineté numérique et la cybersécurité vous sera proposé (sous réserve des sujets soumis au CFP). Nous sommes d'ailleurs à l'écoute de vos besoins, de vos inquiétudes et de vos envies : <a href="https://docs.google.com/forms/d/e/1FAIpQLScqIDatYaa4qO0Pr-rMqgD1nq0WM-4r9wgV4nL53lwRveSbsw/viewform?usp=dialog">répondez à notre enquête</a> en vue de l'élaboration du programme du Forum PHP 2026. <br>
<strong>Des pistes d'ajustement sont d'ores et déjà à l'étude pour 2027 :</strong> des réflexions sont menées en interne relatives à l'AFUP Day 2027 et au Forum PHP 2027. </p>
<p><strong>Toutes ces actions ne porteront leurs fruits que si la communauté reste à nos côtés :</strong> manifestez-nous <a href="https://afup.org/association/devenir-membre">votre soutien</a>, faites parler de nos actions, partagez le <a href="https://afup.org/become-sponsor">dossier de sponsoring</a> à votre CTO, demandez à vos RH de vous envoyer sur nos cycles de conférences, parlez de nos événements et de ce qu'ils vous ont permis d'apprendre dans vos blogs et sur vos réseaux sociaux. La communauté est vibrante, dynamique et généreuse : nous la fédérons depuis longtemps, nous le savons bien ! <br><br><strong>On compte sur vous !</strong></p>]]></description></item><item><title>Profilez vos tests PHPUnit avec OpenTelemetry</title><link>https://www.jdecool.fr/blog/2026/06/01/profilez-vos-tests-phpunit-avec-opentelemetry.html</link><author/><date>Mon, 01 Jun 2026 00:00:00 +0200</date><description><![CDATA[<p>Un projet de développement informatique qui grossit, c’est une base de code qui grossit par la même occasion et la batterie de tests qui évolue en conséquence. On ajoute des tests semaine après semaine, les temps d’exécution s’allongent. Et puis, à un moment, la CI s’arrête net: <code>Allowed memory size exhausted</code>. Le premier réflexe est alors d’augmenter la mémoire allouée à PHP. Et ce cycle peut continuer un certain temps, jusqu’au moment où l’on atteint des seuils critiques.</p>

<!--more-->

<p>Je l’ai rencontré dans plusieurs expériences professionnelles. Corriger des problèmes de mémoire est compliqué et l’outillage restreint. On est donc aveugle, on ne sait pas quels tests consomment de la mémoire, lesquels prennent du temps à s’exécuter. Monitorer une application en production est une bonne pratique, pourquoi n’en serait-il pas de même pour nos tests ?</p>

<p>Pour l’observabilité de nos applications de production, un standard est en train de s’imposer: <a href="https://opentelemetry.io " target="_blank" rel="noopener noreferrer">OpenTelemetry</a>. Pourquoi, alors, ne pas utiliser ce dernier pour également suivre ce qu’il se passe sur nos tests: produire des traces et des métriques que nous pourrions analyser ?</p>

<p>Il existe d’ailleurs une bibliothèque pour intégrer de l’observabilité dans nos tests PHPUnit avec OpenTelemetry: <a href="https://packagist.org/packages/flow-php/phpunit-telemetry-bridge " target="_blank" rel="noopener noreferrer">flow-php/phpunit-telemetry-bridge</a>. Il s’agit d’une extension PHPUnit qui collecte la télémétrie de votre suite de tests et l’exporte vers n’importe quel <em>backend</em> compatible OTLP.</p>

<p>Pour l’installer, rien de plus simple:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">composer require --dev flow-php/phpunit-telemetry-bridge</code></pre></figure>

<p>Il restera ensuite à ajouter la configuration nécessaire dans PHPUnit:</p>

<figure class="highlight"><pre><code class="language-xml" data-lang="xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;phpunit&gt;
    &lt;!-- ... --&gt;
    &lt;extensions&gt;
        &lt;bootstrap class=&quot;Flow\Bridge\PHPUnit\Telemetry\TelemetryExtension&quot;&gt;
            &lt;parameter name=&quot;service_name&quot; value=&quot;phpunit-opentelemetry&quot;/&gt;
            &lt;parameter name=&quot;transport&quot; value=&quot;curl&quot;/&gt;
            &lt;parameter name=&quot;endpoint&quot; value=&quot;http://localhost:4318&quot;/&gt;
            &lt;parameter name=&quot;emit_traces&quot; value=&quot;true&quot;/&gt;
            &lt;parameter name=&quot;emit_metrics&quot; value=&quot;true&quot;/&gt;
            &lt;parameter name=&quot;emit_test_spans&quot; value=&quot;true&quot;/&gt;
            &lt;parameter name=&quot;emit_test_case_spans&quot; value=&quot;true&quot;/&gt;
            &lt;parameter name=&quot;curl_connect_timeout_ms&quot; value=&quot;1000&quot;/&gt;
            &lt;parameter name=&quot;curl_timeout_ms&quot; value=&quot;2000&quot;/&gt;
        &lt;/bootstrap&gt;
    &lt;/extensions&gt;
&lt;/phpunit&gt;</code></pre></figure>

<p>Une fois configuré, il ne reste plus qu’à lancer vos tests, les données de télémétrie seront automatiquement envoyées, vous permettant ensuite de les visualiser dans des tableaux de bord.</p>

<center>
    <img src="/img/blog/20260601-profilez-vos-tests-phpunit-avec-opentelemetry/phpunit-otel-overview.png" alt="Vue d'ensemble de la télémétrie d'une suite de tests PHPUnit dans un tableau de bord" />
</center>

<p>On y retrouvera chaque suite de tests avec le détail de chaque test: sa durée, son empreinte mémoire, son statut.</p>

<center>
    <img src="/img/blog/20260601-profilez-vos-tests-phpunit-avec-opentelemetry/phpunit-otel-memory.png" alt="Consommation mémoire des tests PHPUnit visualisée via OpenTelemetry" />
</center>

<p>Quelques points d’attention néanmoins. Instrumenter chaque test a un coût. Cela ne sera pas forcément intéressant sur une petite suite de test. C’est surtout lorsque le projet commence à grossir que ce type de donnée a de la valeur et que vous souhaitez arrêter de naviguer à l’aveugle.</p>

<p>Et si vous désirez en savoir plus, n’hésitez pas à consulter <a href="https://flow-php.com/documentation/components/bridges/phpunit-telemetry-bridge/ " target="_blank" rel="noopener noreferrer">la documentation de l’outil</a>.</p>
]]></description></item><item><title>AFUP Day 2026 Paris, retour sur une journ&#xE9;e dans l'&#xE9;cosyst&#xE8;me PHP</title><link>https://jolicode.com/blog/afup-day-2026-paris-retour-sur-une-journee-dans-l-ecosysteme-php</link><author>JoliCode Team</author><date>Tue, 26 May 2026 15:30:00 +0200</date><description><![CDATA[<p>Ce vendredi 22 mai 2026, l'équipe JoliCode était présente à l'AFUP Day 2026 Paris à l'ESGI Paris pour une journée de conférences. L'ambiance était conviviale et studieuse, avec un programme axé sur l'IA, l'architecture et la productivité.</p>
<p>Nous avons eu l'honneur de présenter deux sujets, Loïck Piera sur Castor 🦫 et Grégoire Pineau sur Symfony IA.</p>
<h2>Les temps forts techniques</h2>
<p>Le programme a couvert des thématiques essentielles qui font évoluer nos pratiques au quotidien, et a aussi rappelé quelques bases que nous connaissons bien.</p>
<p><picture class="js-dialog-target" data-original-url="/media/original/2026/afup-day/salle-conference.jpg" data-original-width="3589" data-original-height="2262"><source type="image/webp" srcset="/media/cache/content-webp/2026/afup-day/salle-conference.4ddd7dc1.webp" /><source type="image/jpeg" srcset="/media/cache/content/2026/afup-day/salle-conference.jpg" /><img loading="lazy" decoding="async" style="width: 996px; ; aspect-ratio: calc(3589 / 2262)" src="https://jolicode.com//media/cache/content/2026/afup-day/salle-conference.jpg" alt="La salle de la conférence" /></picture></p>
<h3>Intelligence Artificielle et Innovation dans Symfony</h3>
<p>L'IA a été au cœur de plusieurs échanges, montrant son intégration progressive dans nos outils quotidiens :</p>
<p><strong>Symfony AI et Embeddings</strong> : Grégoire Pineau a présenté son retour sur l'utilisation des embeddings dans Symfony pour des recherches et rapprochements sémantiques poussés - <a href="https://jolicode.com/blog/notre-retour-sur-le-symfonylive-paris-2026#embeddings-en-php-symfony-ai-en-pratique-gregoire-pineau">nous vous invitons à retrouver notre récapitulatif de Symfony Live où nous en parlions déjà</a>.</p>
<p><strong>Assistance Générative</strong> : Florence Cauchy a partagé un guide pratique sur l'intégration de GitHub Copilot et Claude dans nos workflows. Nous partageons ses conseils :</p>
<ul>
<li>Mise en place de skills ;</li>
<li>Définition de guidelines pour les agents ;</li>
<li>Exploiter aussi bien le CLI que les agents cloud.</li>
</ul>
<p>Si vous n'avez pas encore d'assistance par LLM pour coder, c'était une bonne introduction !</p>
<h3>Outillage, Productivité et Modernisation</h3>
<p>Nous avons exploré de nouveaux outils pour optimiser notre productivité :</p>
<p><strong>Castor</strong> : Loïck Piera nous a fait découvrir les coulisses de ce <a rel="nofollow noopener noreferrer" href="https://castor.jolicode.com/">task runner open source</a> indispensable 😉 (oui c'est nous qui le développons). L'occasion de parler des outils sur lesquels repose Castor comme <code>symfony/console</code>, et de parler de fonctionnalités peu connues comme :</p>
<ul>
<li>Les events pour étendre le fonctionnement ;</li>
<li>Les imports distants pour utiliser des tasks extérieures ;</li>
<li>Les remote exécutions, pour lancer des outils sans les installer.</li>
</ul>
<p>Nous vous invitons <a rel="nofollow noopener noreferrer" href="https://speakerdeck.com/pyrech/sous-le-capot-de-castor-le-task-runner-php">à lire les slides qui sont pleines d'informations</a> !</p>
<p><strong>Sidekicks applicatifs</strong> : Nicolas Grekas a présenté comment tirer partie de Caddy/FrankenPHP pour faciliter la configuration de nos applications HTTP. Grâce à ces sidekicks, l'application peut continuer à tourner en mode worker (donc sans avoir à redémarrer de 0 à chaque requête, comme c'est le cas avec PHP-FPM) mais en ayant sa configuration à jour :</p>
<ul>
<li>côté application, on récupère une valeur de configuration, qui est locale et toujours à jour, grâce à un simple appel à une fonction ;</li>
<li>un thread tourne à côté pour mettre à jour cette valeur dès qu'il y a un changement (ce thread est setup très simplement dans Caddy / FrankenPHP).</li>
</ul>
<p>Il aimerait voir cette nouvelle manière de faire devenir un standard dans l'écosystème PHP, à la place du traditionnel php-fpm, afin d'être égaux avec les technos alternatives à PHP comme node, qui proposent des comportement similaires.</p>
<h3>Architecture et Qualité du Code</h3>
<p>La durabilité logicielle et les bonnes pratiques restent fondamentales :</p>
<p><strong>Qualité avec PHPStan</strong> : Vincent Langlet &amp; Étienne Dhennin nous font la présentation de leur mise en place d'un écosystème de qualité chez Weglot.
Ils nous ont présenté les différents fixers, linters et outils de tests disponibles et qu'ils ont utilisés, pour répondre à leur définition de la qualité : <strong>la capacité d'un système à remplir son rôle aujourd'hui comme demain</strong>, au travers de trois dimensions : fonctionnelle, temporelle et humaine.
La direction de l'entreprise avait besoin de justifications pour le temps passé à faire de la qualité, et ils ont donc introduit un métrique original : le taux de hotfix en production (et donc oui il a baissé).</p>
<p>Parmi leurs retours d’expérience sur PHPStan en particulier :</p>
<ul>
<li>Ne pas générer de baseline sur les projets legacy, prioriser les niveaux &quot;bas&quot; (jusqu’au niveau 5) - nous sommes partagés sur ce point car la baseline permet justement de pouvoir contribuer à des projets legacy avec une analyse statique élevé sur nos nouveaux développements ;</li>
<li>Privilégier les extensions spécifiques à votre projet (<code>phpstan-doctrine</code> par exemple) aux niveaux 9 et 10, souvent superflus.</li>
</ul>
<p><strong>API Design Pragmatique</strong> : Amaury Bouchard a exploré des alternatives au design d'APIs. Il critique pas mal JWT car ça ne gère pas l'expiration par exemple. Un des points que nous partageons est que HTTP Basic aujourd'hui c'est tout à fait valide et sécurisé (car en HTTPS il n'y a pas de &quot;fuite&quot; des identifiants).
Le take-away du talk concerne le choix entre <abbr title="Remote Procedure Call">RPC</abbr> et REST. Vouloir utiliser absolument du REST alors que notre API ne gère pas des états mais des actions est un mauvais choix de design. En résumé :</p>
<ul>
<li>RPC pour les API qui font des actions (envoyer un mail, publier des redirections...) ;</li>
<li>REST pour les API qui modifient des états, le CRUD, etc.</li>
</ul>
<p>En bref, un appel à la simplicité, quitte à remettre en questions des dogmes acquis depuis très longtemps, de très bons sujets de réflexion lors de votre prochaine conception d’API.</p>
<p><strong>Tests sans Mocks</strong> : Imen Ezzine a présenté une solution intéressante pour tester efficacement des APIs externes sans recourir aux mocks - et surtout pour pallier à l'imcompatibilité de <a rel="nofollow noopener noreferrer" href="https://php-vcr.github.io/">PHP-VCR</a> avec le client HTTP de Symfony.</p>
<p>En utilisant le format HAR,  un nouveau decorator RecorderHttpClient, et un simple attribut <code>#[UseRecord]</code> sur les fonctions de test (via le bridge phpunit), nous pouvons enregistrer une réponse API, de sorte à pouvoir utiliser plus tard cet enregistrement pour simuler des appels à une API externe. Ainsi, pas besoin de créer un mock ! L’appel est joué réellement une seule fois, puis réutilisé directement au moment de lancer les tests. Un mécanisme permet de configurer son mode d’enregistrement, pour rafraîchir périodiquement, rejouer l’appel à chaque fois ou au contraire le verrouiller.</p>
<p>L’ensemble apparaît comme une solution efficace et très élégante pour s’affranchir d’une problématique que nous avons tous déjà rencontrée. Une PR est soumise sur Symfony 8.2.</p>
<p><strong>Gestion des Exceptions</strong> : Smaïne Milianni a récapitulé les meilleures pratiques pour gérer et logguer les exceptions proprement. De bons rappels et peut être même des guidelines à coller dans nos <code>AGENTS.md</code> !</p>
<ul>
<li>Relancer systématiquement les exceptions attrapées dans les couches “profondes” de l’application (métier, infra…), pour les gérer uniquement dans les couches d’interface.</li>
<li>Ne pas catcher la terre entière (<code>catch \Exception</code>) ;</li>
<li>Ne pas catcher <code>Throwable</code> si on est bas niveau ;</li>
<li>Ne jamais &quot;rien faire&quot; dans le &quot;catch&quot; (il faut au moins un log) ;</li>
<li>Utiliser une exception pour faire du contrôle de flow classique (nous sommes un peu partagé à ce sujet aussi - c'est parfois élégant) ;</li>
<li>Catcher et relancer une exception sans n'avoir rien changé / rien fait : autant de rien catcher ;</li>
<li>ne jamais oublier l'argument <code>$previous</code> quand on rethrow / mute une exception ;</li>
<li>créer des exceptions custom, utiliser des constructeurs statiques... pour nos exceptions métier.</li>
</ul>
<p><picture class="js-dialog-target" data-original-url="/media/original/2026/afup-day/http-mock.jpg" data-original-width="4080" data-original-height="3072"><source type="image/webp" srcset="/media/cache/content-webp/2026/afup-day/http-mock.42f43150.webp" /><source type="image/jpeg" srcset="/media/cache/content/2026/afup-day/http-mock.jpg" /><img loading="lazy" decoding="async" style="width: 996px; ; aspect-ratio: calc(4080 / 3072)" src="https://jolicode.com//media/cache/content/2026/afup-day/http-mock.jpg" alt="Imen Ezzine sur scène" /></picture></p>
<h3>Sujets intemporels</h3>
<p><strong>Passkeys et WebAuthn</strong> : Sylvain Combraque nous a fait un rapide cours historique sur les manières de stocker les mots de passe utilisateurs, du très sécurisé plaintext jusqu'au hashage, sans oublier l'<a rel="nofollow noopener noreferrer" href="https://openid.net/developers/how-connect-works/">OICD</a> pour nous montrer comment avec le temps, nous avons pu répondre aux questions de sécurité liées à nos systèmes d'authentification.
Il nous a finalement expliqué qu'au final, la manière la plus simple de sécuriser nos mots de passe, c'est de ne pas en avoir ! Chose possible aujourd'hui et depuis 2015 grâce aux Passkeys, une solution d'authentification locale, unique par compte, et impossible à faire fuiter. Cela fait 10 ans que nous en entendons parler et que les briques techniques existent, et pourtant, ce n'est toujours pas &quot;grand public&quot; ! Les mots de passe ont encore de beaux jours devant eux... À nous de faire bouger les lignes !</p>
<p><strong>Gotchas PHP</strong> : Frédéric Bouchery a animé une session interactive sur les comportements inattendus et les subtilités du langage. Par exemple, saviez-vous que <code>new daTeTimE();</code> fonctionne aussi bien que <code>new DateTime();</code> mais que vos constantes elles, sont sensibles à la casse ?
C'était une façon très ludique de nous expliquer des principes du langage comme la compilation, les changements de syntaxes...</p>
<h2>Conclusion</h2>
<p>Au-delà de la technique, l'AFUP Day a été un moment de partage plein d'échanges et riche en apprentissages. Nous sommes vraiment ravis de retrouver un événement à Paris.</p>
<p>Les perspectives ouvertes par l'IA et la modernisation des outils promettent des projets passionnants pour JoliCode. Un grand merci à l'AFUP Paris et à tous les bénévoles pour cette organisation impeccable 👏 Comptez sur nous l'année prochaine !</p>]]></description></item><item><title>Bilan de l'AFUP Day 2026</title><link>https://afup.org/news/1258-bilan-afupday2026</link><author/><date>Tue, 26 May 2026 11:52:00 +0200</date><description><![CDATA[<h3>La communauté réunie</h3>
<p>Nous étions 75 environ à Bordeaux,  90 à Lille, 220 à Lyon, et 80 à Paris, réunis toute une journée pour profiter du partage des savoirs proposé par des conférenciers et conférencières de talent et du soutien des entreprises de l'écosystème : merci au public, aux speakers et aux sponsors d'avoir été des nôtres ce 22 mai.<br>
Et c'est toujours une ambiance conviviale et bienveillante qui a marqué les esprits : les différents publics ont souligné l'atmosphère unique de chaque édition, permettant les échanges, les rencontres, les discussions de fond et la découverte. Bravo et merci à toutes et tous de participer à cette ambiance propre à la communauté !</p>
<h3>Des conférences au top</h3>
<p>Si on en croit les retours sur OpenFeedback à <a href="https://openfeedback.io/afupday2026bordeaux/2026-05-22">Bordeaux</a>, <a href="https://openfeedback.io/afupday2026lille/2026-05-22">Lille</a>, <a href="https://openfeedback.io/afupday2026lyon/2026-05-22">Lyon</a> et <a href="https://openfeedback.io/afupday2026paris/2026-05-22">Paris</a>, les conférences ont permis à la communauté de progresser et d'apprendre de nouvelles choses. Un partage de connaissance permis grâce à la générosité des speakers qui prennent de leur temps pour venir faire profiter le public de leur savoir : nous ne pourrions rien sans vous, un grand merci !<br>
Si vous n'avez pas encore partagé vos retours avec nos speakers, il est encore temps de le faire : ainsi, vous les aidez à peaufiner leurs futures présentations. </p>
<h3>Une édition plus difficile cette année</h3>
<p>Si rassembler plus de 460 personnes en ces temps bousculés pour notre secteur est une belle réussite, l'événement connaît cependant des difficultés pour la première fois depuis 2019. Ainsi, pour la première fois, 3 éditions sur les 4 n'étaient pas complètes, alors que nous étions restés sur des jauges habituelles pour l'événement. <br>
Côté sponsors, nous avons également accusé un déficit. Pour la première fois également, nous n'avons pas atteint notre objectif de sponsoring. Nous vous parlerons de cette problématique et de son impact dans un article début juin. </p>
<p><strong>Encore merci au public, aux speakers, aux sponsors, mais aussi à nos équipes d'organisation qui ont travaillé dans l'ombre pendant des mois, d'être venus vivre ce moment fort avec nous. Et pour le prochain rendez-vous de la communauté, ça sera les 8-9 octobre à Disneyland Paris pour le <a href="http://event.afup.org">Forum PHP 2026</a> : rejoignez-nous ! </strong></p>]]></description></item><item><title>Le Forum PHP 2026 est annonc&#xE9;</title><link>https://afup.org/news/1257-forumphp2026-annonce</link><author/><date>Sat, 23 May 2026 08:00:00 +0200</date><description><![CDATA[<p>Le Forum PHP revient à l'Hôtel New York - The Art of Marvel pour une cinquième édition consécutive, et un programme qui nous permettra d'y voir plus clair sur l'avenir de nos métiers. Venez apprendre, découvrir, progresser et vous perfectionner grâce à des conférences, ateliers, tables rondes et autres espaces d'échanges, et un but en tête : comment garder une longueur d'avance dans un secteur en pleine mutation.</p>
<h3>Un programme technique et proche des besoins actuels des devs</h3>
<p>Le Forum PHP est avant tout le rendez-vous de tout l'écosystème PHP. PHP pur, architecture, frameworks, performance, sécurité, qualité de code ou des intégrations, le programme visera à aller en profondeur sur ces sujets : c'est la grande force du Forum PHP !<br>
<strong>Mais notre environnement change, nos métiers évoluent : il faut en parler.</strong> L'IA s'étant invitée dans notre quotidien, elle sera forcément au programme de l'événement. On vous promet cependant une chose : pas de blabla, pas de plans sur la comète, mais l'accent porté sur le pragmatisme, l'actuel et le concret, ou comment rester des devs pertinent·e·s alors que le secteur se modifie en profondeur.<br>
Et c'est aussi tout l'environnement du développement qui se transforme. Souveraineté numérique, impact de l'IA sur l'open source, questions éthiques autour du code, intégration des juniors : ces sujets se vivent au quotidien dans nos équipes. <br><br>Parlons-en lors de conférences pointues, d’ateliers et d'espaces d'échanges pour dessiner ensemble notre avenir. L’appel à conférences est en cours jusqu’au 22 juin, 23h59 : <a href="http://cfp.afup.org">soumettez vos sujets</a> !</p>
<h3>La billetterie est ouverte, et on fait tout pour limiter l'impact sur vos budgets</h3>
<p>Si vous préférez rejoindre notre public, inscrivez-vous maintenant pour profiter du meilleur prix. La billetterie a ouvert au tarif "Les Yeux Fermés", c'est à dire avant la publication du programme (nombre de places limité à ce tarif). La place pour les membres AFUP est ainsi proposée à 170€HT, et la place pour le grand public est à 280€HT. <br><br>
<strong>Pour limiter l'impact du Forum PHP sur vos budgets,</strong> la matinée du jeudi démarrera vers 10h, vous permettant d'arriver avec les premiers trains sans manquer les conférences. <br>
Côté hébergement, la plateforme Disney, proposant des hôtels (et tickets pour les parcs) à un tarif préférentiel, est toujours disponible si vous souhaitez vous loger au plus près du Forum PHP. Notre partenariat avec les hôtels B&amp;B est également renouvelé, proposant cette année des tarifs préférentiels auprès de deux établissements.</p>
<h3>Entreprises ou freelances, sponsorisez le Forum PHP !</h3>
<p>Le soutien de l'écosystème est primordial pour l'AFUP et le Forum PHP : nous vous partageons depuis quelques temps les <a href="https://afup.org/news/1256-des-nouvelles-situation-afup-mai2026">difficultés que l'association rencontre</a>, en ces temps troublés.<br><br>
<strong>PHP est au coeur de votre activité ? </strong>Marquez votre soutien au langage et à l'association qui fédère les devs PHP en France. Demandez le <a href="https://afup.org/event/forumphp2026/sponsor/become-sponsor">dossier de sponsoring</a> : avec une variété de propositions s'adaptant à tous les budgets, il y a forcément une offre qui correspond à vos besoins.<br>
<strong>Vous êtes dev PHP ou freelance ? </strong> L'<a href="https://drive.google.com/file/d/1hkG2RKryeIa8aZLGyo27Qhv-F03n3JQy/view?usp=drive_link">offre de sponsoring Chocolat</a> vous permet de soutenir l'événement - et l'AFUP - à votre niveau ! Pour 800€HT, ce sponsoring vous donne accès à l'événement tout en apportant votre pierre à l'organisation de notre rassemblement. D'avance : MERCI </p>
<p><strong>Rien de tel que se réunir, dialoguer et partager nos points de vue, autour de notre passion commune pour le langage: membres du public, speakers, sponsors, rejoignez-nous !</strong></p>]]></description></item><item><title>Utiliser PHP avec Airflow</title><link>https://www.jdecool.fr/blog/2026/05/18/utiliser-php-avec-airflow.html</link><author/><date>Mon, 18 May 2026 00:00:00 +0200</date><description><![CDATA[<p>Je travaille actuellement sur un projet orienté BI pour lequel des scripts d’analyse et de visualisation de données sont écrits en Python et sont orchestrés par <a href="https://airflow.apache.org " target="_blank" rel="noopener noreferrer">Apache Airflow</a>. Airflow est un outil Python, initialement conçu et pensé pour l’écosystème Python. Mais travaillant essentiellement en PHP, je me suis demandé s’il était possible d’y faire tourner des scripts PHP.</p>

<!--more-->

<p>En parcourant la documentation d’Airflow, on peut se rendre compte que l’outil permet bien plus que d’exécuter des scripts Python. Si Python y est fortement intégré, en réalité, Airflow est capable d’exécuter n’importe quelle commande d’un terminal. Il est donc possible d’orchestrer tout type de script, et donc du PHP.</p>

<p>Les tâches (appelées DAG pour <em>Directed Acyclic Graph</em>) Airflow sont lancées au travers d’opérateur. Parmi ceux disponibles, l’opérateur <code>BashOperator</code> permet d’exécuter une commande dans un <em>worker</em> Airflow sur lequel serait présent PHP et le script à lancer.</p>

<p>Prenons par exemple le script PHP suivant:</p>

<figure class="highlight"><pre><code class="language-php" data-lang="php">&lt;?php

echo &quot;Hello from PHP script!\n&quot;;
echo &quot;Execution time: &quot; . date(&#39;Y-m-d H:i:s&#39;) . &quot;\n&quot;;</code></pre></figure>

<p>La création d’un DAG passe par l’écriture du script Python (c’est le seul moment où ce denier est nécessaire). La mise en place de notre tâche PHP pourrait se faire ainsi:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python">from airflow import DAG
from airflow.providers.standard.operators.bash import BashOperator
from datetime import datetime

with DAG(
    dag_id=&quot;run_php_script&quot;,
    description=&quot;A DAG that runs a PHP script&quot;,
    schedule=None,
    start_date=datetime(2024, 1, 1),
    catchup=False,
    tags=[&quot;php&quot;],
) as dag:

    run_php_script = BashOperator(
        task_id=&quot;run_php_script&quot;,
        bash_command=&quot;php /path/to/scripts/hello.php&quot;,
    )</code></pre></figure>

<p>Avec ce simple code Python, Airflow déclenche notre script PHP (qui lui peut être bien plus complexe). Les sorties standard et d’erreur sont automatiquement capturées dans les logs Airflow. On peut également bénéficier de toute la mécanique de <em>retry</em>, de monitoring et d’alerting Airflow.</p>

<p>Si vous désirez tester par vous même, vous trouverez tout le nécessaire dans <a href="https://github.com/jdecool/airflow-php-demo " target="_blank" rel="noopener noreferrer">ce dépôt</a> pour démarrer un environnement Airflow avec tout le nécessaire.</p>
]]></description></item><item><title>Comment int&#xE9;grer l'IA dans son workflow UX/UI</title><link>https://jolicode.com/blog/comment-integrer-l-ia-dans-son-workflow-ux-ui</link><author>JoliCode Team</author><date>Tue, 12 May 2026 12:42:00 +0200</date><description><![CDATA[<p><em><strong>&quot;On a déjà deux agents IA qui tournent en interne.&quot;</strong></em></p>
<p>Ce n'est pas en réunion que j'ai entendu ça. C'est en mission chez un client, dans leurs bureaux. Ce genre de phrase, je l'entends de plus en plus souvent maintenant, entre deux écrans, dans les couloirs, pendant la pause déjeuner. L'IA fait désormais partie du quotidien.</p>
<p>Quelques semaines plus tard, un autre client nous présente deux pages de maquettes générées par Claude, structure de page et premières intentions de contenu, pour expliquer ce qu'il voulait avant même d’ouvrir Figma.</p>
<p>C'est ce constat qui nous a poussés à écrire cet article, pour poser ce que ça change dans notre façon de travailler, et ce qu'on peut en tirer concrètement pour nos projets et nos clients.</p>
<p>Ce que j'ai trouvé en explorant, c'est que l'IA ne remplace pas le designer, elle redistribue son temps et son attention. Certaines tâches s'accélèrent, d'autres disparaissent, et de nouvelles compétences deviennent importantes. La principale d'entre elles, j'y reviendrai, est la capacité à formuler des demandes précises. Savoir parler à une IA, ça s'apprend et ça change tout à la qualité des résultats.</p>
<h2>Le prompt : la compétence clé</h2>
<p>Avant de parler workflow et processus, un point essentiel, la qualité des résultats dépend de la qualité de la demande. C'est le cœur du sujet.</p>
<p>Un bon prompt, c'est un bon brief. Précis, contextualisé, avec une intention claire et des contraintes définies. L'erreur la plus fréquente, celle que j'ai faite moi-même au début, c'est de rester trop vague en espérant que l'IA devine l'intention derrière la demande.</p>
<p><strong>ex : Prompt trop vague</strong><br />
<em>&quot;Génère un UX flow pour un site e-commerce.&quot;</em></p>
<p><picture class="js-dialog-target" data-original-url="/media/original/2026/workflow-ia-ux-ui/article-ai-design-01.jpg" data-original-width="1920" data-original-height="1080"><source type="image/webp" srcset="/media/cache/content-webp/2026/workflow-ia-ux-ui/article-ai-design-01.58842894.webp" /><source type="image/jpeg" srcset="/media/cache/content/2026/workflow-ia-ux-ui/article-ai-design-01.jpg" /><img loading="lazy" decoding="async" style="width: 996px; ; aspect-ratio: calc(1920 / 1080)" src="https://jolicode.com//media/cache/content/2026/workflow-ia-ux-ui/article-ai-design-01.jpg" alt="Génère un UX flow pour un site e-commerce" /></picture></p>
<p><strong>ex : Prompt structuré</strong><br />
<em>&quot;Génère un UX flow pour la page produit d'un site e-commerce de mode féminine, destiné à des femmes de 25–40 ans sur mobile. Inclure : découverte produit, sélection de taille, ajout au panier, paiement, et edge cases (taille indisponible, rupture de stock, code promo invalide). Format en étapes claires avec états d'erreur et micro-interactions.&quot;.</em></p>
<p><picture class="js-dialog-target" data-original-url="/media/original/2026/workflow-ia-ux-ui/article-ai-design-02.jpg" data-original-width="1920" data-original-height="1080"><source type="image/webp" srcset="/media/cache/content-webp/2026/workflow-ia-ux-ui/article-ai-design-02.cf2c3f4f.webp" /><source type="image/jpeg" srcset="/media/cache/content/2026/workflow-ia-ux-ui/article-ai-design-02.jpg" /><img loading="lazy" decoding="async" style="width: 996px; ; aspect-ratio: calc(1920 / 1080)" src="https://jolicode.com//media/cache/content/2026/workflow-ia-ux-ui/article-ai-design-02.jpg" alt="Génère un UX flow pour la page produit d’un site e-commerce " /></picture></p>
<p>La différence se joue sur <strong>la précision de la demande</strong>. Un prompt efficace s'articule autour de cinq dimensions. Elles font la différence entre un résultat générique et une base vraiment exploitable :</p>
<ol>
<li><strong>La tâche</strong> : Ce que l’IA doit faire concrètement, générer un écran, proposer un flow, décliner un composant.</li>
<li><strong>Le contexte</strong> : Où s'intègre cet écran ou ce parcours dans l'expérience globale ? Quel est l'état précédent ?</li>
<li><strong>Les éléments clés du design</strong> : Les caractéristiques visuelles ou fonctionnelles importantes que l'IA doit intégrer dans sa proposition.</li>
<li><strong>Les comportements attendus</strong> : Comment réagissent ces éléments lors de l'interaction (hover, tap, scroll, états vides, erreurs…)</li>
<li><strong>Les contraintes :</strong> Le support, la mise en page, le style visuel, les guidelines de marque. Plus ces contraintes sont précises, plus le résultat est pertinent.</li>
</ol>
<p>J'aime les métaphores, alors en voici une : l'IA ressemble à un nouveau collaborateur. Il ne connaît pas encore le contexte, les clients, les process. Sans brief précis, il produit quelque chose de générique. Bien briefé, il va vite et produit une base exploitable.</p>
<p>Apprendre à prompter, c'est apprendre à collaborer avec l'IA, formuler ce qu'on veut, donner le bon niveau de contexte, et itérer ensemble.</p>
<h2>Intégrer l'IA à chaque étape</h2>
<p><strong>Maintenant qu'on a posé ce cadre, voyons comment ça se traduit dans le travail. J'ai organisé cette exploration en trois phases : explorer, concevoir et produire. L'IA n'y joue pas le même rôle à chaque étape.</strong></p>
<p>Une précision importante avant de commencer : &quot;plus vite&quot; ne veut pas dire &quot;sans effort&quot;. Une partie du temps gagné en génération est réinvestie en correction, ajustement et validation. L'IA produit une base et non un livrable.</p>
<p><strong>Explorer plus vite et plus largement</strong></p>
<p>Un brief flou est l'une des principales sources de perte de temps en début de projet. Avant, cela voulait dire plusieurs allers-retours avant même de commencer à concevoir. Aujourd'hui, on peut envoyer le brief tel quel à un agent conversationnel, demander une structure claire avec l'utilisateur cible, le problème, les objectifs attendus et les questions de clarification manquantes, et avoir une base de travail en quelques minutes.</p>
<p>Sur les UX flows, même constat. L'IA est capable de générer rapidement plusieurs scénarios, explorer des alternatives et identifier des états qu'on aurait pu oublier (erreurs, compte vide, connexion perdue..). La décision reste entièrement humaine : simplifier, prioriser, arbitrer.</p>
<p><strong>Concevoir et itérer sans blocage</strong></p>
<p>Pour cette phase de conception, j'ai testé spécifiquement deux outils : Figma Make et Claude Design.</p>
<p>Figma Make permet de générer plusieurs propositions d'écrans à partir d'une description ou de frames, de tester des patterns qu'on n'aurait pas spontanément tentés, et d'explorer des déclinaisons visuelles rapidement. C'est un véritable moteur d'expérimentation. Par défaut, il génère des écrans génériques, sans personnalité, sans cohérence graphique. Cette limite se réduit selon ce qu'on lui fournit un prompt détaillé ou en faisant plusieurs itérations.</p>
<p>Claude Design va plus loin dans l'aboutissement. La richesse de l'interface et la finesse des ajustements possibles sont intéressantes. C'est à ce jour la solution qui produit les prototypes les plus aboutis parmi celles que j'ai testées. Si le prompt semble insuffisant, Claude Design propose automatiquement un questionnaire pour affiner la demande avant de générer.</p>
<p><picture class="js-dialog-target" data-original-url="/media/original/2026/workflow-ia-ux-ui/article-ai-design-03.jpg" data-original-width="1920" data-original-height="1080"><source type="image/webp" srcset="/media/cache/content-webp/2026/workflow-ia-ux-ui/article-ai-design-03.424b3206.webp" /><source type="image/jpeg" srcset="/media/cache/content/2026/workflow-ia-ux-ui/article-ai-design-03.jpg" /><img loading="lazy" decoding="async" style="width: 996px; ; aspect-ratio: calc(1920 / 1080)" src="https://jolicode.com//media/cache/content/2026/workflow-ia-ux-ui/article-ai-design-03.jpg" alt="Exemple de Claude" /></picture></p>
<p>Autre détail qui a son importance, Claude Design propose deux modes d'itération complémentaires : le chat pour les changements globaux, et les commentaires inline pour les ajustements précis sur un élément spécifique. En pratique, ça permet de générer rapidement des visuels utiles pour aligner une équipe, transformer une idée produit en prototype testable.</p>
<p><picture class="js-dialog-target" data-original-url="/media/original/2026/workflow-ia-ux-ui/article-ai-design-04.jpg" data-original-width="1920" data-original-height="1080"><source type="image/webp" srcset="/media/cache/content-webp/2026/workflow-ia-ux-ui/article-ai-design-04.5660d1fd.webp" /><source type="image/jpeg" srcset="/media/cache/content/2026/workflow-ia-ux-ui/article-ai-design-04.jpg" /><img loading="lazy" decoding="async" style="width: 996px; ; aspect-ratio: calc(1920 / 1080)" src="https://jolicode.com//media/cache/content/2026/workflow-ia-ux-ui/article-ai-design-04.jpg" alt="Exemple de Claude" /></picture></p>
<p>Dans les deux cas, ce que ces outils produisent est une base de travail, pas un livrable finalisé. Le designer reste celui qui la transforme en proposition exploitable.</p>
<p>Au-delà des écrans, l'IA change aussi la façon de rédiger les interfaces. Sur le microcopy, ces petits textes qui guident l'utilisateur à chaque étape, c'est une aide précieuse. En définissant le ton et le style de communication de la marque dans le prompt, on peut générer rapidement des textes cohérents pour tous les états d'interface : succès, erreur, chargement, état vide.</p>
<p><strong>Produire et livrer plus efficacement</strong></p>
<p>Le handoff, ce moment où le designer passe le relais au développeur, est souvent un moment de friction. L'IA ne le supprime pas, mais elle le fluidifie. En connectant Figma à Claude via le protocole MCP (Model Context Protocol), Claude peut lire directement la structure du fichier (composants, calques, propriétés…) et produire une base de documentation de specs. C'est une piste que je n'ai pas encore testée personnellement, mais qui mérite d'être mentionnée. La qualité du résultat dépendrait directement de la structure du fichier Figma : un fichier bien nommé avec des composants propres donnerait un résultat exploitable.</p>
<p>C'est d'ailleurs une règle qui vaut pour tout l'article : l'IA amplifie que ce qui est déjà structuré. Un design system solide n'est plus seulement un outil de cohérence visuelle, c'est ce qui rend le travail exploitable par l'IA.</p>
<h2>Et côté client, qu’est-ce que ça change vraiment ?</h2>
<p>Les deux anecdotes d'introduction ne sont pas des cas isolés, elles disent quelque chose d'important : le niveau de préparation et d'attente des clients change. Quand un client arrive avec des maquettes générées par IA pour illustrer son idée, il ne cherche pas à faire le travail du designer. Il cherche à être compris plus vite, à aligner plus tôt, à éviter les allers-retours.</p>
<p>Pour le designer, ça change la nature de la conversation. On ne part plus d'une page blanche commune, on part d'idées déjà mises en forme, parfois précises, parfois approximatives mais toujours révélatrices de ce que le client a en tête. Le client montre une direction, une intention mais pas une solution. C'est au designer de définir ce qu'il y a derrière.</p>
<p>La prochaine étape naturelle, que j'imagine, c'est l'atelier client en live : générer des variantes d'écrans directement en réunion pour itérer en temps réel sur une direction. L'enjeu est simple : aligner plus vite.</p>
<h2>Pour conclure</h2>
<p>L'IA transforme réellement la pratique du design. Elle accélère certaines étapes, structure des réflexions qui prenaient du temps, et ouvre des possibilités d'exploration.</p>
<p>Elle ne connaît pas les utilisateurs, leurs habitudes, leurs frustrations, ce qui les fait décrocher. La créativité graphique, la sensibilité visuelle, la cohérence d'une marque, tout cela reste entièrement humain. Mais elle propose des directions qu'un designer seul n'aurait pas explorées, pas parce qu'elles sont meilleures, mais parce qu'elle n'a pas nos biais.</p>
<p>Sur un prochain sujet, nous pourrons approfondir un aspect particulier : comment configurer Claude avec des instructions personnalisées (system prompts, custom instructions) pour lui donner un contexte permanent et des compétences adaptées à la pratique du designer.</p>]]></description></item><item><title>Derni&#xE8;re semaine de billetterie pour l'AFUP Day 2026</title><link>https://afup.org/news/1255-derniere-semaine-billetterie-afupday2026</link><author/><date>Mon, 11 May 2026 06:43:00 +0200</date><description><![CDATA[<h3>Un événement qualitatif où que vous choisissiez d'aller</h3>
<p>Tech pure, arrivée fracassante de l'IA dans notre quotidien, bonnes pratiques, outils qui facilitent le quotidien, sujets plus sociaux ou managériaux... Une chose est sûre : quelle que soit la destination que vous choisirez, comptez sur une programmation de qualité, proposée par des expert·e·s trié·e·s sur le volet, qui vous permettront de voir vos projets sous un nouvel angle. <br>
Alors que notre secteur est en pleine mutation, échanger, partager et réfléchir collectivement n'a jamais été aussi précieux. C'est ensemble qu'on avance le mieux ! </p>
<h3>La place au tarif de soutien</h3>
<p>La billetterie propose actuellement les billets à 110€HT, quelle que soit l'édition à laquelle vous souhaitez participer. Une journée de conférences techniques et actuelles, qui soufflera un vent d'inspiration et de fraîcheur pour aborder vos projets avec un nouvel oeil ! <br>
Prendre votre place a aussi un impact particulier pour l'AFUP. Nous vous parlons depuis quelques semaines de nos inquiétudes pour l'avenir de l'AFUP. En prenant vos places pour l'AFUP Day 2026, non seulement vous profitez du partage de connaissances, mais vous apportez également un soutien significatif à l'association. Nos conférences sont des parenthèses précieuses pour que la diffusion des connaissances en PHP continue : chaque action compte ! </p>
<p>Alors<a href="https://event.afup.org"> prenez vos places</a> d'ici vendredi 15 mai, 23h59, et rejoignez-nous lors de ce grand rassemblement de la communauté PHP !</p>]]></description></item><item><title>Faire des requ&#xEA;tes CTE avec Doctrine ORM en PHP</title><link>https://www.jdecool.fr/blog/2026/05/09/faire-des-requetes-cte-avec-doctrine-orm-en-php.html</link><author/><date>Sat, 09 May 2026 00:00:00 +0200</date><description><![CDATA[<p>J’ai déjà évoqué sur ce blog que <a href="/blog/2026/02/18/boring-technology-les-bases-de-donnees-relationnelles.html">le développement “moderne” avec les ORM masque les fonctionnalités avancées des SGBD</a> au point que dorénavant les développeurs ne maitrisent et ne connaissent guère plus que le classique <code>SELECT ... FROM ... WHERE ...</code>.</p>

<p>Dans les mécanismes méconnus et qui pourtant, pourrait permettre de soulager certains traitements applicatifs, on retrouve les CTE (<em>Common Table Expressions</em>). Voyons comment les utiliser avec Doctrine ORM en PHP.</p>

<!--more-->

<p>Commençons par expliquer ce qu’est une CTE. Il s’agit d’une sous-requête utilisable comme une table temporaire. C’est une technique utile pour décomposer des requêtes complexes, éviter la duplication de sous-sélections ou écrire des requêtes récursives.</p>

<p>En presque 20 ans d’expérience professionnelle, j’ai très rarement eu l’occasion d’en voir dans le code que je peux être amené à parcourir quotidiennement. Prenons par exemple, un blog où les articles sont rattachés à des catégories, ces dernières organisées sous forme arborescente sous la forme <code>Category(id, label, parent_id)</code>. Je suis certain que, si l’on demande à un développeur de récupérer toutes les catégories parentes d’une catégorie précise, ce dernier fera le traitement en PHP avec un code ressemblant au suivant:</p>

<figure class="highlight"><pre><code class="language-php" data-lang="php">// src/Repository/CategoryRepository.php
class CategoryRepository extends ServiceEntityRepository
{
    // ...

    /**
     * @return list&lt;Category&gt;
     */
    function getCatagoriesWithParents(int $categoryId): array
    {
        $category = $this-&gt;categoryRepository-&gt;find($categoryId);

        $categories = [
            $category,
        ];

        while ($parent = $category-&gt;getParent()) {
            $categories[] = $parent;
        }

        return $categories;
    }
}</code></pre></figure>

<p>L’inconvénient majeur ici est qu’en interne, Doctrine va faire une requête à chaque tour de boucle. C’est ce que l’on appelle <a href="/blog/2015/12/17/le-probleme-n+1.html">le problème N+1</a>. Pour résoudre ce problème, via une requête SQL, il n’est pas possible de faire un simple <code>SELECT ... FROM ... WHERE</code>, c’est exactement dans ce cas qu’une CTE récursive est utile. Cette dernière peut être écrite de la manière suivante:</p>

<figure class="highlight"><pre><code class="language-sql" data-lang="sql">WITH RECURSIVE cte_category AS (
    -- point de départ
    SELECT id, label, parent_id, 0 AS depth
    FROM category
    WHERE id = :id

    UNION ALL

    -- récursivité
    SELECT c.id, c.label, c.parent_id, cp.depth + 1
    FROM category c
    INNER JOIN cte_category cp ON c.id = cp.parent_id
)
SELECT id, label, parent_id FROM cte_category
ORDER BY depth DESC</code></pre></figure>

<p>La requête commence par récupérer la catégorie identifiée par <code>:id</code>, puis se joint récursivement à elle-même en remontant via <code>parent_id</code> jusqu’à ce qu’il n’y ait plus de parent. La colonne <code>depth</code> permet ensuite de trier du plus ancien ancêtre jusqu’à la catégorie cible.</p>

<p>Le problème pour faire cette requête avec un ORM tel que Doctrine, c’est que ce dernier ne permet pas de gérer les CTE nativement au travers de son <code>QueryBuilder</code> ni même en <code>DQL</code>. Il est alors nécessaire de passer par une requête native où le résultat final sera mappé sur un objet.</p>

<figure class="highlight"><pre><code class="language-php" data-lang="php">// src/Repository/CategoryRepository.php
class CategoryRepository extends ServiceEntityRepository
{
    // ...

    public function findAllWithParents(int $categoryId)
    {
        $rsm = new ResultSetMappingBuilder($this-&gt;getEntityManager());
        $rsm-&gt;addRootEntityFromClassMetadata(Category::class, &#39;c&#39;);

        $sql = &lt;&lt;&lt;SQL
            WITH RECURSIVE with_categories AS (
                SELECT id, label, parent_id, 0 AS depth
                FROM category
                WHERE id = :id

                UNION ALL

                SELECT c.id, c.label, c.parent_id, cp.depth + 1
                FROM category c
                INNER JOIN with_categories cp ON c.id = cp.parent_id
            )
            SELECT id, label, parent_id FROM with_categories
            ORDER BY depth DESC
        SQL;

        return $this-&gt;getEntityManager()
            -&gt;createNativeQuery($sql, $rsm)
            -&gt;setParameter(&#39;id&#39;, $categoryId)
            -&gt;getResult();
    }
}</code></pre></figure>

<p>Dans le code précédent, le <code>ResultSetMappingBuilder</code> se charge de faire correspondre les colonnes retournées avec les propriétés de l’entité <code>Category</code>. On obtient alors en sortie une liste d’instances de <code>Category</code> ordonnés de la racine jusqu’à la catégorie demandée.</p>

<p>Avec ce type de requête, on peut faire énormément de choses, il est par exemple, possible de récupérer tous les articles appartenant à une catégorie enfant en réutilisant la requête précédente:</p>

<figure class="highlight"><pre><code class="language-sql" data-lang="sql">WITH RECURSIVE category_path AS (
    SELECT id, label, parent_id
    FROM category
    WHERE id = :id

    UNION ALL

    SELECT c.id, c.label, c.parent_id
    FROM category c
    INNER JOIN category_path cp ON c.id = cp.parent_id
)
SELECT a.id, a.title, a.category_id
FROM article a
INNER JOIN category_path cp ON a.category_id = cp.id
ORDER BY a.published_date DESC</code></pre></figure>

<p>En une seule requête, on récupère tous les articles liés à la catégorie cible ou à n’importe lequel de ses ancêtres. Sans CTE récursive, il faudrait soit faire plusieurs requêtes successives pour parcourir la hiérarchie une approche bien moins efficace. Un point d’attention néanmoins, étant donné que l’on exécute une requête SQL native, il faudra faire attention à la portabilité de cette dernière, les CTE pouvant ne pas être disponible sur toutes les bases de données supportées par Doctrine.</p>

<p>De plus, l’utilisation du <code>ResultSetMappingBuilder</code> nécessite que le <code>SELECT</code> de la requête contienne l’ensemble des colonnes nécessaire à l’alimentation de l’objet. À défaut, l’entité sera hydratée de manière incomplète sans qu’aucune erreur explicite ne soit levée.</p>
]]></description></item><item><title>Claude Code, Cursor, Symfony/AI, Vercel AI SDK : 3 formations pour garder la main</title><link>https://jolicode.com/blog/claude-code-cursor-symfony-ai-vercel-ai-sdk-3-formations-pour-garder-la-main</link><author>JoliCode Team</author><date>Tue, 28 Apr 2026 15:42:00 +0200</date><description><![CDATA[<p>Trois formations IA sont disponibles dès maintenant sur <a href="https://jolicampus.com">JoliCampus</a>, avec des sessions ouvertes à l'inscription. Elles sont construites sur ce qu'on pratique chez JoliCode et Premier Octet : des projets clients qui tournent en production avec des agents de code IA, des outils qu'on utilise tous les jours, des patterns qu'on a éprouvés, testés et parfois jetés !</p>
<p>Voici ce qu'elles contiennent :</p>
<h2>Maîtriser les agents IA pour le développement</h2>
<p><picture class="js-dialog-target" data-original-url="/media/original/2026/formation-ia-article/1.png" data-original-width="1600" data-original-height="900"><source type="image/webp" srcset="/media/cache/content-webp/2026/formation-ia-article/1.00556eaa.webp" /><source type="image/png" srcset="/media/cache/content/2026/formation-ia-article/1.png" /><img loading="lazy" decoding="async" style="width: 996px; ; aspect-ratio: calc(1600 / 900)" src="https://jolicode.com//media/cache/content/2026/formation-ia-article/1.png" alt="les agents IA pour le développement" /></picture></p>
<p>La porte d'entrée ! Une journée pour sortir de l'autocomplete et entrer dans le travail avec des agents. Au programme : fonctionnement réel d'un agent (mémoire, contexte, permissions), standardisation des conventions d'équipe via le fichier <code>AGENTS.md</code> (agnostique, fonctionne avec Claude Code, Codex, Cursor), utilisation de l'agent comme QA automatisée avec génération de tests, lecture des logs et boucle d'auto-réparation sous supervision, puis création de commandes personnalisées pour industrialiser chez vous les revues de code, la détection de N+1, de failles XSS, et les patterns propres à votre équipe.</p>
<p><a href="https://jolicampus.com/formations/maitriser-les-agents-ia-pour-le-developpement">Voir la formation</a></p>
<h2>L'IA pour développeurs frontend, avec Cursor et le Vercel AI SDK</h2>
<p><picture class="js-dialog-target" data-original-url="/media/original/2026/formation-ia-article/2.png" data-original-width="1600" data-original-height="900"><source type="image/webp" srcset="/media/cache/content-webp/2026/formation-ia-article/2.97fd7971.webp" /><source type="image/png" srcset="/media/cache/content/2026/formation-ia-article/2.png" /><img loading="lazy" decoding="async" style="width: 996px; ; aspect-ratio: calc(1600 / 900)" src="https://jolicode.com//media/cache/content/2026/formation-ia-article/2.png" alt="IA pour développeurs frontend" /></picture></p>
<p>Les deux faces du sujet. Jour 1 côté workflow : Cursor dans ses trois modes (Ask, Plan, Agent), commandes custom, intégration d'outils via MCP, <code>.cursorrules</code> pour que l'agent respecte l'architecture du projet, gestion du contexte sur les sessions longues.</p>
<p>Jour 2 côté produit : construire un vrai assistant avec le Vercel AI SDK (streaming, tool calling, architecture RAG sur vos contenus, typage strict avec Zod), déploiement et les questions qui vont avec (coûts API, monitoring, stratégies de cache).</p>
<p><a href="https://jolicampus.com/formations/ia-developpement-frontend-cursor-vercel-ai-sdk">Voir la formation</a></p>
<h2>Maîtriser l'IA avec Symfony</h2>
<p><picture class="js-dialog-target" data-original-url="/media/original/2026/formation-ia-article/3.png" data-original-width="1600" data-original-height="900"><source type="image/webp" srcset="/media/cache/content-webp/2026/formation-ia-article/3.1a9a7438.webp" /><source type="image/png" srcset="/media/cache/content/2026/formation-ia-article/3.png" /><img loading="lazy" decoding="async" style="width: 996px; ; aspect-ratio: calc(1600 / 900)" src="https://jolicode.com//media/cache/content/2026/formation-ia-article/3.png" alt="IA avec Symfony" /></picture></p>
<p>Pour intégrer l'IA générative dans vos applications Symfony avec la même rigueur que le reste de votre code. On s'appuie <a rel="nofollow noopener noreferrer" href="https://ai.symfony.com/">sur Symfony/AI</a>, le composant officiel de l'écosystème, auquel on contribue directement.</p>
<p>Socle de 2 jours : fondamentaux des LLM, ChatInterface, typage fort des sorties en objets PHP, RAG complet sur vos documents avec pgvector. Puis deux modules optionnels : le premier transforme votre application en orchestrateur d'agents capables d'appeler votre code PHP et vos APIs internes, le second aborde MCP, le McpBundle, les stratégies de test, le monitoring en production et la maîtrise des coûts.</p>
<p><a href="https://jolicampus.com/formations/maitriser-ia-symfony">Voir la formation</a></p>
<h2>Et si vous ne codez pas ?</h2>
<p>POs, chefs de projets, dirigeantes et dirigeants de boîte tech : l'IA change aussi la façon dont vos équipes travaillent, à quelle cadence, sur quelle échelle de temps. Notre formation <a href="https://jolicampus.com/formations/culture-engineering">Culture Engineering</a> a un module dédié pour comprendre ce qui bouge pour mieux collaborer avec vos équipes tech.</p>
<h2>Pourquoi chez nous ?</h2>
<p>On forme sur ce qu'on pratique. Nos projets tournent en production avec ces outils, on contribue directement à Symfony/AI, et on publie sur <a href="https://jolicode.com/blog/tag/ia">notre blog</a> ce qu'on apprend. Ces formations vont évoluer avec l'écosystème. Certains modules seront réécrits dans six mois. Un participant d'aujourd'hui peut revenir dans un an sur une session mise à jour, et ce sera la suite logique, pas une répétition 🙂</p>
<p>Toutes nos formations <a href="https://jolicampus.com/formations/financement">sont certifiées Qualiopi, finançables via les OPCO</a>, et modulables en intra-entreprise. Un doute sur la formation qui correspond à votre équipe ? <a href="https://jolicampus.com/contact">Écrivez-nous !</a>.</p>]]></description></item><item><title>Plan de migration vers Tailwind CSS v4 &#x1F680; : la m&#xE9;thode (presque) sans douleur</title><link>https://jolicode.com/blog/plan-de-migration-vers-tailwind-css-v4-la-methode-presque-sans-douleur</link><author>JoliCode Team</author><date>Mon, 27 Apr 2026 10:20:00 +0200</date><description><![CDATA[<p>Ça y est, le grand jour est arrivé ! Vous avez enfin décidé de vous attaquer à cette fameuse dette technique qui vous fait faire des cauchemars la nuit. 😅</p>
<p>Beaucoup de nos projets (et sans doute les vôtres) reposent encore sur d’anciens frameworks CSS basés sur <a rel="nofollow noopener noreferrer" href="https://sass-lang.com/">Sass</a>, comme <a rel="nofollow noopener noreferrer" href="https://getbootstrap.com/docs/4.6/getting-started/introduction/">Bootstrap v4</a> ou d'autres solutions maison (coucou <a href="https://jolicode.com/blog/notre-framework-dinterface-atomic-builder-partie-2">Atomic Builder</a> 👋). Et soyons honnêtes : maintenir et mettre à jour ces projets relève souvent du parcours du combattant. On fait face à un manque cruel de flexibilité, la dette technique s’accumule, et les dépendances commencent à poser plus de soucis qu'elles n'en résolvent.</p>
<p>Sans oublier que Sass, bien qu’ayant été notre fidèle compagnon pendant de très (très) nombreuses années, devient de moins en moins indispensable avec l'évolution fulgurante du CSS natif. Nous avons d’ailleurs eu l’occasion d’en parler dans notre article expliquant pourquoi <a href="https://jolicode.com/blog/passer-a-postcss-pour-un-projet-sans-sass">passer à PostCSS</a>.</p>
<p>Le choix s'est donc naturellement porté vers <strong>Tailwind CSS v4</strong>. C'est une solution qui a fait ses preuves chez JoliCode (<a href="https://jolicode.com/blog/jai-teste-tailwind-css">J'ai testé Tailwind CSS</a>) et, cerise sur le gâteau, cette nouvelle version ne nécessite aucune dépendance liée à Sass.</p>
<p>L’année dernière, nous avons amorcé des phases de migration pour plusieurs de nos projets clients. Aujourd’hui, j’ai envie de partager avec vous quelques retours d’expérience et une méthodologie (testée et approuvée ✅) pour que ces migrations se passent le mieux possible !</p>
<h2>Phase 1 : Migration des feuilles de styles CSS 🎨</h2>
<p>L’objectif de cette première phase est simple : utiliser du CSS natif et dire officiellement au revoir à notre bon vieux Sass. 👋</p>
<p>La toute première étape consiste à migrer la configuration globale de votre projet (généralement définie dans des fichiers tels que <code>variables.scss</code> ou <code>global.scss</code>) vers un système basé sur des <a rel="nofollow noopener noreferrer" href="https://developer.mozilla.org/fr/docs/Web/CSS/Reference/Properties/--*"><em>custom properties</em></a> (variables CSS), en ciblant la racine du document HTML via la pseudo-classe <a rel="nofollow noopener noreferrer" href="https://developer.mozilla.org/fr/docs/Web/CSS/Reference/Selectors/:root"><code>:root</code></a>.</p>
<p><strong>Avant (Sass) :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-2">$color-primary: </span><span class="syntax-3">#f7d325</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-2">$color-secondary: </span><span class="syntax-3">#ff2951</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-2">$font-family-sans-serif: </span><span class="syntax-1">"Source Sans Pro"</span><span class="syntax-2">, </span><span class="syntax-9">sans-serif</span><span class="syntax-2">;</span></span></code></pre>
<p><strong>Après (CSS natif) :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-8">:root</span><span class="syntax-2"> {</span></span>
<span class="line"><span class="syntax-2">  --color-primary: </span><span class="syntax-3">#f7d325</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-2">  --color-secondary: </span><span class="syntax-3">#ff2951</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-2">  --font-sans: </span><span class="syntax-1">"Source Sans Pro"</span><span class="syntax-2">, </span><span class="syntax-9">sans-serif</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-2">}</span></span></code></pre>

<div class="c-alert c-alert--note">
    <p class="c-alert__title">
                    <span class="c-icon c-icon--monospace">
                <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="c-icon__svg" focusable="false" viewBox="0 0 70 71"><path fill-rule="nonzero" d="M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9"/></svg>
            </span>
                        <strong>Info</strong>
    </p>
    <div class="c-alert__content">
                <p><br />
Utilisez dès maintenant la <a rel="nofollow noopener noreferrer" href="https://tailwindcss.com/docs/theme#default-theme-variable-reference">nomenclature de Tailwind CSS v4</a> pour nommer vos variables CSS. Ça vous fera gagner un temps précieux pour la suite. 👌</p>
        </div>
</div>

<h3>Cas n°1 : Les variables et fonctions</h3>
<p>Une fois votre thème mis en place, votre première mission sera de remplacer tous vos <code>$</code> par des <code>var(--)</code>. C'est aussi l'occasion de traduire vos fonctions Sass en CSS moderne. 👀</p>
<p><strong>Avant (Sass) :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-8">.button</span><span class="syntax-2"> {</span></span>
<span class="line"><span class="syntax-5">  width</span><span class="syntax-2">: math.</span><span class="syntax-9">div</span><span class="syntax-2">(</span><span class="syntax-3">100</span><span class="syntax-4">%</span><span class="syntax-2">, </span><span class="syntax-3">3</span><span class="syntax-2">);</span></span>
<span class="line"><span class="syntax-5">  background-color</span><span class="syntax-2">: $color-primary;</span></span>
<span class="line"><span class="syntax-2">  </span></span>
<span class="line"><span class="syntax-4">  &#x26;</span><span class="syntax-8">:hover</span><span class="syntax-2">,</span></span>
<span class="line"><span class="syntax-4">  &#x26;</span><span class="syntax-8">:focus-visible</span><span class="syntax-2">,</span></span>
<span class="line"><span class="syntax-4">  &#x26;</span><span class="syntax-8">:active</span><span class="syntax-2"> {</span></span>
<span class="line"><span class="syntax-5">    background-color</span><span class="syntax-2">: </span><span class="syntax-9">darken</span><span class="syntax-2">($color-primary, </span><span class="syntax-3">15</span><span class="syntax-4">%</span><span class="syntax-2">);</span></span>
<span class="line"><span class="syntax-2">  }</span></span>
<span class="line"><span class="syntax-2">}</span></span></code></pre>
<p><strong>Après (CSS natif) :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-8">.button</span><span class="syntax-2"> {</span></span>
<span class="line"><span class="syntax-10">  /* Le CSS natif s'occupe des calculs dynamiquement ! */</span></span>
<span class="line"><span class="syntax-5">  width</span><span class="syntax-2">: </span><span class="syntax-9">calc</span><span class="syntax-2">(</span><span class="syntax-3">100</span><span class="syntax-4">%</span><span class="syntax-4"> /</span><span class="syntax-3"> 3</span><span class="syntax-2">);</span></span>
<span class="line"><span class="syntax-5">  background-color</span><span class="syntax-2">: </span><span class="syntax-9">var</span><span class="syntax-2">(--color-primary);</span></span>
<span class="line"><span class="syntax-2">  </span></span>
<span class="line"><span class="syntax-4">  &#x26;</span><span class="syntax-8">:hover</span><span class="syntax-2">,</span></span>
<span class="line"><span class="syntax-4">  &#x26;</span><span class="syntax-8">:focus-visible</span><span class="syntax-2">,</span></span>
<span class="line"><span class="syntax-4">  &#x26;</span><span class="syntax-8">:active</span><span class="syntax-2"> {</span></span>
<span class="line"><span class="syntax-10">    /* Magie ! Les couleurs relatives en CSS natif */</span></span>
<span class="line"><span class="syntax-5">    background-color</span><span class="syntax-2">: </span><span class="syntax-9">hsl</span><span class="syntax-2">(</span><span class="syntax-12">from</span><span class="syntax-9"> var</span><span class="syntax-2">(--color-primary) </span><span class="syntax-12">h</span><span class="syntax-12"> s</span><span class="syntax-9"> calc</span><span class="syntax-2">(</span><span class="syntax-12">l</span><span class="syntax-4"> -</span><span class="syntax-3"> 15</span><span class="syntax-2">)); </span></span>
<span class="line"><span class="syntax-2">  }</span></span>
<span class="line"><span class="syntax-2">}</span></span></code></pre>
<p>Aujourd'hui, le CSS natif est devenu suffisamment mature pour remplacer la quasi-totalité des fonctions Sass. C’est particulièrement vrai pour les manipulations de couleurs, qui s’écrivent désormais très simplement grâce aux <a href="https://jolicode.com/blog/les-couleurs-relatives-en-css">couleurs relatives en CSS</a>. 🎨</p>
<h3>Cas n°2 : Le piège du sélecteur d’imbrication <code>&amp;</code> et du BEM</h3>
<p><strong>La bonne nouvelle</strong>, c'est que le CSS natif gère désormais très bien le <a rel="nofollow noopener noreferrer" href="https://developer.mozilla.org/fr/docs/Web/CSS/Reference/Selectors/Nesting_selector"><em>nesting</em></a> (l'imbrication). L'esperluette (<code>&amp;</code>) pour cibler des états comme <code>&amp;:hover</code> ou <code>&amp;:focus</code> fonctionne parfaitement sans Sass.</p>
<p><strong>La mauvaise nouvelle ?</strong> Si vous utilisiez le <code>&amp;</code> pour concaténer des classes, typiquement avec la <a rel="nofollow noopener noreferrer" href="https://getbem.com/">méthodologie BEM</a> (<code>&amp;__element</code> ou <code>&amp;--modifier</code>), le CSS natif ne comprendra pas cette syntaxe. Il va falloir « désimbriquer » ces éléments.</p>
<p><strong>Avant (Sass) :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-8">.link</span><span class="syntax-2"> {</span></span>
<span class="line"><span class="syntax-5">  color</span><span class="syntax-2">: $color-primary;</span></span>
<span class="line"><span class="syntax-2">  </span></span>
<span class="line"><span class="syntax-4">  &#x26;</span><span class="syntax-8">:hover</span><span class="syntax-2">,</span></span>
<span class="line"><span class="syntax-4">  &#x26;</span><span class="syntax-8">:focus-visible</span><span class="syntax-2">,</span></span>
<span class="line"><span class="syntax-4">  &#x26;</span><span class="syntax-8">:active</span><span class="syntax-2"> {</span></span>
<span class="line"><span class="syntax-5">    color</span><span class="syntax-2">: $color-secondary;</span></span>
<span class="line"><span class="syntax-2">  }</span></span>
<span class="line"></span>
<span class="line"><span class="syntax-10">  /* Sass va compiler ce code en ".link--secondary" */</span></span>
<span class="line"><span class="syntax-4">  &#x26;</span><span class="syntax-2">--secondary {</span></span>
<span class="line"><span class="syntax-5">    color</span><span class="syntax-2">: $color-secondary;</span></span>
<span class="line"><span class="syntax-2">  }</span></span>
<span class="line"><span class="syntax-2">}</span></span></code></pre>
<p><strong>Après (CSS natif) :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-8">.link</span><span class="syntax-2"> {</span></span>
<span class="line"><span class="syntax-5">  color</span><span class="syntax-2">: </span><span class="syntax-9">var</span><span class="syntax-2">(--color-primary);</span></span>
<span class="line"><span class="syntax-2">  </span></span>
<span class="line"><span class="syntax-10">  /* Ce code fonctionne toujours en CSS natif ! */</span></span>
<span class="line"><span class="syntax-4">  &#x26;</span><span class="syntax-8">:hover</span><span class="syntax-2">,</span></span>
<span class="line"><span class="syntax-4">  &#x26;</span><span class="syntax-8">:focus-visible</span><span class="syntax-2">,</span></span>
<span class="line"><span class="syntax-4">  &#x26;</span><span class="syntax-8">:active</span><span class="syntax-2"> {</span></span>
<span class="line"><span class="syntax-5">    color</span><span class="syntax-2">: </span><span class="syntax-9">var</span><span class="syntax-2">(--color-secondary);</span></span>
<span class="line"><span class="syntax-2">  }</span></span>
<span class="line"><span class="syntax-2">}</span></span>
<span class="line"></span>
<span class="line"><span class="syntax-10">/* Il faut sortir la classe enfant de l'imbrication */</span></span>
<span class="line"><span class="syntax-8">.link--secondary</span><span class="syntax-2"> {</span></span>
<span class="line"><span class="syntax-5">  color</span><span class="syntax-2">: </span><span class="syntax-9">var</span><span class="syntax-2">(--color-secondary);</span></span>
<span class="line"><span class="syntax-2">}</span></span></code></pre>
<h3>Cas n°3 : Les media queries</h3>
<p>Pour les <a rel="nofollow noopener noreferrer" href="https://developer.mozilla.org/fr/docs/Web/CSS/Guides/Media_queries/Using"><em>media queries</em></a> générées via des mixins ou des fonctions Sass (du type <code>@include media-breakpoint-up(md)</code>), passez simplement les valeurs en dur pour le moment (<code>@media (min-width: 768px)</code>). Nous nous en occuperons plus tard, lors de l'intégration de Tailwind.</p>
<h3>Cas n°4 : Les mixins, boucles et règles complexes</h3>
<p>Pour les cas très spécifiques (boucles <code>@for</code>, mixins complexes), la solution la plus pragmatique est de récupérer la version déjà compilée et de la coller dans votre fichier CSS.</p>
<p><strong>Avant (Sass) :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-4">@for</span><span class="syntax-2"> $i </span><span class="syntax-4">from</span><span class="syntax-3"> 1</span><span class="syntax-4"> through</span><span class="syntax-3"> 3</span><span class="syntax-2"> {</span></span>
<span class="line"><span class="syntax-8">  .button--size-</span><span class="syntax-2">#{$i} {</span></span>
<span class="line"><span class="syntax-5">    padding</span><span class="syntax-2">: #{$i}rem;</span></span>
<span class="line"><span class="syntax-2">  }</span></span>
<span class="line"><span class="syntax-2">}</span></span></code></pre>
<p><strong>Après (CSS natif) :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-8">.button--size-1</span><span class="syntax-2"> { </span><span class="syntax-5">padding</span><span class="syntax-2">: </span><span class="syntax-3">1</span><span class="syntax-4">rem</span><span class="syntax-2">; }</span></span>
<span class="line"><span class="syntax-8">.button--size-2</span><span class="syntax-2"> { </span><span class="syntax-5">padding</span><span class="syntax-2">: </span><span class="syntax-3">2</span><span class="syntax-4">rem</span><span class="syntax-2">; }</span></span>
<span class="line"><span class="syntax-8">.button--size-3</span><span class="syntax-2"> { </span><span class="syntax-5">padding</span><span class="syntax-2">: </span><span class="syntax-3">3</span><span class="syntax-4">rem</span><span class="syntax-2">; }</span></span></code></pre>
<p>À la fin de cette phase, <strong>le code CSS de votre projet n'utilise techniquement plus aucune fonctionnalité propre à Sass. 👏</strong></p>
<h2>Phase 2 : Migration (provisoire) des classes utilitaires 🧰</h2>
<p>C'est l'étape de la « prise de masse » avant la sèche. 💪</p>
<p>L'idée est de récupérer le CSS compilé de toutes les classes utilitaires de votre ancien framework et de les stocker dans un nouveau dossier, par exemple <code>utilities/</code>. Pour garder l'esprit clair, séparez-les par thématique : <code>utilities/typography.css</code>, <code>utilities/spacing.css</code>, etc.</p>
<p><strong>Exemple d'un fichier <code>utilities/spacing.css</code> (récupéré depuis Bootstrap v4) :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-8">.mt-1</span><span class="syntax-2"> { </span><span class="syntax-5">margin-top</span><span class="syntax-2">: </span><span class="syntax-3">0.25</span><span class="syntax-4">rem</span><span class="syntax-4"> !important</span><span class="syntax-2">; }</span></span>
<span class="line"><span class="syntax-8">.mt-2</span><span class="syntax-2"> { </span><span class="syntax-5">margin-top</span><span class="syntax-2">: </span><span class="syntax-3">0.5</span><span class="syntax-4">rem</span><span class="syntax-4"> !important</span><span class="syntax-2">; }</span></span>
<span class="line"><span class="syntax-8">.mb-3</span><span class="syntax-2"> { </span><span class="syntax-5">margin-bottom</span><span class="syntax-2">: </span><span class="syntax-3">1</span><span class="syntax-4">rem</span><span class="syntax-4"> !important</span><span class="syntax-2">; }</span></span>
<span class="line"><span class="syntax-10">/* ... */</span></span></code></pre>
<p>Votre CSS global va grossir temporairement. C'est normal, ne paniquez pas ! Ces fichiers agiront comme un filet de sécurité visuel en attendant que Tailwind prenne le relais. 😮‍💨</p>
<h2>Phase 3 : On débranche l’ancien framework CSS 💔</h2>
<p>Si les phases 1 et 2 ont été faites minutieusement, vous pouvez supprimer l'ancien framework CSS de vos dépendances. Visuellement, rien ne devrait bouger. 🤞</p>

<div class="c-alert c-alert--warning">
    <p class="c-alert__title">
                    <span class="c-icon c-icon--monospace">
                <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="c-icon__svg" focusable="false" viewBox="0 0 70 62"><path fill-rule="evenodd" d="m41.198 3.519 27.854 47.924C71.752 56.135 68.377 62 62.806 62H7.098c-5.402 0-8.947-5.865-6.077-10.557L28.875 3.52c2.7-4.692 9.622-4.692 12.323 0Zm-8.586 2.944L5.427 52.936C4.274 54.725 5.592 57 7.734 57h54.37c2.306 0 3.624-2.275 2.471-4.063L37.39 6.464c-.988-1.95-3.79-1.95-4.778 0ZM33 45.9c1.1-1.1 3.1-1.1 4.2 0 .2.2.3.3.4.5s.2.3.3.5.2.4.2.6c.1.2.1.4.1.6 0 .8-.3 1.6-.9 2.1-.5.6-1.3.9-2.1.9s-1.6-.3-2.3-.9c-.6-.5-.9-1.3-.9-2.1 0-.2.1-.4.1-.6.1-.2.1-.4.2-.6s.2-.3.3-.5.3-.4.4-.5m2.2-27.1c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V21.8c0-1.7 1.3-3 3-3"/></svg>
            </span>
                        <strong>Avertissement</strong>
    </p>
    <div class="c-alert__content">
                <p><br />
<strong>Attention aux comportements JS !</strong> Si votre ancien framework (comme Bootstrap) gérait des modales, des info-bulles ou des menus déroulants, la suppression de son CSS (et de ses scripts associés) risque de casser les animations ou le positionnement. Prévoyez un peu de temps de développement pour vérifier les interactions JavaScript et réécrire ces quelques composants. Profitez-en pour utiliser du JS vanilla ou du CSS moderne (<a rel="nofollow noopener noreferrer" href="https://developer.mozilla.org/fr/docs/Web/API/Popover_API">l'API Popover native</a> est parfaite pour ça, par exemple !).</p>
        </div>
</div>

<h2>Phase 4 : L'adieu à Sass 🫡</h2>
<p>C'est l'heure de désinstaller une fois pour toutes <code>sass</code> (ou <code>node-sass</code>). N’oubliez pas de renommer tous vos fichiers <code>.scss</code> en <code>.css</code>.</p>
<p>Lors de cette étape, il est très probable que le compilateur échoue lors de son premier lancement. Si la console se remplit d'erreurs, pas de panique, c'est un grand classique ! Cela s'explique généralement par l'une de ces trois raisons :</p>
<ol>
<li><strong>L'import fantôme</strong> : Votre <em>bundler</em> (comme <a rel="nofollow noopener noreferrer" href="https://webpack.js.org/">Webpack</a>) ou votre point d'entrée JavaScript (le fameux <code>app.js</code>) pointe toujours vers un fichier portant l’extension <code>.scss</code>. Pensez à bien mettre à jour vos chemins d'importation.</li>
<li><strong>Le reliquat de Sass</strong> : Il reste quelques <code>$</code>, <code>@use</code>, etc. qui traînent dans vos fichiers CSS. Pensez à les supprimer.</li>
<li><strong>Les commentaires</strong> : Les commentaires d'une seule ligne écrits avec <code>//</code> étaient valides avec Sass, mais pas avec votre CSS natif.</li>
</ol>
<p><strong>L'erreur classique à corriger :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-10">// Ce commentaire était valide en Sass, mais fera planter votre CSS natif !</span></span>
<span class="line"><span class="syntax-8">.element</span><span class="syntax-2"> { </span><span class="syntax-5">color</span><span class="syntax-2">: </span><span class="syntax-9">var</span><span class="syntax-2">(--color-primary); }</span></span>
<span class="line"></span>
<span class="line"><span class="syntax-10">/* Remplacez-le par la syntaxe standard ! */</span></span>
<span class="line"><span class="syntax-8">.element</span><span class="syntax-2"> { </span><span class="syntax-5">color</span><span class="syntax-2">: </span><span class="syntax-9">var</span><span class="syntax-2">(--color-primary); }</span></span></code></pre>
<p>Une fois ces petits ajustements faits, respirez un grand coup. Le plus dur techniquement est derrière vous. 🎉</p>
<h2>Phase 5 : L'entrée en scène de Tailwind CSS v4 🤩</h2>
<p>Il est enfin temps d'installer Tailwind CSS v4 et <a rel="nofollow noopener noreferrer" href="https://postcss.org/">PostCSS</a> (indispensable dans nos projets Symfony avec <a rel="nofollow noopener noreferrer" href="https://symfony.com/doc/current/frontend/encore/index.html">Webpack Encore</a>). Je vous conseille de suivre la <a rel="nofollow noopener noreferrer" href="https://tailwindcss.com/docs/installation/using-postcss">documentation officielle</a> pour le <em>setup</em> initial.</p>
<h3>Le piège de l'import global (et du Preflight)</h3>
<p><strong>La documentation officielle vous recommandera d’importer Tailwind de cette façon :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-4">@import</span><span class="syntax-1"> "tailwindcss"</span><span class="syntax-2">;</span></span></code></pre>
<p>En utilisant cet import global, Tailwind embarque par défaut plusieurs <a rel="nofollow noopener noreferrer" href="https://developer.mozilla.org/fr/docs/Web/CSS/Reference/At-rules/@layer"><em>layers CSS</em></a> (<code>theme</code>, <code>base</code>, <code>components</code> et <code>utilities</code>), injectant par la même occasion son propre <em>reset CSS</em> (Preflight). Si votre projet possédait déjà un fichier de <em>reset</em> (comme <code>normalize.css</code>), vous risquez d’avoir des conflits.</p>

<div class="c-alert c-alert--note">
    <p class="c-alert__title">
                    <span class="c-icon c-icon--monospace">
                <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="c-icon__svg" focusable="false" viewBox="0 0 70 71"><path fill-rule="nonzero" d="M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9"/></svg>
            </span>
                        <strong>Info</strong>
    </p>
    <div class="c-alert__content">
                <p><br />
<a rel="nofollow noopener noreferrer" href="https://tailwindcss.com/docs/preflight#disabling-preflight">Désactivez Preflight dans un premier temps</a>, ainsi que l'ensemble des <em>layers CSS</em> importés pour limiter les régressions et les problématiques liées à la <a rel="nofollow noopener noreferrer" href="https://developer.mozilla.org/fr/docs/Web/CSS/Guides/Cascade/Introduction">cascade CSS</a>.</p>
        </div>
</div>

<p><strong>Pour cela, privilégiez des imports ciblés (sans les <em>layers CSS</em>) :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-4">@import</span><span class="syntax-1"> "tailwindcss/theme.css"</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-4">@import</span><span class="syntax-1"> "tailwindcss/utilities.css"</span><span class="syntax-2">;</span></span></code></pre>
<h3>La magie de la directive <code>@theme</code></h3>
<p>La révolution de la v4, c'est qu'elle est <em>CSS-first</em>. Fini l'énorme fichier <code>tailwind.config.js</code> à la racine de votre projet ! Le thème que vous aviez anticipé lors de la phase 1 s'intègre désormais presque nativement grâce à la nouvelle directive <code>@theme</code>.</p>
<p><strong>Avant :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-8">:root</span><span class="syntax-2"> {</span></span>
<span class="line"><span class="syntax-2">  --color-primary: </span><span class="syntax-3">#f7d325</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-2">  --color-secondary: </span><span class="syntax-3">#ff2951</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-2">  --font-sans: </span><span class="syntax-1">"Source Sans Pro"</span><span class="syntax-2">, </span><span class="syntax-9">sans-serif</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-2">}</span></span></code></pre>
<p><strong>Après :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-2">@theme static {</span></span>
<span class="line"><span class="syntax-2">  --color-primary: </span><span class="syntax-3">#f7d325</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-2">  --color-secondary: </span><span class="syntax-3">#ff2951</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-2">  --font-sans: </span><span class="syntax-1">"Source Sans Pro"</span><span class="syntax-2">, </span><span class="syntax-9">sans-serif</span><span class="syntax-2">;</span></span>
<span class="line"><span class="syntax-2">}</span></span></code></pre>

<div class="c-alert c-alert--note">
    <p class="c-alert__title">
                    <span class="c-icon c-icon--monospace">
                <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="c-icon__svg" focusable="false" viewBox="0 0 70 71"><path fill-rule="nonzero" d="M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9"/></svg>
            </span>
                        <strong>Info</strong>
    </p>
    <div class="c-alert__content">
                <p><br />
<strong>Gardez vos variables CSS accessibles partout !</strong>  Pensez à générer vos variables CSS avec le comportement <code>static</code>, comme indiqué dans la <a rel="nofollow noopener noreferrer" href="https://tailwindcss.com/docs/theme#generating-all-css-variables">documentation Tailwind</a>. Ainsi, elles ne seront pas limitées aux classes utilitaires de Tailwind, mais resteront utilisables partout dans vos propres composants CSS personnalisés.</p>
        </div>
</div>

<p>C'est aussi le moment parfait pour utiliser la directive <code>@variant</code> afin d'uniformiser ces fameuses <em>media queries</em> que nous avions mises en dur plus tôt !</p>
<p><strong>Avant :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-4">@media</span><span class="syntax-2"> (</span><span class="syntax-5">min-width</span><span class="syntax-2">: </span><span class="syntax-3">768</span><span class="syntax-4">px</span><span class="syntax-2">) {</span></span>
<span class="line"><span class="syntax-8">  .card</span><span class="syntax-2"> {</span></span>
<span class="line"><span class="syntax-5">    color</span><span class="syntax-2">: </span><span class="syntax-9">var</span><span class="syntax-2">(--color-primary);</span></span>
<span class="line"><span class="syntax-2">  }</span></span>
<span class="line"><span class="syntax-2">}</span></span></code></pre>
<p><strong>Après :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-2">@variant md {</span></span>
<span class="line"><span class="syntax-8">  .card</span><span class="syntax-2"> {</span></span>
<span class="line"><span class="syntax-5">    color</span><span class="syntax-2">: </span><span class="syntax-9">var</span><span class="syntax-2">(--color-primary);</span></span>
<span class="line"><span class="syntax-2">  }</span></span>
<span class="line"><span class="syntax-2">}</span></span></code></pre>
<p>Ce petit changement vous garantit que tout votre CSS sur-mesure restera parfaitement synchronisé avec le comportement natif des classes utilitaires de Tailwind.</p>
<h2>Phase 6 : Le nettoyage de printemps 🧹</h2>
<p>Je préfère être honnête : cette étape demande de la patience. 🙃</p>
<p>L'objectif est de supprimer le dossier <code>utilities/</code> créé à la phase 2. Pour ce faire, vous allez devoir remplacer dans tous vos templates HTML/Twig les anciennes classes par les nouvelles générées par Tailwind.</p>
<p>Vos meilleurs alliés ici seront l'outil de recherche de votre IDE, les expressions régulières (Regex), et bien sûr votre LLM (<em>Large Language Model</em>) favori (ChatGPT, Claude, GitHub Copilot, etc.) pour automatiser un maximum le processus.</p>
<p><strong>Avant (Bootstrap v4) :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-2">&#x3C;</span><span class="syntax-4">div</span><span class="syntax-8"> class</span><span class="syntax-2">=</span><span class="syntax-1">"d-flex flex-column flex-md-row"</span><span class="syntax-2">>...&#x3C;/</span><span class="syntax-4">div</span><span class="syntax-2">></span></span></code></pre>
<p><strong>Après (Tailwind CSS v4) :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-2">&#x3C;</span><span class="syntax-4">div</span><span class="syntax-8"> class</span><span class="syntax-2">=</span><span class="syntax-1">"flex flex-col md:flex-row"</span><span class="syntax-2">>...&#x3C;/</span><span class="syntax-4">div</span><span class="syntax-2">></span></span></code></pre>
<h3>Le boss final : Les grilles 😎</h3>
<p>Il n'y a pas de solution magique ici. Les vieux frameworks utilisaient généralement <a rel="nofollow noopener noreferrer" href="https://developer.mozilla.org/fr/docs/Web/CSS/Guides/Flexible_box_layout">Flexbox</a> pour simuler un comportement de grille. Tailwind, lui, utilise le module natif <a rel="nofollow noopener noreferrer" href="https://developer.mozilla.org/fr/docs/Web/CSS/Guides/Grid_layout"><em>CSS Grid Layout</em></a> (modèle de disposition en grille).</p>
<p>Il va falloir repasser sur vos grilles manuellement. 😬</p>
<p><strong>L'ancienne approche (Flexbox) :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-2">&#x3C;</span><span class="syntax-4">div</span><span class="syntax-8"> class</span><span class="syntax-2">=</span><span class="syntax-1">"row"</span><span class="syntax-2">></span></span>
<span class="line"><span class="syntax-2">  &#x3C;</span><span class="syntax-4">div</span><span class="syntax-8"> class</span><span class="syntax-2">=</span><span class="syntax-1">"col-12 col-md-6"</span><span class="syntax-2">>...&#x3C;/</span><span class="syntax-4">div</span><span class="syntax-2">></span></span>
<span class="line"><span class="syntax-2">  &#x3C;</span><span class="syntax-4">div</span><span class="syntax-8"> class</span><span class="syntax-2">=</span><span class="syntax-1">"col-12 col-md-6"</span><span class="syntax-2">>...&#x3C;/</span><span class="syntax-4">div</span><span class="syntax-2">></span></span>
<span class="line"><span class="syntax-2">&#x3C;/</span><span class="syntax-4">div</span><span class="syntax-2">></span></span></code></pre>
<p><strong>La nouvelle approche (Grid) :</strong></p>
<pre class="syntax-0" tabindex="0"><code><span class="line"><span class="syntax-2">&#x3C;</span><span class="syntax-4">div</span><span class="syntax-8"> class</span><span class="syntax-2">=</span><span class="syntax-1">"grid grid-cols-1 md:grid-cols-2"</span><span class="syntax-2">></span></span>
<span class="line"><span class="syntax-2">  &#x3C;</span><span class="syntax-4">div</span><span class="syntax-2">>...&#x3C;/</span><span class="syntax-4">div</span><span class="syntax-2">></span></span>
<span class="line"><span class="syntax-2">  &#x3C;</span><span class="syntax-4">div</span><span class="syntax-2">>...&#x3C;/</span><span class="syntax-4">div</span><span class="syntax-2">></span></span>
<span class="line"><span class="syntax-2">&#x3C;/</span><span class="syntax-4">div</span><span class="syntax-2">></span></span></code></pre>
<p>C'est fastidieux, mais le DOM en ressort considérablement allégé et plus lisible. 🥳</p>
<h2>Conclusion : Tout ça pour ça ? 🤔</h2>
<p>Oui, mille fois oui. Migrer vers Tailwind CSS v4 n'est pas un parcours de santé pour un projet existant. Mais les avantages sont colossaux.</p>
<p>Vous dites adieu à l'usine à gaz qu'était devenu Sass au fil des années. Le poids final de votre CSS sera drastiquement réduit puisque Tailwind ne compile que ce que vous utilisez réellement. Sans parler des performances pures : avec son nouveau moteur écrit en Rust, les temps de compilation sont si rapides qu’on les remarque à peine.</p>
<p>Enfin, la <em>Developer Experience</em> (DX) de vos équipes s'en trouvera transformée. C'est un investissement en temps conséquent, mais c'est un cadeau inestimable que vous faites au futur de votre projet. 🎁</p>
<h3>Et concrètement, comment on vend ça à sa direction ?</h3>
<p>C'est souvent la question qui fâche. On sait que le chantier est nécessaire, mais il est difficile d'estimer le temps que cela va prendre sans y laisser des plumes, d'où <strong>l'effet tunnel</strong> qui effraie les équipes produit.</p>
<p>Pour débloquer la situation, nous avons l'habitude chez JoliCode de commencer par un <strong>audit flash (1 à 2 jours)</strong>. L'objectif est de poser les choses à plat pour vous fournir un plan de bataille concret à défendre en interne :</p>
<ol>
<li><strong>Un état des lieux clair</strong> de la dette front actuelle (dépendances, risques, obsolescence).</li>
<li><strong>Une estimation chiffrée</strong> de la migration, découpée par phase.</li>
<li><strong>Un plan de déploiement progressif</strong> qui ne bloque pas votre <em>roadmap</em> produit.</li>
<li><strong>Les bénéfices attendus</strong> (gains de performance, vélocité des équipes, etc.).</li>
</ol>
<p>Si vous avez besoin d'aide pour cadrer votre propre migration, <a href="https://jolicode.com/contact">n'hésitez pas à nous faire signe</a> !</p>
<p>J’espère que ce retour d’expérience vous sera utile et vous donnera le courage de sauter le pas. N'hésitez pas à partager vos propres astuces ou vos galères de migration dans les commentaires ! 🙂</p>]]></description></item><item><title>Vibrations, livraison par Pok&#xE9;mon Go et la complexit&#xE9; du temps</title><link>https://www.synolia.com/synolab/outils/vibrations-livraison-par-pokemon-go-et-la-complexite-du-temps/</link><author>Estelle M.</author><date>Thu, 16 Apr 2026 17:03:45 +0200</date><description><![CDATA[<p>Découvrez notre veille technique du mois de mars entre Web Haptics, rumeurs sur Windows 12, livraisons de pizzas avec Pokémon Go et une valorisation de PHP !</p>
<h2 class="western">1, Donner de la vie à vos applications mobiles</h2>
<p>Votre téléphone ne se contente pas d’afficher ou de sonner ; il vit. À chaque notification, message ou appel, une vibration plus ou moins subtile vient accompagner ce que vous voyez sur l’écran.</p>
<p>Ce retour haptique, popularisé notamment par <strong>Apple</strong>, fait aujourd’hui partie intégrante de l’expérience mobile, en ajoutant une dimension tactile à nos interfaces.</p>
<p>Depuis longtemps, cette fonctionnalité était réservée aux applications natives. Mais aujourd’hui, grâce à des solutions comme <a href="https://haptics.lochie.me/">Web Haptics</a>, on peut désormais intégrer des vibrations directement dans les navigateurs.</p>
<p>Au-delà de son aspect technique, l’intérêt de <strong>Web Haptics</strong> réside dans sa capacité à démocratiser une pratique UX jusqu’ici réservée aux applications mobiles. Grâce à son API simple et ses modèles prêts à l’emploi, les développeurs peuvent intégrer rapidement des retours haptiques adaptés à leurs besoins, et plonger leurs utilisateurs dans des interfaces plus immersives, et intuitives.</p>
<h2 class="western">2. Les utilisateurs en colère contre Windows.</h2>
<p>L’expansion de l’IA fait polémique, de nos jours, c’est un fait. Et vous, que diriez-vous si demain, votre système d’exploitation annonçait vouloir utiliser de l’IA pour gérer vos dossiers, vos photos et vos vidéos stockés sur votre ordinateur ?</p>
<p>Selon une rumeur largement relayée, <strong>Windows 12</strong> serait sur le point de sortir avec une approche radicalement différente de ses prédécesseurs, avec un système module fortement centré sur l’intelligence artificielle, et potentiellement accessible via un abonnement. Et si l’on en croit <a href="https://windows.developpez.com/actu/380780/Windows-12-devrait-sortir-cette-annee-sous-forme-d-OS-entierement-modulaire-accessible-via-un-abonnement-et-axe-sur-une-strategie-IA-qui-provoque-deja-un-rejet-massif-de-Windows-11/">cet article</a> de Patrick Ruiz, cette idée est loin de plaire à tout le monde…</p>
<p>Même si l’article revient en réalité sur des informations incertaines, basées sur des rumeurs et des interprétations de projets internes, il démontre malgré tout l’inquiétude fondée des utilisateurs. Selon ces derniers, ces évolutions comme une véritable dérive vers un système plus intrusif, plus dépendant des services en ligne et potentiellement moins maîtrisable. C’est une approche assez polémique qui soulève une question primordiale ; à partir de quel moment notre ordinateur passera d’outil personnel à service ?</p>
<h2 class="western">3. Pokémon Go aide aux livraisons de pizza</h2>
<p><strong>Pokémon Go</strong> a été un succès mondial ; à sa sortie en 2016, il a comptabilisé plus de 500 millions de téléchargements en l’espace de 2 mois. Premier grand succès de la réalité augmentée, il a fait parcourir des milliards de kilomètres à ses utilisateurs prêts à tout pour attraper leur Pokémon préféré, via la caméra de leur téléphone.</p>
<p>Mais cette mine de données, récoltées par ces chasseurs de Pokémons, qu’est-elle devenue, aujourd’hui ? <a href="https://www.technologyreview.com/2026/03/10/1134099/how-pokemon-go-is-helping-robots-deliver-pizza-on-time/">Cet article</a> de la <strong>MIT Technology Review</strong> nous répond ; ils ont modélisé le monde… pour livrer des pizzas.</p>
<p>Cette réutilisation de données pose évidemment des questions. D’un point de vue industriel, on comprend leur immense valeur. Mais d’un point de vue utilisateur, on peut se demander à quel point chacune de nos interactions numériques alimente des systèmes bien au-delà de leur usage initial ?</p>
<p><strong>Pokémon Go</strong> n’était donc pas seulement un jeu en réalité augmentée ; c’était, sans le dire explicitement, une gigantesque opération de cartographie collective dont les bénéficiaires ne sont plus seulement les joueurs.</p>
<h2 class="western">4. Les fausses idées sur le temps.</h2>
<p>En informatique, la notion de temps, globalement universelle, n’est pas seulement une suite de secondes ou de dates ; c’est un calcul plein d’exceptions, de subtilités, et de pièges invisibles. En outre, à chaque fois que l’on peut penser maîtriser une règle simple, une situation concrète vient la remettre en question.</p>
<p>Le site <a href="https://yourcalendricalfallacyis.com/">YourCalendricalFallacyIs</a> illustre parfaitement cela, en confrontant nos idées reçues sur le calcul du temps. À travers une série d’énoncés expliqués et corrigés, il met en évidence les nombreux cas où nos intuitions échouent face à la complexité réelle des calendriers.</p>
<p>Un outil pédagogique et presque ludique, qui rappelle aux développeurs que le temps n’est jamais une donnée « simple ».</p>
<h2 class="western">5. Réaliser des maquettes en un seul clic.</h2>
<p>Dans nos articles de veille, nous avons souvent parlé d’applications web pour créer plein de choses avec un seul clic. Aujourd’hui, dans cette même idée, nous vous présentons <a href="https://stitch.withgoogle.com/">Stitch</a>.</p>
<p>Concevoir une interface est un vrai métier, qui est très souvent composé d’allers-retours incessants entre idées, envies, et faisabilité.</p>
<p>Stitch, de Google, s’inscrit dans une volonté de simplifier radicalement la réalisation de maquettes. Avec l’utilisation d’un prompt classique des IA, il permet de générer des interfaces complètes à partir de croquis ou d’une simple description, tout en produisant du code exploitable. L’outil ne se limite donc pas à la création visuelle ; il relie directement l’idée au développement.</p>
<p>Néanmoins, si l’application semble innovante et efficace, il faut toujours s’interroger sur la place de la conception traditionnelle face à ce type d’automatisation. Car, après tout, ne vaut-il pas mieux privilégier la qualité au détriment de la vitesse ?</p>
<h2 class="western">6. Voyez le bon côté de PHP</h2>
<p>Le langage <strong>PHP</strong> est souvent associé à des anciennes limites et à certaines critiques historiques, au point d’être parfois perçu comme un langage dépassé, malgré sa place centrale dans le développement web. Heureusement, le site <a href="https://haphpiness.com/#/">HaPHPiness</a> est là pour nous aider à voir ses bons côtés !</p>
<p>Organisé par thématique, ce site met en lumière les évolutions récentes de PHP ; typage renforcé, fonctions plus cohérentes, meilleures pratiques de sécurité… Chaque point est accompagné d’exemples concrets, montrant comment ses améliorations simplifient le développement et rendent le langage plus fiable, et plus accessible.</p>
<p>Au-delà de son ton volontairement positif, <strong>HaPHPines</strong><strong>s</strong> joue aussi un rôle pédagogique intéressant : il permet de prendre du recul sur l’image souvent datée de PHP et de découvrir un langage qui continue d’évoluer activement. Et c’est aussi un très bon outil pour les développeurs qui souhaitent se (re)mettre à jour, alors allez-y jeter un œil !</p>
<h2 class="western">Pour aller plus loin&#8230;</h2>
<p>Voici les autres liens que vous avez partagés ce mois-ci, bonne lecture !</p>
<ul>
<li><a href="https://yggleak.top/fr"><span style="font-weight: 400;">YGGLeak</span></a></li>
<li><a href="https://f2r.github.io/en/static-closures"><span style="font-weight: 400;">Why use static closures? | F2R Articles</span></a></li>
<li><a href="https://www.washingtonpost.com/technology/2026/03/04/anthropic-ai-iran-campaign/"><span style="font-weight: 400;">Anthropic’s AI tool Claude central to U.S. campaign in Iran, amid a bitter feud</span></a></li>
<li><a href="https://www.linkedin.com/posts/juozas_chatgpt-is-abandoning-agentic-commerce-its-activity-7435308306329473025-Ncy0/"><span style="font-weight: 400;">LinkedIn : ChatGPT is abandoning agentic commerce.</span></a></li>
<li><a href="https://www.numerama.com/tech/2186497-il-parait-que-les-logiciels-meurent-en-silence-cest-quoi-la-saaspocalypse.html"><span style="font-weight: 400;">SaaSpocalypse : l&rsquo;effondrement des logiciels SaaS &#8211; Numerama</span></a></li>
<li><a href="https://decouverte.jems-group.com/hubfs/MiniLB_Syst%C3%A8mesAgentiques.pdf"><span style="font-weight: 400;">SYSTÈMES AGENTIQUES REDONNER LE POUVOIR AUX MÉTIERS</span></a></li>
<li><a href="https://www.lesnumeriques.com/intelligence-artificielle/c-est-probablement-fini-nvidia-arrete-les-frais-avec-openai-et-tout-le-secteur-de-l-ia-retient-son-souffle-n252435.html"><span style="font-weight: 400;">“C&rsquo;est probablement fini” : Nvidia arrête les frais avec OpenAI, tout le secteur de l&rsquo;IA retient son souffle &#8211; Les Numériques</span></a></li>
<li><a href="https://andrewlock.net/working-with-stacked-branches-in-git-is-easier-with-update-refs/"><span style="font-weight: 400;">Working with stacked branches in Git is easier with &#8211;update-refs</span></a></li>
<li><a href="https://www.wheresyoured.at/the-beginning-of-history/"><span style="font-weight: 400;">The Beginning Of History</span></a></li>
<li><a href="https://www.anthropic.com/research/labor-market-impacts"><span style="font-weight: 400;">Labor market impacts of AI: A new measure and early evidence \ Anthropic</span></a></li>
<li><a href="https://uxcode.fr/articles/php-composer-font-optimizer"><span style="font-weight: 400;">font-optimizer pour réduire de 90% le poids de vos polices dans vos projets PHP</span></a></li>
<li><a href="https://bloomberg.github.io/js-blog/post/temporal/"><span style="font-weight: 400;">Temporal: The 9-Year Journey to Fix Time in JavaScript | Bloomberg JS Blog</span></a></li>
<li><a href="https://www.blogdumoderateur.com/anthropic-classement-metiers-menaces-ia/"><span style="font-weight: 400;">Anthropic dresse un classement des métiers les plus menacés par l’IA</span></a></li>
<li><a href="https://developer.mozilla.org/fr/docs/Web/CSS/Reference/Values/image/image-set"><span style="font-weight: 400;">image-set() &#8211; CSS | MDN</span></a></li>
<li><a href="https://www.linkedin.com/posts/julien-bideau_mes-devs-tapent-de-moins-en-moins-de-code-share-7439988838808190976-zFA8"><span style="font-weight: 400;">LinkedIn : Les devs de mon équipe tapent de moins en moins de code.</span></a></li>
<li><a href="https://www.forbes.com/sites/joetoscano1/2026/03/06/google-just-patented-the-end-of-your-website/"><span style="font-weight: 400;">Google Just Patented The End Of Your Website</span></a></li>
<li><a href="https://gnugat.github.io/2026/03/25/turn-you-php-app-into-a-standalone-binary.html"><span style="font-weight: 400;">Turn your PHP app into a standalone binary</span></a></li>
<li><a href="https://deathbyclawd.com/?url=https://smile.eu/"><span style="font-weight: 400;">SaaSpocalypse Survival Scanner — Death Report</span></a></li>
<li><a href="https://somnai-dreams.github.io/pretext-demos/"><span style="font-weight: 400;">Pretext Demos</span></a></li>
<li><a href="https://github.com/chenglou/pretext"><span style="font-weight: 400;">GitHub &#8211; chenglou/pretext: Fast, accurate &amp; comprehensive text measurement &amp; layout</span></a></li>
<li><a href="https://www.lexpress.fr/economie/high-tech/anthropic-une-fuite-revele-les-risques-de-la-future-ia-claude-mythos-pour-la-cybersecurite-MNECU7RIXRDC5GUSEOFC7WYHCQ/?cmp_redirect=true"><span style="font-weight: 400;">Anthropic : une fuite révèle les risques de la future IA « Claude Mythos » pour la cybersécurité – L&rsquo;Express</span></a></li>
<li><a href="https://www.youtube.com/watch?v=jNMWkD5VsZ8"><span style="font-weight: 400;">Beating every possible game of Pokemon Platinum at the same time</span></a></li>
<li><a href="https://wind-waker-js.vercel.app/"><span style="font-weight: 400;">Wind Waker JS</span></a></li>
</ul>
<p>Cet article <a href="https://www.synolia.com/synolab/outils/vibrations-livraison-par-pokemon-go-et-la-complexite-du-temps/">Vibrations, livraison par Pokémon Go et la complexité du temps</a> est apparu en premier sur <a href="https://www.synolia.com">Synolia, agence e-commerce, CRM, Data, PIM/DAM, OMS</a>.</p>
]]></description></item><item><title>Notre retour sur le SymfonyLive Paris 2026</title><link>https://jolicode.com/blog/notre-retour-sur-le-symfonylive-paris-2026</link><author>JoliCode Team</author><date>Wed, 08 Apr 2026 15:42:00 +0200</date><description><![CDATA[<p>Les années passent, mais certaines traditions restent immuables. Il y a quelques jours, la communauté s'est de nouveau réunie à la Cité Universitaire pour l'édition 2026 du Symfony Live Paris.</p>
<p>Si le monde de la tech avance à toute vitesse, le cru 2026 conserve la recette qui a fait son succès. Nous étions, comme à notre habitude, présents au rendez-vous. Voici notre retour sur une édition qui prouve que Symfony reste à la pointe des évolutions.</p>
<h2>Keynote de Fabien Potencier</h2>
<p><picture class="js-dialog-target" data-original-url="/media/original/2026/symfony-live-paris-2026/Fabien_TUI.jpg" data-original-width="3143" data-original-height="1922"><source type="image/webp" srcset="/media/cache/content-webp/2026/symfony-live-paris-2026/Fabien_TUI.4a744ec6.webp" /><source type="image/jpeg" srcset="/media/cache/content/2026/symfony-live-paris-2026/Fabien_TUI.jpg" /><img loading="lazy" decoding="async" style="width: 996px; ; aspect-ratio: calc(3143 / 1922)" src="https://jolicode.com//media/cache/content/2026/symfony-live-paris-2026/Fabien_TUI.jpg" alt="Fabien pendant la Keynote" /></picture></p>
<p>Fabien Potencier nous a présenté un composant Symfony que beaucoup de gens attendaient impatiemment depuis sa première annonce datant de la <a href="https://jolicode.com/blog/du-code-des-gaufres-et-des-bds-nous-etions-a-la-symfonycon-a-bruxelles#keynote-d-ouverture-facon-fabpot">SymfonyCon 2023</a> : Symfony TUI (pour Terminal User Interface) !</p>
<p>Avec l'arrivée récente et massive de l’IA dans les habitudes de travail de beaucoup de développeurs, Fabien a trouvé une raison parfaite de relancer son travail sur le composant TUI pour permettre une utilisation plus ergonomique et avancée des LLMs directement depuis un terminal.</p>
<p>On a d’ailleurs eu le droit à une démonstration de son propre coding agent pour voir en direct ce à quoi on pourrait s’attendre avec l'adoption de ce composant pour discuter avec des LLMs. Et le rendu rivalise avec ce qui peut aujourd’hui être proposé directement dans nos IDEs.</p>
<p>Mais au-delà de l’intégration évidente avec l’IA, le composant TUI, c’est aussi une évolution du composant Symfony Console que l’on utilise tous (pour rappel, il s’agit de l’un des  premiers composants de l’écosystème Symfony, sorti il y a 15 ans maintenant). L’objectif ici, c’est de laisser Console s’occuper des parties commandes/arguments/output, pour concentrer toute la partie affichage, interaction et interactivité dans TUI.</p>
<p>Lors de ce talk assez orienté technique, Fabien a expliqué comment TUI fonctionne sous le capot. Sans entrer dans les détails ici, on pourra retenir :</p>
<ul>
<li>Il existe trois manières de gérer le style: à la manière stylesheet avec des sélecteurs semblables au CSS ; avec des classes utilitaires, comme on le ferait en Tailwind ; ou directement in-line, pour prendre la main de manière ponctuelle comme c'est possible en HTML. Les breakpoints sont aussi gérés avec des media-queries, pour un rendu nativement responsive ;</li>
<li>Beaucoup de Widgets sont disponibles nativement pour gérer la majorité des cas d’usages (ex: TextWidget pour affichage de texte et ASCII, InputWidget pour les entrées textuelles, SelectListWidget pour des listes scrollables, etc.) mais il est évidemment possible de créer nos propres widgets pour des cas particuliers ;</li>
<li>TUI utilise PHP Fibers et Revolt pour assurer un affichage et des animations complètement asynchrones et une gestion parallèle de l’affichage et de l’interactivité avec le développeur.</li>
</ul>
<p>La liste s’allonge évidemment, et toutes les informations sont disponibles sur le <a rel="nofollow noopener noreferrer" href="https://symfony.com/blog/introducing-the-symfony-tui-component">Blog Post dédié à l’arrivée de Symfony TUI</a>.</p>
<p>Pour conclure cette première conférence, Fabien s'est montré particulièrement enthousiaste. Ce nouveau composant Symfony ouvre des portes immenses, aussi bien pour l'affichage dans la console que pour l'intégration de l'intelligence artificielle.</p>
<p>Pour prouver la puissance de son outil, il a même fait une démonstration impressionnante : un jeu de <strong>Tetris</strong> tournant en direct dans son terminal ! Le rendu est fluide et visuellement bluffant, montrant que l'on peut désormais créer de véritables interfaces graphiques (TUI) modernes, directement en PHP.</p>
<p>Pour terminer, Fabien a lancé une idée très originale pour le futur des contributions sur ce composant : plutôt que d'envoyer une solution toute prête, les développeurs pourraient simplement partager un <strong>prompt</strong> (une instruction pour l'IA).</p>
<p>Fabien utiliserait alors son propre assistant et ses propres ressources pour transformer ces instructions en code réel et donner vie aux futures améliorations du projet !</p>
<h2>La communauté au rythme de l’IA</h2>
<p>On se souvient du <strong>SymfonyLive 2024</strong>, où l’IA générative d'images s'invitait déjà dans de nombreuses présentations. À l'époque, c’était encore une nouveauté impressionnante, bien que limitée.
Cette année, le changement est radical : les <strong>agents IA</strong> étaient omniprésents dans bon nombre de sujets. Cela reflète parfaitement l'évolution de notre quotidien de développeur, qui s'accélère de plus en plus dans cette direction.</p>
<p>Aujourd'hui, l'IA n'est plus juste un gadget visuel, elle est au cœur du développement de Symfony :</p>
<ul>
<li>Les nouvelles versions de <strong>Twig</strong> (le moteur de templates de Symfony) sont désormais gérées de manière quasi automatique par un agent ;</li>
<li>Une grande partie du code source est également générée par des assistants intelligents.</li>
</ul>
<p>Cependant, un point reste essentiel : l'IA aide à produire, mais elle ne remplace pas l'humain. La <strong>responsabilité</strong> finale appartient toujours à la personne qui valide le code. C'est le développeur qui vérifie et garantit la qualité de ce qui est produit avant de l'envoyer en production.</p>
<h3>L’IA au service des devs : Anatomie d'un assistant de Code Review - Thomas Boileau</h3>
<p>La volonté de Thomas est de normaliser l’utilisation de l’IA au sein de sa société. Son constat est qu’il est difficile d’intervenir dans le cycle de développement logiciel, au niveau individuel, car l’usage de l’IA est encore très inégal selon les développeurs. C’est pour ça qu’il a préféré intervenir sur la CI.</p>
<p>Lors de sa présentation, il nous expose donc un cas pratique. Son but : construire un assistant IA qui intervient au moment des code reviews.</p>
<p>Techniquement, dès qu'une PR est labellisé Ready For Review (RFR) alors un webhook est lancé, et l’assistant IA est déclenché.</p>
<p>Ici, ce que l'on apprend n’est pas réellement comment utiliser l’IA, mais plutôt un rappel bienvenu sur l’adoption d’une fonctionnalité par des utilisateurs. Il nous a bien expliqué qu’après sa première itération, quasiment personne n'interagissait avec son agent de revue de code.</p>
<p>En effet, comme toutes les nouveautés, l’essentiel, c'est de construire avec les utilisateurs finaux et d’éviter au maximum la friction. C’est donc après avoir mesuré l’usage de son bot et consulté les développeurs (utilisateurs) qu’il a proposé une nouvelle version, cette fois-ci plus utile pour tout le monde.</p>
<p>Évidement, Thomas nous rappelle qu’il aurait pu utiliser une solution sur étagère, mais il souligne les contraintes réglementaires qui s'appliquent à son domaine.</p>
<p>Sa conclusion est que le plus important dans ce projet reste la DX pour avoir une bonne adoption, et surtout que ce bot IA ne remplace pas l'humain, il est créé dans le but d’être seulement un plus dans la boucle.</p>
<h3>Développer un Coding Agent en PHP : dans les coulisses du &quot;Harness&quot; - Fabien Potencier</h3>
<p><picture class="js-dialog-target" data-original-url="/media/original/2026/symfony-live-paris-2026/fabien-potencies-ia-niveau.jpg" data-original-width="1600" data-original-height="738"><source type="image/webp" srcset="/media/cache/content-webp/2026/symfony-live-paris-2026/fabien-potencies-ia-niveau.d189aa63.webp" /><source type="image/png" srcset="/media/cache/content/2026/symfony-live-paris-2026/fabien-potencies-ia-niveau.jpg" /><img loading="lazy" decoding="async" style="width: 996px; ; aspect-ratio: calc(1600 / 738)" src="https://jolicode.com//media/cache/content/2026/symfony-live-paris-2026/fabien-potencies-ia-niveau.jpg" alt="Les différents niveaux d'utilisation de l'IA selon Fabien" /></picture></p>
<p>Tout a commencé par un défi sur Twitter. Un utilisateur affirmait qu'il était impossible de créer un <strong>coding agent</strong> (un assistant capable de coder de façon autonome) performant en utilisant le langage PHP. Piqué au vif, Fabien Potencier, le créateur de Symfony, a décidé de prouver le contraire en développant son propre assistant.</p>
<p>Plutôt que d'utiliser des outils tout prêts comme GitHub Copilot ou Claude Code, fabriquer son propre agent permet de comprendre les coulisses de l’intelligence artificielle. On découvre alors comment se déroule réellement une &quot;discussion&quot; entre un développeur et un modèle de langage (LLM).</p>
<p>Pour rendre l'outil agréable à utiliser, il a utilisé le composant <strong>TUI de Symfony</strong>. Cela permet d'avoir une interface textuelle interactive directement dans sa console.</p>
<p>Pour que l'agent soit intelligent, il doit communiquer avec un modèle (comme Claude Opus, Claude Sonnet, GPT 5). Mais les modèles évoluent sans cesse. Fabien a donc créé un petit outil, <a rel="nofollow noopener noreferrer" href="https://github.com/symfony/models-dev">symfony/models-dev</a>, qui répertorie de manière automatique tous les modèles disponibles afin de toujours utiliser la version la plus récente.</p>
<p>Discuter avec un chat, c'est facile. Mais pour qu'un agent soit utile, il doit pouvoir agir sur votre ordinateur. C'est là qu'interviennent les <em>Tools</em> (outils). De manière surprenante, Fabien n'a eu besoin que de 4 outils de base :</p>
<ul>
<li><em>Read</em> : pour lire le contenu d'un fichier.</li>
<li><em>Write</em> : pour créer un nouveau fichier.</li>
<li><em>Update</em> : pour modifier un fichier existant sans gaspiller de token.</li>
<li><em>Bash</em> : pour exécuter n'importe quelle commande (lancer des tests, installer un package, etc.).</li>
</ul>
<p>Un agent classique oublie tout dès que la session se ferme. Pour corriger cela, chaque échange est enregistré dans une base de données.</p>
<p>L’astuce géniale ? Cette mémoire est stockée sous forme d'<em>arbre</em>. Cela permet à l'agent de revenir en arrière à un point précis pour tester une autre solution si la première n'a pas fonctionné.</p>
<p>Plus on discute avec l'IA, plus le &quot;contexte&quot; (le nombre de mots envoyés) devient important. Si on dépasse la limite du modèle, il sature. Fabien a donc mis en place un système de compression intelligent :</p>
<ol>
<li>Il garde toujours le tout premier message (les instructions de base).</li>
<li>Il garde les derniers messages récents.</li>
<li><em>Il résume</em> tout ce qui se trouve entre les deux.</li>
</ol>
<p><strong>Les conseils de Fabien</strong></p>
<p>Durant sa démonstration, il a partagé quelques astuces précieuses :</p>
<ul>
<li><em>Les Skills</em> : Dès qu'il réalise qu'il répète une tâche, ou une instruction, il crée un &quot;skill&quot; (une compétence) pour son agent. C'est comme écrire des tests : on a l'impression de perdre du temps au début, mais on en gagne énormément sur le long terme ;</li>
<li><em>Le regard neuf</em> : Parfois, l'IA s'embrouille. Fabien conseille de lui demander d'analyser la situation avec &quot;un regard neuf&quot; (<em>fresh eyes</em>). Cela donne souvent des résultats spectaculaires pour débloquer un bug complexe.</li>
</ul>
<p>Bien qu'il n'ait pas eu le temps de tout montrer, notamment son système d'orchestration dans le cloud, la preuve est faite : <strong>le PHP est un langage de choix pour l'intelligence artificielle !</strong></p>
<h3>Embeddings en PHP : Symfony AI en pratique - Grégoire Pineau</h3>
<p><picture class="js-dialog-target" data-original-url="/media/original/2026/symfony-live-paris-2026/gregoire-symfony-ai.jpg" data-original-width="4080" data-original-height="3072"><source type="image/webp" srcset="/media/cache/content-webp/2026/symfony-live-paris-2026/gregoire-symfony-ai.c803838f.webp" /><source type="image/jpeg" srcset="/media/cache/content/2026/symfony-live-paris-2026/gregoire-symfony-ai.jpg" /><img loading="lazy" decoding="async" style="width: 996px; ; aspect-ratio: calc(4080 / 3072)" src="https://jolicode.com//media/cache/content/2026/symfony-live-paris-2026/gregoire-symfony-ai.jpg" alt="Grégoire lors de sa conférence sur Symfony AI" /></picture></p>
<p>Le talk de <a href="https://jolicode.com/qui-sommes-nous/equipe/gregoire-pineau">Grégoire</a> nous met tout de suite dans la pratique avec une vraie mise en situation autour des embeddings et de la similarité de contenu.</p>
<p>Il part sur un cas d’usage qui parle à tout le monde : faire correspondre des URLs entre un ancien et un nouveau site. Là où on pourrait partir sur des règles compliquées ou du matching approximatif, les embeddings offrent une solution plus robuste : on compare directement le sens sémantique des pages.</p>
<p>Ce qui marche particulièrement bien, c’est le fil rouge visuel de la conférence. Un schéma du processus est affiché, puis réutilisé à chaque étape. Ce qui rend la progression assez claire : on comprend où on en est, ce qu’on fait, et pourquoi on le fait.</p>
<p>La conférence prend le temps de poser les bases :</p>
<ul>
<li>ce qu’est un embedding (une représentation vectorielle d’un contenu) ;</li>
<li>à quoi ça sert (mesurer de la similarité sémantique) ;</li>
<li>et surtout dans quels cas ça devient utile.</li>
</ul>
<p>Ensuite, on rentre dans le concret :</p>
<ul>
<li>comment choisir un modèle selon son besoin ;</li>
<li>comment vectoriser ses données depuis Symfony ;</li>
<li>où stocker ces vecteurs (PostgreSQL, Redis, etc.) ;</li>
<li>et comment les requêter efficacement.</li>
</ul>
<p>L'intérêt de cette présentation, c'est que Grégoire nous a montré que tout se fait sans quitter l’écosystème Symfony, grâce à Symfony AI. Cette initiative fournit ainsi toutes les abstractions nécessaires pour manipuler modèles, stores, agents et bien plus encore. N'hésitez pas à consulter le site dédié à <a rel="nofollow noopener noreferrer" href="https://ai.symfony.com/">Symfony AI</a> pour découvrir tout ça.
<a rel="nofollow noopener noreferrer" href="https://speakerdeck.com/lyrixx/embeddings-symfony-ai-en-pratique">Vous pouvez retrouver ses slides en ligne</a>.</p>
<h2>Retours d’expériences et présentations techniques</h2>
<p>Tous ces nouveaux outils basés sur l'IA ne doivent pas éclipser l'importance de la technicité en dehors de l'intelligence artificielle ! Au contraire, nous apprécions également les outils éprouvés et bien établis, et les retours d'expériences de manière générale.</p>
<h3>Chiffrez vos données avec Doctrine, en restant recherchable - Jérôme Tamarelle</h3>
<p>Lors de cette conférence, Jérôme Tamarelle a rappelé un point essentiel : la sécurité des données ne concerne pas uniquement les informations directement identifiantes (comme un email ou un nom), mais aussi les données indirectes. Croisées entre elles, ces dernières peuvent suffire à identifier une personne.</p>
<p>Il est important de distinguer deux approches souvent confondues :</p>
<ul>
<li><em>Le chiffrement</em> : les données sont transformées de manière réversible. On peut les déchiffrer à l’aide d’une clé ;</li>
<li><em>Le hashage</em> : il s’agit d’une empreinte unique et fixe d’une donnée. Cette opération est irréversible.
Plusieurs types de chiffrement existent, dont le chiffrement aléatoire et le chiffrement déterministe.</li>
</ul>
<p>Avec le <em>chiffrement aléatoire</em>, chaque valeur est chiffrée différemment, même si elle est identique à une autre.
Par exemple, une même adresse email enregistrée deux fois en base produira deux valeurs chiffrées différentes.</p>
<p>Avec le <em>chiffrement déterministe</em>, une même donnée produira toujours le même résultat chiffré.</p>
<p>Avec Doctrine, on encapsule le chiffrement directement dans Doctrine via des types personnalisés.
Un champ devient &quot;chiffré&quot; simplement par sa définition.</p>
<p>Mais du coup, on ne peut plus faire de recherche sur ces champs sans passer par Doctrine.</p>
<p>Sécuriser ses données, c’est accepter de complexifier son application. Et surtout, le faire dès la conception.</p>
<h3>Doctrine inheritance - Rémi JANOT</h3>
<p>Rémi commence par présenter la différence entre héritage (mapper une hiérarchie de classe) et polymorphisme (une clef étrangère qui pointe vers une autre classe) car le vocabulaire utilisé par les différents ORM et framework peuvent parfois prêter à confusion.</p>
<p>S'ensuit un rappel bienvenu de l’héritage implémenté directement au niveau de Doctrine ; avec des exemples concrets, il passe en revue toutes les combinaisons possibles : soit avec des MappedSuperClass, soit des DiscriminatorMap.</p>
<p>Fort de son expérience, il nous explique aussi qu’il est possible d’assez facilement passer d’une architecture Single Table Inheritance (STI) vers Class Table Inheritance (CTI). Donc les choix techniques ne sont pas forcément figés, les projets évoluent, et des solutions sont toujours possibles. Il nous rappelle aussi que lorsqu’on utilise les CTI, il est important de bien vérifier les index pour gagner en performance.</p>
<h3>JSON + SQL : hérésie ou élégance ? Retour d'expérience - Rémy Bonfils, Olivier FOURNY</h3>
<p>On reste ici dans le thème de la modélisation de nos bases de données avec un retour d’expérience sur l’utilisation de JSON dans nos tables SQL, via l’exemple d’une application mobile offline permettant de configurer des maisons imprimées en 3D (oui oui).</p>
<p>Étant donné la nature très flexible des paramètres d’impression (80000 configurations possibles, importées depuis un CSV), la question de comment les stocker se pose dès le départ.
Rémy et Olivier nous expliquent vouloir tout d’abord se diriger vers une modélisation de type Entity-Attribute-Valeur, où les paramètres ne sont pas représentés par des colonnes dans nos tables, mais par des lignes (ça doit parler aux personnes devant travailler sur des projets Magento ou Drupal) : beaucoup de flexibilité évidemment, mais aussi l’impossibilité d’avoir un minimum de structure dans nos données (tout est varchar). Et surtout des requêtes beaucoup plus complexes et donc des performances catastrophiques.</p>
<p>L’équipe se penche donc sur une autre solution : le stockage des paramètres dans des colonnes de la base de données en format JSON.</p>
<p>Et la surprise, les performances sont à peine inférieures à une modélisation classique de base de données (avec des tables liées par des clés étrangères), mais en gardant la flexibilité voulue ! Et grâce aux fonctions SQL permettant de manipuler du JSON, les requêtes restent simples et lisibles.</p>
<p>Pour notre part, à JoliCode, nous avons l’habitude de profiter du type JSON dans nos bases de données relationnelles (PostgreSQL ou MySQL), et nous vous le recommandons lorsque le besoin s’en fait ressentir.</p>
<h3>ClickHouse pour les développeurs Symfony - Romain Neutron</h3>
<p><picture class="js-dialog-target" data-original-url="/media/original/2026/symfony-live-paris-2026/clickhouse-romain-neutron.jpg" data-original-width="1600" data-original-height="901"><source type="image/webp" srcset="/media/cache/content-webp/2026/symfony-live-paris-2026/clickhouse-romain-neutron.6afbb7d4.webp" /><source type="image/jpeg" srcset="/media/cache/content/2026/symfony-live-paris-2026/clickhouse-romain-neutron.jpg" /><img loading="lazy" decoding="async" style="width: 996px; ; aspect-ratio: calc(1600 / 901)" src="https://jolicode.com//media/cache/content/2026/symfony-live-paris-2026/clickhouse-romain-neutron.jpg" alt="Romain Neutron lors de son talk sur ClickHouse" /></picture></p>
<p>Dans cette session, Romain Neutron a abordé la problématique de la gestion des données analytiques et des logs à grande échelle, des domaines où les bases relationnelles classiques comme MySQL ou PostgreSQL atteignent souvent leurs limites. Il a présenté <a rel="nofollow noopener noreferrer" href="https://clickhouse.com/">ClickHouse</a>, une base de données orientée colonnes ultra-performante, comme la solution idéale pour traiter des volumes massifs de données en temps réel. L'idée centrale n'est pas de remplacer votre base de données habituelle, mais de l'épauler pour des besoins spécifiques d'agrégation et de dashboards instantanés.</p>
<p>Côté technique, la conférence a mis en lumière la simplicité d'intégration de ClickHouse dans l'écosystème Symfony. Il a également partagé des benchmarks impressionnants comparant les temps de réponse sur des agrégations de plusieurs millions de lignes, montrant que ClickHouse peut transformer des requêtes de plusieurs secondes en résultats quasi instantanés.</p>
<p>Enfin, Romain a insisté sur les bonnes pratiques et les pièges à éviter, notamment sur la structure des données et le choix des moteurs de table (comme MergeTree). C'est un talk indispensable pour les développeurs cherchant à scaler leur stack analytique tout en restant dans un environnement PHP familier. De notre côté, on utilise ClickHouse dans plusieurs projets, surtout dans <a rel="nofollow noopener noreferrer" href="https://redirection.io/">redirection.io</a> pour les parties logs, analytics et crawler. On ne peut donc que vous recommander de vous y intéresser.</p>
<p>Ses slides sont <a rel="nofollow noopener noreferrer" href="https://speakerdeck.com/romainneutron/clickhouse-for-symfony-developers-symfonylive-paris-2026">disponibles en ligne</a> pour retrouver toutes les informations importantes.</p>
<h2>Symfony UX et la suite Hotwired</h2>
<p>Cette année, on continue de parler de Symfony UX, et en particulier, deux retours d’expériences spécifiquement axés sur <a rel="nofollow noopener noreferrer" href="https://hotwired.dev/">hotwired.dev</a>.</p>
<h3>Du web au mobile avec Symfony &amp; Hotwire Native - Imad ZAIRIG</h3>
<p>Imad nous a préparé une conférence sur le nouveau bundle Symfony UX Native qui utilise Hotwire Native. Basé sur un exemple, on peut voir comment l’application est architecturée.</p>
<p>On a eu l'exemple d'un bouton, rendu avec un composant bouton mobile, mais dont l'événement click est écouté par stimulus du côté de l’application Symfony</p>
<p>Cela fonctionne avec des webview pour que ce soit toujours Symfony qui gère le back-end et le front, mais avec des comportements mobiles gérés nativement (ex : la navigation et les transitions). On note qu’il y a quand même encore quelques fois ou il faut lancer le projet mobile avec Xcode par exemple pour iOS.</p>
<p>Pour utiliser les capacités natives des applications mobiles (type appareil photo) il faut passer par des &quot;Bridges Component&quot;, ça demande malgré tout du code côté mobile.</p>
<p>Selon la complexité de l’application et la taille de l’équipe, Symfony UX Native est une piste à explorer.</p>
<h3>Édition simultanée : facile avec Symfony UX - David Buchmann</h3>
<p>Au travers d’un exemple concret David nous a fait voir comment intégrer toute la suite d’outils de hotwired.dev. Il commence avec Turbo (et un peu de Turbo Frames aussi) et nous fait voir à quel point c’est bien intégré à Symfony UX. Puis sa conférence continue avec la mise en place de Mercure (grâce à son intégration dans FrankenPHP), et finalement le tout s’intègre parfaitement via des contrôleurs Stimulus qui écoutent les messages Mercure.</p>
<p>Il résume les avantages de Hotwire comme ceci : la logique reste dans le backend, la sécurité est intégrée. La complexité du frontend s’en trouve réduite.</p>
<h2>Conclusion</h2>
<p>Si cette édition du SymfonyLive Paris 2026 nous a offert un aperçu saisissant de l'intégration massive de l'Intelligence Artificielle au cœur de l'écosystème Symfony, elle prouve une chose essentielle : l'importance des conférences n'a jamais été aussi grande. Notre métier de développeur est en pleine mutation, et nous avons fort à faire pour rester à jour.</p>
<p>Le fil conducteur de cette année reste cependant une évidence : quelle que soit la puissance des outils, <strong>l'humain reste au centre de la boucle</strong>. L'IA est un assistant extraordinaire pour la production de code et les tâches répétitives, mais c'est bien la communauté, la validation humaine et le partage de connaissances qui garantissent la qualité et la progression de notre écosystème.
Merci aux organisateurs, aux conférenciers, et à la communauté d'avoir fait de cette édition 2026 un moment marquant, et rendez-vous l'année prochaine pour continuer à naviguer ensemble dans le futur du développement web !</p>]]></description></item><item><title>Symfony Live 2026 &#x2013; retours</title><link>https://www.gameandme.fr/divers/symfony-live-2026-retours/</link><author>Yohann Nizon</author><date>Wed, 01 Apr 2026 09:52:19 +0200</date><description><![CDATA[<p>Récemment, j&#8217;ai eu la chance de participer au Symfony Live 2026, l&#8217;occasion pour moi de rencontrer mes collègues de Smile, et de voir les dernières tendances du développement PHP. Je vous propose ici un petit retour de cette sympathique expérience. Text User interface: L&#8217;ouverture de la conférence a été faite par Fabien Potencier &#8211; l&#8217;auteur ... <a title="Symfony Live 2026 &#8211; retours" class="read-more" href="https://www.gameandme.fr/divers/symfony-live-2026-retours/" aria-label="En savoir plus sur Symfony Live 2026 &#8211; retours">Lire la suite</a></p>
<p>Cet article <a href="https://www.gameandme.fr/divers/symfony-live-2026-retours/">Symfony Live 2026 &#8211; retours</a> est apparu en premier sur <a href="https://www.gameandme.fr">Game And Me</a>.</p>
]]></description></item></channel></rss>
