<?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: hugo lassiege</title>
    <description>The latest articles on DEV Community by hugo lassiege (@hlassiege).</description>
    <link>https://dev.to/hlassiege</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%2F2907574%2F13258f77-a809-4747-82a6-51652548e3cc.jpg</url>
      <title>DEV Community: hugo lassiege</title>
      <link>https://dev.to/hlassiege</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hlassiege"/>
    <language>en</language>
    <item>
      <title>My AI stack for developing products without breaking the bank</title>
      <dc:creator>hugo lassiege</dc:creator>
      <pubDate>Wed, 15 Jul 2026 16:24:37 +0000</pubDate>
      <link>https://dev.to/hlassiege/my-ai-stack-for-developing-products-without-breaking-the-bank-57l6</link>
      <guid>https://dev.to/hlassiege/my-ai-stack-for-developing-products-without-breaking-the-bank-57l6</guid>
      <description>&lt;p&gt;Can you really build applications today with AI AND only spend 20 euros a month?&lt;/p&gt;

&lt;p&gt;I'm currently developing two projects: &lt;strong&gt;&lt;a href="https://broadcast.hakanai.io/" rel="noopener noreferrer"&gt;Hakanai&lt;/a&gt;&lt;/strong&gt; (for static blogs) and &lt;strong&gt;&lt;a href="https://writizzy.com/" rel="noopener noreferrer"&gt;Writizzy&lt;/a&gt;&lt;/strong&gt;, which just passed 550 users in 6 months. But combined, I don't make more than 250 euros a month and my principle is not to spend more than what I earn.&lt;/p&gt;

&lt;p&gt;So I have a big constraint: budget. I try to build my applications without burning tokens at the speed of light. And I wanted to share with you how I do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Writizzy?
&lt;/h2&gt;

&lt;p&gt;Quick reminder to set the context: Writizzy is an equivalent to Medium or Substack. A platform to write, send newsletters and monetize your content. I think it's vital to have spaces for expression outside the GAFAM (and outside the US). It's the software that runs the blog you're reading.&lt;/p&gt;

&lt;p&gt;Code-wise, we have three big pieces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;API&lt;/strong&gt; in Kotlin.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;Admin App&lt;/strong&gt; in Nuxt.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;"blog" App&lt;/strong&gt; (that serves the content) in Nuxt as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To work, I mostly use &lt;strong&gt;Claude&lt;/strong&gt;. Why? Because it remains the best-rated model on the code benchmarks.&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%2Fuguqssogmhpnkg2x6fs6.jpg" 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%2Fuguqssogmhpnkg2x6fs6.jpg" alt="Claude's ratings on the SWE leaderboard" width="799" height="524"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But I'm thinking about an alternative. I'll tell you more about it at the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context Engineering: Constraining the AI
&lt;/h2&gt;

&lt;p&gt;The idea to work effectively is to give the LLM the necessary and &lt;em&gt;sufficient&lt;/em&gt; context to limit its solution space. We want to give it guidelines and ways to verify itself that it stays within the lines. If you don't do that, don't be surprised when the AI hallucinates APIs or uses old doc versions. It's normal.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Claude.md file
&lt;/h3&gt;

&lt;p&gt;It's the entry point. You'll find the project objective, the general structure to prevent the AI from browsing through all the code randomly, and especially the list of &lt;strong&gt;specs&lt;/strong&gt;.&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%2F2xacbss4c1riv2zpe51c.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%2F2xacbss4c1riv2zpe51c.png" alt="list of writizzy specs" width="800" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This list is fundamental in my development process, I'll come back to it later. It also serves as an index for Claude.&lt;/p&gt;

&lt;p&gt;Note that I have other &lt;a href="http://claude.md" rel="noopener noreferrer"&gt;claude.md&lt;/a&gt; files in subdirectories, these files are loaded by Claude if it enters them. I have one per application (app, blog, and api). I use it to list available technologies but I tend to use rules more and more.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Rules (.claude/rules)
&lt;/h3&gt;

&lt;p&gt;These are rules that can be loaded conditionally. For example here, if Claude enters the &lt;code&gt;/app&lt;/code&gt; directory, it loads the Nuxt rules.&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%2Fx9xpco3mljdwmcgtpi64.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%2Fx9xpco3mljdwmcgtpi64.png" alt="header that defines loading conditions" width="178" height="127"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this rules file I define the patterns I want to see used, for example:&lt;br&gt;
obligation to use Nuxt UI, to run a &lt;code&gt;typecheck&lt;/code&gt; or &lt;code&gt;eslint&lt;/code&gt; pass before finishing, etc.&lt;/p&gt;

&lt;p&gt;These rules, I built them empirically. That is, the file was empty originally, then I added rules based on the errors I saw coming back. More recently I'm trying to limit Claude's exploration so I systematically ask him if he saw patterns he should document in the rules to avoid spending tons of tokens exploring. Plus I don't really like him guessing based on the code he encounters.&lt;/p&gt;

&lt;p&gt;Example of a question I recently asked him:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I'm looking to improve the rules I've already put in place. I find that you had to read a lot of files to understand the context and patterns. Why? Give me a summary of the patterns you were looking to understand to see if we can make them into rules&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Actually, building these rules files is a kind of &lt;strong&gt;safety harness&lt;/strong&gt;. But you also need to understand that it's not a guarantee because LLMs will regularly ignore rules. So you need to strengthen control means: tests, linter, CI quality gates, etc…&lt;/p&gt;

&lt;p&gt;I note that it's easier for those used to developing with good practices to work correctly with AI. These practices serve as safeguards but for many companies, it was a bit of window dressing but not very effective in practice. And precisely, in companies where these methods aren't mature, AI doesn't take off. One wonders why... Or not…&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Analyst" command
&lt;/h3&gt;

&lt;p&gt;A command is a sort of pre-prompt. I use an &lt;code&gt;analyst&lt;/code&gt; command for brainstorming. I ask him to be a specialist in blogging, IndieWeb and decentralization. I ask him to challenge my needs, detect inconsistencies and most importantly &lt;strong&gt;to write the specs&lt;/strong&gt;.&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%2F04ssegfotooe948xzm1f.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%2F04ssegfotooe948xzm1f.png" alt="analyst command file" width="798" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Specs before code
&lt;/h2&gt;

&lt;p&gt;Earlier I talked about specs that are listed in the claude.md file.&lt;/p&gt;

&lt;p&gt;Let's talk a bit about my dev cycle.. I systematically go through a spec before any significant piece of code.&lt;br&gt;
(Funny when I think back to when I launched &lt;strong&gt;Malt&lt;/strong&gt;, there wasn't much written at first…)&lt;/p&gt;

&lt;p&gt;With AI, the spec has two major interests:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Frame the expected result&lt;/strong&gt;. We first define the direction, user flows, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Save tokens in the future&lt;/strong&gt;: By listing the technical entry points and key files in the spec, Claude doesn't need to search everywhere.&lt;/li&gt;
&lt;/ol&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%2Ffme5ljztprv87dtpfyt5.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%2Ffme5ljztprv87dtpfyt5.png" alt="list of technical entry points" width="800" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Indeed, when Claude reworks a feature that already exists, he'll use the spec file to understand how it works and especially easily get back to the key files. With these specs, he'll save a lot of time on that, and especially it will be more accurate than letting him guess.&lt;/p&gt;

&lt;p&gt;Once the spec is validated, I create an &lt;strong&gt;implementation plan&lt;/strong&gt; (well, Claude creates a plan). For that, I have a skill that imposes my practices: each step must be deployable, it must use feature flags if necessary, etc...&lt;/p&gt;

&lt;h2&gt;
  
  
  The art of managing your context window
&lt;/h2&gt;

&lt;p&gt;Here I need to come back to the notion of context window because it's important for optimizing your monthly budget. What costs a lot is endless sessions. With each exchange, you send back the entire history. After a while, a simple "Ok" can cost tens of thousands of tokens. So already, that costs a lot.&lt;/p&gt;

&lt;p&gt;But the second cool side effect, the fuller the window, the "dumber" the AI becomes. So I try never to exceed &lt;strong&gt;50% capacity&lt;/strong&gt;. That's the whole point of implementation plans. The idea is to break it down into small autonomous tasks and change sessions regularly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skills vs MCP: The efficiency battle
&lt;/h2&gt;

&lt;p&gt;I mentioned skills before. I use them heavily to be more efficient. And there's a real parallel between efficiency and cost so I need to talk about it here.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Skill&lt;/strong&gt; is a competency loaded on demand. For example, my Nuxt skill forces Claude to go read the doc online (in Markdown format) rather than relying on his outdated training data.&lt;/p&gt;

&lt;p&gt;I have skills for different things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;APIs that specify my design patterns: api-design, db-migration, write-spec&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's sometimes a bit more than "just" specifying patterns, for example the db-migration skill lists exactly which tools to use (Flyway), the location of migration files, the file format. And on the pattern side we find my key principles of deployment strategy (rolling update, etc…)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;bento, nuxt, nuxt-content, nuxt-ui to specify how to fetch the doc&lt;/li&gt;
&lt;li&gt;skills that explain how to use a CLI, for example sentry-fix-issues which explains how to use the sentry CLI to get information, diagnose, and fix&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the other hand, we have &lt;strong&gt;MCP&lt;/strong&gt; (Model Context Protocol). It's powerful too, but it takes up more space in the context because each method of the MCP is included in the context window. Where a skill, it's only the skill description that's included.&lt;/p&gt;

&lt;p&gt;So I prefer Skills, except for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP IntelliJ&lt;/strong&gt;: To properly navigate code and compile.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP Stripe&lt;/strong&gt;: To debug configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude-mem&lt;/strong&gt;: supposed to remember key decisions between sessions (I'm still testing, the gain isn't clear yet).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What about design?
&lt;/h2&gt;

&lt;p&gt;Recently, I switched to &lt;strong&gt;Claude Design&lt;/strong&gt;. It became my tool of choice. I brief, I comment on areas, I modify on the fly, and once the HTML is clean, I give it to my Claude agent for implementation. It's well above what I was doing before in simple prompting. The results are really stunning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Balance sheet: How to stick to the budget?
&lt;/h2&gt;

&lt;p&gt;In short, with all that I manage to get by for 20€ a month but I still have a few extra tricks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Token compression&lt;/strong&gt;: I use compression strategies (like &lt;a href="https://www.rtk-ai.app/" rel="noopener noreferrer"&gt;rtk&lt;/a&gt;) or I create my own tools to only send LLMs what's necessary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Online AIs&lt;/strong&gt;: If I want to brainstorm "for free", I go to Gemini. I'm generally less of a fan but it can serve as a relay before going to really work with the analyst for example.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But mostly, I have an incredible trick… I close my work session when Claude tells me I've exceeded my quota. Plain and simple.&lt;/p&gt;

&lt;p&gt;When it's done, I stop. I go do woodworking, cooking, I write an article (that's exactly what I'm doing right now ^^).&lt;/p&gt;

&lt;p&gt;It seems like nothing but this discipline is actually healthy. I don't see it only as a constraint but also as an opportunity to do something else, think, consolidate what I've learned, etc… It joins what I wrote in a recent article, it's good &lt;a href="https://eventuallymaking.io/p/coding-10x-faster-what-s-the-real-benefit" rel="noopener noreferrer"&gt;to save time, but you have to know how to make the most of it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next step: I'm testing local models. The idea is to delegate small implementation tasks to my machine and keep Claude only for strategic planning. It's really just the beginning so I'll talk about it more later.&lt;/p&gt;

&lt;p&gt;I'm far from being as advanced as possible on these topics and especially I'm in a solo context. But if you have your own tips to share, don't hesitate in the comments, they'll be read carefully.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>AI and Ecology, Fantasy or Convenient Scapegoat?</title>
      <dc:creator>hugo lassiege</dc:creator>
      <pubDate>Mon, 13 Jul 2026 13:12:22 +0000</pubDate>
      <link>https://dev.to/hlassiege/ai-and-ecology-fantasy-or-convenient-scapegoat-1jng</link>
      <guid>https://dev.to/hlassiege/ai-and-ecology-fantasy-or-convenient-scapegoat-1jng</guid>
      <description>&lt;p&gt;It's hard to talk about AI these days. I've rarely, if ever, seen a subject so polarized in tech.&lt;/p&gt;

&lt;p&gt;You could tell me I have a short memory. The internet sparked plenty of criticism around the destruction of brick-and-mortar retail, print media, and the end of human interaction. Same for mobile, with added, legitimate reproaches about addiction and the ease of surveilling individuals. We could also mention crypto, a massive Ponzi scheme for some, a way to reclaim power from central banks for others.&lt;/p&gt;

&lt;p&gt;And yet, with AI, I feel like we've crossed a threshold. There would be only two possibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Doomers (declinists) who envision an ecological apocalypse, total destruction of employment and placing public opinions under the guardianship of Big Tech controlling AI.&lt;/li&gt;
&lt;li&gt;And the Bloomers (accelerationists), who advocate blind faith in progress, convinced that AI will liberate humanity, eradicate diseases and generate infinite growth, and for whom slowing research is the real crime.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pick a side, friend, and if you don't, others will do it for you. The "safest" bet is not to talk about it at all, but burying my head in the sand feels cowardly, if not impossible when you work in tech.&lt;/p&gt;

&lt;p&gt;Simply put, I need to stick my head out and try this exercise without resorting to clichés. And since we're in the middle of a heat wave, it seems obvious that the first subject to address is ecology. Is AI as catastrophic as people say? Is the impact of an AI query truly astronomically higher than a Google search? How does it compare with other digital uses? Let's take some time to look at all this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ecological Impact of AI
&lt;/h2&gt;

&lt;p&gt;First, let's establish some basics about what we call ecological impact.&lt;/p&gt;

&lt;p&gt;This impact falls into several categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Electricity consumption (which translates into CO2 emissions)&lt;/li&gt;
&lt;li&gt;Water consumption for cooling data centers&lt;/li&gt;
&lt;li&gt;Resource extraction, required to build the data centers and user devices themselves&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To keep things simple, an AI consumes energy at two distinct stages: during &lt;strong&gt;training&lt;/strong&gt; (when the model is created, like Gemini, Llama, Claude, etc.) and during &lt;strong&gt;inference&lt;/strong&gt; (when users actually query the model).&lt;/p&gt;

&lt;h3&gt;
  
  
  Electrical consumption during training
&lt;/h3&gt;

&lt;p&gt;When looking at carbon footprints, models vary wildly, but the estimated range for training a single major model sits between 500 and 12,000 tonnes of CO2 equivalent.&lt;/p&gt;

&lt;p&gt;To put that into perspective:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;CO2 Equivalent&lt;/th&gt;
&lt;th&gt;Paris-New York Round Trips&lt;/th&gt;
&lt;th&gt;Annual US Household Consumption&lt;/th&gt;
&lt;th&gt;Annual French Household Consumption&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Smaller/open modelDeepseek, Lama etc…&lt;/td&gt;
&lt;td&gt;~500 tonnes&lt;/td&gt;
&lt;td&gt;370&lt;/td&gt;
&lt;td&gt;130&lt;/td&gt;
&lt;td&gt;2,100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude, Codex etc…&lt;/td&gt;
&lt;td&gt;~12,000 tonnes&lt;/td&gt;
&lt;td&gt;8000&lt;/td&gt;
&lt;td&gt;3,200&lt;/td&gt;
&lt;td&gt;50,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;::callout{type=primary}&lt;br&gt;
The massive gap between US and French household equivalents stems from the fact that France’s energy mix relies heavily on nuclear power, which is virtually carbon-free.&lt;br&gt;
::&lt;/p&gt;

&lt;h3&gt;
  
  
  Electricity Consumption During Usage
&lt;/h3&gt;

&lt;p&gt;Operational consumption is another moving target. It depends on the complexity of the prompt, the location of the data center (and its corresponding energy mix), the model being used, and so on.&lt;/p&gt;

&lt;p&gt;But it's estimated to vary between &lt;a href="https://projetcelsius.com/blog/combien-consomme-requete-chatgpt/" rel="noopener noreferrer"&gt;0.03g&lt;/a&gt;, and &lt;a href="https://mistral.ai/news/our-contribution-to-a-global-environmental-standard-for-ai/" rel="noopener noreferrer"&gt;1g of CO2&lt;/a&gt; per request. We'll see below how that compares with internet, gaming, streaming etc…&lt;/p&gt;

&lt;h3&gt;
  
  
  Water consumption
&lt;/h3&gt;

&lt;p&gt;To talk about water consumption, we must first address a common misconception: &lt;strong&gt;No, we don't destroy water&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Earth’s water operates in a closed loop. When water is used in a cooling system, whether in a data center, nuclear power plant or anything else, it's not destroyed. When water evaporates, it eventually falls back as rain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;However&lt;/strong&gt;, evaporation causes water to displace. If water moves more than 800km, the region where it was drawn from has effectively lost it, temporarily but lost nonetheless.&lt;/p&gt;

&lt;p&gt;In ecology, we distinguish &lt;strong&gt;water&lt;/strong&gt; &lt;strong&gt;withdrawal&lt;/strong&gt; (borrowing water and returning it to the same place after use) and &lt;strong&gt;water&lt;/strong&gt; &lt;strong&gt;consumption&lt;/strong&gt; (drawing water and evaporating or releasing it elsewhere, making it unavailable locally). AI &lt;em&gt;consumes&lt;/em&gt; water.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On a planetary scale&lt;/strong&gt;, it's not necessarily a problem. &lt;strong&gt;On a local scale&lt;/strong&gt;, however, it can trigger severe water stress, creating direct competition between residents, agriculture, and data centers.&lt;/p&gt;

&lt;p&gt;To be fair, technology is advancing. The majority of new data center projects use closed-loop water systems so water isn't evaporated. Some countries (Ireland, Sweden, Finland) take advantage of their cold climate to reduce water needs by 90% and we see other systems emerging. But to look at the flip side, the vast majority of &lt;strong&gt;existing&lt;/strong&gt; data centers use evaporation systems and in any case, these systems require electricity which creates tensions, for example in Sweden or Ireland.&lt;/p&gt;

&lt;p&gt;Now that we've said all that, what's the consumption for evaporation data centers?&lt;/p&gt;

&lt;p&gt;Training a recent model is estimated to consume approximately 40 to 80 million liters (a small lake). In a water-stressed region, that can make a difference.&lt;/p&gt;

&lt;p&gt;And if we look at usage, for a request, it's between &lt;a href="https://www.eesi.org/articles/view/data-centers-and-water-consumption" rel="noopener noreferrer"&gt;2 and 6.5ml&lt;/a&gt; of water per request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resource extraction
&lt;/h3&gt;

&lt;p&gt;::callout{type=warning}&lt;br&gt;
This section is the trickiest for me because it’s the one I’m least familiar with, and honestly, it probably deserves an entire article of its own. So, while we will only scratch the surface here, I promise to dive much deeper into this specific topic in a future post.&lt;br&gt;
::&lt;/p&gt;

&lt;p&gt;We often focus on electricity and water, but the environmental footprint of mining is one of AI's biggest blind spots.&lt;/p&gt;

&lt;p&gt;To run AIs or train models, you need ultra-powerful equipment and colossal infrastructure that will require copper, aluminum, cobalt, lithium, nickel, rare earths and I imagine I'm forgetting some.&lt;/p&gt;

&lt;p&gt;Well, these resources are in limited quantities on earth but I'll discuss that in a future article, recycling in this sector is currently negligible but moreover, the extraction itself is extremely polluting.&lt;/p&gt;

&lt;p&gt;To make matters worse, we must add that current equipment becomes obsolete much faster. In the AI race, we replace equipment much faster. Certainly, new equipment is more efficient, particularly in terms of energy but this ultra-rapid rotation creates a volume of electronic waste we don't know how to manage.&lt;/p&gt;

&lt;p&gt;Despite everything, I don't yet know from which angle and with which figures to illustrate all this, especially since these subjects also pull along many other geopolitical subjects (tension over Taiwan, tension over rare earths etc…), so we'll set that aside for future publication. We already have plenty to do with the first two subjects.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI vs. Other Digital Habits
&lt;/h2&gt;

&lt;p&gt;With these orders of magnitude in mind, is AI "&lt;strong&gt;stratospherically&lt;/strong&gt;" different from the rest?&lt;/p&gt;

&lt;p&gt;How does it compare with a Google search for example? Or with streaming, video call, an online video game?&lt;/p&gt;

&lt;h3&gt;
  
  
  AI vs Internet
&lt;/h3&gt;

&lt;p&gt;By comparison, a query to a search engine (Google) is approximately &lt;a href="http://news.bbc.co.uk/2/hi/technology/7823387.stm" rel="noopener noreferrer"&gt;0.2g of CO2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Depending on the complexity of the question and the model used, an AI prompt can cost slightly less than a Google search, or up to &lt;strong&gt;five times more&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;So, it is not "stratospherically" higher than a standard web search.&lt;/p&gt;

&lt;p&gt;Furthermore, if a topic requires you to do multiple Google searches and open several websites to find your answer, the gap quickly narrows, and can even reverse.&lt;/p&gt;

&lt;p&gt;But we must separate &lt;strong&gt;simple&lt;/strong&gt; uses: "give me the strawberry pie recipe", from &lt;strong&gt;complex&lt;/strong&gt; uses: "analyze this PDF document of several megabytes for me and create an application that displays results with charts".&lt;/p&gt;

&lt;h3&gt;
  
  
  AI vs gaming vs streaming vs video call
&lt;/h3&gt;

&lt;p&gt;I propose we do an exercise and compare 1 hour of streaming, 1 hour of gaming, 1 hour of video call, and one hour of AI-assisted software development (a relatively power-consuming use).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;1 hour of gaming&lt;/th&gt;
&lt;th&gt;1 hour of streaming&lt;/th&gt;
&lt;th&gt;1 hour of video call&lt;/th&gt;
&lt;th&gt;1 hour of dev with AI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;50g CO2 (source &lt;a href="https://greenly.earth/blog/secteurs/quelle-est-l-empreinte-carbone-des-jeux-video" rel="noopener noreferrer"&gt;greenly&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;55 to 100g of CO2 (source &lt;a href="https://www.carbonbrief.org/factcheck-what-is-the-carbon-footprint-of-streaming-video-on-netflix/" rel="noopener noreferrer"&gt;IEA&lt;/a&gt; and &lt;a href="https://about.netflix.com/en/news/the-true-climate-impact-of-streaming" rel="noopener noreferrer"&gt;netflix&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;30 to 60g of CO2 (source &lt;a href="https://about.netflix.com/en/news/the-true-climate-impact-of-streaming" rel="noopener noreferrer"&gt;ubigreen&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;30 to 140g of CO2 (source &lt;a href="https://www.idleforest.com/carbon-footprint/claude" rel="noopener noreferrer"&gt;idleforest&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;::callout{type=primary}&lt;br&gt;
Why such strong variations when considering AI-assisted development? Because it encompasses vastly different habits. Consumption will be drastically different between an "amateur" coder copy-pasting a few lines from a browser, a "pro" user partially delegating tasks within their code editor, and an "intensive" power-user running automated tools where code generation is almost entirely outsourced.&lt;br&gt;
::&lt;/p&gt;

&lt;p&gt;In other words:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is only a &lt;strong&gt;2x ratio&lt;/strong&gt; between gaming and &lt;strong&gt;professional&lt;/strong&gt; AI-assisted development.&lt;/li&gt;
&lt;li&gt;video call is what consumes the least&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming&lt;/strong&gt; is remarkably close to &lt;strong&gt;professional&lt;/strong&gt; AI development usage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No matter how you look at the data, it is hard to find evidence of a "stratospheric" gap.&lt;/p&gt;

&lt;h3&gt;
  
  
  What About Creation Costs?
&lt;/h3&gt;

&lt;p&gt;And to go further, we could look at the impact of AI model creation compared to the ecological impact of creating a video game, or a movie.&lt;/p&gt;

&lt;p&gt;An &lt;strong&gt;AAA video game&lt;/strong&gt; (big-budget), developed by a team of 150 people, costs between &lt;strong&gt;500 and 3,000 tonnes of CO2&lt;/strong&gt; depending on development time, travel, and motion-capture filming. To this, we must add the annual maintenance for live-service games that push out continuous updates and DLCs (like &lt;em&gt;World of Warcraft&lt;/em&gt; or &lt;em&gt;Overwatch&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;For a big budget film, we can estimate a carbon cost &lt;a href="https://www.youtube.com/watch?v=9oOjjwagGP8" rel="noopener noreferrer"&gt;between 3,000 and 4,000t of CO2&lt;/a&gt;, including transport, filming locations, generators, set construction.&lt;/p&gt;

&lt;p&gt;Granted, training a massive AI model can cost more than a single movie, but the difference isn't orders of magnitude apart. More importantly, we must remember that &lt;strong&gt;the world releases thousands of films and video games every year&lt;/strong&gt;, whereas the creation of new foundational AI models remains relatively rare.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, is AI Not That Serious After All?
&lt;/h2&gt;

&lt;p&gt;Let's be careful here. It would be &lt;strong&gt;lazy whataboutism&lt;/strong&gt; to simply say, &lt;em&gt;"Sure, AI is bad, but look at how much worse everything else is."&lt;/em&gt; That is missing the point. The real goal here is to question our consumption habits as a whole.&lt;/p&gt;

&lt;p&gt;What is certain, however, is that the reality is far more nuanced than the mainstream narrative suggests. Today, the hyper-focus on AI serves as a very convenient distraction, allowing us to forget the environmental cost of our other digital habits. But you don't earn &lt;strong&gt;moral virtue points&lt;/strong&gt; by campaigning against AI while actively indulging in online gaming, streaming blockbusters, or flying to international sports events.&lt;/p&gt;

&lt;p&gt;If you've followed the numbers well, the ecological impact of AI is relatively close to other impacts in digital (streaming and gaming for example).&lt;/p&gt;

&lt;p&gt;That doesn't mean it's good. In the world we live in, &lt;strong&gt;each additional tension on the planet is to be questioned&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But it forces us to realize that &lt;em&gt;all&lt;/em&gt; of our digital behaviors need to be reassessed, not just the fact that &lt;em&gt;"I asked ChatGPT a question."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I don't pretend to be able to rank these activities against one another. Comparing gaming, streaming, and professional workloads is highly complex. And even within professional uses, And I certainly won't decide, &lt;strong&gt;on my own&lt;/strong&gt;, what constitutes a "good" or "bad" use of technology. But collectively, &lt;strong&gt;we might soon be forced to make those choices&lt;/strong&gt;, &lt;strong&gt;not out of kindness, but by constraints&lt;/strong&gt; (See next chapter). &lt;/p&gt;

&lt;p&gt;The core issue isn't about outright banning AI. This is precisely what organizations like &lt;a href="https://www.youtube.com/watch?t=4013&amp;amp;v=WT67wMsEE2I&amp;amp;feature=youtu.be" rel="noopener noreferrer"&gt;Shift Project&lt;/a&gt;, France’s leading think tank on the energy transition, are trying to convey: we need to look at data volumes and digital use cases in their entirety. The argument isn't that we should abandon AI altogether, but rather that we cannot afford its current, unchecked trajectory&lt;/p&gt;

&lt;p&gt;Let's take an example: the FIFA World Cup generates between 9 and 15 million tonnes of CO2, which is roughly equivalent to the annual energy consumption of &lt;em&gt;all&lt;/em&gt; US data centers combined..&lt;/p&gt;

&lt;p&gt;Again, the idea isn't to say, they do worse. We'll get nowhere with that mindset.&lt;/p&gt;

&lt;p&gt;But I like this example because of the contrast it highlights. Playing football doesn't cost much. Gathering thousands of people across 3 countries and having them fly everywhere is absurd, as is air-conditioning football stadiums, or trying to organize winter games in a desert country.&lt;/p&gt;

&lt;p&gt;AI operates on the exact same spectrum. There is a massive gulf between a professional, high-utility application, like using AI in biochemistry, mathematics, meteorology, drug discovery, medical imaging, satellite analysis, or precision agriculture, and a purely recreational use aimed at generating thousands of &lt;strong&gt;Ghibli-style images&lt;/strong&gt; just to dump them on social media. Yes, we can, and should, question the latter (and that’s an understatement).&lt;/p&gt;

&lt;p&gt;Ultimately, understanding these orders of magnitude is what empowers us to make informed choices instead of just parroting the absurdities we hear on TV. Once you know the real numbers, you can weigh your choices accurately.&lt;/p&gt;

&lt;p&gt;I said earlier that one hour of video call was between 30 and 60g of CO2. Ok, but that might replace a Paris Lyon trip. By car it's between 60 and 90kg of CO2 saved. By train it's about 1kg.&lt;/p&gt;

&lt;p&gt;Similarly, one hour of streaming costs about 100g of CO2. But if it prevented you from driving 20km to the local movie theater (which would cost around 4.4kg of CO2 in car emissions), streaming turns out to be "not so bad" after all.&lt;/p&gt;

&lt;p&gt;In the end, once we have the data, it is up to each of us to make those choices.&lt;/p&gt;

&lt;h2&gt;
  
  
  What About the Explosion of Data Centers?
&lt;/h2&gt;

&lt;p&gt;A question I asked myself before writing this article was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the carbon footprint of AI is actually pretty close to our other digital habits, and assuming it replaces some of them (if I’m using AI, I’m not doing something else), why on earth are we building so many new data centers?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ok, this question might seem naive but it's estimated that data center electricity consumption could double, or even triple by 2030 (See &lt;a href="https://www.bcg.com/press/19february2025-data-centers-vers-une-hausse-de-la-demande-energetique-mondiale-de-16-par-an-dici-2028" rel="noopener noreferrer"&gt;BCG&lt;/a&gt; study and this &lt;a href="https://www.iris-france.org/les-nouvelles-technologies-ia-data-centers-sont-elles-compatibles-avec-les-objectifs-de-durabilite-environnementale/" rel="noopener noreferrer"&gt;IRIS&lt;/a&gt; article). So why? Is it linked to AI?&lt;/p&gt;

&lt;p&gt;According to articles, partly yes, but only partly. The majority of electricity consumed by data centers (about 2/3) should be dedicated to historical digital uses and acceleration of cloud migrations.&lt;/p&gt;

&lt;p&gt;Yes AI plays a role, but it's mainly that digital is taking up more and more space. The share of digital in global CO2 emissions went from 2% in 2010 to about 4% today, with an annual increase of about 6% &lt;strong&gt;even though the global objective is to reduce our emissions by 5% per year to hope to ++stabilize++ the climate&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Where AI genuinely worsens the problem compared to other tech is its rapid hardware obsolescence. However, the root cause is the massive scale-up of &lt;em&gt;all&lt;/em&gt; our digital habits: the ubiquity of 4K/8K streaming, cloud gaming, high-fidelity music streaming, and the explosion of connected IoT (Internet of Things) devices.&lt;/p&gt;

&lt;h3&gt;
  
  
  Forecasts vs. Financial Bubbles
&lt;/h3&gt;

&lt;p&gt;Of course, we should take these data center growth forecasts with a grain of salt. They remain predictions. They could easily be overestimated, just like the predicted "tidal wave" of data that was supposed to arrive with 5G but never quite materialized.&lt;/p&gt;

&lt;p&gt;Many of these projections are pushed by tech giants that have bet their entire financial futures on these exact growth scenarios. If you are Nvidia, Google, or Oracle, you have no choice but to reassure your shareholders by guaranteeing this growth will happen to justify the colossal investments already made. Honestly, if the AI financial bubble were to burst tomorrow, it might actually be good news for the planet, as it would instantly ease the pressure on our resources.&lt;/p&gt;

&lt;p&gt;That being said, we are looking at contracts that are already signed, budgets locked in, and massive public announcements, like the &lt;a href="https://openai.com/index/announcing-the-stargate-project/" rel="noopener noreferrer"&gt;Stargate&lt;/a&gt; project or &lt;a href="https://investir.lesechos.fr/marches-indices/enquetes/la-commission-europeenne-annonce-200-milliards-dinvestissements-pour-stimuler-le-developpement-de-lia-et-du-cloud-souverain-sur-le-continent-2235590" rel="noopener noreferrer"&gt;Europe's future investments&lt;/a&gt;. Every current scenario predicts a 2x to 3x increase in demand. Digital consumption is going up, and AI-related infrastructure is leading the charge.&lt;/p&gt;

&lt;p&gt;Will these digital habits replace physical ones (like my earlier example of a video call replacing a car trip)? Or are they purely &lt;strong&gt;additive&lt;/strong&gt;? Evidence suggests they are additive. While some AI applications will certainly accelerate decarbonization in specific industries, that impact remains relatively marginal for now.&lt;/p&gt;

&lt;p&gt;And, according to the Shift Project, it's mainly that the electrical consumption needed to run all these data centers will exceed our infrastructure capacity, and thus &lt;strong&gt;force using thermal sources (gas power plant) to compensate or &lt;a href="https://www.theguardian.com/world/article/2024/jul/23/ireland-datacentres-overtake-electricity-use-of-all-homes-combined-figures-show" rel="noopener noreferrer"&gt;create usage conflicts&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So yes, it's alarming. Again, it's not about banning AI, the subject is much more global than that. How do we make our consumption and pressure on the planet decrease?&lt;/p&gt;

&lt;p&gt;At our individual scale, we have to audit our own habits. We need to question our obsession with upgrading devices, over-equipping our homes, and engaging in mindless, heavy recreational uses (like generating endless Ghibli images just for a laugh).&lt;/p&gt;

&lt;p&gt;At a collective level, we will eventually be forced, likely much sooner than we think, to make hard choices. We will have to decide whether to route precious electricity to a data center or to &lt;strong&gt;power and heat local homes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But let’s remember one crucial thing: the future isn’t set in stone. If we collectively choose to consume less, we won't need the electricity these companies are dying to sell us. If data centers are multiplying, it's only because there is a planned demand for them. To paraphrase a famous French comedian: &lt;em&gt;"To think that if people just stopped buying, it wouldn't sell anymore!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

</description>
      <category>ai</category>
    </item>
    <item>
      <title>Are We in the Era of Shitty Code?</title>
      <dc:creator>hugo lassiege</dc:creator>
      <pubDate>Fri, 03 Jul 2026 07:28:34 +0000</pubDate>
      <link>https://dev.to/hlassiege/are-we-in-the-era-of-shitty-code-31a0</link>
      <guid>https://dev.to/hlassiege/are-we-in-the-era-of-shitty-code-31a0</guid>
      <description>&lt;p&gt;There isn't a day that goes by without discovering a major security flaw or data breach somewhere.&lt;/p&gt;

&lt;p&gt;Just yesterday, it was Github, which apparently opened &lt;a href="https://www.varonis.com/blog/github-breach" rel="noopener noreferrer"&gt;part of its code as open source&lt;/a&gt;. Unintentionally, of course, but we can still appreciate the gesture.&lt;/p&gt;

&lt;p&gt;Meanwhile, in France, it's &lt;a href="https://www.lecanardenchaine.fr/technologie-sciences/53846-lecornu-debranche-l-anssi-accusee-de-ne-pas-avoir-securise-des-sites-sensibles-de-l-etat" rel="noopener noreferrer"&gt;3 data thefts per day on average&lt;/a&gt; according to this article. But don't worry, apparently our prime minister has a solution: fire the director of the national cybersecurity agency (ANSSI)…&lt;/p&gt;

&lt;p&gt;That said, the massive breach of France's &lt;strong&gt;secure&lt;/strong&gt; identity document agency (ANTS) by a 15-year-old hacker via &lt;a href="https://www.frandroid.com/culture-tech/securite-applications/3086647_cest-inquietant-le-piratage-de-lants-france-titres-viendrait-dune-faille-absurde" rel="noopener noreferrer"&gt;a trivial vulnerability&lt;/a&gt; was kind of the straw that broke the camel's back.&lt;/p&gt;

&lt;p&gt;And according to some, AI would be responsible for the general decline in software quality, for example at GitHub where uptime would be in constant decline since GPT3 on this chart (and the arrival of Microslop but let's focus on just one culprit).&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%2Fxvczrgef5q1q5ea9485o.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%2Fxvczrgef5q1q5ea9485o.png" alt="Github uptime since Microsoft acquisition" width="800" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay, that's trolling. But some people wonder seriously: is AI really the source of all these problems?&lt;/p&gt;

&lt;p&gt;That's what we're going to try to see. We're going to talk about the real reasons for the CVE surge but it will mostly be a pretext to wonder if we're really at the dawn of an era of AI-generated shitty code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Rise in CVEs
&lt;/h2&gt;

&lt;p&gt;First, why am I talking about CVEs, and what is a CVE anyway?&lt;/p&gt;

&lt;p&gt;A CVE (Common Vulnerabilities and Exposures) is a standard that catalogs all known vulnerabilities in a large database. Each CVE is listed under a super sexy little name, like &lt;a href="https://nvd.nist.gov/vuln/detail/CVE-2024-12345" rel="noopener noreferrer"&gt;CVE-2024-12345&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;(Yes, it's quite thrilling, isn't it?)&lt;/p&gt;

&lt;p&gt;So you're going to tell me, in the software world we talk about bugs, not CVEs, and you'd be quite right. But there's no catalog of bugs for all known software.&lt;/p&gt;

&lt;p&gt;So here I'm taking the number of annual CVEs as a kind of indicator of the number of bugs in software because I think there's a small correlation between a decline in quality and an increase in security holes. At first glance anyway, but we'll come back to that.&lt;/p&gt;

&lt;p&gt;And if we look at it, well the acceleration could date from 2022 with however a first step &lt;a href="https://www.cvefind.com/en/cve-statistic-date.html" rel="noopener noreferrer"&gt;around 2017.&lt;/a&gt;&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%2F77flnad6ggqgwcg1mp73.jpg" 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%2F77flnad6ggqgwcg1mp73.jpg" alt="CVE increase since 1998" width="799" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we take 2017 as the turning point, it becomes hard to blame AI for the problem even with the bad faith of a a Marseille football fan (hey Max, if you're reading this, you know how you guys love to exaggerate).&lt;/p&gt;

&lt;p&gt;Broadly speaking, the CVE increase since 2017 is probably explained by a mix of factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;greater pressure on software which is now expected to have a new version every day&lt;/li&gt;
&lt;li&gt;a general explosion of digital technology&lt;/li&gt;
&lt;li&gt;many more security programs (bug bounties)&lt;/li&gt;
&lt;li&gt;an increase in the number of software dedicated to cybersecurity.&lt;/li&gt;
&lt;li&gt;major geopolitical conflicts and more frequent cyber attacks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Actually, it's not unlikely that we're measuring the number of thermometers (and not just the temperature), so the rise of the cyber industry, and on the other hand, the rise of digital technology which is becoming an economic and geopolitical stake in a rather unstable context for years.&lt;/p&gt;

&lt;p&gt;Now we can still question the post-2022 period because the slope seems to be climbing as fast as Trump's court cases.&lt;/p&gt;

&lt;p&gt;However, I'd say that CVEs aren't the best proxy for measuring software quality because as we've seen, CVEs can increase for rather exogenous reasons. It's not because we're controlling things better than before and scrutinizing more that there are really more bugs.&lt;/p&gt;

&lt;p&gt;So we're going to go back to the starting point and look at a sentence I hear very often: AI is the death of craft.&lt;/p&gt;

&lt;h2&gt;
  
  
  With AI, It's the Death of Craft
&lt;/h2&gt;

&lt;p&gt;Craft, for those not following along, is kind of an umbrella term to group together a whole set of practices aimed at producing "quality" software. Assuming we can define what quality software is, but that's another subject.&lt;/p&gt;

&lt;p&gt;With AI we wouldn't be crafting anymore and technical expertise, the appeal of the profession for many, would have disappeared.&lt;/p&gt;

&lt;p&gt;Wait, here we have two different subjects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the appeal of the profession would disappear because we wouldn't be crafting anymore&lt;/li&gt;
&lt;li&gt;the code produced by AI would be to computing what English cuisine is to world gastronomy, i.e., not great.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Well, let's set the record straight on the second point: no, English cuisine is vastly worse)&lt;/p&gt;

&lt;p&gt;So some people have tried to determine whether code produced by AI was necessarily less "sustainable" than code produced by a human, in short, whether we were really building mountains of technical debt at light speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Slop and Software Maintenance
&lt;/h2&gt;

&lt;p&gt;Do you know &lt;a href="https://www.linkedin.com/in/dave-farley-a67927/" rel="noopener noreferrer"&gt;Dave Farley&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;Dave Farley is notably one of the authors of the book Continuous Delivery released in 2010.&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%2Fpns4m16fq6c3qg1k4vmr.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%2Fpns4m16fq6c3qg1k4vmr.png" alt="Continuous Delivery book cover" width="400" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It was one of my reference professional books for years at a time when there was a lot of talk about software factories, quality, and continuous deployment. I worked for years on these subjects, particularly from 2006 on distributed CI concepts. I then worked from 2010 on industrialization themes, then from 2012 on continuous deployment.&lt;/p&gt;

&lt;p&gt;(okay grandpa but continue your story)&lt;/p&gt;

&lt;p&gt;Well &lt;a href="https://www.youtube.com/watch?v=b9EbCb5A408" rel="noopener noreferrer"&gt;Dave Farley participated in a study&lt;/a&gt; that aimed precisely to determine which code was more maintainable between code produced by AI or code produced by humans. Was there more &lt;strong&gt;technical debt&lt;/strong&gt; in AI-produced code? And the answer is… &lt;strong&gt;no&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To be frank, even I'm surprised, and yet not at all.&lt;/p&gt;

&lt;p&gt;I'm surprised because I know the engineering effort required for AI to work well. Without that effort, the code quickly looks like a slightly lopsided Frankenstein.&lt;/p&gt;

&lt;p&gt;But, I imagine the study is about professionals who had time to set up their usual tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building an Agentic Software Factory
&lt;/h2&gt;

&lt;p&gt;I told you, I love building software factories, I've been working on this subject for years.&lt;/p&gt;

&lt;p&gt;A software factory is nothing more than a manufacturing process, more or less industrialized and automated.&lt;/p&gt;

&lt;p&gt;A code generator, however powerful, &lt;strong&gt;is not a software factory, it's just one of the factory's machines&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And if you just let &lt;strong&gt;one&lt;/strong&gt; machine run, it's not enough.&lt;/p&gt;

&lt;p&gt;When a dev only does copy-paste from ChatGPT from a browser, it's barely 10% of the work and yes, the result isn't great and often requires a lot of manual adjustments.&lt;/p&gt;

&lt;p&gt;Now, being a bit provocative, I'd say that tweaking the generated code is a failure. Imagine the same thing in a car factory. If artisans had to retouch every car produced, it would be a bit surprising. And, I'm no specialist, but I don't think that's how it works.&lt;/p&gt;

&lt;p&gt;Anyway, you need the other tools.&lt;/p&gt;

&lt;p&gt;In our profession, this consists of building test and validation harnesses, defining rules to follow and finding ways to verify they're being followed, creating skills to guide AI agents' work and prevent them from crashing, allowing them to verify themselves with tools and therefore correct themselves, etc…&lt;/p&gt;

&lt;p&gt;And that, actually, &lt;strong&gt;is craft&lt;/strong&gt;. Using AI agents makes those famous best practices mandatory that everyone talks about but you don't see so often in practice.&lt;/p&gt;

&lt;p&gt;All the people I see right now in the &lt;a href="https://www.devw.ai/" rel="noopener noreferrer"&gt;DevWithAI&lt;/a&gt; community are craftspeople trying to improve their software factory. They're people working on token compression tools like &lt;a href="https://www.rtk-ai.app/" rel="noopener noreferrer"&gt;rtk&lt;/a&gt;, tools to simplify and standardize usage at team scale like &lt;a href="https://packmind.com/" rel="noopener noreferrer"&gt;packmind&lt;/a&gt;, &lt;a href="https://cc.bruniaux.com/" rel="noopener noreferrer"&gt;usage guides like Florian's&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Craft hasn't disappeared. It has changed.&lt;/p&gt;

&lt;p&gt;For a company, there's substantial effort required to introduce these tools, build a software factory on top, create tooling, do platform engineering, and you need people for that, the same way you need people to design, maintain, and evolve production chains in factories.&lt;/p&gt;

&lt;p&gt;Briefly, no, the appeal of the profession hasn't disappeared in my view, for people who love craft and technical work.&lt;/p&gt;

&lt;p&gt;And if we talk about quality, I'm going to surprise you but, at my scale, I see significant quality leaps in my applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Writizzy Example
&lt;/h2&gt;

&lt;p&gt;I develop several products, &lt;a href="https://hakanai.io/" rel="noopener noreferrer"&gt;Hakanai&lt;/a&gt; and &lt;a href="https://writizzy.com/" rel="noopener noreferrer"&gt;Writizzy&lt;/a&gt;. I recently put &lt;a href="https://bloggrify.com/" rel="noopener noreferrer"&gt;Bloggrify&lt;/a&gt; in maintenance mode.&lt;/p&gt;

&lt;p&gt;Bloggrify started in 2022, Hakanai in 2023 and Writizzy in 2025.&lt;/p&gt;

&lt;p&gt;For the first two, I barely used AI at first then gradually introduced some copy-paste from very discreet use of inline agents. So more than 90% of the code was produced by hand.&lt;/p&gt;

&lt;p&gt;Well today I'd say that it shows.&lt;/p&gt;

&lt;p&gt;Wait, I'm proud of what I did with Hakanai and Bloggrify. But to build an application solo you have to be &lt;strong&gt;very&lt;/strong&gt; good at everything: building the application, its logic, its ergonomics, its UI, its robustness. But that's not my case. So sometimes there are places where it's a bit less good and it shows.&lt;/p&gt;

&lt;p&gt;And beyond that, doing everything very well takes time. When building a product, you always have to choose between "perfect" and "good enough for most uses". The difference between the two being the time available.&lt;/p&gt;

&lt;p&gt;Sometimes you give up, you know it's not perfect, but you've just spent 1 week on it, and frankly for a 5% quality improvement, forget it, we're not spending another week on it. That will be for next time. That famous next time that never comes.&lt;/p&gt;

&lt;p&gt;Actually the quality of Hakanai and Bloggrify was variable. There were shortcuts taken, pages with ergonomics straight out of the 2000s web, and it doesn't matter, that's how we used to build products.&lt;/p&gt;

&lt;p&gt;With Writizzy, that's completely different. I don't take shortcuts. First and foremost because to produce correct code with AI, you need a proper software factory, and that includes safeguards like tests, linters, etc… So it's non-negotiable, whereas ultimately it was negotiable before for Hakanai and Bloggrify.&lt;/p&gt;

&lt;p&gt;Plus, the cost of refactoring has become almost zero. The small thing missing for a feature to be perfect, now I can do it.&lt;/p&gt;

&lt;p&gt;Actually today I can take the time to handle edge cases, those famous little exceptions that before you'd leave aside because "that never happens". I can use the time saved on dev to think more about security, robustness, scalability.&lt;/p&gt;

&lt;p&gt;Producing code has become a commodity. So I take advantage of it to produce very good code.&lt;/p&gt;

&lt;p&gt;Briefly, I'm still crafting, but its nature has changed.&lt;/p&gt;

&lt;p&gt;And what I produce is better quality. So I'm not convinced we've entered an era of shitty code.&lt;/p&gt;

&lt;p&gt;Now there's still a nuance to add. AI drastically lowers the barrier to entry. That's good, but it means we end up with a flood of stillborn software, things that won't survive longer than a shitcoin promoted by a Dubai influencer, and companies that are going to cut hard on R&amp;amp;D costs because… laziness, and what if we increased margins. So yes, under those conditions, we're going to have an increase in the number of poorly produced software, software that will die after 3 months because their creators will get bored very quickly faced with the reality of actually managing an IT product. But good products will on the other hand be much better because craft will be a sine qua non condition for not crashing in a world where everything will move much faster.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Transactional Outbox Pattern with PostgreSQL and RabbitMQ</title>
      <dc:creator>hugo lassiege</dc:creator>
      <pubDate>Sun, 28 Jun 2026 15:34:48 +0000</pubDate>
      <link>https://dev.to/hlassiege/the-transactional-outbox-pattern-with-postgresql-and-rabbitmq-4e2</link>
      <guid>https://dev.to/hlassiege/the-transactional-outbox-pattern-with-postgresql-and-rabbitmq-4e2</guid>
      <description>&lt;p&gt;How do you write to two different systems, for example RabbitMQ and PostgreSQL?&lt;/p&gt;

&lt;p&gt;You might say it's pretty straightforward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;   
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; 
    &lt;span class="c1"&gt;// save in database through JPA&lt;/span&gt;
    &lt;span class="n"&gt;myRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myEntity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// post message&lt;/span&gt;
    &lt;span class="n"&gt;broker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publishEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&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;But what happens if there's an error, if the SQL transaction fails? You end up with a message published but for an operation that never actually took place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two-phase commit transactions
&lt;/h2&gt;

&lt;p&gt;This is a relatively common problem and we're used to handling it with &lt;a href="https://en.wikipedia.org/wiki/Two-phase_commit_protocol" rel="noopener noreferrer"&gt;2 Phase commit&lt;/a&gt; transactions. The idea being to introduce a transaction across all operations to external systems, including RabbitMQ here.&lt;/p&gt;

&lt;p&gt;It's a somewhat complex mechanism that requires an additional coordinator to ensure everything goes well before validating the write across all systems.&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%2F8fvkxgu6toe9m0mlyapf.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%2F8fvkxgu6toe9m0mlyapf.png" alt="2 phase commit workflow" width="400" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But there are several problems.&lt;/p&gt;

&lt;p&gt;The first is that it's not possible to do &lt;a href="https://docs.spring.io/spring-amqp/reference/amqp/transactions.html" rel="noopener noreferrer"&gt;2PC transactions with RabbitMQ&lt;/a&gt; (also called XA transactions). Which, you'll agree, is already a relatively significant problem in itself.&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%2F4idloom0jonlmh19crz9.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%2F4idloom0jonlmh19crz9.png" alt="excerpt from documentation confirming absence of XA transactions with RabbitMQ" width="638" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We could use ActiveMQ which supports 2PC transactions. But on the other hand, it would be a shame to write an article about PostgreSQL and RabbitMQ just to conclude that you need to use ActiveMQ instead, right?&lt;/p&gt;

&lt;p&gt;The second problem is that in any case, a 2PC transaction penalizes the overall system performance. To synchronize two or more systems, not only will the total operation time never be less than the minimal time of the slowest system, but you also add a cost related to coordination.&lt;/p&gt;

&lt;p&gt;The last problem is that you degrade overall availability. Availability being the multiplication of the average availability of each system. For example with two systems with 99% availability, the total system achieves 98% availability.&lt;/p&gt;

&lt;p&gt;$0.99 \times 0.99 = 0.9801$&lt;/p&gt;

&lt;p&gt;And that was basically the state of my knowledge until yesterday. I had already used XA transactions, I had already dealt with distributed systems issues and I've worked on plenty of mitigation scenarios for this type of problem.&lt;/p&gt;

&lt;p&gt;Yesterday I added RabbitMQ to Writizzy's stack and I used another solution: the &lt;a href="https://microservices.io/patterns/data/transactional-outbox.html" rel="noopener noreferrer"&gt;Transactional Outbox&lt;/a&gt; pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  The transactional outbox pattern
&lt;/h2&gt;

&lt;p&gt;While the underlying concept isn't necessarily new, this specific pattern was popularized by Chris Richardson between 2014 and 2016.&lt;/p&gt;

&lt;p&gt;The concept is "simple":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rely only on the database to publish an event (step 1)&lt;/li&gt;
&lt;li&gt;read the table in an asynchronous job (step 2)&lt;/li&gt;
&lt;li&gt;publish to the message queue from the job (step 3)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code becomes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;   
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; 
    &lt;span class="c1"&gt;// save in database through JPA&lt;/span&gt;
    &lt;span class="n"&gt;myRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myEntity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// save in database&lt;/span&gt;
    &lt;span class="n"&gt;outboxRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&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;We now have only &lt;strong&gt;a single transaction&lt;/strong&gt;, and the &lt;strong&gt;guarantee that the event is published only if the transaction succeeds&lt;/strong&gt;. Event that will be sent to RabbitMQ later.&lt;/p&gt;

&lt;p&gt;Now, to actually send to RabbitMQ, you need a job to read the table:&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Scheduled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fixedDelay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;SchedulerLock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;outbox_publisher&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lockAtMostFor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PT30S&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lockAtLeastFor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PT0S&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;outboxPublisherService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publishPending&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;Note the use of &lt;a href="https://www.baeldung.com/shedlock-spring" rel="noopener noreferrer"&gt;SchedulerLock&lt;/a&gt; here which allows the use of schedulers in a multi-node context, to avoid simultaneous reading by multiple applications. This allows replacing Quartz which I frequently used until now.&lt;/p&gt;

&lt;p&gt;And you need the publication service code&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="nx"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;publishPending&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="nx"&gt;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;outboxEventRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findTop20ByOrderByCreatedAtAsc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;publishEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;publishEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OutboxEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;rabbitTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoke&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;convertAndSend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;routingKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
                    &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;HEADER_ORIGINAL_EXCHANGE&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exchange&lt;/span&gt;
                    &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;HEADER_ORIGINAL_ROUTING_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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;routingKey&lt;/span&gt;
                    &lt;span class="nx"&gt;message&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForConfirmsOrDie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nx"&gt;outboxEventRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to publish outbox event ${event.id} (${event.eventType})&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&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;Note that we've enabled RabbitMQ's &lt;a href="https://www.rabbitmq.com/tutorials/tutorial-seven-java" rel="noopener noreferrer"&gt;publisher confirms&lt;/a&gt; mode and we're waiting for write confirmation: &lt;code&gt;t.waitForConfirmsOrDie(5_000)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;But you could say to me: "you have the problem of double write to two systems again".&lt;/p&gt;

&lt;p&gt;And... you're not wrong.&lt;/p&gt;

&lt;p&gt;But it's simpler here. I only have 3 cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Everything goes well, nothing to say, it's perfect.&lt;/li&gt;
&lt;li&gt;RabbitMQ fails =&amp;gt; we go into the exception, we log, we exit, the message stays in the table and will be retried&lt;/li&gt;
&lt;li&gt;The PostgreSQL transaction fails. We go into the exception but the message is already sent!!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The 3rd case is important, it will happen so you need to handle it. This implies that each consumer must be able to be &lt;strong&gt;idempotent&lt;/strong&gt; on message reception. That is, each consumer must be able to accept the same event twice without incident.&lt;/p&gt;

&lt;p&gt;Here strategies are multiple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;read the message ID and store it somewhere to check that we don't do the operation twice, ideal for calls to external services&lt;/li&gt;
&lt;li&gt;trigger idempotent operations (Example: set status = pending, even if we do it twice, it doesn't pose a problem)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;etc...&lt;/p&gt;

&lt;h2&gt;
  
  
  And error handling?
&lt;/h2&gt;

&lt;p&gt;But there's an issue, if RabbitMQ goes down, if it's unreachable, if the disk is full, we're going to log the exception and with a batch running every 500ms I'll let you imagine the astronomical amount of logs that will produce. So we need a &lt;a href="https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern" rel="noopener noreferrer"&gt;Circuit Breaker&lt;/a&gt;. Conveniently, we can use &lt;a href="https://resilience4j.readme.io/docs/getting-started" rel="noopener noreferrer"&gt;resilience4j&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With a circuit breaker, we'll cut off sending to Rabbit in case of error and wait a bit before retrying. I'll leave the code here but won't detail it, that would be the subject of another article.&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="nx"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;circuitBreakerRegistry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;circuitBreaker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rabbitmq-outbox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;eventPublisher&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onStateTransition&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RabbitMQ circuit breaker: ${event.stateTransition}&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;CircuitBreaker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rabbitmq-outbox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fallbackMethod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;skipOnOpenCircuit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;publishPending&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="nx"&gt;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;outboxEventRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findTop20ByOrderByCreatedAtAsc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;publishEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;skipOnOpenCircuit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CallNotPermittedException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Circuit open — state transition already logged via eventPublisher&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;publishEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OutboxEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;rabbitTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoke&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;convertAndSend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;routingKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
                &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;HEADER_ORIGINAL_EXCHANGE&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exchange&lt;/span&gt;
                &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;HEADER_ORIGINAL_ROUTING_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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;routingKey&lt;/span&gt;
                &lt;span class="nx"&gt;message&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForConfirmsOrDie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;outboxEventRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AmqpException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;  &lt;span class="c1"&gt;// broker error → propagates to circuit breaker&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to publish outbox event ${event.id} (${event.eventType})&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&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;h2&gt;
  
  
  Challenging your own developer beliefs with AI
&lt;/h2&gt;

&lt;p&gt;As I said earlier, I was well acquainted with the XA transaction mechanism and the state of my knowledge for solving this problem stopped there.&lt;/p&gt;

&lt;p&gt;I wondered with curiosity if Claude would propose a more elegant implementation and I was rather surprised. In this specific kind of case, it was the perfect opportunity to try learning &lt;strong&gt;with&lt;/strong&gt; AI instead of just suffering code we don't understand.&lt;/p&gt;

&lt;p&gt;Letting AI write code without oversight, unsurprisingly it's rarely good. You need an expert eye and ultimately the code produced remains our responsibility and we must be able to understand it. So you need to find a middle ground between letting it do its thing and micro-managing the AI.&lt;/p&gt;

&lt;p&gt;By giving it my constraints (stemming from my experience), it was the agent that came up with the Transactional Outbox pattern proposal. And while I was initially a bit skeptical, I tried to understand each part of the code to make it my own by asking multiple questions. The code wasn't perfect, it was following these exchanges that was added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the publish confirm pattern&lt;/li&gt;
&lt;li&gt;resilience with resilience4j&lt;/li&gt;
&lt;li&gt;the scheduler lock to avoid multi-node issues&lt;/li&gt;
&lt;li&gt;the addition of messageid in headers (for deduplication)&lt;/li&gt;
&lt;li&gt;claude had added a state management on messages that wasn't relevant&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So yes, it wasn't a smooth plan but it was a good way for me to force myself to get updated, do some research and in short, learn new things.&lt;/p&gt;

&lt;p&gt;Beyond the technical aspect of this post, I mainly wanted to illustrate the method I use to code with an agent, which allows me to combine productivity AND craft.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>eventdriven</category>
    </item>
    <item>
      <title>How I Do Design as a Backend Developer</title>
      <dc:creator>hugo lassiege</dc:creator>
      <pubDate>Thu, 25 Jun 2026 07:40:54 +0000</pubDate>
      <link>https://dev.to/hlassiege/how-i-do-design-as-a-backend-developer-2fib</link>
      <guid>https://dev.to/hlassiege/how-i-do-design-as-a-backend-developer-2fib</guid>
      <description>&lt;p&gt;Imagine the place. It's dark, it's only 6pm but winter and cold have arrived.&lt;/p&gt;

&lt;p&gt;We are in a classroom, unhygienic and unheated, like all suburban Paris classrooms, because the high priests of 'Macronomics' decided it was more interesting to pay millions of Euros for McKinsey PowerPoints than to &lt;em&gt;actually&lt;/em&gt; invest in our future! ... I'm getting off track&lt;/p&gt;

&lt;p&gt;Briefly, a circle of people gathers, on small wooden chairs that hurt like hell. I'm present, looking grim. Actually no I wasn't listening to Jean-Michel who just spoke but I'm still trying to understand how the Spurs managed to mess up so badly in the last games of the 2026 NBA Finals.&lt;/p&gt;

&lt;p&gt;And then I get up and introduce myself.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Hello I'm Hugo Lassiège, I'm first and foremost a backend developer and here's my first version of Malt in 2012"&lt;/em&gt;&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%2Fufj7xtvyocam7jofng34.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%2Fufj7xtvyocam7jofng34.png" alt="Malt version from 2012 (never shown publicly, and we can understand why)" width="512" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Basically, I'm terrible at design.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So how in 2026 do I manage to propose designs like this blog's? That's what I propose to show you in this post.&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%2Fqqim6tqvtl0vincusz5n.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%2Fqqim6tqvtl0vincusz5n.png" alt="thumbnail of the eventuallycoding.com blog" width="400" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2011, the army of clones
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;If the somewhat historical part doesn't interest you that much, you can skip directly to the next sections to go to my current workflow which is described further down.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;2012 was quite a long time ago and at the time I was using &lt;a href="https://getbootstrap.com/" rel="noopener noreferrer"&gt;Bootstrap&lt;/a&gt; primarily, which explains that little very characteristic style from the first screenshot (mixed with frankly questionable aesthetic tastes, I'll grant you).&lt;/p&gt;

&lt;p&gt;Because yes, in 2011 a framework came out, Bootstrap if you've been following, which offered to make available the official UI kit used at Twitter. Inside we found the guidelines for making buttons, forms, progress bars, all in the spirit of the blue bird. We also found all the tools to more easily build responsive websites with the famous grid system that would last a long time. A very long time.&lt;/p&gt;

&lt;p&gt;For backend devs like me, that is to say those with more than modest taste for design, it was an opportunity to start with simple bases to finally make websites that don't look like they came out of a koala's acid trip imagination.&lt;/p&gt;

&lt;p&gt;Following Bootstrap several other libraries came out, &lt;a href="https://get.foundation/index.html" rel="noopener noreferrer"&gt;Foundation&lt;/a&gt;, &lt;a href="https://materializecss.com/" rel="noopener noreferrer"&gt;Materializecss&lt;/a&gt;, and so on but always with that little syndrome for sites using them of "but damn, what is this army of clones".&lt;/p&gt;

&lt;h2&gt;
  
  
  From atomic design to design systems
&lt;/h2&gt;

&lt;p&gt;Now it was nice to have a standard grid and buttons, but you still had to assemble them together. If buttons, input fields, text blocks were atoms, you needed to give a structure to assemble them in the form of molecules.&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%2Fmmt1f786q02u24gw9shy.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%2Fmmt1f786q02u24gw9shy.webp" width="600" height="450"&gt;&lt;/a&gt;&lt;br&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%2Fmf8hr9d3brb4onejydkd.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%2Fmf8hr9d3brb4onejydkd.webp" width="600" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is exactly this analogy that would be progressively pushed starting in 2013/2014 by Brad Frost, under the name of &lt;a href="https://atomicdesign.bradfrost.com/" rel="noopener noreferrer"&gt;Atomic Design&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This notion of molecules is really what you might call a component. A component fixes the assembly of our molecules as well as a behavior. And that's exactly what we find in several frameworks released in the same period, React (2013), Vue (2014) and even more generally Web Components (2011).&lt;/p&gt;

&lt;p&gt;As an aside, in my previous company (Malt) we grew in the same period, we thought it would be a great idea to mix all of this. We did Bootstrap, then we grouped all of that with an in-house framework (HopModules), we added Vue around 2015, then built Web Components, then used Nuxt a bit later to finally realize around 2023 that actually, that was a lot of everything...&lt;/p&gt;

&lt;p&gt;(But &lt;a href="https://eventuallymaking.io/p/the-singapore-project-migration-to-nuxt" rel="noopener noreferrer"&gt;it's another story I tell here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Briefly, we started building "components" that we assembled, standardized to finally arrive at what we call today, &lt;a href="https://www.figma.com/blog/design-systems-101-what-is-a-design-system/" rel="noopener noreferrer"&gt;a design system&lt;/a&gt;, that is to say sorts of Lego bricks that you can assemble together and that guarantee a certain identity, a voice for a site. Design systems that are even often public and that you can &lt;a href="https://designsystemsrepo.com/design-systems" rel="noopener noreferrer"&gt;find&lt;/a&gt; and &lt;a href="https://adele.uxpin.com/" rel="noopener noreferrer"&gt;study&lt;/a&gt; on different sites.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tailwind and design tokens
&lt;/h2&gt;

&lt;p&gt;To build a design system, there was a missing brick. It's nice to say for example that your buttons have a border radius of 4px. 4, not 2, not 6 but 4.&lt;/p&gt;

&lt;p&gt;And it's rather simple to express globally:&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="nc"&gt;.button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&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;But imagine you also want to align this radius on all your cards?&lt;/p&gt;

&lt;p&gt;Imagine you want to define a global padding, a "primary" color for your main buttons, the possible font sizes across the entire site.&lt;/p&gt;

&lt;p&gt;If the button is the atom, in reality there was a missing element below, how do you create our atoms?&lt;/p&gt;

&lt;p&gt;During the 2010s (perhaps via &lt;strong&gt;Jina Anne&lt;/strong&gt; at Salesforce in 2014), someone introduces a new term: the design token.&lt;/p&gt;

&lt;p&gt;A design token is the definition of the interface structure by these tokens.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;#0055ff&lt;/code&gt; becomes the color-brand-primary token&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;16px&lt;/code&gt; becomes the spacing-medium&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;etc...&lt;/p&gt;

&lt;p&gt;And that's exactly the approach of &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt; in 2017 which arrives with definitions of design tokens via a bunch of utility classes that will become very very used in current websites.&lt;/p&gt;

&lt;p&gt;So, Tailwind doesn't get unanimity for many reasons and it was never introduced at Malt. But for my new projects, it became the default solution. And now we can talk about my design workflow :)&lt;/p&gt;

&lt;h2&gt;
  
  
  My pre-2024 workflow, Tailwind and its component libraries
&lt;/h2&gt;

&lt;p&gt;Pre-2024 (soon we'll talk about the pre-AI agent era...) I launched &lt;a href="https://hakanai.io/" rel="noopener noreferrer"&gt;Hakanai.io&lt;/a&gt;. Let's not fool ourselves, it's better than what I was doing in 2012, but it's also as cheerful as an autumn day in Melun.&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%2Fel29kykgk88o0x1n7ntv.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%2Fel29kykgk88o0x1n7ntv.png" alt="hakanai.io interface created in 2024" width="400" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Despite this, here we find my 2024 design workflow, the use of Tailwind and component libraries such as &lt;a href="http://preline.io" rel="noopener noreferrer"&gt;preline.io&lt;/a&gt; or &lt;a href="http://Flowbite.com" rel="noopener noreferrer"&gt;Flowbite.com&lt;/a&gt;. It's an assembly of more or less visually coherent blocks but which have the advantage of having structural coherence through Tailwind's design tokens.&lt;/p&gt;

&lt;p&gt;It seems like nothing but it's already good. The spacing and breathing are consistent, the colors give an identity, the fonts are uniform, in short, it's a design system properly used. So yes, even Harry Potter drained of his soul by a Dementor looks better, but it works.&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%2F1zy995h7d0ej8q8l915z.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%2F1zy995h7d0ej8q8l915z.png" alt="a Dementor expressing its love for Harry" width="400" height="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that's broadly the spirit of most "indie" sites released in that period. Correct. But nothing to write home about.&lt;/p&gt;

&lt;h2&gt;
  
  
  And then came AI
&lt;/h2&gt;

&lt;p&gt;Starting in 2024 through early 2025, my workflow became less manual. I started building interfaces by asking Claude to do it. It was still copy-paste between the chat in the browser and my IDE. It was only HTML/JS/CSS and then I did the adaptation by hand.&lt;/p&gt;

&lt;p&gt;And then Claude came into the IDE, so it could directly create mockups in my code. However Claude reproduces the style it sees in the other pages, so since the site was created pre-2024, well it reproduced my somewhat "meeehhh" style from before.&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%2Fh83fk0rfuxm84agfeg1b.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%2Fh83fk0rfuxm84agfeg1b.png" alt="the broadcast.hakanai.io dashboard" width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So already in 2025, I started changing my workflow for certain pages and especially the first &lt;a href="https://writizzy.com/themes" rel="noopener noreferrer"&gt;themes available&lt;/a&gt; on writizzy.com. I started from a blank slate, giving some constraints but making sure Claude didn't rely on existing work.&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%2Fgy5yas8ncjovji4mx881.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%2Fgy5yas8ncjovji4mx881.png" alt="forge theme on writizzy" width="400" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sometimes I asked it to draw inspiration from known software, here with Notion:&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%2F4s0n6v5xvi2g5h9am1bl.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%2F4s0n6v5xvi2g5h9am1bl.png" alt="notion theme on writizzy" width="400" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Despite this, Claude has a certain tendency after a while to produce pretty much the same patterns and you can increasingly recognize an interface made by an AI. It's clean, it's better than what I produce alone, but it lacks originality. For admin interface in a SAAS it's not a big deal but to stand out on the internet, you have to make more effort.&lt;/p&gt;

&lt;p&gt;I started seeing skills appear to avoid the "slopification": &lt;a href="https://www.tasteskill.dev/" rel="noopener noreferrer"&gt;tasteskill&lt;/a&gt; and &lt;a href="https://impeccable.style/" rel="noopener noreferrer"&gt;impeccable&lt;/a&gt; but which I haven't personally tested. On the other hand, for some time I've been systematically using &lt;a href="https://www.anthropic.com/news/claude-design-anthropic-labs" rel="noopener noreferrer"&gt;Claude Design&lt;/a&gt; and there, I see a significant improvement in what I produce.&lt;/p&gt;

&lt;p&gt;First, what is Claude Design?&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%2F9vgammta5bpbjpzkx9nk.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%2F9vgammta5bpbjpzkx9nk.png" alt="Claude Design interface" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Claude Design is specially optimized for interface creation, whether for its system instructions which are optimized for UI (but not UX!!), and through its tooling.&lt;/p&gt;

&lt;p&gt;CD will indeed be able to visualize the result and iterate autonomously via screenshots. You'll be able to have a "tweaks" panel to visualize options, make low fidelity mockups or finished interfaces, comment on areas, etc...&lt;/p&gt;

&lt;p&gt;But especially CD can start from a screenshot to get a vibe. So if I take the tux theme we can see in the screenshot above, I started from &lt;a href="https://www.mux.com/" rel="noopener noreferrer"&gt;a site whose graphic vibe I appreciated&lt;/a&gt;, I gave it an image and instructions for it to capture the essence: the grids, the particularly bright accents in certain places, the thick and tilted underlines etc... I had to iterate, the first draft had issues but the result is rather interesting.&lt;/p&gt;

&lt;p&gt;The big limitation is that CD can't understand animations (except to copy paste the code from the html page), yet that's one of the strengths of the original site and for now it remains a weakness of my workflow.&lt;/p&gt;

&lt;p&gt;Another use case is to start from a design system. You can define one directly, or iterate based on the model that seems most relevant to you. Here for example, it's the Writizzy dashboard in its new style. And now each new page starts from this one which serves as a "design system" to build the next ones.&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%2Fe4zwqvfgqfqs2z8jdlex.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%2Fe4zwqvfgqfqs2z8jdlex.png" alt="Writizzy design system" width="799" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In short my workflows are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;creating a mockup with Claude Design with 3 options:

&lt;ul&gt;
&lt;li&gt;from a brief&lt;/li&gt;
&lt;li&gt;from a screenshot&lt;/li&gt;
&lt;li&gt;from a design system&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;implementation in my own code via an "implement-mockup" skill that explains how to transcribe the html code produced by Claude in my technologies (nuxt-ui, tailwind)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And more recently, I'm testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use of agent-browser or playwright to test the interface, especially in responsive&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So don't be mistaken, I much prefer working with a designer and I would when Writizzy gets bigger. But to get started, for an indie site, it remains much better than what I could do before and the level of expectations from users has become so much greater now that anyway, it's no longer really an option to do "like before". Because yes, design gives a voice to a site, an identity. That and of course the text, on the page, the way you address readers. Without identity, a site, an application, doesn't hold your attention.&lt;/p&gt;

&lt;p&gt;Of course it's less true on a blog post, you come to read the content, not the container, or on an admin interface of a software. But still, you don't want to be punished visually and give the impression of "meeehhh".&lt;/p&gt;

&lt;p&gt;Now let's take a step back, is it perfect? No.&lt;/p&gt;

&lt;p&gt;Yes, if you look at the path traveled in 15 years, it's easier to build websites. Since Bootstrap we've sought order, structure, standardization and it works.&lt;/p&gt;

&lt;p&gt;But at what cost? We've classified components, documented margins, frozen systems. We've rationalized beauty. The problem is that by wanting to systematize everything, we've created an incredibly clean web, but terribly boring. A web without friction, but without soul. And AI, by its very nature, is the absolute queen for reproducing this kind of lukewarm consensus. If you ask it for "clean", it will give you clean.&lt;/p&gt;

&lt;p&gt;Do I sometimes feel a little nostalgic for the ugly web of the early 2000s? Maybe.&lt;/p&gt;

&lt;p&gt;Well, not always.... It depends! I don't know!!!&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%2Fn7o5rmjmkeqlg190k5ln.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%2Fn7o5rmjmkeqlg190k5ln.png" alt="yorkshire breeding site from the 2000s" width="400" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But maybe that's exactly it, if AI relieves us of the plumbing, if the technique is automated, it's an opportunity for us to put back intention, identity, emotion (I didn't think I'd write that one day). Maybe that's what the evolution of the coming years looks like, breaking the rules, and making ugly things again, but with a beautiful voice.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
