<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator>
  <link href="https://kevinmeyvaert.be/feed.xml" rel="self" type="application/atom+xml"/>
  <link href="https://kevinmeyvaert.be/" rel="alternate" type="text/html"/>
  <updated>2026-04-20T07:02:49+02:00</updated>
  <id>https://kevinmeyvaert.be/feed.xml</id>

  <title type="html">Kevin Meyvaert - Articles</title>
  <subtitle>Kevin Meyvaert - photographer based in Gent, Belgium. Personal work on film. Full stack engineer.</subtitle>
  <author>
    <name>Kevin Meyvaert</name>
  </author>

  
  
    <entry>
      <title type="html">From manual emails to a fully automated photo pass system</title>
      <link href="https://kevinmeyvaert.be/articles/photo-pass-automation/" rel="alternate" type="text/html" title="From manual emails to a fully automated photo pass system"/>
      <published>2026-01-24T00:00:00+01:00</published>
      <updated>2026-01-24T00:00:00+01:00</updated>
      <id>https://kevinmeyvaert.be/articles/photo-pass-automation/</id>
      <content type="html" xml:base="https://kevinmeyvaert.be/articles/photo-pass-automation/">&lt;p&gt;I have been part of the photography collective Wannabes for about seven years now, already before covid. Over time I gradually took on the responsibility of handling most of the photo pass requests for the photographers in the collective. At first this felt manageable. It slowly turned into a surprisingly heavy operational task.&lt;/p&gt;

&lt;p&gt;Requesting photo passes sounds simple, but it rarely is. For every concert, you need to figure out who the promoter is, find the right press contact, send out emails and then follow everything up. Applications need to be sent well in advance, while replies often arrive only a day or two before the show. In the meantime you are tracking approvals, rejections and confirmations, and making sure every photographer knows where they stand.&lt;/p&gt;

&lt;p&gt;Doing this for a handful of shows is fine. Doing it for dozens every month is not. A lot of time went into searching press websites and copying information around. Because the process was so tedious, I sometimes postponed applications. That meant we were late and missed photo passes. It also meant I was not very motivated to let more photographers join the collective, simply because that would create even more manual work.&lt;/p&gt;

&lt;p&gt;At some point it became clear that this could not stay this way. The process needed to be automated. Not only to save time, but also to make it consistent and scalable.&lt;/p&gt;

&lt;h2 id=&quot;the-first-attempt-at-automation&quot;&gt;The first attempt at automation&lt;/h2&gt;

&lt;p&gt;I had already seen n8n pass by on social media a couple of times as a workflow automation tool. I never really took the time to try it properly. In October of last year I decided to spend a full day turning my entire manual workflow into an automated one. I started with n8n Cloud because I wanted to move fast and avoid spending time on infrastructure before knowing whether the idea would even work.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/articles/n8n-workflow.png&quot; alt=&quot;n8n workflow automation&quot; /&gt;
&lt;em&gt;The n8n workflow that automated photo pass requests. It handled everything from scraping shows to generating emails.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The first version was surprisingly extensive. I scraped the Live Nation press website to keep a list of upcoming shows in a Google Sheet, since most of our concerts come from there. I created a form using Tally where photographers could submit their requests. Every submission triggered a workflow that matched the request to the correct show and enriched it with promoter contact details. On the first day of each month, another workflow generated draft emails using ChatGPT, ready to be sent to promoters.&lt;/p&gt;

&lt;h2 id=&quot;using-it-for-real&quot;&gt;Using it for real&lt;/h2&gt;

&lt;p&gt;We decided to actually use this setup for the last couple of months of the year. It worked well enough to already save a lot of time. At the same time, n8n Cloud turned out to be quite expensive, so I moved everything to my own NAS. I cleaned up the workflows and added Discord notifications for every important step. That way I only had to intervene when something was missing or when a show was not coming from Live Nation.&lt;/p&gt;

&lt;p&gt;Incoming replies were still handled manually. I received a notification in Discord and then checked my mailbox to see whether a request was approved, rejected or simply acknowledged. It was not perfect, but it was a big improvement. That is how we closed the year.&lt;/p&gt;

&lt;h2 id=&quot;a-new-job-and-a-new-idea&quot;&gt;A new job and a new idea&lt;/h2&gt;

&lt;p&gt;In December 2025 I switched jobs. I had mostly worked as a front end engineer before, but my new role is much more full stack. I was looking for a real project to get more comfortable with the technologies we use at work. The timing felt right to turn the automated workflows into a proper application.&lt;/p&gt;

&lt;p&gt;Over the course of a weekend I built the bare bones of what is now our photo pass request system. Photographers can log in and see a dashboard with their requests and their current status. They can browse and search through upcoming concerts that are automatically fetched from multiple sources.&lt;/p&gt;

&lt;h2 id=&quot;one-central-place-for-all-concert-data&quot;&gt;One central place for all concert data&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/articles/concerts-dashboard.png&quot; alt=&quot;Concert dashboard showing upcoming shows&quot; /&gt;
&lt;em&gt;The concert dashboard where photographers can browse and search upcoming shows. All press contacts and venue information are automatically fetched.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The system now scrapes not only Live Nation, but also venues like AB and De Roma, as well as other places our photographers frequently cover. Because all shows already exist in the system, photographers can simply type an artist name and select the correct show. All relevant press and promoter information is already attached. Almost no manual searching is needed anymore.&lt;/p&gt;

&lt;h2 id=&quot;automating-the-follow-up&quot;&gt;Automating the follow up&lt;/h2&gt;

&lt;p&gt;On the admin side, all incoming requests are visible in one place. At first this was mostly an interface layer on top of the existing automation. Soon after, I decided to automate the follow up process as well.&lt;/p&gt;

&lt;p&gt;I built a service that monitors my mailbox and automatically classifies incoming emails. It extracts which show and which request the email refers to, updates the status in the system and sends a notification to the photographer when a decision is made.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/articles/email-logs.png&quot; alt=&quot;Email logs dashboard&quot; /&gt;
&lt;em&gt;The email logs interface showing automated classification of incoming emails. The system extracts show details, matches requests, and updates statuses automatically.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If a promoter replies that a photographer is approved under certain conditions, those conditions are included automatically. The photographer receives an email with all the details without me having to touch anything. I still receive Discord notifications so I can keep an eye on things and step in when needed.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/articles/discord-notifications.jpg&quot; alt=&quot;Discord notifications on mobile&quot; /&gt;
&lt;em&gt;Discord notifications keep me informed of new concerts, photo requests, and any issues that need manual intervention.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;handling-fairness-inside-the-collective&quot;&gt;Handling fairness inside the collective&lt;/h2&gt;

&lt;p&gt;One tricky edge case was handling multiple photographers requesting the same show. I built a simple assignment system that keeps track of how many opportunities each photographer has had. That makes it easier to distribute assignments more fairly across the collective.&lt;/p&gt;

&lt;h2 id=&quot;where-the-system-stands-today&quot;&gt;Where the system stands today&lt;/h2&gt;

&lt;p&gt;The application now runs on Google Cloud and is actively used by Wannabes. It saves time, reduces mistakes and removes a lot of mental overhead. Most importantly, it made the whole process enjoyable again. Building it genuinely made me happy.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/articles/admin-dashboard.png&quot; alt=&quot;Admin dashboard overview&quot; /&gt;
&lt;em&gt;The admin dashboard showing pending requests, recent updates, and new submissions. Everything I need to manage the collective in one place.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What this project confirmed for me is how much I enjoy solving real world problems through automation. If you have a workflow in your business, hobby or organization that feels clunky or overly manual, I am always happy to think along. Sometimes a bit of advice is enough. Sometimes it makes sense to build something. Either way, feel free to reach out.&lt;/p&gt;</content>

      
      <summary type="html">How I transformed a tedious manual photo pass request process into a fully automated system that saves hours every month and made managing a photography collective enjoyable again.</summary>
      

      
        
        <category term="automation"/>
        
        <category term="software-engineering"/>
        
      

      
        
        <category term="automation"/>
        
        <category term="n8n"/>
        
        <category term="workflow"/>
        
        <category term="ai"/>
        
        <category term="full-stack"/>
        
        <category term="gpt"/>
        
        <category term="discord"/>
        
        <category term="email-processing"/>
        
      
    </entry>
  
    <entry>
      <title type="html">A junior software engineer’s AI usage guide</title>
      <link href="https://kevinmeyvaert.be/articles/junior-ai-usage-guide/" rel="alternate" type="text/html" title="A junior software engineer&apos;s AI usage guide"/>
      <published>2025-11-20T00:00:00+01:00</published>
      <updated>2025-11-20T00:00:00+01:00</updated>
      <id>https://kevinmeyvaert.be/articles/junior-ai-usage-guide/</id>
      <content type="html" xml:base="https://kevinmeyvaert.be/articles/junior-ai-usage-guide/">&lt;p&gt;Lately, in conversations with other engineers, I’ve noticed a growing concern: AI is widening the gap between junior and senior developers. Seniors know how to structure problems and recognize good solutions, so AI feels like a boost. Juniors, still learning those fundamentals, often feel unsure about how to get the same value from these tools.&lt;/p&gt;

&lt;p&gt;My goal in writing this article is to share what I’ve learned and help make that gap feel smaller. With the right approach, AI can accelerate your growth instead of overwhelming it, supporting you as you build your engineering foundation.&lt;/p&gt;

&lt;p&gt;As you transition to professional engineering, you’ll find that artificial intelligence coding assistants are powerful tools. Think of AI not as a replacement for your growing skills, but as an advanced co-pilot that helps you work faster while you still learn to navigate.&lt;/p&gt;

&lt;p&gt;Your primary goal right now is to &lt;strong&gt;build deep foundational knowledge&lt;/strong&gt;. This guide outlines how to use AI tools (like Cursor, GitHub Copilot, or Tabnine) to support your learning, not short-circuit it.&lt;/p&gt;

&lt;h2 id=&quot;llm-101-models-vs-tools-the-engine-vs-the-car&quot;&gt;LLM 101: models vs tools (the engine vs the car)&lt;/h2&gt;

&lt;p&gt;The distinction between a model and a tool is fundamental to understanding AI-assisted coding. The interchangeable use of these terms in common conversation can be confusing, but knowing the difference is key to maximizing AI utility.&lt;/p&gt;

&lt;h3 id=&quot;the-model-the-core-intelligence&quot;&gt;The model: the core intelligence&lt;/h3&gt;

&lt;p&gt;The Large Language Model (LLM) is the &lt;strong&gt;engine&lt;/strong&gt; that generates code and answers. Its performance is defined by its inherent characteristics:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Context window&lt;/strong&gt;: This is the limit of how much code and conversation history the model can “see” at any one time. A larger context window allows the model to understand more complex, multi-file problems.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Reasoning capability&lt;/strong&gt;: Models differ in their ability to plan and execute tasks. Better models exhibit superior chain-of-thought reasoning, meaning they can break down a complex problem into logical steps before writing the code.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Examples of models&lt;/strong&gt;: GPT-5.1, Claude Sonnet 4.5, Gemini 3.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;the-tool-the-workflow-integration&quot;&gt;The tool: the workflow integration&lt;/h3&gt;

&lt;p&gt;The tool is the product or interface that integrates the model into your workflow (e.g., your IDE or CLI). The tool can significantly influence the model’s effective performance:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Tool calling&lt;/strong&gt;: The tool enables the model to interact with external systems. For example, a tool might allow the model to read and edit multiple files in your codebase, giving it a much broader effective context than the model alone could handle.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Optimization&lt;/strong&gt;: tools are often specifically optimized for certain models. For example, the product Claude Code is designed and optimized to work seamlessly with Anthropic models like Claude Sonnet 4.5.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Examples of tools&lt;/strong&gt;: ChatGPT, GitHub Copilot, Cursor, Claude Code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;deep-dive-on-context-model-context-protocol-mcp&quot;&gt;Deep dive on context: model context protocol (MCP)&lt;/h2&gt;

&lt;p&gt;The model context protocol (MCP) is an open standard that allows AI assistants (the tool/client) to dynamically fetch real-time, version-specific information from external services (MCP servers) and inject it into the model’s context window. This is how the AI can access up-to-date documentation or interact with your file system. While this capability is incredibly powerful, it introduces a major pitfall: &lt;strong&gt;context bloat&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-danger-of-context-bloat&quot;&gt;The danger of context bloat&lt;/h3&gt;

&lt;p&gt;Context bloat occurs when the model’s limited context window is overloaded with excessive or irrelevant information. When this happens, the model’s performance degrades rapidly. It struggles to find the “signal” in the “noise,” leading to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Lost focus&lt;/strong&gt;: the model pays attention to irrelevant details instead of the task at hand.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Increased latency &amp;amp; cost&lt;/strong&gt;: every message sent to the model requires re-sending the entire bloated context.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Accuracy decay&lt;/strong&gt;: the model’s ability to reason and recall correct information drops (the “lost in the middle” problem).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;starting-smart-context7-mcp&quot;&gt;Starting smart: context7 MCP&lt;/h3&gt;

&lt;p&gt;Context7 MCP is a prime example of an MCP server that exclusively focuses on providing clean, up-to-date, version-specific documentation for popular libraries and frameworks. It is designed to minimize bloat and combat hallucinations by only giving the model what it needs. No unnecessary logs, files, or chat history. This is the safest way to leverage tool-augmented context while you develop an understanding of context management.&lt;/p&gt;

&lt;div class=&quot;tip-box&quot;&gt;
&lt;p&gt;&lt;strong&gt;Tip!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Although MCP is all the hype. Use it wisely. Personally, I only use &lt;a href=&quot;https://github.com/upstash/context7&quot;&gt;Context7&lt;/a&gt; and &lt;a href=&quot;https://github.com/microsoft/playwright-mcp&quot;&gt;Playwright&lt;/a&gt;. I don&apos;t use Figma MCP as it translates the design to a json representation, bloating the context of your session. Screenshots work as good or even better!&lt;/p&gt;
&lt;/div&gt;

&lt;h2 id=&quot;crucial-ground-rules&quot;&gt;Crucial ground rules&lt;/h2&gt;

&lt;p&gt;Before you begin, these rules are non-negotiable. They protect your learning curve and your code quality.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;No blind copy-paste&lt;/strong&gt;: Never accept and commit a large block of AI-generated code that you do not fully understand. If you can’t explain it to another engineer, you shouldn’t check it in.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;No entire features&lt;/strong&gt;: Avoid prompts like “Write a function that handles user login and authentication”. Break down the task into small, manageable components and write them yourself, assisted by the AI.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Always verify&lt;/strong&gt;: AI can sometimes hallucinate (make up facts or code). Always verify the AI’s suggestions against security policies, coding standards, and project-specific constraints. &lt;strong&gt;You are the final authority&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Document&lt;/strong&gt;: When you use a complex piece of AI-generated code, make a note of it (e.g., in a code comment or a pull request description) to maintain clarity and accountability.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;tip-box&quot;&gt;
&lt;p&gt;&lt;strong&gt;Tip!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I know it&apos;s very tempting to go with the flow when using AI. Pumping out features fast feels good. But know that building an understanding of how good software works takes time, effort and critical thinking. Knowing and understanding code will make you a better engineer in the long run! So don&apos;t cut corners.&lt;/p&gt;
&lt;/div&gt;

&lt;h2 id=&quot;the-5-phase-learning-roadmap&quot;&gt;The 5-phase learning roadmap&lt;/h2&gt;

&lt;p&gt;This guide is about using AI to accelerate your learning curve, not to skip it. By focusing on comprehension and using the AI as an interactive tutor and assistant, you will quickly develop the instincts of a great engineer.&lt;/p&gt;

&lt;p&gt;The following phases are designed to systematically build your skills:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Phase 1&lt;/strong&gt;: AI as a smart autocomplete (code and learn)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Phase 2&lt;/strong&gt;: AI as a knowledge agent (ask and understand)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Phase 3&lt;/strong&gt;: AI as a peer reviewer (ideate and refine)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Phase 4&lt;/strong&gt;: The Socratic prompt (learning by asking)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Phase 5&lt;/strong&gt;: Never stop experimenting (the evolving landscape)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;phase-1-ai-as-a-smart-autocomplete-code-and-learn&quot;&gt;Phase 1: AI as a smart autocomplete (code and learn)&lt;/h2&gt;

&lt;p&gt;The most effective way for you to start is by treating the AI as an extremely smart autocomplete function. This keeps you &lt;strong&gt;in the driver’s seat&lt;/strong&gt;, ensuring you reason through the problem and write the code yourself.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Own the code&lt;/strong&gt;: Write the first few characters or a comment describing what you need. Let the AI suggest the rest. &lt;strong&gt;Do not prompt the entire feature&lt;/strong&gt;. You should be able to write the code yourself if the AI suggestion disappears.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Reason first, accept second&lt;/strong&gt;: Before accepting an AI-generated snippet, pause and ask yourself:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Why&lt;/strong&gt; did the AI suggest this method or syntax?&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;How&lt;/strong&gt; does this code work, line by line?&lt;/li&gt;
      &lt;li&gt;Is this the most &lt;strong&gt;efficient&lt;/strong&gt; or &lt;strong&gt;cleanest&lt;/strong&gt; way to solve the problem?&lt;/li&gt;
      &lt;li&gt;Is this &lt;strong&gt;simple, readable&lt;/strong&gt; code?&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Small, repetitive tasks only&lt;/strong&gt;: Use autocomplete for:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Boilerplate&lt;/strong&gt; (e.g., setting up a basic class structure, function signature).&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Simple, repetitive patterns&lt;/strong&gt; (e.g., initializing a list, basic error handling).&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Syntax lookup&lt;/strong&gt; in a new or unfamiliar library.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;tip-box&quot;&gt;
&lt;p&gt;&lt;strong&gt;Tip!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It&apos;s always useful to go read up on some framework/library documentation. Deep understanding of core principals and patterns will make you understand new things faster as you encounter them.&lt;/p&gt;
&lt;/div&gt;

&lt;h2 id=&quot;phase-2-ai-as-a-knowledge-agent-ask-and-understand&quot;&gt;Phase 2: AI as a knowledge agent (ask and understand)&lt;/h2&gt;

&lt;p&gt;Your AI tool often has a chat or agent mode. Use this for contextual questions that would typically require digging through documentation or interrupting a senior teammate.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Goal&lt;/th&gt;
      &lt;th&gt;Your Prompt Strategy&lt;/th&gt;
      &lt;th&gt;Why this helps you learn&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Understanding code&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;“Explain this block of code” or “What does this library function do and why is it used here?”&lt;/td&gt;
      &lt;td&gt;It forces you to analyze existing code, and the AI provides an instant, relevant explanation.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Design choices&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;“Why is this API endpoint implemented with a POST request instead of a GET?” or “Why was this specific database pattern chosen?”&lt;/td&gt;
      &lt;td&gt;It gives you historical and architectural context, building your understanding of system design.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Refactoring&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;“I’m thinking of renaming this variable to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;currentUserProfile&lt;/code&gt;. Review the pros and cons of this change.”&lt;/td&gt;
      &lt;td&gt;It teaches you the value of code clarity and maintainability.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;div class=&quot;tip-box&quot;&gt;
&lt;p&gt;&lt;strong&gt;Tip!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Use your AI coding assistant as a first line of information when you have questions or if you want to bounce ideas. Verify assumptions. Explore options. But don&apos;t also forget you can reach out to other engineers you&apos;re working with! They&apos;ll gladly point you in the right direction.&lt;/p&gt;
&lt;/div&gt;

&lt;h2 id=&quot;phase-3-ai-as-a-peer-reviewer-ideate-and-refine&quot;&gt;Phase 3: AI as a peer reviewer (ideate and refine)&lt;/h2&gt;

&lt;p&gt;Once you have a functional idea or have started an implementation, use the AI to bounce off ideas and challenge your own assumptions.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Ask for alternatives&lt;/strong&gt;: “I’ve implemented this logic using a dictionary/map. Can you suggest 2-3 alternative ways to implement this, and what are the &lt;strong&gt;upsides and downsides&lt;/strong&gt; of each (e.g., performance, readability, memory use)?”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Review your logic&lt;/strong&gt;: Write out a piece of pseudocode or a comment explaining your planned implementation, and ask the AI to critique it. “I plan to do X, then Y, then Z. Is there a case I’m missing? Is there a more standard pattern for this?”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Critical evaluation is key&lt;/strong&gt;: Your human mind is better at critical thinking, nuanced requirements, and considering the overall system context. Always verify the AI’s suggestions against security policies, coding standards, and project-specific constraints. Remember, AI can sometimes hallucinate (make up facts or code). &lt;strong&gt;You are the final authority&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;tip-box&quot;&gt;
&lt;p&gt;&lt;strong&gt;Tip!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Try moving out of your code editor. Explain the problem you want to solve to a model that doesn&apos;t have access to the codebase. That way it won&apos;t be confined to the limitations of the current implementation of your project. I call this &lt;strong&gt;prompting outside of the box&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;

&lt;h2 id=&quot;phase-4-the-socratic-prompt-learning-by-asking&quot;&gt;Phase 4: the Socratic prompt (learning by asking)&lt;/h2&gt;

&lt;p&gt;To maximize your learning, train your AI to act like a Socratic tutor. It should ask questions and guide you, not simply hand over the solution. This is essential for developing your debugging and problem-solving muscle.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Problem Area&lt;/th&gt;
      &lt;th&gt;Instead of this prompt…&lt;/th&gt;
      &lt;th&gt;Use this guided prompt…&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Component Logic&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;“Write a component that fetches data and displays a loading state.”&lt;/td&gt;
      &lt;td&gt;“I’ve started building a data-fetching component. Before you write the implementation, explain the best place to handle asynchronous logic in a functional React component, and why that hook is the preferred method.”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Debugging State&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;“Fix this bug where my component updates twice when I click a button.”&lt;/td&gt;
      &lt;td&gt;“I have a double-render issue when state changes. List the top 3 common causes of this in a React component and suggest a debugging step I can take to isolate the cause in my specific code.”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Performance/Best Practices&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;“Rewrite this function to be faster.” (Referring to a complex render function)&lt;/td&gt;
      &lt;td&gt;“Review my component’s rendering function. Do you see any opportunities to use React’s built-in memoization hooks (like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useMemo&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useCallback&lt;/code&gt;)? If so, explain the performance trade-off of applying the hook here.”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;New Hook/Library&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;“Write code to manage global state with Redux.”&lt;/td&gt;
      &lt;td&gt;“I need to manage state across multiple components. Compare Context API vs. Zustand for a small-to-medium-sized React application. Don’t provide code, but list the pros and cons of each in terms of boilerplate and learning curve.”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Refactoring&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;“Refactor this component.” (Large file with mixed logic)&lt;/td&gt;
      &lt;td&gt;“This component has too many responsibilities. Suggest a plan for breaking it down into 2-3 smaller, single-purpose components. Outline what the new components should be named and what props they will accept.”&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;div class=&quot;tip-box&quot;&gt;
&lt;p&gt;&lt;strong&gt;Tip!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You&apos;ll get more value from your coding agent if you ask it to ask you questions (or if you&apos;re using Claude, try setting its output style to &quot;learning&quot;). When responding try to avoid adding your own bias, because LLMs are pleasers. i.e. don&apos;t say &quot;Isn&apos;t it better that we do it this way?&quot; as it will probably agree with you. Try asking &quot;What are the benefits or possible pitfalls when we do it this way?&quot;.&lt;/p&gt;
&lt;/div&gt;

&lt;h2 id=&quot;phase-5-never-stop-experimenting-the-evolving-landscape&quot;&gt;Phase 5: never stop experimenting (the evolving landscape)&lt;/h2&gt;

&lt;p&gt;The moment you become comfortable with an AI tool is the moment you must start looking for its successor. The AI engineering landscape is evolving at a breakneck pace. New models, agents, and IDE integrations are released weekly, constantly raising the bar for what’s possible. Your ability to &lt;strong&gt;adapt and experiment&lt;/strong&gt; is the final, and perhaps most critical, skill to cultivate.&lt;/p&gt;

&lt;h3 id=&quot;the-30-day-testing-cycle&quot;&gt;The 30-day testing cycle&lt;/h3&gt;

&lt;p&gt;Your biggest competitive advantage in this field will be your willingness to test new technologies. &lt;strong&gt;You only truly learn about new technology when you use it&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Consume reviews&lt;/strong&gt;: Dedicate a small amount of time each week to consuming content about new models (like GPT-5.1, Gemini 3, or Claude Sonnet 4.5) and new tools (like Antigravity, Aider, or new Cursor features). Watch videos, listen to podcasts, and read newsletters to hear about other developers’ &lt;strong&gt;real-world experiences&lt;/strong&gt; and benchmarks.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Install and test&lt;/strong&gt;: When a new tool or model update seems promising, install it.
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Run the tests&lt;/strong&gt;: Use it to write or refactor a component with existing unit tests. Does the generated code pass the tests on the first try?&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Check the flow&lt;/strong&gt;: Use it for a day or two on a small feature. Does it genuinely enhance your workflow? Is it faster, less distracting, and more context-aware than your current setup?&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Switch or stay&lt;/strong&gt;:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;If it enhances your workflow&lt;/strong&gt;: Switch immediately. The cost of switching is minimal compared to the long-term gains in efficiency and learning.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;If it doesn’t&lt;/strong&gt;: Revert to your current tool. You haven’t lost anything, but you’ve gained &lt;strong&gt;invaluable context&lt;/strong&gt; about the technology’s current limitations and strengths.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;tip-box&quot;&gt;
&lt;p&gt;&lt;strong&gt;Tip!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Find information sources that work for you. (Twitter, Reddit, newsletters,..) But remember many tech influencers are very opinionated. That&apos;s why it is so important to get hands-on, read the docs, and form your own opinion.&lt;/p&gt;
&lt;/div&gt;

&lt;h3 id=&quot;the-ultimate-learning-metric-agentic-reflexes&quot;&gt;The ultimate learning metric: agentic reflexes&lt;/h3&gt;

&lt;p&gt;Pay close attention to tools that emphasize &lt;strong&gt;agentic behavior&lt;/strong&gt;. These are tools designed to handle multi-step tasks, like creating a full feature or fixing a complex bug across multiple files, by planning and executing code changes while keeping you in the loop for verification.&lt;/p&gt;

&lt;p&gt;The transition from simple autocomplete to a collaborative, multi-step agent is the future of AI-assisted coding. The true measure of your professional growth will be your ability to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Critique the plan&lt;/strong&gt;: Can you immediately identify a flaw in the agent’s multi-step plan before it writes any code (a reflex honed in Phase 3)?&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Verify the code&lt;/strong&gt;: Can you quickly explain why the agent’s complex, multi-file solution works (a reflex honed in Phase 1)?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By actively testing new tools, you are not just chasing productivity; you are testing and refining the core engineering instincts you built throughout this guide. The goal isn’t to be a subscriber to every tool, but to be an informed evaluator. By actively testing and comparing, you ensure your personal AI co-pilot remains the most powerful and effective assistant available, keeping your skills relevant and your productivity high.&lt;/p&gt;</content>

      
      <summary type="html">A comprehensive guide for junior developers on using AI coding assistants effectively while building deep foundational knowledge. Learn the 5-phase roadmap from smart autocomplete to agentic workflows.</summary>
      

      
        
        <category term="ai"/>
        
        <category term="software engineering"/>
        
      

      
        
        <category term="ai"/>
        
        <category term="coding-assistants"/>
        
        <category term="learning"/>
        
        <category term="best-practices"/>
        
        <category term="llm"/>
        
        <category term="mcp"/>
        
      
    </entry>
  
    <entry>
      <title type="html">I built an IKEA price checker for a friend’s renovation</title>
      <link href="https://kevinmeyvaert.be/articles/ikea-price-checker/" rel="alternate" type="text/html" title="I built an IKEA price checker for a friend&apos;s renovation"/>
      <published>2025-11-12T00:00:00+01:00</published>
      <updated>2025-11-12T00:00:00+01:00</updated>
      <id>https://kevinmeyvaert.be/articles/ikea-price-checker/</id>
      <content type="html" xml:base="https://kevinmeyvaert.be/articles/ikea-price-checker/">&lt;p&gt;A friend was redoing his kitchen.
He told me IKEA prices aren’t the same everywhere.
Same product, different country, different price.
He checked them one by one across Belgium, the Netherlands, and France.&lt;/p&gt;

&lt;p&gt;His kitchen plan had ninety-eight products from the IKEA kitchen configurator.
That sounded painful to track by hand.&lt;/p&gt;

&lt;p&gt;He said, “It’s strange. The exact same cabinet is cheaper across the border.”
That line stuck with me.
He didn’t need another spreadsheet.
He needed a small tool that did the checking for him.&lt;/p&gt;

&lt;p&gt;So I started building one that same afternoon.&lt;/p&gt;

&lt;h2 id=&quot;from-idea-to-first-version&quot;&gt;From idea to first version&lt;/h2&gt;

&lt;p&gt;I didn’t start by writing code.
I asked GPT-5 what would make a good IKEA price comparison tool.
It suggested simple features: search by product code, compare countries, highlight the lowest price, show stock, and maybe handle full shopping lists later.&lt;/p&gt;

&lt;p&gt;Then I opened Claude Code to build it.
Within minutes, we had a working prototype.
Type an IKEA article number and it fetched prices from Belgium, the Netherlands, and France.
It took less time to make than opening three IKEA sites by hand.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/articles/komprare-v1.jpg&quot; alt=&quot;KOMPRÅRE first version&quot; /&gt;
&lt;em&gt;The first working version of the IKEA price checker. “ship it?” I send to my friend via text.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;building-beyond-the-basics&quot;&gt;Building beyond the basics&lt;/h2&gt;

&lt;p&gt;Once the basics worked, I asked GPT-5 what else people might want.
It suggested checking stock by store, uploading shopping lists, exporting results, and adding a browser plugin.
That became my plan.&lt;/p&gt;

&lt;p&gt;I added live store availability first.
IKEA has no public product API, so I scraped pages and checked the numbers against the site until they matched.&lt;/p&gt;

&lt;p&gt;Then came shopping list mode.
You can upload an IKEA shopping list PDF.
The app reads the product codes, checks prices across countries, and shows which stores give the lowest total.
It can export that plan as a clear PDF.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/articles/komprare-v2.jpg&quot; alt=&quot;KOMPRÅRE second version&quot; /&gt;
&lt;em&gt;I just prompted the shopping list (pdf) feature. “will it work first try”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The next step was the Chrome plugin.
It adds a section that appears automatically on IKEA product pages.
The section shows the price comparison for that product right inside the page.
It feels native, as if IKEA quietly added it themselves.&lt;/p&gt;

&lt;p&gt;Those ideas came from GPT-5 brainstorms.
Claude Code helped turn them into working parts.&lt;/p&gt;

&lt;h2 id=&quot;two-ais-different-jobs&quot;&gt;Two AIs, different jobs&lt;/h2&gt;

&lt;p&gt;Claude Code handled development.
It wrote functions, API routes, and scrapers fast.
When something failed, I gave it the error and product ID.
It helped narrow the fix.&lt;/p&gt;

&lt;p&gt;GPT-5 handled the product side.
It asked why a feature mattered and how people would use it.
That mix kept the code moving and the scope tight.&lt;/p&gt;

&lt;div class=&quot;image-grid&quot;&gt;
  &lt;div class=&quot;image-grid-item&quot;&gt;
    &lt;img src=&quot;/assets/images/articles/komprare-pm-1.png&quot; alt=&quot;KOMPRÅRE name brainstorm&quot; /&gt;
  &lt;/div&gt;
  &lt;div class=&quot;image-grid-item&quot;&gt;
    &lt;img src=&quot;/assets/images/articles/komprare-pm-2.png&quot; alt=&quot;KOMPRÅRE logo design&quot; /&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;I wasn’t feeling creative naming this project, but GPT-5 helped. He also helped out making the logo, who needs desigers anyway 🤷‍♂️&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;managing-the-project&quot;&gt;Managing the project&lt;/h2&gt;

&lt;p&gt;I gave Claude Code my article &lt;a href=&quot;/articles/architecting-for-ai/&quot;&gt;Architecting for AI&lt;/a&gt; as context.
That shaped the structure.
All generated code lived in one Nx monorepo.
The scraping, authentication and analytics logic were shared by the web app and the Chrome plugin.
It kept the setup clean instead of a collection of one-off scripts.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/articles/komprare-arch.png&quot; alt=&quot;Project tree&quot; /&gt;
&lt;em&gt;I like clean project trees and efficient code reuse 🤝🏻&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;clauding-on-the-go&quot;&gt;Clauding on the go&lt;/h2&gt;

&lt;p&gt;While building this project, I tried a new feature from Claude.
They have a mobile app where you can link your repository and send prompts from your phone.
The next day after finishing the app, I was at work when my girlfriend texted me.
She couldn’t import shopping lists from the IKEA mobile app because those links used a different format.
At the coffee machine, I opened Claude on my iPhone, pasted her link, and typed:&lt;/p&gt;

&lt;div class=&quot;image-grid&quot;&gt;
  &lt;div class=&quot;image-grid-item&quot;&gt;
    &lt;img src=&quot;/assets/images/articles/komprare-claude-1.jpg&quot; alt=&quot;The prompt&quot; /&gt;
  &lt;/div&gt;
  &lt;div class=&quot;image-grid-item&quot;&gt;
    &lt;img src=&quot;/assets/images/articles/komprare-claude-2.jpg&quot; alt=&quot;The pull requests&quot; /&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;It’s the productmanagerification of software development&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;what-the-app-does-now&quot;&gt;What the app does now&lt;/h2&gt;

&lt;p&gt;You can type any IKEA product code.
It shows prices for Belgium, the Netherlands, France, and Germany.
It marks the lowest price.
It checks stock for specific stores.
You can upload a shopping list and get a per-store breakdown with totals.
And on IKEA’s own pages, the Chrome plugin shows the same comparison right below the product details.&lt;/p&gt;

&lt;p&gt;You can find the code on &lt;a href=&quot;https://github.com/kevinmeyvaert/ikea-compare&quot;&gt;GitHub&lt;/a&gt;
and visit the live app at &lt;a href=&quot;https://komprare.vercel.app&quot;&gt;komprare.vercel.app&lt;/a&gt;.
The Chrome plugin is in review and should be publicly available soon.&lt;/p&gt;</content>

      
      <summary type="html">How I built a price comparison tool that saved €1,000 on a kitchen renovation by checking IKEA prices across Belgium, the Netherlands, France, and Germany.</summary>
      

      
        
        <category term="web-development"/>
        
        <category term="ai"/>
        
      

      
        
        <category term="ikea"/>
        
        <category term="web-scraping"/>
        
        <category term="side-project"/>
        
        <category term="chrome-extension"/>
        
      
    </entry>
  
    <entry>
      <title type="html">Migrating my photography blog with AI: Lessons in iterative development</title>
      <link href="https://kevinmeyvaert.be/articles/migrating-with-ai-iterative-development/" rel="alternate" type="text/html" title="Migrating my photography blog with AI: Lessons in iterative development"/>
      <published>2025-11-02T00:00:00+01:00</published>
      <updated>2025-11-02T00:00:00+01:00</updated>
      <id>https://kevinmeyvaert.be/articles/migrating-with-ai-iterative-development/</id>
      <content type="html" xml:base="https://kevinmeyvaert.be/articles/migrating-with-ai-iterative-development/">&lt;p&gt;I recently migrated my photography blog from Tumblr to Jekyll with help from Claude Code. The project took a few hours and involved migrating 85 posts, optimizing 839 MB of images, and building a custom blog section. But more interesting than the technical outcome was learning how to effectively work with an AI coding assistant.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/articles/my-tumblr.webp&quot; alt=&quot;My Tumblr page&quot; /&gt;
&lt;em&gt;This is a screenshot from my Tumblr. Looks familiar?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This isn’t a tutorial. It’s a reflection on what works (and what doesn’t) when building something real with AI.&lt;/p&gt;

&lt;h2 id=&quot;starting-with-context&quot;&gt;Starting with context&lt;/h2&gt;

&lt;p&gt;My first prompt was straightforward:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“I created this fresh Jekyll project. I have a Tumblr blog at the moment, but I want to migrate it to a Jekyll setup so I can host it on my GitHub. First of all, I want you to analyze my current setup on Tumblr and recreate the layout as it is today. The url of the blog is kevinmeyvaert.be”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What worked here: I provided the actual URL instead of describing the design. I stated the end goal (GitHub Pages hosting). I asked for analysis first, implementation second.&lt;/p&gt;

&lt;p&gt;The AI analyzed my Tumblr theme and started building. But immediately, we hit the first lesson.&lt;/p&gt;

&lt;h2 id=&quot;visual-feedback-beats-everything&quot;&gt;Visual feedback beats everything&lt;/h2&gt;

&lt;p&gt;The initial build looked clean but wrong. Instead of my sidebar navigation, it created a horizontal header. I could have written a paragraph explaining the difference, but instead:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“The first image is my tumbler, which you can see has a sidebar. And the second image is what you created, which has a header and no sidebar. Please make the desktop version look like the tumbler one.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two screenshots. One sentence. Problem solved.&lt;/p&gt;

&lt;p&gt;Lesson: When something doesn’t match your vision, show it. Screenshots, mockups, or rough sketches communicate infinitely faster than descriptions.&lt;/p&gt;

&lt;h2 id=&quot;the-masonry-grid-saga&quot;&gt;The masonry grid saga&lt;/h2&gt;

&lt;p&gt;This is where things got interesting. My Tumblr blog used a masonry layout. Images of different aspect ratios flowed naturally into two columns. Simple to see, harder to build.&lt;/p&gt;

&lt;p&gt;The AI initially used Masonry.js, a popular JavaScript library. It worked, but loaded all 85 images at once. The page took forever to load.&lt;/p&gt;

&lt;p&gt;Next attempt: CSS Grid with calculated row spans based on image aspect ratios. This should have worked. But instead, I got massive gaps between images. Huge, unusable vertical spaces.&lt;/p&gt;

&lt;p&gt;I tried giving feedback:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;“The gaps are huge now (they got bigger since last change)”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Still broken. Time for directness:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“maybe reevaluate your approach ultrathink”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This moment taught me the most important lesson: When you’re stuck in local optimization (tweaking values, trying variations), sometimes you need to step back entirely. Don’t be polite. Be direct. “This approach isn’t working” is valuable feedback.&lt;/p&gt;

&lt;p&gt;After my prompt, the AI switched approaches entirely to CSS multi-column layout:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.photo-grid&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;column-count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;column-gap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;30px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.photo-item&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;break-inside&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;avoid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;margin-bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;30px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Perfect. Zero JavaScript. True masonry effect. The right solution was simpler all along.&lt;/p&gt;

&lt;p&gt;Lesson: Don’t get attached to the first approach. If something fundamental isn’t working, say so and explore alternatives.&lt;/p&gt;

&lt;h2 id=&quot;asking-for-best-practices&quot;&gt;Asking for best practices&lt;/h2&gt;

&lt;p&gt;I had 839 MB of images sitting in the project. I didn’t know if Jekyll had built-in optimization, so I asked:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“The images that we’re showing now on the website are pretty big. Does Jekyll offer some kind of image optimization plugin or resizer so we can ship more reasonably sized images?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The AI recommended &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jekyll_picture_tag&lt;/code&gt;, configured it for AVIF/WebP/JPEG with multiple responsive sizes, and ran the optimization.&lt;/p&gt;

&lt;p&gt;Result: 839 MB to 21 MB (97.5% reduction)&lt;/p&gt;

&lt;p&gt;Lesson: You don’t need to know every solution. Ask about best practices for the problem you’re trying to solve. “Does X have a way to handle Y?” is a powerful prompt pattern.&lt;/p&gt;

&lt;h2 id=&quot;when-constraints-change-plans&quot;&gt;When constraints change plans&lt;/h2&gt;

&lt;p&gt;Later in the project, I wanted better URL structure. The blog listing was at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/blog&lt;/code&gt; but individual articles used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/articles/article-title&lt;/code&gt;. Confusing.&lt;/p&gt;

&lt;p&gt;My request:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;“It’s kind of confusing that the overview of the blog is slash blog, but that the detailed pages are slash articles. Can you align those?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The AI suggested renaming &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_posts&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_photos&lt;/code&gt; for consistency. We did it. Then discovered: the standard jekyll-paginate plugin only works with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_posts&lt;/code&gt; directory, and GitHub Pages doesn’t support the v2 plugin that handles custom collections.&lt;/p&gt;

&lt;p&gt;We had two options. Use GitHub Actions for custom builds (complex). Keep &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_posts&lt;/code&gt; as the directory name (simple).&lt;/p&gt;

&lt;p&gt;I chose option 1: keep it simple. The URLs are clear even if the directory name isn’t perfect.&lt;/p&gt;

&lt;p&gt;Lesson: Real projects have constraints (hosting limitations, plugin compatibility, deadlines). Perfect architecture sometimes loses to practical shipping.&lt;/p&gt;

&lt;h2 id=&quot;prompting-patterns-that-worked&quot;&gt;Prompting patterns that worked&lt;/h2&gt;

&lt;p&gt;Looking back at the conversation, the most effective prompts were:&lt;/p&gt;

&lt;p&gt;Incremental and specific. Not “Make my site better” but “Can you make the pagination navigation more subtle?”&lt;/p&gt;

&lt;p&gt;Visual when possible. Not “The layout should have a sidebar on the left” but [Screenshot] + “Please make it look like this”&lt;/p&gt;

&lt;p&gt;Direct about problems. Not “The spacing seems a bit off” but “The gaps are huge now, maybe reevaluate your approach”&lt;/p&gt;

&lt;p&gt;Context-rich for complex requests. Not “Add a blog” but “I want a separate page on my blog where I can post technical blog posts. These articles shouldn’t appear in the feed of photos on the homepage.”&lt;/p&gt;

&lt;p&gt;Questions over commands. Not “Implement image compression” but “Does Jekyll offer some kind of image optimization plugin?”&lt;/p&gt;

&lt;h2 id=&quot;what-we-built&quot;&gt;What we built&lt;/h2&gt;

&lt;p&gt;In a few hours:&lt;/p&gt;

&lt;p&gt;We migrated 85 photo posts from Tumblr HTML. We optimized and served responsive images (AVIF/WebP/JPEG). We built masonry grid with pagination (10 photos per page). We added separate blog section for technical writing. We made it all GitHub Pages compatible.&lt;/p&gt;

&lt;p&gt;The conversation had ~50 exchanges. Most were quick iterations and refinements.&lt;/p&gt;

&lt;h2 id=&quot;takeaways-for-ai-assisted-development&quot;&gt;Takeaways for AI-assisted development&lt;/h2&gt;

&lt;p&gt;Start with context. Give the AI your actual URL, existing code, or screenshots. Context beats lengthy descriptions.&lt;/p&gt;

&lt;p&gt;Iterate quickly. Don’t try to specify everything upfront. Build, review, adjust. The feedback loop is your friend.&lt;/p&gt;

&lt;p&gt;Use visual feedback. Screenshots communicate design intent faster than any written description.&lt;/p&gt;

&lt;p&gt;Be direct when stuck. If an approach isn’t working, say “this isn’t working, try something else.” Don’t optimize a bad solution.&lt;/p&gt;

&lt;p&gt;Ask questions. You don’t need to know every tool or library. Ask “what’s the best practice for X?” and learn from the answer.&lt;/p&gt;

&lt;p&gt;Embrace constraints. Perfect architecture isn’t always practical. Ship the thing that works with your constraints.&lt;/p&gt;

&lt;p&gt;Review, don’t just accept. The first solution often works, but not always optimally. The masonry grid needed three attempts to get right.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Want predictable results from AI coding agents?&lt;/strong&gt; The iterative approach described here works great for smaller projects. For larger codebases and enterprise applications, I’ve developed a more structured approach using NX, module boundaries, and code generators that makes AI-generated code consistently production-ready. Learn more in &lt;a href=&quot;/articles/architecting-for-ai/&quot;&gt;Architecting for AI: How NX, module boundaries, and code generators transformed my development workflow&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;the-real-value&quot;&gt;The real value&lt;/h2&gt;

&lt;p&gt;The final blog works great. But the real value wasn’t the code. It was learning how to collaborate effectively with AI tooling. These patterns apply whether you’re building a blog, refactoring an app, or debugging production issues.&lt;/p&gt;

&lt;p&gt;The best collaboration happens when you provide clear context, give direct feedback, iterate quickly, know when to push back, and stay focused on shipping.&lt;/p&gt;

&lt;p&gt;Now I have a blog I control, hosted on GitHub, with optimized images and clean URLs. And I learned a better way to work with AI in the process.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;You can view the final result at &lt;a href=&quot;https://kevinmeyvaert.be&quot;&gt;kevinmeyvaert.be&lt;/a&gt; and the source code on &lt;a href=&quot;https://github.com/kevinmeyvaert/blog&quot;&gt;GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content>

      
      <summary type="html">What I learned about effective AI collaboration while migrating 85 photos from Tumblr to Jekyll.</summary>
      

      
        
        <category term="web-development"/>
        
        <category term="ai"/>
        
      

      
        
        <category term="jekyll"/>
        
        <category term="claude"/>
        
        <category term="workflow"/>
        
        <category term="migration"/>
        
      
    </entry>
  
    <entry>
      <title type="html">Architecting for AI: How NX, module boundaries, and code generators transformed my development workflow</title>
      <link href="https://kevinmeyvaert.be/articles/architecting-for-ai/" rel="alternate" type="text/html" title="Architecting for AI: How NX, module boundaries, and code generators transformed my development workflow"/>
      <published>2025-08-25T00:00:00+02:00</published>
      <updated>2025-08-25T00:00:00+02:00</updated>
      <id>https://kevinmeyvaert.be/articles/architecting-for-ai/</id>
      <content type="html" xml:base="https://kevinmeyvaert.be/articles/architecting-for-ai/">&lt;p&gt;When ChatGPT appeared, every dev team rushed to integrate it into their workflows. I did too. But I quickly discovered a problem. AI assistants generate code that violates architectural rules. They introduce coupling. They break dependency boundaries. They don’t understand your architecture unless you teach them.&lt;/p&gt;

&lt;p&gt;This is the story of how I prepared my enterprise Angular app for the AI age. I didn’t write better prompts. I enforced better architecture.&lt;/p&gt;

&lt;h2 id=&quot;the-problem-ai-assistants-dont-know-your-architecture&quot;&gt;The problem: AI assistants don’t know your architecture&lt;/h2&gt;

&lt;p&gt;Here’s a scenario you’ve probably lived through. You ask your AI assistant to “add a new feature to the stories module.” It generates code enthusiastically. But the code imports from unrelated modules. It creates files in the wrong directories. It uses outdated Angular patterns from its training data. It violates your team’s linting rules and breaks your dependency graph.&lt;/p&gt;

&lt;p&gt;That was my daily reality until I realized something. AI assistants need guardrails, not guidelines.&lt;/p&gt;

&lt;h2 id=&quot;nx-the-monorepo-that-thinks-like-an-architect&quot;&gt;NX: The monorepo that thinks like an architect&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://nx.dev&quot;&gt;NX&lt;/a&gt; is more than a monorepo tool. It’s a framework for architectural discipline. When I migrated my social-style application to NX, I wasn’t just cleaning up the repo. I was creating a structure both humans and AIs could navigate without ambiguity.&lt;/p&gt;

&lt;p&gt;My app had features for posts, stories, notifications, and more. Think of it like an internal Instagram engineering system. Every domain needed a clear home. Every dependency needed to be intentional.&lt;/p&gt;

&lt;p&gt;NX gave me three things I couldn’t get anywhere else. First, explicit project boundaries. When I ask the AI to “add a feature to the stories domain,” it knows exactly where the files belong. Second, a dependency graph that works like an executable contract. My AI and my fellow developers can query this graph to understand what depends on what. Third, a predictable project structure. Every feature library follows the same layout.&lt;/p&gt;

&lt;p&gt;Here’s what that looks like:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;libs/posts/feat-scheduler/
├── src/
│   ├── lib/
│   │   ├── components/
│   │   ├── services/
│   │   └── guards/
│   └── index.ts
├── jest.config.ts
└── project.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This predictability eliminates guesswork for humans and machines.&lt;/p&gt;

&lt;h2 id=&quot;module-boundaries-your-architectural-immune-system&quot;&gt;Module boundaries: Your architectural immune system&lt;/h2&gt;

&lt;p&gt;The key to AI-safe architecture isn’t documentation. It’s enforcement. I use ESLint’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@nx/enforce-module-boundaries&lt;/code&gt; rule. This rule relies on a two-dimensional tagging system that defines both the domain and type of each library.&lt;/p&gt;

&lt;p&gt;Each library belongs to a domain that represents a business area. In a large social app, you might have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domain:users&lt;/code&gt; for authentication, profiles, and relationships. You’d have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domain:posts&lt;/code&gt; for content creation and feed features. You’d have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domain:stories&lt;/code&gt; for ephemeral stories and highlights, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domain:notifications&lt;/code&gt; for alerts and engagement, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domain:ads&lt;/code&gt; for monetization, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domain:shared&lt;/code&gt; for utilities that cross domain boundaries.&lt;/p&gt;

&lt;p&gt;Domains depend on each other intentionally, not by accident. Posts can depend on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;users&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notifications&lt;/code&gt; for author and engagement data. But posts cannot depend on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ads&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stories&lt;/code&gt;. Here’s how I configure that:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;sourceTag&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;domain:posts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;onlyDependOnLibsWithTags&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;domain:posts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;domain:shared&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;domain:users&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;domain:notifications&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Each library also declares its architectural role through type tags. A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type:feature&lt;/code&gt; library contains smart, routed components. A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type:ui&lt;/code&gt; library contains dumb, reusable components. A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type:data-access&lt;/code&gt; library handles state management and data orchestration. A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type:api-service&lt;/code&gt; library wraps API clients and adapters. A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type:model&lt;/code&gt; library holds TypeScript types and interfaces. A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type:util&lt;/code&gt; library contains framework-agnostic utilities.&lt;/p&gt;

&lt;p&gt;Here’s an example rule for UI components:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;sourceTag&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;type:ui&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;onlyDependOnLibsWithTags&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;type:ui&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;type:model&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;type:util&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;type:data-access&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;UI code can’t call APIs directly. It must go through the data-access layer. No documentation needed. No human enforcement required.&lt;/p&gt;

&lt;p&gt;Libraries must satisfy both domain and type constraints. A library tagged &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[&quot;domain:stories&quot;, &quot;type:feature&quot;]&lt;/code&gt; must respect both sets of boundaries. It can import &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domain:shared, type:ui&lt;/code&gt;. It cannot import &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domain:ads, type:ui&lt;/code&gt;. It cannot import &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domain:shared, type:api-service&lt;/code&gt; from a UI component. The boundaries are enforced automatically. AI has no room to color outside the lines.&lt;/p&gt;

&lt;h2 id=&quot;what-happens-when-ai-hits-a-boundary&quot;&gt;What happens when AI hits a boundary&lt;/h2&gt;

&lt;p&gt;When AI-generated code violates a rule, it doesn’t fail silently. It hits a lint wall:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;npx nx lint posts-feat-scheduler

ERROR: 15:1  @nx/enforce-module-boundaries
A project tagged with &lt;span class=&quot;s2&quot;&gt;&quot;domain:posts&quot;&lt;/span&gt; can only depend on libs tagged with
&lt;span class=&quot;s2&quot;&gt;&quot;domain:posts&quot;&lt;/span&gt;, &lt;span class=&quot;s2&quot;&gt;&quot;domain:shared&quot;&lt;/span&gt;, &lt;span class=&quot;s2&quot;&gt;&quot;domain:users&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The feedback is immediate and clear. The AI adapts. Next time, it generates correct imports. Your architecture becomes the AI’s training data.&lt;/p&gt;

&lt;h2 id=&quot;custom-generators-teaching-ai-to-fish&quot;&gt;Custom generators: Teaching AI to fish&lt;/h2&gt;

&lt;p&gt;Boundaries defend your architecture. Generators let you go on offense. I built custom NX generators that wrap my architectural patterns into reusable commands.&lt;/p&gt;

&lt;p&gt;I can create a new feature module with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npx nx g @app/generators:library feat-highlights --directory=stories&lt;/code&gt;. I can create a shared UI component with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npx nx g @app/generators:shared-component carousel&lt;/code&gt;. I can create a lazy-loaded module with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npx nx g @app/generators:module profile-settings&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Every generator creates files in the right places. It applies architectural tags. It sets up linting, tests, and i18n. It uses modern Angular syntax like signals, standalone components, and the inject() function. The AI doesn’t need to generate boilerplate. It just calls the right generator.&lt;/p&gt;

&lt;h2 id=&quot;claudemd-context-for-ai-not-rules-for-humans&quot;&gt;CLAUDE.md: Context for AI, not rules for humans&lt;/h2&gt;

&lt;p&gt;Many teams maintain massive documentation files describing coding conventions. I deleted mine. I replaced it with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLAUDE.md&lt;/code&gt; file that focuses on context, not rules:&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gh&quot;&gt;# CLAUDE.md - Project Context&lt;/span&gt;

&lt;span class=&quot;gu&quot;&gt;## Overview&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Social platform for sharing content (posts, stories, messages)
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Key domains: users, posts, stories, notifications

&lt;span class=&quot;gu&quot;&gt;## Domain Terms&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &quot;Reel&quot; = short-form video (domain:stories)
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &quot;Boost&quot; = paid post promotion (domain:ads)
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &quot;Insights&quot; = analytics dashboard for creators

&lt;span class=&quot;gu&quot;&gt;## Test Accounts&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; qaInfluencerUser: creator account
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; qaModeratorUser: admin privileges
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Everything that can be enforced through tooling gets enforced. Linting, code structure, import rules, and formatting are not written down. They’re baked into the tools.&lt;/p&gt;

&lt;h2 id=&quot;enforcement-over-documentation&quot;&gt;Enforcement over documentation&lt;/h2&gt;

&lt;p&gt;If a rule can be enforced, don’t document it. This creates immediate feedback for AI and humans. Your tooling configuration becomes your single source of truth. When you update ESLint rules, you update AI behavior. There’s zero drift between documentation and implementation.&lt;/p&gt;

&lt;h2 id=&quot;real-results&quot;&gt;Real results&lt;/h2&gt;

&lt;p&gt;Since I adopted this approach, module boundary violations dropped to near zero. AI assistants generate code that passes lint on the first try. New developers onboard in days, not weeks. Code reviews focus on business logic, not structure. My AI assistants now self-lint before suggesting commits.&lt;/p&gt;

&lt;h2 id=&quot;what-i-wish-id-known-earlier&quot;&gt;What I wish I’d known earlier&lt;/h2&gt;

&lt;p&gt;Start with boundaries, not features. Architectural constraints teach AI faster than documentation. Treat generators as infrastructure. They’re as critical as CI/CD pipelines. Use tags, types, and tooling to make wrong code impossible. Lint early and often. If it doesn’t lint, it doesn’t ship. Treat your architectural context like code and version-control it.&lt;/p&gt;

&lt;h2 id=&quot;when-architecture-meets-ai&quot;&gt;When architecture meets AI&lt;/h2&gt;

&lt;p&gt;Once your architecture enforces itself, AI becomes a productive collaborator instead of a liability. Onboarding happens in minutes, whether the new team member is human or AI. Every library follows the same structure. Refactors become safe. Consistency scales with your team size.&lt;/p&gt;

&lt;p&gt;When AI and architecture align, velocity and stability increase together. That’s rare in software engineering.&lt;/p&gt;

&lt;h2 id=&quot;the-takeaway&quot;&gt;The takeaway&lt;/h2&gt;

&lt;p&gt;The teams that thrive in the AI era won’t be the ones with the best prompts. They’ll be the ones with the best architectures. Make your architecture enforceable. Make your AI architecturally aware. Let your development team and your AI move faster, safely.&lt;/p&gt;</content>

      
      <summary type="html">How I used NX, module boundaries, and code generators to make my AI assistants produce production-ready, architecture-compliant code.</summary>
      

      
        
        <category term="architecture"/>
        
        <category term="ai"/>
        
      

      
        
        <category term="ai"/>
        
        <category term="nx"/>
        
        <category term="architecture"/>
        
        <category term="angular"/>
        
        <category term="monorepo"/>
        
        <category term="developer-productivity"/>
        
      
    </entry>
  
    <entry>
      <title type="html">Automating my workflow with AI: A web engineer’s journey</title>
      <link href="https://kevinmeyvaert.be/articles/automating-workflow-with-ai/" rel="alternate" type="text/html" title="Automating my workflow with AI: A web engineer&apos;s journey"/>
      <published>2025-03-04T00:00:00+01:00</published>
      <updated>2025-03-04T00:00:00+01:00</updated>
      <id>https://kevinmeyvaert.be/articles/automating-workflow-with-ai/</id>
      <content type="html" xml:base="https://kevinmeyvaert.be/articles/automating-workflow-with-ai/">&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Update (August 2025):&lt;/strong&gt; This article is outdated. I no longer use Cursor to build software. My approach has evolved significantly since writing this post. For a more recent and comprehensive look at how I integrate AI into my development workflow, see &lt;a href=&quot;/articles/architecting-for-ai/&quot;&gt;Architecting for AI: How NX, module boundaries, and code generators transformed my development workflow&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’m lazy. Not in a bad way, but in the “how can I get more done with less effort?” way. As a web engineer, I look for ways to improve my workflows. I want to spend less time on repetitive tasks and more time on creative problem-solving. AI has become my tool of choice. It helps me build software faster and smarter. Cursor is an AI-powered coding assistant that has changed how I develop software.&lt;/p&gt;

&lt;h2 id=&quot;using-cursor-to-build-software&quot;&gt;Using Cursor to build software&lt;/h2&gt;

&lt;p&gt;Cursor helps me accelerate my workflow. It writes boilerplate code, debugs issues, and generates complex logic. The integration with my IDE is smooth. I can generate functions and components quickly. I get instant code suggestions and improvements. I debug and troubleshoot problems faster. I automate repetitive coding tasks.&lt;/p&gt;

&lt;p&gt;With this help, I focus more on high-level architecture and problem-solving. I don’t get stuck on syntax and routine coding patterns. Getting started is easy. Install it, integrate it into your workflow, and watch it handle the tedious parts. But anyone can use Cursor. I’ve found ways to use it better. I’ll share how I get the most out of it.&lt;/p&gt;

&lt;h2 id=&quot;be-smart-about-prompting&quot;&gt;Be smart about prompting&lt;/h2&gt;

&lt;p&gt;Crafting good prompts is critical when working with AI tools. The quality of the output depends on the quality of the input. Context matters. Clear, well-structured instructions help AI models generate meaningful responses.&lt;/p&gt;

&lt;p&gt;Here are some principles to keep in mind. Be specific. Vague prompts lead to generic responses. Define what you need in detail. Provide context. The more information you give, the better the AI understands your request. Iterate and refine. If the first response isn’t right, tweak your prompt and try again. Use examples. They help guide AI-generated content. Break down complex queries. Smaller steps improve accuracy.&lt;/p&gt;

&lt;p&gt;Mastering prompt engineering unlocks AI’s full potential. It makes AI a powerful assistant rather than just a tool. But prompting comes with challenges.&lt;/p&gt;

&lt;h2 id=&quot;treat-your-agent-like-an-intern&quot;&gt;Treat your agent like an intern&lt;/h2&gt;

&lt;p&gt;One challenge is making sure AI-generated code follows your team’s coding standards. Your AI assistant is like an intern or junior engineer. It needs guidance to produce high-quality work. AI can generate impressive results, but it doesn’t know your team’s best practices. I use Cursor Rules to align the AI’s output with our coding standards.&lt;/p&gt;

&lt;p&gt;Cursor Rules act as safeguards. They enforce best practices and prevent inconsistent code. The rules cover formatting standards. They check for consistent indentation, naming conventions, and style guidelines. They enforce code quality. They avoid anti-patterns, enforce modularity, and require proper documentation. They address performance. They encourage efficient algorithms and minimize unnecessary computations. They handle security. They require safe data handling, authentication flows, and API interactions.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/articles/cursor-rules-example.webp&quot; alt=&quot;Cursor screenshot&quot; /&gt;
&lt;em&gt;image: an example of some of the cursor rules I have in place for the project I’m currently working on.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;These are the classic boy scout rules. Always leave the code better than you found it. Just like when we mentor interns and junior engineers, we need to train our AI assistant.&lt;/p&gt;

&lt;h2 id=&quot;getting-structured-output-in-large-scale-projects&quot;&gt;Getting structured output in large-scale projects&lt;/h2&gt;

&lt;p&gt;Another hurdle is consistency of output. Large language models are stochastic. If you ask an AI to generate the same code multiple times, you’ll get different implementations. This leads to fragmented code, redundant logic, and a lack of cohesion. In small scripts, this might not matter. In larger projects with multiple developers, it’s a problem. You need more than good prompts.&lt;/p&gt;

&lt;p&gt;I use Nx Generators to address this. Nx Generators automate the creation of components, modules, and configurations from predefined templates. They establish scaffolding structures that guide my Cursor agent. Nx Generators provide a consistent framework for AI-generated code.&lt;/p&gt;

&lt;p&gt;Combined with Cursor rules, I can instruct the agent to use specific generators for certain actions. This reduces inconsistencies. Every component, service, or feature is generated with the same patterns. Collaboration becomes easier. Large projects remain maintainable because AI follows an organized framework.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/articles/nx-generator-example.webp&quot; alt=&quot;Cursor screenshot&quot; /&gt;
&lt;em&gt;image: here i’m asking to move a component to the “shared components”, which triggers a Cursor rule that defines that the agent has to use our shared-component Nx Generator. Which makes sure it is scaffolded and exported from the correct location.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Generators are just a small part of what Nx offers. Nx is powerful for managing large-scale projects. It enforces modularity, optimizes builds, and streamlines workflows across teams. Using Nx, I make sure AI-generated code fits into the project and improves the architecture.&lt;/p&gt;

&lt;h2 id=&quot;introduction-to-model-context-protocol-mcp&quot;&gt;Introduction to Model Context Protocol (MCP)&lt;/h2&gt;

&lt;p&gt;MCP is an open protocol developed by Anthropic. It helps AI understand and interact with the tools engineers use every day. It acts as a bridge between a large language model and external services. Think codebases, issue trackers, and deployment pipelines. Anthropic describes it like this: “Think of MCP like a USB-C port for AI applications. Just as USB-C provides a standardized way to connect your devices to various peripherals and accessories, MCP provides a standardized way to connect AI models to different data sources and tools.”&lt;/p&gt;

&lt;p&gt;MCP provides structured context to AI models. This improves response accuracy. It allows interactions with external systems. This reduces manual overhead. It enables automation through AI-driven commands in development workflows.&lt;/p&gt;

&lt;p&gt;Engineers can create more intelligent integrations between AI tools and their software development lifecycle.&lt;/p&gt;

&lt;h2 id=&quot;building-a-custom-mcp-server-for-azure-devops&quot;&gt;Building a custom MCP server for Azure DevOps&lt;/h2&gt;

&lt;p&gt;AI-assisted coding had already improved my development speed. Recently I took automation one step further. I built a custom MCP server to interface with Azure DevOps directly from Cursor chat.&lt;/p&gt;

&lt;p&gt;This integration lets me interact with Azure DevOps without leaving my development environment. I can retrieve work items, tasks, and issues from Azure DevOps using AI-generated queries. I can access acceptance criteria directly. This allows me to create precise prompts and generate test cases based on project requirements. If I come across new tasks while coding, I can ask my agent to create a new work item. This way I can pick it up later without losing context. AI agents get a better understanding of the current codebase by referencing work items and related repositories.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/articles/azure-devops-mcp.webp&quot; alt=&quot;Image description&quot; /&gt;
&lt;em&gt;image: here you see our agent using the Azure Devops MCP to access the context of a work item, which it then analyzes and tells us what the most important test case is for that feature.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;By integrating Azure DevOps with Cursor, I’ve reduced context-switching and manual interactions. My workflow is far more efficient.&lt;/p&gt;

&lt;h2 id=&quot;the-future-of-ai-driven-development&quot;&gt;The future of AI-driven development&lt;/h2&gt;

&lt;p&gt;AI-powered coding assistants like Cursor are reshaping how engineers build software. With AI-driven automation, we can reduce friction in our workflows. We minimize repetitive tasks. We focus more on creativity and problem-solving. My custom MCP for Azure DevOps is just the beginning. I’m constantly exploring new ways to integrate AI into my development process.&lt;/p&gt;

&lt;p&gt;But what’s exciting today might feel outdated in just a few months or weeks. The pace of AI development is relentless. New breakthroughs, tools, and best practices emerge constantly. I can already imagine reading this post again after summer. I’ll realize how much further my workflow has evolved.&lt;/p&gt;

&lt;p&gt;If you’re a developer looking to optimize your workflow, I recommend adopting AI tools now. The possibilities are vast, and the time savings are real. Coding without these tools feels like doing manual labor on a field. Slow, tedious, and exhausting. Now, we have tractors that let us work more productively. The industry is moving forward at a rapid pace. Those who don’t adopt AI-powered development will struggle to keep up.&lt;/p&gt;

&lt;p&gt;The choice is clear. Adapt and accelerate, or risk being left behind.&lt;/p&gt;

&lt;p&gt;Are you using AI in your development workflow? I’d love to hear about your experiences!&lt;/p&gt;</content>

      
      <summary type="html">This article explores how I use AI-powered tools like Cursor and Model Context Protocol (MCP) to automate my web development workflow, enhance efficiency, and stay ahead in the rapidly evolving AI-driven coding landscape.</summary>
      

      
        
        <category term="web-development"/>
        
        <category term="ai"/>
        
      

      
        
        <category term="cursor"/>
        
        <category term="ai"/>
        
        <category term="automation"/>
        
        <category term="mcp"/>
        
        <category term="azure-devops"/>
        
      
    </entry>
  
</feed>
