<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Thomas Bonnet</title>
    <description>The latest articles on DEV Community by Thomas Bonnet (@thomasbnt).</description>
    <link>https://dev.to/thomasbnt</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F18254%2Feb9322d6-40dc-4312-b39e-7b03c75ed018.png</url>
      <title>DEV Community: Thomas Bonnet</title>
      <link>https://dev.to/thomasbnt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thomasbnt"/>
    <language>en</language>
    <item>
      <title>How to Create Your Own Personal Blog with Astro</title>
      <dc:creator>Thomas Bonnet</dc:creator>
      <pubDate>Sat, 08 Aug 2026 10:05:11 +0000</pubDate>
      <link>https://dev.to/thomasbnt/how-to-create-your-own-personal-blog-with-astro-5d7h</link>
      <guid>https://dev.to/thomasbnt/how-to-create-your-own-personal-blog-with-astro-5d7h</guid>
      <description>&lt;p&gt;You want a personal blog without spending three months on it or paying for a CMS every month. &lt;br&gt;
This site you’re reading right now is exactly that: an &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro&lt;/a&gt; blog. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk88yxqqjlm7tnnf1ozwj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk88yxqqjlm7tnnf1ozwj.png" alt="An example of a post on my website under Astro" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No database, no admin panel to maintain&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Markdown files&lt;/li&gt;
&lt;li&gt;Content Collections&lt;/li&gt;
&lt;li&gt;A bit of Tailwind&lt;/li&gt;
&lt;li&gt;And a static deployment on Cloudflare Workers. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s how to set it up, using the same building blocks as the ones used here.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A personal blog is a project you should be able to maintain on your own, for as long as you want. No heavy external dependencies, no CMS that disappears overnight, no server to run. Just a Git repository, Markdown files, and static hosting.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Why Astro for a blog
&lt;/h2&gt;

&lt;p&gt;Astro itself explains its philosophy in &lt;a href="https://docs.astro.build/fr/concepts/why-astro/" rel="noopener noreferrer"&gt;“Why Astro”&lt;/a&gt;: a framework designed for content-focused sites, server-side by default. In practical terms, this means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity&lt;/strong&gt;: no state management, no JavaScript framework to learn just to write an article. &lt;code&gt;.astro&lt;/code&gt; components and Markdown that’s it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt;: Astro renders static HTML by default; no JavaScript is sent to the client unless you explicitly request it (&lt;code&gt;client:load&lt;/code&gt;, etc.). For a blog, this is exactly what you need: content that’s easy to read, not a SPA.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast deployment&lt;/strong&gt;: static build, no servers to run, deploys to any CDN in seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Markdown content&lt;/strong&gt;: each post is a &lt;code&gt;.md&lt;/code&gt; file, versioned in Git, editable in any text editor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy to maintain&lt;/strong&gt;: Content collections provide data typing for front matter using Zod, which is validated directly during the build a front matter error breaks the build rather than silently crashing in production.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Create the project
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create astro@latest &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--template&lt;/span&gt; minimal
&lt;span class="nb"&gt;cd &lt;/span&gt;mon-blog
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Once Tailwind and the content collections have been added (next steps), the directory structure of a minimal Astro blog looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-blog/
├── astro.config.ts
├── package.json
├── tsconfig.json
└── src/
    ├── content.config.ts       # article structure (Zod)
    ├── styles/
    │   └── global.css          # import Tailwind
    ├── data/
    │   └── blog/
    │       ├── premier-article/
    │       │   ├── index.md
    │       │   └── banner.webp
    │       └── deuxieme-article/
    │           └── index.md
    ├── layouts/
    │   ├── Layout.astro        # &amp;lt;head&amp;gt;, meta, theme
    │   └── PostDetails.astro   # The rendering of an article
    ├── components/
    │   └── Header.astro
    └── pages/
        ├── index.astro
        └── blog/
            ├── index.astro         # List of Articles
            └── [...slug]/
                └── index.astro     # article page (dynamic route)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A deliberately simplified structure to get started: in a real project, you’ll quickly add a pagination route (&lt;code&gt;[...page].astro&lt;/code&gt;) and quite a few other components as you go.&lt;/p&gt;

&lt;p&gt;Add Tailwind (v4, via the Vite plugin, no need to manage a &lt;code&gt;tailwind.config.js&lt;/code&gt; file):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;tailwindcss @tailwindcss/vite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// astro.config.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;astro/config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;tailwindcss&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@tailwindcss/vite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;vite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;tailwindcss&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a CSS entry point that imports Tailwind:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* src/styles/global.css */&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"tailwindcss"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Articles in Content Collections
&lt;/h2&gt;

&lt;p&gt;Each article is stored in its own folder under &lt;code&gt;src/data/blog/&amp;lt;slug&amp;gt;/&lt;/code&gt;, along with an &lt;code&gt;index.md&lt;/code&gt; file and its images (&lt;code&gt;banner.webp&lt;/code&gt;, screenshots, etc.). &lt;br&gt;
This keeps each article self-contained: there’s no need to search through a shared &lt;code&gt;public/images&lt;/code&gt; folder to find an asset.&lt;/p&gt;

&lt;p&gt;The schema is declared once, in &lt;code&gt;src/content.config.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineCollection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;astro:content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;glob&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;astro/loaders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineCollection&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;**/[^_]*.md&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/data/blog&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;pubDatetime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;modDatetime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;draft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;notes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
      &lt;span class="na"&gt;ogImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;image&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;collections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;blog&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So an article is just that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;My&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;First&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Article"&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;short&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tagline&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;lists&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;SEO."&lt;/span&gt;
&lt;span class="na"&gt;pubDatetime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2026-08-10&lt;/span&gt;
&lt;span class="na"&gt;draft&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Astro&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

Markdown content here.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;draft&lt;/code&gt; is set to &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;pubDatetime&lt;/code&gt; is in the future, the article should not appear in the listings: &lt;br&gt;
This is a filter you must apply yourself when calling &lt;code&gt;getCollection&lt;/code&gt;; Astro does not do this for you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getCollection&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;astro:content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isPublished&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pubDatetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;draft&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;isPublished&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Layouts: One for the list, one for the post
&lt;/h2&gt;

&lt;p&gt;Two layouts are enough for the vast majority of personal blogs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A base layout (&lt;code&gt;Layout.astro&lt;/code&gt;) that sets the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, the OpenGraph meta tags, and the light/dark theme.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;PostDetails.astro&lt;/code&gt; that receives a collection entry, displays the title and dates, and renders the Markdown via &lt;code&gt;render(post)&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
import { render } from "astro:content";
const { post } = Astro.props;
const { Content } = await render(post);
---

&amp;lt;article&amp;gt;
  &amp;lt;Content /&amp;gt;
&amp;lt;/article&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All typographic styling (headings, quotes, code) is handled by a Tailwind class like &lt;code&gt;prose&lt;/code&gt; applied to this &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt;, &lt;br&gt;
not by hand-written CSS on a per-element basis.&lt;/p&gt;
&lt;h2&gt;
  
  
  Lightweight i18n: a dictionary, not a router
&lt;/h2&gt;

&lt;p&gt;There’s no need for Astro’s native i18n routing if the site has only one content language and just interface labels to translate. &lt;br&gt;
A simple dictionary is all you need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/i18n/ui.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultLang&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ui&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nav.home&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Accueil&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;footer.rights&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tous droits réservés&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nav.home&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Home&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;footer.rights&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;All rights reserved&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/i18n/utils.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultLang&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./ui&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useTranslations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;keyof &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;defaultLang&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;defaultLang&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a component: &lt;code&gt;const t = useTranslations(lang); t(“nav.home”)&lt;/code&gt;. &lt;br&gt;
Zero external dependencies, zero JSON translation files to load dynamically: everything is typed and validated during the build by TypeScript.&lt;/p&gt;

&lt;p&gt;For the articles themselves, rather than having duplicate content in two languages, an optional &lt;code&gt;englishUrl&lt;/code&gt; field in the front matter is enough to point to an English version published elsewhere (dev.to, for example), without having to maintain a complete translation that’s synchronized with every change.&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploying to Cloudflare Workers
&lt;/h2&gt;

&lt;p&gt;Astro in static mode generates a &lt;code&gt;dist/&lt;/code&gt; folder ready for hosting. &lt;br&gt;
Cloudflare Workers can serve these static assets directly (no need for Pages) via Wrangler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; wrangler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="c1"&gt;// wrangler.jsonc&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-blog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compatibility_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-01-01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"assets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"directory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
npx wrangler deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each &lt;code&gt;wrangler deploy&lt;/code&gt; command pushes assets to the Cloudflare edge. &lt;br&gt;
For automated CI/CD (push = deployment), connect your Git repo in the Cloudflare Workers dashboard or add a &lt;code&gt;wrangler deploy&lt;/code&gt; step to your pipeline (GitHub Actions, for example).&lt;/p&gt;

&lt;p&gt;No servers to manage, no cold starts for static content. &lt;br&gt;
Cloudflare’s CDN serves files directly from the edge, and you can get right back to writing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t want to start from scratch?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://thomasbnt.dev" rel="noopener noreferrer"&gt;My website&lt;/a&gt; is actually based on an existing theme: &lt;a href="https://github.com/satnaing/astro-paper" rel="noopener noreferrer"&gt;AstroPaper&lt;/a&gt;, a minimalist, responsive, accessible, and SEO-friendly blog theme, which I then customized to my liking (Tailwind v4, lightweight i18n, TOC, etc.). &lt;/p&gt;

&lt;p&gt;No need to build everything from scratch: the &lt;a href="https://astro.build/themes/" rel="noopener noreferrer"&gt;official Astro theme marketplace&lt;/a&gt; lists dozens of ready-to-use templates (blogs, portfolios, documentation, e-commerce) that you can clone and then customize.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Brick&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Astro&lt;/td&gt;
&lt;td&gt;Generates static HTML; no JS by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Content collections&lt;/td&gt;
&lt;td&gt;Markdown articles, validated by a Zod schema&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tailwind v4&lt;/td&gt;
&lt;td&gt;Styling, with no configuration files to maintain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;i18n dictionary&lt;/td&gt;
&lt;td&gt;Translated interface labels, without heavy routing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare Workers&lt;/td&gt;
&lt;td&gt;Static hosting, one-command deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A personal blog with Astro has very few moving parts: Markdown files organized into folders, a Zod schema that keeps the front matter clean, Tailwind for styling, and a static site generator that pushes everything to a CDN. No operations to manage, nothing to run continuously, just write, commit, push.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn More
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.astro.build/en/" rel="noopener noreferrer"&gt;Official Astro Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.astro.build/en/guides/content-collections/" rel="noopener noreferrer"&gt;Content Collections&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.astro.build/en/basics/layouts/" rel="noopener noreferrer"&gt;Layouts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.astro.build/en/guides/internationalization/" rel="noopener noreferrer"&gt;Internationalization (i18n)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.astro.build/en/guides/view-transitions/" rel="noopener noreferrer"&gt;View Transitions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/workers/static-assets/" rel="noopener noreferrer"&gt;Cloudflare Workers: Static Assets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>astro</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>css</category>
    </item>
    <item>
      <title>Passkeys Explained Simply</title>
      <dc:creator>Thomas Bonnet</dc:creator>
      <pubDate>Mon, 27 Jul 2026 21:09:04 +0000</pubDate>
      <link>https://dev.to/thomasbnt/passkeys-explained-simply-52jk</link>
      <guid>https://dev.to/thomasbnt/passkeys-explained-simply-52jk</guid>
      <description>&lt;p&gt;You’ve probably seen that little prompt that says “Sign in with Face ID” or “Use a passkey” instead of the traditional password field. That’s a passkey. And no, it’s not just a password hidden behind your fingerprint.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The best password is the one you never have to type.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Problem with Passwords
&lt;/h2&gt;

&lt;p&gt;A password is a secret that &lt;strong&gt;you&lt;/strong&gt; have to remember, and that the &lt;strong&gt;website&lt;/strong&gt; has to store (ideally hashed). The problem is that this system relies entirely on two weak points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You, who reuse the same password everywhere because you’re tired of coming up with a new one.&lt;/li&gt;
&lt;li&gt;The website, which can be hacked and have its password database leaked.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add phishing to that: a fake website that perfectly mimics the real one can steal your password without you even realizing it. And this isn’t just a minor issue: as the &lt;a href="https://www.verizon.com/business/resources/reports/dbir/" rel="noopener noreferrer"&gt;Verizon DBIR report&lt;/a&gt; reminds us every year, social engineering, phishing, and stolen credentials remain among the leading causes of hacking worldwide, alongside software vulnerabilities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzo69h9bg8kmgjk48j0vf.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzo69h9bg8kmgjk48j0vf.webp" alt="Excerpt from Verizon’s DBIR report on the causes of hacking" width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;Source: &lt;a href="https://www.verizon.com/business/resources/reports/dbir/" rel="noopener noreferrer"&gt;Data Breach Investigations Report, Verizon&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Even with a password manager and 2FA, the fundamental vulnerability remains: a secret that you could accidentally share.&lt;/p&gt;

&lt;h2&gt;
  
  
  What exactly is a passkey?
&lt;/h2&gt;

&lt;p&gt;A passkey is based on &lt;strong&gt;asymmetric cryptography&lt;/strong&gt;, the same principle as SSH, if you've ever generated a key pair to connect to a server.&lt;/p&gt;

&lt;p&gt;When you create a passkey for a website:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your device (phone, computer, security token) generates a &lt;strong&gt;key pair&lt;/strong&gt;: a private key and a public key.&lt;/li&gt;
&lt;li&gt;The private key &lt;strong&gt;never&lt;/strong&gt; leaves your device. It is stored in a secure chip: the &lt;a href="https://support.apple.com/guide/security/secure-enclave-sec59b0b31ff/web" rel="noopener noreferrer"&gt;Secure Enclave&lt;/a&gt; on iPhone/Mac, the &lt;a href="https://learn.microsoft.com/fr-fr/%20windows/security/hardware-security/tpm/trusted-platform-module-overview" rel="noopener noreferrer"&gt;TPM&lt;/a&gt; on Windows, or directly on the chip of a USB key such as a &lt;a href="https://www.yubico.com/authentication-standards/fido2/" rel="noopener noreferrer"&gt;YubiKey&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The public key, on the other hand, is sent to the website and stored on the server.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To log in, the site sends you a challenge (a random number), your device signs it with the private key, and the site verifies the signature using the public key. If they match, you're authenticated.&lt;/p&gt;

&lt;p&gt;No secrets ever travel over the network only a signature. It’s written in black and white in the &lt;a href="https://www.w3.org/TR/webauthn-3/" rel="noopener noreferrer"&gt;W3C WebAuthn spec&lt;/a&gt;: nothing to steal, nothing to phish.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why It’s More Secure
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Password&lt;/th&gt;
&lt;th&gt;Passkey&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Shared secret, transmitted with every login&lt;/td&gt;
&lt;td&gt;Nothing is transmitted, just a signature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reusable across multiple sites&lt;/td&gt;
&lt;td&gt;Unique per site, tied to the domain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vulnerable to phishing&lt;/td&gt;
&lt;td&gt;Resistant to phishing (tied to the exact domain)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can be leaked in the event of a server breach&lt;/td&gt;
&lt;td&gt;The server stores only the public key, which is useless to an attacker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Must be memorized or managed via a manager&lt;/td&gt;
&lt;td&gt;Unlocked via biometrics or a local code&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The key point: a passkey is &lt;strong&gt;linked to the domain&lt;/strong&gt; on which it was created. If you go to &lt;code&gt;paypa1.com&lt;/code&gt; instead of &lt;code&gt;paypal.com&lt;/code&gt;, your device won't even offer the passkey. It's the browser/OS that performs this verification, not you. Google &lt;a href="https://blog.google/innovation-and-ai/technology/safety-security/the-beginning-of-the-end-of-the-password/" rel="noopener noreferrer"&gt;explains it very well&lt;/a&gt;: unlike a password, a passkey is cryptographically bound to the site for which it was created, so it’s impossible to intercept or reuse on a fake site. They’re resistant to phishing by design, not because of the user’s good faith.&lt;/p&gt;

&lt;h2&gt;
  
  
  Under the Hood: WebAuthn and FIDO2
&lt;/h2&gt;

&lt;p&gt;Technically, passkeys are based on the &lt;strong&gt;&lt;a href="https://www.w3.org/TR/webauthn-3/" rel="noopener noreferrer"&gt;WebAuthn&lt;/a&gt;&lt;/strong&gt; (W3C) standard, which is part of the broader &lt;strong&gt;&lt;a href="https://fidoalliance.org/fido2/" rel="noopener noreferrer"&gt;FIDO2&lt;/a&gt;&lt;/strong&gt; standard promoted by the FIDO Alliance (whose members include Google, Apple, Microsoft, and Yubico). It is this API that browsers expose via JavaScript to create and use credentials.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmxqm00pn5ndq1rolcavi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmxqm00pn5ndq1rolcavi.png" alt="Diagram of the WebAuthn registration flow between the user, the browser, and the server" width="794" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;Diagram of the registration flow, based on the &lt;a href="https://www.w3.org/TR/webauthn-3/" rel="noopener noreferrer"&gt;W3C WebAuthn spec&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Creating a client-side passkey looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;credential&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// provided by the server&lt;/span&gt;
    &lt;span class="na"&gt;rp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My Super Site&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mysupersite.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;thomas@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Thomas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;pubKeyCredParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;alg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="c1"&gt;// ES256&lt;/span&gt;
    &lt;span class="na"&gt;authenticatorSelection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;userVerification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to log in with an existing passkey:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assertion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// provided by the server&lt;/span&gt;
    &lt;span class="na"&gt;userVerification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server generates the &lt;code&gt;challenge&lt;/code&gt;, the browser handles all the biometrics and unlocking, and sends you back a signature to verify. On the server side, libraries like &lt;a href="https://simplewebauthn.dev/" rel="noopener noreferrer"&gt;&lt;code&gt;@simplewebauthn/server&lt;/code&gt;&lt;/a&gt; in Node.js do most of the verification work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What if I lose my phone?
&lt;/h2&gt;

&lt;p&gt;This is the question that stumps everyone, and if this topic resonates with you, I’ve already written an entire article on the &lt;a href="https://thomasbnt.dev/blog/telephone-vole-2fa-cauchemar-commence-apres/" rel="noopener noreferrer"&gt;hassles of 2FA after a phone theft&lt;/a&gt;, the same “single point of failure” principle applies to passkeys. Since 2022, Apple, Google, and Microsoft have added &lt;strong&gt;passkey synchronization&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On iOS/macOS, they sync via your &lt;a href="https://support.apple.com/fr-fr/102195" rel="noopener noreferrer"&gt;iCloud Keychain&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;On Android/Chrome, they sync via your &lt;a href="https://blog.google/innovation-and-ai/technology/safety-security/google-password-manager-passkeys-update-september-2024/" rel="noopener noreferrer"&gt;Google Account&lt;/a&gt;, using Google Password Manager, which encrypts everything end-to-end.&lt;/li&gt;
&lt;li&gt;On Windows, via &lt;a href="https://support.microsoft.com/fr-fr/windows/passkeys-dans-windows-301c8944-5ea2-452b-9886-97e4d2ef4422" rel="noopener noreferrer"&gt;Windows Hello&lt;/a&gt;, although cross-device synchronization is even more limited there than with Apple or Google.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if you lose your phone but regain access to your iCloud or Google account on a new device, your passkeys will be restored as well. You can also use a &lt;a href="https://thomasbnt.dev/blog/telephone-vole-2fa-cauchemar-commence-apres#une-cl%C3%A9-de-s%C3%A9curit%C3%A9-physique-fido2--yubikey" rel="noopener noreferrer"&gt;physical security key (YubiKey)&lt;/a&gt; as a backup, which doesn’t sync but works on any compatible device exactly the same advice as for traditional 2FA: never put all your eggs in one basket.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use Them Today
&lt;/h2&gt;

&lt;p&gt;More and more services already offer them: Google, Apple, GitHub, Microsoft, PayPal, Amazon, X... Google has even gone a step further by making &lt;a href="https://blog.google/innovation-and-ai/technology/safety-security/passkeys-default-google-accounts/" rel="noopener noreferrer"&gt;passkeys the default for personal accounts&lt;/a&gt;. You’ll usually find them in your account’s security settings, under “Access Key” or “Passkey.”&lt;/p&gt;

&lt;p&gt;The typical workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to the service's security settings.&lt;/li&gt;
&lt;li&gt;Click “Add an access key” or “Create a passkey.”&lt;/li&gt;
&lt;li&gt;Your browser or operating system will ask you to confirm using Face ID, Touch ID, Windows Hello, or your phone's passcode.&lt;/li&gt;
&lt;li&gt;That's it. The next time you log in, you won't need a password.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to manage your passkeys somewhere other than your OS’s keychain (useful if you’re on multiple different platforms), &lt;a href="https://go.getproton.me/SH2BG" rel="noopener noreferrer"&gt;ProtonPass&lt;/a&gt; can also create and store them—end-to-end encrypted—in the same place as your passwords.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Answer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What is it based on?&lt;/td&gt;
&lt;td&gt;Asymmetric cryptography (private/public key pair)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Does the password travel over the network?&lt;/td&gt;
&lt;td&gt;No, never&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resistant to phishing?&lt;/td&gt;
&lt;td&gt;Yes, by design (domain-bound)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technical standard&lt;/td&gt;
&lt;td&gt;WebAuthn / FIDO2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Device loss&lt;/td&gt;
&lt;td&gt;Recoverable via iCloud/Google sync, or physical backup key&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Passkeys aren't just “a password hidden behind a fingerprint”, they represent a complete shift in the model: nothing to remember, nothing to steal. If a service offers you the option to create one, go for it it's significantly more secure and faster than your current password.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The ProtonPass link in this article is an affiliate link, which means I may receive a commission if you decide to sign up through this link, at no additional cost to you. This helps me cover hosting and domain name fees. Thank you for your support!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>api</category>
      <category>webdev</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Clean up Docker images that are taking up space</title>
      <dc:creator>Thomas Bonnet</dc:creator>
      <pubDate>Tue, 21 Jul 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/thomasbnt/clean-up-docker-images-that-are-taking-up-space-2oem</link>
      <guid>https://dev.to/thomasbnt/clean-up-docker-images-that-are-taking-up-space-2oem</guid>
      <description>&lt;p&gt;You deploy, you rebuild, you test things. And one day, you look at your server and you have only 2 GB of free space left. Congratulations, Docker has eaten it all up.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your Docker hard drive hates you. And it’s your fault.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What exactly is the problem?
&lt;/h2&gt;

&lt;p&gt;Docker accumulates images over time. Every time you run a &lt;code&gt;docker build&lt;/code&gt;, if anything changes, it creates new layers and keeps the old ones. These old layers which have no tags, no names, and no purpose are called dangling images.&lt;/p&gt;

&lt;p&gt;They serve no purpose. They take up space. And Docker doesn’t delete them on its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  See where you stand
&lt;/h2&gt;

&lt;p&gt;Before deleting anything, there’s just one thing to do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker system &lt;span class="nb"&gt;df&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows you in a few lines how much space is being used—and, more importantly, how much is available. That’s your starting point.&lt;/p&gt;

&lt;p&gt;Want to see the images in detail?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker images &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"table {{.Repository}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Tag}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.ID}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Size}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Delete dangling images
&lt;/h2&gt;

&lt;p&gt;Dangling images are intermediate layers that are left behind. They have neither a tag nor a name. To delete them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image prune
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker asks for your confirmation, shows you what it's going to delete, and you confirm. Clean, fast, and risk-free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go a step further
&lt;/h2&gt;

&lt;p&gt;If you have entire images that are no longer used by any container (even stopped ones), you can delete them as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image prune &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-a&lt;/code&gt; option changes everything: now it no longer targets just dangling images, but all unreferenced images. That’s what you want in most cases on a production or staging server.&lt;/p&gt;

&lt;p&gt;You can filter by age if you want to keep recent images:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image prune &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s2"&gt;"until=720h"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It only deletes items that are more than 30 days old. This is handy if you have regular deployments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reset Everything
&lt;/h2&gt;

&lt;p&gt;If you want to reset everything images, stopped containers, orphaned volumes, unused networks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker system prune &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--volumes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I don’t recommend doing this on a production server without first checking what’s running. But on a dev machine or a VPS that you rebuild regularly, it’s perfect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automating All of This
&lt;/h2&gt;

&lt;p&gt;Instead of doing this manually, you can set up a weekly cron job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# crontab -e&lt;/span&gt;
0 3 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; 0 docker image prune &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/docker-prune.log 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every Sunday at 3 AM, it cleans up without asking for confirmation (-f), and logs the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker system df&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View used space&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker image prune&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove dangling images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker image prune -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove everything that is no longer in use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker image prune -a --filter “until=720h”&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Same as above, but only for old images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker system prune -a --volumes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reset everything to zero&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Start with &lt;code&gt;docker system df&lt;/code&gt;. In 90% of cases, running &lt;code&gt;docker image prune -a&lt;/code&gt; is enough to free up several gigabytes without breaking anything.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>productivity</category>
      <category>devops</category>
    </item>
    <item>
      <title>RES, my personal safeguard against AI</title>
      <dc:creator>Thomas Bonnet</dc:creator>
      <pubDate>Thu, 16 Jul 2026 21:17:26 +0000</pubDate>
      <link>https://dev.to/thomasbnt/res-my-personal-safeguard-against-ai-3ja8</link>
      <guid>https://dev.to/thomasbnt/res-my-personal-safeguard-against-ai-3ja8</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;The more I use AI in my daily life, the longer my conversations get. And the longer a conversation goes on, the greater the risk that the AI will &lt;strong&gt;stray&lt;/strong&gt; from my initial instructions. A change in tone, a forgotten constraint, a format that’s no longer followed… Nothing dramatic in itself, but enough to waste time rereading each response in detail to make sure it’s still consistent.&lt;/p&gt;

&lt;p&gt;Just to be clear: this flag doesn’t mean you can skip rereading what the AI gives you and checking it yourself—especially before taking action. It’s an extra warning sign, not a substitute for your own vigilance.&lt;/p&gt;

&lt;p&gt;The thing is, I never realize it right away. The AI doesn’t tell me, “By the way, I skipped that instruction you gave me 10 messages ago.” I notice it after the fact, usually once it’s already a little too late.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trick I use
&lt;/h2&gt;

&lt;p&gt;I saw a tip in a YouTube video (a source I haven't been able to find again, unfortunately 🙈): ask the AI to write &lt;strong&gt;a specific word of your choice at the very beginning of each response.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In my case, that word is &lt;code&gt;RES&lt;/code&gt;. I added it to my preferences:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every time you reply to me, start with “RES.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And since then, every reply has started like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RES

[response]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why It Works
&lt;/h2&gt;

&lt;p&gt;The principle is simple: it’s a &lt;strong&gt;constant prompt&lt;/strong&gt; that appears in all my conversations, and I can check it at a glance. If the word is there, I know the AI still has my instructions in mind when responding. If it &lt;strong&gt;disappears&lt;/strong&gt;, that’s my warning sign: something got lost along the way, so I need to reread the response more carefully—or even remind the AI of my instructions.&lt;/p&gt;

&lt;p&gt;It’s a bit like a canary in a coal mine. 🐤 The canary doesn’t solve anything on its own; it does just one thing: signal that something unusual is happening, before it becomes a real problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Comparison with Security
&lt;/h2&gt;

&lt;p&gt;As I delved deeper into the subject, I realized that this concept already exists in cybersecurity, under the name &lt;strong&gt;canary token&lt;/strong&gt;. It’s a unique string of characters, hidden in the system prompt of an AI application, designed to detect prompt injection attacks: if this token appears (or disappears) from the response, it indicates an attempt at manipulation.&lt;/p&gt;

&lt;p&gt;My use of it has nothing to do with security, but the underlying principle is the same: a constant signal, the absence of which reveals an anomaly.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Canary token (security)&lt;/th&gt;
&lt;th&gt;My token (“RES”)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What it protects against&lt;/td&gt;
&lt;td&gt;An attacker manipulating the AI&lt;/td&gt;
&lt;td&gt;A natural drift of the AI itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The word is...&lt;/td&gt;
&lt;td&gt;Hidden; the AI must never reveal it&lt;/td&gt;
&lt;td&gt;Visible and acknowledged; it is part of the response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How it is generated&lt;/td&gt;
&lt;td&gt;A random string, unique to each session&lt;/td&gt;
&lt;td&gt;A word chosen once and kept as is&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Who verifies it&lt;/td&gt;
&lt;td&gt;An automated script&lt;/td&gt;
&lt;td&gt;Me, by eye&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The key difference: the canary token protects against an &lt;strong&gt;external&lt;/strong&gt; threat. My “RES” monitors an &lt;strong&gt;internal&lt;/strong&gt; drift specific to how the model operates on a conversation that drags on.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Actually Changes
&lt;/h2&gt;

&lt;p&gt;Well, it doesn't guarantee anything 100%, it's not magic. But in practice, it's helped me to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spot more quickly when I need to steer a conversation back on track if it strays from my initial request&lt;/li&gt;
&lt;li&gt;Keep a simple visual cue, without having to reread everything in detail every time&lt;/li&gt;
&lt;li&gt;Have a stable reference point, no matter the topic or length of the exchange
It’s a simple, free tip that takes two seconds to set up. If you want to try it out, just add a line to your custom instructions or to the system prompt of your favorite AI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do you have any similar tips for staying in control of your conversations with AI? Let me know on &lt;a href="https://thomasbnt.dev/discord" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; or on social media! 💬&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Acronyms and Jargon in Development and Open Source</title>
      <dc:creator>Thomas Bonnet</dc:creator>
      <pubDate>Wed, 01 Jul 2026 16:35:59 +0000</pubDate>
      <link>https://dev.to/thomasbnt/acronyms-and-jargon-in-development-and-open-source-45m9</link>
      <guid>https://dev.to/thomasbnt/acronyms-and-jargon-in-development-and-open-source-45m9</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;You join an open-source project, read a discussion on GitHub, and then... PR, LGTM, WONTFIX, RFC. You nod politely, but you have no idea what any of that means.&lt;/p&gt;

&lt;p&gt;That’s normal. The world of development has developed its own dialect over the years, shaped by Git tools, open-source practices, and team communication habits. Here’s a glossary to help you decode it all. ✊&lt;/p&gt;

&lt;h2&gt;
  
  
  Git and GitHub/GitLab
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Basic Actions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Commit&lt;/strong&gt;: a snapshot of your code at a given moment. Each commit has a message explaining what you changed and why.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Push / Pull&lt;/strong&gt;: &lt;code&gt;push&lt;/code&gt; sends your commits to the remote repository. &lt;code&gt;pull&lt;/code&gt; fetches changes from the remote repository to your local machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fork&lt;/strong&gt;: a copy of a repository in your own space. You fork a project so you can contribute to it without affecting the original.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clone&lt;/strong&gt;: download a local copy of a remote repository to work on it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Branches and Merging
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Branch&lt;/strong&gt;: a parallel development path. You create a branch to work on a feature or a fix without affecting the main branch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merge&lt;/strong&gt;: to merge changes from one branch into another. “Merging a PR” means integrating the proposed code into the main repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rebase&lt;/strong&gt;: Reapplying your commits on top of another branch to maintain a clean, linear history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conflict&lt;/strong&gt;: When two people have modified the same lines of code differently. Git doesn’t know which one to choose, so it asks you to decide.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stash&lt;/strong&gt;: Set aside changes you’re working on without committing them, so you can quickly switch branches.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pull Requests and Issues
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PR (Pull Request) / MR (Merge Request)&lt;/strong&gt;: A proposed change. You submit your branch so it can be reviewed and integrated into the project. PR is the GitHub term; MR is the GitLab term.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issue&lt;/strong&gt;: a ticket. It can be a reported bug, a requested feature, or a question. The cornerstone of open-source project management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review / Code Review&lt;/strong&gt;: the process of reviewing the code proposed in a PR before it is merged. Someone leaves comments, suggestions, or approves it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Draft PR&lt;/strong&gt;: a PR marked as “in progress.” It indicates that the work isn’t finished, but that you want to share your progress.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Discussion Jargon
&lt;/h2&gt;

&lt;p&gt;These terms often appear in PR and issue comments.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LGTM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Looks Good To Me&lt;/em&gt;: informal approval of a PR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SGTM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Sounds Good To Me&lt;/em&gt;: same idea, for a verbal proposal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;WIP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Work In Progress&lt;/em&gt;: in progress, not yet ready&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RFC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Request For Comments&lt;/em&gt;: seeking feedback before making a decision&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;WONTFIX&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The bug is known but will not be fixed (intentionally)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RTFM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Read The Friendly Manual&lt;/em&gt;: go read the documentation (sometimes said less politely)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Too Long; Didn't Read&lt;/em&gt;: summary of a long text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;nit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Nitpick&lt;/em&gt;: minor comment on style or form, not a deal-breaker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ACK / NACK&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Acknowledged / Negative Acknowledge&lt;/em&gt;: “I agree” / “I disagree”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;+1 / -1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Vote for or against a proposal&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  About Projects and Contributing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;README.md&lt;/strong&gt;: A project's welcome file. It explains what the project is, how to install it, and how to contribute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CONTRIBUTING.md&lt;/strong&gt;: the project’s contribution guidelines. Read this before opening a PR.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CHANGELOG.md&lt;/strong&gt;: the history of changes between each version of the project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ROADMAP.md&lt;/strong&gt;: the project’s roadmap, outlining what’s planned for future versions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AGENTS.md&lt;/strong&gt;: supplements the README file by providing additional—and sometimes detailed—context that agents need: build steps, tests, and conventions that might clutter a README file or aren’t relevant to human contributors.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LICENSE.md / LICENCE.md&lt;/strong&gt;: the terms of use for the code. MIT, GPL, Apache… this determines what you can do with the code. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CI/CD&lt;/strong&gt;: &lt;em&gt;Continuous Integration / Continuous Deployment&lt;/em&gt;. Automated pipelines that test and deploy the code with every push or merge.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;upstream / downstream&lt;/strong&gt;: “upstream” refers to the original project (the one you forked). Downstream is your version or the projects that depend on yours.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maintainer&lt;/strong&gt;: the person (or team) who maintains a project. They review PRs, manage issues, and make decisions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Contributor&lt;/strong&gt;: anyone who contributes to a project, even without direct access to the repository.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Versioning and Releases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SemVer (Semantic Versioning)&lt;/strong&gt;: the &lt;code&gt;MAJOR.MINOR.PATCH&lt;/code&gt; convention (e.g., &lt;code&gt;2.4.1&lt;/code&gt;). A breaking change increments the MAJOR version. A new feature increments the MINOR version. A bug fix increments the PATCH version.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tag&lt;/strong&gt;: a marker on a commit, usually to indicate a version (&lt;code&gt;v1.0.0&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Release&lt;/strong&gt;: the official publication of a software version, often accompanied by a changelog and downloadable artifacts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breaking change&lt;/strong&gt;: a change that breaks compatibility with previous versions. This should be clearly indicated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deprecation&lt;/strong&gt;: marking a feature as obsolete before removing it in a future version.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Right Mindset
&lt;/h2&gt;

&lt;p&gt;The jargon may seem intimidating, but it’s there to speed up communication, not to exclude anyone. No one expects you to know all of this by heart right from the start.&lt;br&gt;
The best way to learn: contribute. Open issues, read other people’s PRs, ask questions. Most open-source communities are welcoming to beginners, especially if you’ve read &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; and &lt;code&gt;README.md&lt;/code&gt; beforehand. 😄&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn More
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://rogerdudler.github.io/git-guide/index.html" rel="noopener noreferrer"&gt;Git - A Short Guide&lt;/a&gt;: The basics of Git in French&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/firstcontributions/first-contributions/tree/main?tab=readme-ov-file" rel="noopener noreferrer"&gt;First Contributions&lt;/a&gt;: a step-by-step guide to your first pull request&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://choosealicense.com" rel="noopener noreferrer"&gt;Choose a License&lt;/a&gt;: understanding open-source licenses&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://semver.org/" rel="noopener noreferrer"&gt;Semantic Versioning&lt;/a&gt;: Understanding semantic versioning&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://agents.md/" rel="noopener noreferrer"&gt;Agents.md&lt;/a&gt;: A guide to writing a clear and useful &lt;code&gt;AGENTS.md&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Find me on &lt;a href="https://github.com/thomasbnt" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or &lt;a href="https://thomasbnt.dev/discord" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; to chat about development and open source!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>learning</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Which AI should you choose in 2026? Claude, Perplexity, Gemini, or ChatGPT</title>
      <dc:creator>Thomas Bonnet</dc:creator>
      <pubDate>Mon, 01 Jun 2026 00:07:18 +0000</pubDate>
      <link>https://dev.to/thomasbnt/which-ai-should-you-choose-in-2026-claude-perplexity-gemini-or-chatgpt-5bd9</link>
      <guid>https://dev.to/thomasbnt/which-ai-should-you-choose-in-2026-claude-perplexity-gemini-or-chatgpt-5bd9</guid>
      <description>&lt;h2&gt;
  
  
  Claude Code — My daily dev tool
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqhukxsz54lo0jgvaoil2.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqhukxsz54lo0jgvaoil2.webp" alt="Claude homepage" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://claude.ai/code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt;&lt;/strong&gt; by Anthropic is the one I use the most for development, by far. What sets it apart from the others: it integrates directly into the terminal and editor, it can read and modify files, navigate an entire codebase, and understand the global context of the project. Not just responding to a copy-pasted snippet in a chat window.&lt;/p&gt;

&lt;p&gt;In practice, when I have an idea, I ask it to structure the project and challenge my choices. And to be clear: I challenge it too. 😄 I sometimes disagree with its suggestions, and that's often where the conversation becomes interesting. It's a tool, not an oracle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Perplexity — My reference for research
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F95jr1pohemiwppxtlh7s.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F95jr1pohemiwppxtlh7s.webp" alt="Perplexity homepage" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.perplexity.ai/" rel="noopener noreferrer"&gt;Perplexity&lt;/a&gt;&lt;/strong&gt; is my main tool when I need a reliable and verifiable answer. It's a response engine that systematically cites its sources — you ask a question, it answers with excerpts from real web pages and direct links. No more hallucinations without references.&lt;/p&gt;

&lt;p&gt;However, I use it almost exclusively on desktop. On smartphone, it's flooded with messages pushing the paid version. Understandable from their side, but frankly annoying when you just want to do a quick search. 🙄&lt;/p&gt;

&lt;h2&gt;
  
  
  Gemini — For those in the Google ecosystem
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvrb5l61o8ctriismjokd.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvrb5l61o8ctriismjokd.webp" alt="Gemini homepage" width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://gemini.google.com/" rel="noopener noreferrer"&gt;Gemini&lt;/a&gt;&lt;/strong&gt; is Google's AI, and its main advantage is integration with Gmail, Docs, Drive, Sheets, and Google Search. I have a Google Pixel, and on that side, it does integrate very well with its own ecosystem. It's practical for analyzing documents or getting a quick summary without leaving the interface.&lt;/p&gt;

&lt;p&gt;That said, in terms of responses, it sometimes falters. 😬 Not systematically, but regularly enough that I stay on guard. And if privacy is a priority for you, it's worth thinking twice before entrusting it with your documents — I talk about this in &lt;a href="https://dev.to/blog/se-securiser-sur-internet"&gt;my article on securing yourself on the Internet&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ChatGPT — The natural entry point
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flhet7go4t0eeqjwe212s.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flhet7go4t0eeqjwe212s.webp" alt="ChatGPT homepage" width="800" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://chatgpt.com/" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt;&lt;/strong&gt; by OpenAI is the most known and most versatile AI. Writing, code, analysis, translation, summary, creativity... it does a bit of everything, often very well. The free version (GPT-4o mini) remains capable for most common uses. It's also the easiest AI to get started with if you're a beginner.&lt;/p&gt;

&lt;p&gt;Personally, I don't really buy into it. The main reason? OpenAI's partnership with the Pentagon, which triggered a big wave of uninstallations and boycotts. This is not a technical controversy — it's a question of ethics and values. The idea that "consumer" AI could be used in a military context raises legitimate questions that each person must decide for themselves.&lt;/p&gt;

&lt;p&gt;Also note: by default, your data may be used to improve the models — check in the settings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick comparison
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;My opinion at the time of writing this article — subject to change.&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criterion&lt;/th&gt;
&lt;th&gt;Perplexity&lt;/th&gt;
&lt;th&gt;Claude Code&lt;/th&gt;
&lt;th&gt;Gemini&lt;/th&gt;
&lt;th&gt;ChatGPT&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sourced web search&lt;/td&gt;
&lt;td&gt;✅ Excellent&lt;/td&gt;
&lt;td&gt;❌ Limited&lt;/td&gt;
&lt;td&gt;✅ Good&lt;/td&gt;
&lt;td&gt;⚠️ Hallucinates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Development / Code&lt;/td&gt;
&lt;td&gt;⚠️ Basic&lt;/td&gt;
&lt;td&gt;✅ Excellent&lt;/td&gt;
&lt;td&gt;⚠️ Correct&lt;/td&gt;
&lt;td&gt;✅ Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ecosystem integration&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;✅ Google&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mobile usage&lt;/td&gt;
&lt;td&gt;⚠️ Subscription&lt;/td&gt;
&lt;td&gt;✅ Correct&lt;/td&gt;
&lt;td&gt;✅ Pixel native&lt;/td&gt;
&lt;td&gt;✅ Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Privacy&lt;/td&gt;
&lt;td&gt;✅ Correct&lt;/td&gt;
&lt;td&gt;✅ Correct&lt;/td&gt;
&lt;td&gt;⚠️ Check&lt;/td&gt;
&lt;td&gt;⚠️ Check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Free access&lt;/td&gt;
&lt;td&gt;✅ Limited&lt;/td&gt;
&lt;td&gt;✅ Limited&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ideal for beginners&lt;/td&gt;
&lt;td&gt;⚠️ Oriented&lt;/td&gt;
&lt;td&gt;❌ CLI&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Which AI to choose based on your profile?
&lt;/h2&gt;

&lt;p&gt;There is no universal answer. The right question is: &lt;strong&gt;what do you do most often?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You look for reliable info with sources&lt;/strong&gt; → Perplexity (on desktop preferably)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You code or develop projects&lt;/strong&gt; → Claude Code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You work in Google Workspace or have a Pixel&lt;/strong&gt; → Gemini&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want to do everything without hassle&lt;/strong&gt; → ChatGPT&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want to protect your privacy&lt;/strong&gt; → Claude or Perplexity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And don't hesitate to combine several. Personally, I use Claude Code for dev, Perplexity on desktop when I need a sourced answer, and Gemini for anything related to my phone or Google Workspace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is the best free AI in 2026?
&lt;/h3&gt;

&lt;p&gt;Claude offers a solid reasoning level even without a subscription. Perplexity is better for sourced searches. ChatGPT remains an accessible option, but with the reservations mentioned above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can you use multiple IAs at the same time?
&lt;/h3&gt;

&lt;p&gt;Yes, and it's often the best strategy. Use Perplexity for research, Claude Code for dev, and ChatGPT for writing for example. Each tool has its domain of excellence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are IAs safe for personal data?
&lt;/h3&gt;

&lt;p&gt;It depends on the provider and your settings. By default, most use your data to improve their models. Always check privacy settings. To go further: &lt;a href="https://thomasbnt.dev/blog/se-securiser-sur-internet/" rel="noopener noreferrer"&gt;secure yourself on the Internet (FR)&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ChatGPT or Claude: which one to choose?
&lt;/h3&gt;

&lt;p&gt;For code, long reasoning, and creativity → Claude. For dev integrated into the terminal → Claude Code. For sourced searches as a complement → Perplexity. ChatGPT remains a consumer option, but personally I do without it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;IAs are not interchangeable, and none is perfect. Each has been optimized for certain uses, with its own trade-offs — technical, ethical, and practical. What makes the difference is choosing the right tool for the right task, rather than relying on a single one for everything.&lt;/p&gt;

&lt;p&gt;My current combo: &lt;strong&gt;Claude Code&lt;/strong&gt; for dev, &lt;strong&gt;Perplexity&lt;/strong&gt; on desktop for sourced searches, and &lt;strong&gt;Gemini&lt;/strong&gt; for anything related to my Pixel. ChatGPT, I'm leaving aside for now — the reasons are mentioned above, and my opinion can change.&lt;/p&gt;

&lt;p&gt;What I think of these tools today may not be true in six months. IAs advance quickly, models change, policies too. I'll update this article if needed.&lt;/p&gt;

&lt;p&gt;And you, what do you use daily? Do you combine several or do you stay loyal to just one? Tell me on Discord or on social networks! 💬&lt;/p&gt;

&lt;h2&gt;
  
  
  Go further
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://thomasbnt.dev/blog/se-securiser-sur-internet/" rel="noopener noreferrer"&gt;Secure yourself on the Internet (FR)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.theverge.com/ai-artificial-intelligence/887309/openai-anthropic-dod-military-pentagon-contract-sam-altman-hegseth" rel="noopener noreferrer"&gt;OpenAI and the Pentagon — The Verge (EN)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Some of my articles, including this one, may have been AI-assisted to help me better structure or rephrase my ideas. Opinions remain my own.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>chatgpt</category>
      <category>productivity</category>
    </item>
    <item>
      <title>My summary of 2025 as an IAM consultant and non-web developer</title>
      <dc:creator>Thomas Bonnet</dc:creator>
      <pubDate>Thu, 01 Jan 2026 00:10:05 +0000</pubDate>
      <link>https://dev.to/thomasbnt/my-summary-of-2025-as-an-iam-consultant-and-non-web-developer-58kc</link>
      <guid>https://dev.to/thomasbnt/my-summary-of-2025-as-an-iam-consultant-and-non-web-developer-58kc</guid>
      <description>&lt;p&gt;&lt;strong&gt;This year marks a big change in my life.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I originally taught myself web development and many other aspects of the field. At the beginning of the year, I stopped working as a freelance web developer at my web agency, &lt;a href="https://conceptweb.agency" rel="noopener noreferrer"&gt;Concept'Web&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I then started a new job as an IAM consultant in cybersecurity. When I began, I didn't feel I had enough knowledge to fully make it my full‑time job, and I struggled for several months. But things are now going much better: I feel more confident, I'm learning a lot every day, and I genuinely love what I do. It's a big shift from web development, which I hardly practice anymore. I contribute much less to open source and side projects (which is a bit sad, but mostly a matter of time), though I still maintain a few of them, including &lt;a href="https://thomasbnt.dev" rel="noopener noreferrer"&gt;my personal website&lt;/a&gt; and &lt;a href="https://mrrobot.app" rel="noopener noreferrer"&gt;a Discord bot that helps eliminate cyberbullying&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're curious about what I wish for this new year, you can read it here:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/thomasbnt/2025-me-voila--2p2d" class="crayons-story__hidden-navigation-link"&gt;2025, me voilà&amp;nbsp;! 🥳&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/thomasbnt" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F18254%2Feb9322d6-40dc-4312-b39e-7b03c75ed018.png" alt="thomasbnt profile" class="crayons-avatar__image" width="800" height="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/thomasbnt" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Thomas Bonnet
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Thomas Bonnet
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png" width="166" height="102"&gt;&lt;/a&gt;
              
              &lt;div id="story-author-preview-content-2193112" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/thomasbnt" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F18254%2Feb9322d6-40dc-4312-b39e-7b03c75ed018.png" class="crayons-avatar__image" alt="" width="800" height="800"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Thomas Bonnet&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/thomasbnt/2025-me-voila--2p2d" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jan 7 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/thomasbnt/2025-me-voila--2p2d" id="article-link-2193112"&gt;
          2025, me voilà&amp;nbsp;! 🥳
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/newyearchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;newyearchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/career"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;career&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/thomasbnt/2025-me-voila--2p2d" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;34&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/thomasbnt/2025-me-voila--2p2d#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              8&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;p&gt;Outside of work, I also had &lt;a href="https://mrrobot.app/changelog" rel="noopener noreferrer"&gt;a lot of fun shipping several updates to my biggest Discord bot project&lt;/a&gt;. I notably improved my filter against insults and other offensive language, including homophobia, transphobia, conflicts, sexual content, and degrading comments. I'm very happy with how it's evolving, and I'm proud of the impact it can have on online communities.&lt;/p&gt;

&lt;h2&gt;
  
  
  My DEV Wrapped
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fea53gchypc1u36mpwsyi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fea53gchypc1u36mpwsyi.png" alt="My DEV Wrapped @thomasbnt" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>yearinreview</category>
      <category>devwrapped</category>
      <category>career</category>
    </item>
    <item>
      <title>Share us your project for Hacktoberfest 2025! 🎃</title>
      <dc:creator>Thomas Bonnet</dc:creator>
      <pubDate>Sun, 05 Oct 2025 13:00:44 +0000</pubDate>
      <link>https://dev.to/thomasbnt/share-us-your-project-for-hacktoberfest-2025-59h2</link>
      <guid>https://dev.to/thomasbnt/share-us-your-project-for-hacktoberfest-2025-59h2</guid>
      <description>&lt;p&gt;&lt;em&gt;Last year:&lt;/em&gt; &lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/thomasbnt/share-us-your-project-for-hacktoberfest-2024-ppj" class="crayons-story__hidden-navigation-link"&gt;Share us your project for Hacktoberfest 2024! 🎃&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/thomasbnt/share-us-your-project-for-hacktoberfest-2024-ppj" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Hacktoberfest: Maintainer Spotlight&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/thomasbnt" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F18254%2Feb9322d6-40dc-4312-b39e-7b03c75ed018.png" alt="thomasbnt profile" class="crayons-avatar__image" width="800" height="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/thomasbnt" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Thomas Bonnet
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Thomas Bonnet
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png" width="166" height="102"&gt;&lt;/a&gt;
              
              &lt;div id="story-author-preview-content-2023430" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/thomasbnt" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F18254%2Feb9322d6-40dc-4312-b39e-7b03c75ed018.png" class="crayons-avatar__image" alt="" width="800" height="800"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Thomas Bonnet&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/thomasbnt/share-us-your-project-for-hacktoberfest-2024-ppj" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 2 '24&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/thomasbnt/share-us-your-project-for-hacktoberfest-2024-ppj" id="article-link-2023430"&gt;
          Share us your project for Hacktoberfest 2024! 🎃
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/discuss"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;discuss&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/hacktoberfest"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;hacktoberfest&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/thomasbnt/share-us-your-project-for-hacktoberfest-2024-ppj" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;59&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/thomasbnt/share-us-your-project-for-hacktoberfest-2024-ppj#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              59&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            1 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;h2&gt;
  
  
  📅 When is it happening?
&lt;/h2&gt;

&lt;p&gt;From &lt;strong&gt;October 1st&lt;/strong&gt; to &lt;strong&gt;October 31st&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  👥 Who can join?
&lt;/h2&gt;

&lt;p&gt;Contributors, maintainers, companies, and event organizers!&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 How to participate?
&lt;/h2&gt;

&lt;p&gt;Throughout October, take part by &lt;strong&gt;contributing to Open Source&lt;/strong&gt; and submitting &lt;strong&gt;6 Pull or Merge Requests&lt;/strong&gt; on GitHub or GitLab.&lt;br&gt;&lt;br&gt;
You can keep track of your progress on the &lt;a href="https://hacktoberfest.com/" rel="noopener noreferrer"&gt;Hacktoberfest dashboard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hacktoberfest.com/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl6mwq2nm9a9kun6e24gj.png" alt="Hacktoberfest main page" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;✨ New this year: After &lt;strong&gt;6 valid Pull Requests&lt;/strong&gt; (previously 4), you will earn an &lt;strong&gt;exclusive Hacktoberfest T-shirt&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;More details are available on the &lt;a href="https://hacktoberfest.com/participation/#contributors" rel="noopener noreferrer"&gt;participation page&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;✨ This year, I also want to know &lt;strong&gt;what projects you’re running that need contributors&lt;/strong&gt;!&lt;br&gt;&lt;br&gt;
Feel free to share your repositories in the comments using the embed format below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% embed https://github.com/thomasbnt/devtoprofile %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://hacktoberfest.com/participation/#beginner-resources" rel="noopener noreferrer"&gt;Resources for beginners&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hacktoberfest.com/participation/#pr-mr-details" rel="noopener noreferrer"&gt;Pull/Merge Request details&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hacktoberfest.com/participation/#maintainers" rel="noopener noreferrer"&gt;Info for maintainers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  💚 Open Source
&lt;/h3&gt;

&lt;p&gt;For more posts, check out the &lt;em&gt;Hacktoberfest&lt;/em&gt; tag  &lt;/p&gt;


&lt;div class="ltag__tag ltag__tag__id__4074"&gt;
    &lt;div class="ltag__tag__content"&gt;
      &lt;h2&gt;#&lt;a href="https://dev.to/t/hacktoberfest" class="ltag__tag__link"&gt;hacktoberfest&lt;/a&gt; Follow
&lt;/h2&gt;
      &lt;div class="ltag__tag__summary"&gt;
        Happy hacking! 🎃
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>hacktoberfest</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Best tips to learn music?</title>
      <dc:creator>Thomas Bonnet</dc:creator>
      <pubDate>Sun, 03 Aug 2025 16:05:41 +0000</pubDate>
      <link>https://dev.to/thomasbnt/best-tips-to-learn-music-4e36</link>
      <guid>https://dev.to/thomasbnt/best-tips-to-learn-music-4e36</guid>
      <description>&lt;p&gt;Hello, I have the &lt;a href="https://www.arturia.com/products/hybrid-synths/minilab-3/overview" rel="noopener noreferrer"&gt;Minilab 3 from Arturia&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And I would &lt;strong&gt;learn&lt;/strong&gt; more with &lt;strong&gt;Ableton Live 12 Lite&lt;/strong&gt;, what is your best tips?&lt;/p&gt;

</description>
      <category>learn</category>
      <category>arturia</category>
    </item>
    <item>
      <title>Installing Pi-hole with an LCD screen</title>
      <dc:creator>Thomas Bonnet</dc:creator>
      <pubDate>Sun, 03 Aug 2025 16:02:03 +0000</pubDate>
      <link>https://dev.to/thomasbnt/installing-pi-hole-with-an-lcd-screen-2kjj</link>
      <guid>https://dev.to/thomasbnt/installing-pi-hole-with-an-lcd-screen-2kjj</guid>
      <description>&lt;p&gt;I had already made an article on Pi-hole a few years ago, this one adds the fact that there is an LCD screen.&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/thomasbnt/setup-pi-hole-3gkd" class="crayons-story__hidden-navigation-link"&gt;Setup your Pi-hole&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/thomasbnt" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F18254%2Feb9322d6-40dc-4312-b39e-7b03c75ed018.png" alt="thomasbnt profile" class="crayons-avatar__image" width="800" height="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/thomasbnt" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Thomas Bonnet
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Thomas Bonnet
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png" width="166" height="102"&gt;&lt;/a&gt;
              
              &lt;div id="story-author-preview-content-43462" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/thomasbnt" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F18254%2Feb9322d6-40dc-4312-b39e-7b03c75ed018.png" class="crayons-avatar__image" alt="" width="800" height="800"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Thomas Bonnet&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/thomasbnt/setup-pi-hole-3gkd" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 14 '18&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/thomasbnt/setup-pi-hole-3gkd" id="article-link-43462"&gt;
          Setup your Pi-hole
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/raspberrypi"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;raspberrypi&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/pihole"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;pihole&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/privacy"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;privacy&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/thomasbnt/setup-pi-hole-3gkd" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;54&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/thomasbnt/setup-pi-hole-3gkd#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              6&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;In this article, we will discover how to install Pi-hole with an LCD screen. &lt;br&gt;
The reference of the screen is &lt;strong&gt;XPT2046 Touch 3.5" RPI Display 480x320 16bit&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;We will install &lt;a href="https://pi-hole.net/" rel="noopener noreferrer"&gt;Pi-hole&lt;/a&gt; on a Raspberry Pi 3B. &lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://pi-hole.net/" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;pi-hole.net&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Pi-hole is used to block unwanted requests and URLs that you don't want to load on the network. &lt;br&gt;
It protects you from trackers and optimizes your network traffic.&lt;/p&gt;


&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Prepare Raspbian
&lt;/h3&gt;

&lt;p&gt;First, flash your SD drive from &lt;a href="https://www.balena.io/etcher/" rel="noopener noreferrer"&gt;Etcher&lt;/a&gt; or another application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy73z96lwt1jnv0oqv9cj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy73z96lwt1jnv0oqv9cj.png" alt="Etcher, a software to flash your SD cards and USB sticks to make a bootable" width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wait a few minutes until the disk is ready.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj7u0gnjak4tx0i100797.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj7u0gnjak4tx0i100797.png" alt="The end of the installation on the Etcher software" width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Update the dependencies
&lt;/h3&gt;

&lt;p&gt;Connect to your Raspberry Pi and update the dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait a good few minutes... in the meantime, drink a good cappuccino. ☕&lt;/p&gt;

&lt;p&gt;Install &lt;strong&gt;curl&lt;/strong&gt; and &lt;strong&gt;git&lt;/strong&gt; if this is not available on your small machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;curl &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;git &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Install Pi-hole
&lt;/h3&gt;

&lt;p&gt;Then, when everything is good, you can start installing Pi-hole.&lt;/p&gt;

&lt;p&gt;You can clone from &lt;a href="https://github.com/pi-hole/pi-hole" rel="noopener noreferrer"&gt;the Pi-hole repository&lt;/a&gt; or directly curl with this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; https://install.pi-hole.net | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When asked if you want to install on your Raspberry and put in static IP, check yes, and remember the IP.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwjf60qv8rscfb3w93e3r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwjf60qv8rscfb3w93e3r.png" alt="Installing Pi-hole" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then choose your &lt;strong&gt;preferred DNS&lt;/strong&gt;. You can put more than one if you want. This is editable after installation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgdl7eyxxbns93sb2kxzj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgdl7eyxxbns93sb2kxzj.png" alt="Installing Pi-hole and its DNS" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can select block lists:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5ryw2z5qntv8bwk146cg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5ryw2z5qntv8bwk146cg.png" alt="Installation of Pi-hole and its DNS part. 2" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;✨ I actually have a &lt;strong&gt;huge list with over 1,000,000 blocked domains/IPs&lt;/strong&gt;. &lt;a href="https://github.com/thomasbnt/raspihostblock" rel="noopener noreferrer"&gt;The repository of this list on GitHub&lt;/a&gt;. Feel free to add your own domains/IPs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It will also ask you if you want a &lt;strong&gt;web panel&lt;/strong&gt;.&lt;br&gt;
I strongly recommend that you leave it enabled so that you can connect to the Raspberry Pi with a password and &lt;strong&gt;manage your DNS/blacklist&lt;/strong&gt; and &lt;strong&gt;view your statistics&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Configuring the web panel
&lt;/h3&gt;

&lt;p&gt;As soon as you have finished installing &lt;strong&gt;Pi-hole&lt;/strong&gt;, it will give you the local host address and the generated password.&lt;/p&gt;

&lt;p&gt;To change the password :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;pihole &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a preview of the panel when you are logged in 🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3boe469zoumpdxdw1m0m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3boe469zoumpdxdw1m0m.png" alt="Web panel preview on Pi-hole" width="799" height="390"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting up the LCD screen
&lt;/h2&gt;

&lt;p&gt;The most complicated part is here!&lt;br&gt;
There may be some things I've written that don't work for you, or that aren't essential to do.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Install the LCD screen on the Raspberry Pi
&lt;/h3&gt;

&lt;p&gt;Simple part to do, just put the Display on the branches of the RPI. &lt;br&gt;
I found &lt;a href="https://youtu.be/Z1N2t5zVP88?t=141" rel="noopener noreferrer"&gt;a YouTube video that shows you how to position it&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Install PADD
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/pi-hole/PADD" rel="noopener noreferrer"&gt;PADD&lt;/a&gt;&lt;/strong&gt; is a software that allows you to see the data transmitted by Pi-hole on your screen.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The project has been taken over by Pi-hole, and even if there are some errors, it is still usable.&lt;/p&gt;

&lt;p&gt;To install it and make it executable :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~
wget &lt;span class="nt"&gt;-N&lt;/span&gt; https://raw.githubusercontent.com/pi-hole/PADD/master/padd.sh
&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; +x padd.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make PADD run when the RPI is launched:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run PADD&lt;/span&gt;
&lt;span class="c"&gt;# If we're on the PiTFT screen (ssh is xterm)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TERM&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"linux"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  while&lt;/span&gt; :
  &lt;span class="k"&gt;do&lt;/span&gt;
    ./padd.sh
    &lt;span class="nb"&gt;sleep &lt;/span&gt;1
  &lt;span class="k"&gt;done
fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you wish, you can test the script and view it from your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./padd.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa1wnz7ikwj819gozijdg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa1wnz7ikwj819gozijdg.png" alt="PADD preview on a terminal" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you have done everything, you need to &lt;strong&gt;reboot your machine&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Make the screen turn off at night.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/pi-hole/PADD/wiki/Setup#optional-put-the-display-to-sleep-at-night" rel="noopener noreferrer"&gt;On the PADD wiki&lt;/a&gt;, they show you how to make the screen turn off at night. The script works with &lt;strong&gt;cron&lt;/strong&gt;, to modify it, do &lt;code&gt;sudo crontab -e&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# PiTFT+ SLEEPY TIME&lt;/span&gt;
&lt;span class="c"&gt;# Turn off the PiTFT+ at midnight&lt;/span&gt;
00 00 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo "0" &amp;gt; /sys/class/backlight/soc\:backlight/brightness'&lt;/span&gt;
&lt;span class="c"&gt;# Turn on the PiTFT+ at 8:00 am&lt;/span&gt;
00 08 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo "1" &amp;gt; /sys/class/backlight/soc\:backlight/brightness'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Possible mistakes
&lt;/h2&gt;

&lt;p&gt;During my installation, I had a few errors, including one where the screen would not turn on.&lt;br&gt;
So I forced him to do it with &lt;strong&gt;&lt;a href="https://github.com/adafruit/Raspberry-Pi-Installer-Scripts" rel="noopener noreferrer"&gt;Adafruit&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  A. Installation of Adafruit
&lt;/h3&gt;

&lt;p&gt;Let's already install the prerequisites:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;python3-pip &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then Adafruit &lt;a href="https://github.com/adafruit/Raspberry-Pi-Installer-Scripts" rel="noopener noreferrer"&gt;from the GitHub repository&lt;/a&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~
git clone https://github.com/adafruit/Raspberry-Pi-Installer-Scripts.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, we will install Adafruit :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;Raspberry-Pi-Installer-Scripts
&lt;span class="nb"&gt;sudo &lt;/span&gt;python3 adafruit-pitft.py &lt;span class="nt"&gt;--display&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;35r &lt;span class="nt"&gt;--rotation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;90 &lt;span class="nt"&gt;--install-type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;console
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script will modify &lt;strong&gt;/boot/config.txt&lt;/strong&gt; to make &lt;strong&gt;adafruit-pitft&lt;/strong&gt; run when the RPI is launched.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  See how to do it manually
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /boot/config.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Insert at the end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;all]
&lt;span class="nv"&gt;hdmi_force_hotplug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="nv"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;spi&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;on
&lt;span class="nv"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;i2c1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;on
&lt;span class="nv"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;i2c_arm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;on
&lt;span class="nv"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pitft35-resistive,rotate&lt;span class="o"&gt;=&lt;/span&gt;90,speed&lt;span class="o"&gt;=&lt;/span&gt;20000000,fps&lt;span class="o"&gt;=&lt;/span&gt;20
&lt;span class="nv"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;piscreen,speed&lt;span class="o"&gt;=&lt;/span&gt;16000000,rotate&lt;span class="o"&gt;=&lt;/span&gt;90
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;br&gt;
&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  B. Invalid number" errors
&lt;/h3&gt;

&lt;p&gt;If you get this kind of error on your screen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/home/pi/padd.sh: line 900: &lt;span class="nb"&gt;printf&lt;/span&gt;: 0,3: invalid number
/home/pi/padd.sh: line 244: &lt;span class="nb"&gt;printf&lt;/span&gt;: 42,236: invalid number
/home/pi/padd.sh: line 873: &lt;span class="nb"&gt;printf&lt;/span&gt;: 9,1: invalid number
/home/pi/padd.sh: line 900: &lt;span class="nb"&gt;printf&lt;/span&gt;: 0,91: invalid number
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Leave the &lt;strong&gt;local&lt;/strong&gt; of the Raspberry Pi on &lt;code&gt;en_GB.UTF-8&lt;/code&gt;, &lt;br&gt;
an issue is in progress and therefore displays errors on the Display. (&lt;a href="https://github.com/pi-hole/PADD/issues/178" rel="noopener noreferrer"&gt;View issue on GitHub&lt;/a&gt;)&lt;/p&gt;




&lt;p&gt;Sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pi-hole/PADD/wiki/Setup#setup-padd" rel="noopener noreferrer"&gt;Wiki to configure PADD&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://forums.raspberrypi.com/viewtopic.php?t=178443" rel="noopener noreferrer"&gt;3.5 Inch RPI Display Touch - XPT2046 (480x320) - Install&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pi-hole/PADD/issues/178" rel="noopener noreferrer"&gt;GitHub issue : PADD displays error message about "invalid number"&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;☕&lt;/th&gt;
&lt;th&gt;Check my &lt;a href="https://twitter.com/thomasbnt_" rel="noopener noreferrer"&gt;Twitter account&lt;/a&gt;. You can see many projects and updates. You can also &lt;a href="https://thomasbnt.dev/donate" rel="noopener noreferrer"&gt;support me on Buy Me a Coffee, Stripe or GitHub Sponsors&lt;/a&gt;. Thanks for read my post ! 🤩&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>raspberrypi</category>
      <category>tutorial</category>
      <category>iot</category>
      <category>electronics</category>
    </item>
    <item>
      <title>Why choose DuckDuckGo as a search engine?</title>
      <dc:creator>Thomas Bonnet</dc:creator>
      <pubDate>Fri, 20 Jun 2025 20:26:52 +0000</pubDate>
      <link>https://dev.to/thomasbnt/why-choose-duckduckgo-as-a-search-engine-3mn8</link>
      <guid>https://dev.to/thomasbnt/why-choose-duckduckgo-as-a-search-engine-3mn8</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;For years, I've been testing all kinds of search engines: Google, Bing, Qwant... but nothing really convinced me in the long run. Between data collection, targeted ads, and the feeling of being constantly watched, I eventually got tired of it. So I decided to switch to DuckDuckGo, and frankly, I don't regret it.&lt;/p&gt;

&lt;h2&gt;
  
  
  My experience with DuckDuckGo
&lt;/h2&gt;

&lt;p&gt;The first thing that struck me was &lt;strong&gt;the simplicity of the interface&lt;/strong&gt;. No frills, no useless widgets, just a search bar and you're good to go. I like this minimalist approach: it gets straight to the point and loads quickly, even on an old PC or with a weak 4G connection.&lt;/p&gt;

&lt;p&gt;But &lt;strong&gt;the real selling point is privacy&lt;/strong&gt;. DuckDuckGo doesn't track me, doesn't store my history, and doesn't bombard me with personalized ads all the time. I can search for whatever I want without feeling like the entire web knows my interests. It feels good, seriously.&lt;/p&gt;

&lt;p&gt;Another nice thing: the results are the same for everyone. No “filter bubble” where you're only shown what you want to hear. Here, there's no excessive personalization, everyone starts on an equal footing. It makes a difference, and sometimes allows me to come across sites that I would never have seen with Google.&lt;/p&gt;

&lt;h2&gt;
  
  
  !bang commands: a real game changer
&lt;/h2&gt;

&lt;p&gt;Honestly, I didn't expect to use the &lt;a href="https://duckduckgo.com/bangs" rel="noopener noreferrer"&gt;&lt;code&gt;!bang&lt;/code&gt; commands&lt;/a&gt; so much, but it's clearly become a reflex. For those who don't know, just type an exclamation point followed by a shortcut to search directly on a specific site, without going through the site's home page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://duckduckgo.com/bangs" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd1wzh8k2kl83ke7gbmer.webp" alt="Examples of !bang commands on DuckDuckGo" width="800" height="685"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are a few examples that I use all the time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;!w&lt;/code&gt; to search directly on Wikipedia (example: &lt;code&gt;!w Toulouse&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;!yt&lt;/code&gt; for YouTube (&lt;code&gt;!yt powershell tutorial&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;!gh&lt;/code&gt; for GitHub (&lt;code&gt;!gh automation script&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;!g&lt;/code&gt; for Google (&lt;code&gt;!g baguette&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;!dpl&lt;/code&gt; for Deepl (&lt;code&gt;!dpl how to fix a bug&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are hundreds of them, for almost all known sites. It's super handy, saves a ton of time, and frankly, once you get used to it, you can't do without it.&lt;/p&gt;

&lt;p&gt;Of course, it's not perfect. Sometimes the results are a little less accurate than on Google, especially for very local or highly specific searches. But honestly, for 95% of my needs, it does the job. And I'd rather sacrifice a little convenience for peace of mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In short:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Privacy respected, no data collection&lt;/li&gt;
&lt;li&gt;Simple and fast interface, without overload&lt;/li&gt;
&lt;li&gt;Neutral results, identical for everyone&lt;/li&gt;
&lt;li&gt;Fewer intrusive ads&lt;/li&gt;
&lt;li&gt;Practical features such as &lt;code&gt;!bang&lt;/code&gt;, which are truly indispensable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm not saying that DuckDuckGo is perfect, but for me, it's &lt;strong&gt;the best compromise between efficiency and privacy&lt;/strong&gt;. If you're tired of being tracked everywhere on the web, it's definitely worth a try.&lt;/p&gt;

&lt;p&gt;What search engine do you use? Have you tried DuckDuckGo yet? Let me know what you think on Discord or social media!&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>productivity</category>
    </item>
    <item>
      <title>WeCoded is back!</title>
      <dc:creator>Thomas Bonnet</dc:creator>
      <pubDate>Wed, 05 Mar 2025 15:20:51 +0000</pubDate>
      <link>https://dev.to/thomasbnt/-2jh6</link>
      <guid>https://dev.to/thomasbnt/-2jh6</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/devteam/join-our-first-ever-wecoded-challenge-celebrating-underrepresented-voices-in-tech-through-stories-5m5" class="crayons-story__hidden-navigation-link"&gt;Join Our First-Ever WeCoded Challenge – Celebrating Underrepresented Voices in Tech Through Stories &amp;amp; Code!&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;
          &lt;a class="crayons-logo crayons-logo--l" href="/devteam"&gt;
            &lt;img alt="The DEV Team logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1%2Fd908a186-5651-4a5a-9f76-15200bc6801f.jpg" class="crayons-logo__image" width="800" height="800"&gt;
          &lt;/a&gt;

          &lt;a href="/jess" class="crayons-avatar  crayons-avatar--s absolute -right-2 -bottom-2 border-solid border-2 border-base-inverted  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F264%2Fb75f6edf-df7b-406e-a56b-43facafb352c.jpg" alt="jess profile" class="crayons-avatar__image" width="400" height="400"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/jess" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Jess Lee
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Jess Lee
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png" width="166" height="102"&gt;&lt;/a&gt;
              
              &lt;div id="story-author-preview-content-2309869" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/jess" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F264%2Fb75f6edf-df7b-406e-a56b-43facafb352c.jpg" class="crayons-avatar__image" alt="" width="400" height="400"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Jess Lee&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

            &lt;span&gt;
              &lt;span class="crayons-story__tertiary fw-normal"&gt; for &lt;/span&gt;&lt;a href="/devteam" class="crayons-story__secondary fw-medium"&gt;The DEV Team&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;a href="https://dev.to/devteam/join-our-first-ever-wecoded-challenge-celebrating-underrepresented-voices-in-tech-through-stories-5m5" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 5 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/devteam/join-our-first-ever-wecoded-challenge-celebrating-underrepresented-voices-in-tech-through-stories-5m5" id="article-link-2309869"&gt;
          Join Our First-Ever WeCoded Challenge – Celebrating Underrepresented Voices in Tech Through Stories &amp;amp; Code!
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/wecoded"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;wecoded&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/dei"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;dei&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/career"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;career&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/devteam/join-our-first-ever-wecoded-challenge-celebrating-underrepresented-voices-in-tech-through-stories-5m5" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;165&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/devteam/join-our-first-ever-wecoded-challenge-celebrating-underrepresented-voices-in-tech-through-stories-5m5#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              29&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>devchallenge</category>
      <category>wecoded</category>
      <category>dei</category>
      <category>career</category>
    </item>
  </channel>
</rss>
