<?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: pedro.murinelo@protonmail.com</title>
    <description>The latest articles on DEV Community by pedro.murinelo@protonmail.com (@pedromurinelo).</description>
    <link>https://dev.to/pedromurinelo</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3943013%2Fae937df6-aff2-4798-b48c-38097aeb8739.png</url>
      <title>DEV Community: pedro.murinelo@protonmail.com</title>
      <link>https://dev.to/pedromurinelo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pedromurinelo"/>
    <language>en</language>
    <item>
      <title>How I Prevented Claude Code from Breaking My Architecture with 18 Tests That Run in 0.4 Seconds</title>
      <dc:creator>pedro.murinelo@protonmail.com</dc:creator>
      <pubDate>Thu, 21 May 2026 12:22:35 +0000</pubDate>
      <link>https://dev.to/pedromurinelo/how-i-prevented-claude-code-from-breaking-my-architecture-with-18-tests-that-run-in-04-seconds-1f1o</link>
      <guid>https://dev.to/pedromurinelo/how-i-prevented-claude-code-from-breaking-my-architecture-with-18-tests-that-run-in-04-seconds-1f1o</guid>
      <description>&lt;p&gt;I spent the last few weeks building a production boilerplate for AI Agent and IoT systems. FastAPI, asyncpg, LangGraph, MQTT, pgvector — a complex stack with very specific architectural boundaries that cannot be violated.&lt;/p&gt;

&lt;p&gt;The problem: I was using Claude Code and Cursor to accelerate development. And they are brilliant. But they are also completely agnostic to the architecture you have in your head.&lt;br&gt;
 &lt;a href="https://dev.tourl"&gt;&lt;/a&gt;&lt;br&gt;
Let me show you the kind of thing that happens without protection.&lt;/p&gt;


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

&lt;p&gt;My system has one critical rule: &lt;strong&gt;the &lt;code&gt;IngestionService&lt;/code&gt; must never import SQLModel or SQLAlchemy&lt;/strong&gt;. It's the hot path for IoT telemetry ingestion — asyncpg raw SQL only, zero ORM overhead. This separation is intentional and documented in 20 pages of ARCHITECTURE.md.&lt;/p&gt;

&lt;p&gt;In a typical session, I asked Claude Code to "refactor the IngestionService to be more consistent with the rest of the codebase."&lt;/p&gt;

&lt;p&gt;Generated result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# services/ingestion.py — generated by Claude Code
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlmodel&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;  &lt;span class="c1"&gt;# ← CRITICAL VIOLATION
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;core.database&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_session&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IngestionService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ingest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trace_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;get_session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# ← destroys hot path
&lt;/span&gt;            &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SensorReading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Technically correct. Architecturally catastrophic. Write latency jumps from 0.8ms to 4–6ms. At 5,000 messages per second, that's the difference between a system that holds under load and one that collapses.&lt;/p&gt;

&lt;p&gt;Claude Code doesn't know this. It can't. The architecture lives in my head and in a text document it may or may not have read before generating the code.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Solution: Architecture Fitness Tests
&lt;/h2&gt;

&lt;p&gt;The idea isn't new — Martin Fowler writes about "fitness functions" in &lt;em&gt;Building Evolutionary Architectures&lt;/em&gt;. But the application to AI-assisted development is very concrete: if the model is going to have full refactoring permission, you need tests that fail immediately when an architectural boundary is crossed.&lt;/p&gt;

&lt;p&gt;Not runtime tests. &lt;strong&gt;Static structure tests&lt;/strong&gt; — AST analysis of Python source, zero external dependencies, zero running server needed.&lt;/p&gt;

&lt;p&gt;The full suite runs in &lt;strong&gt;0.4 seconds&lt;/strong&gt;. Pre-commit, not post-deploy.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Concrete Example
&lt;/h2&gt;

&lt;p&gt;The most important test in my system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/test_architecture_fitness.py
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_ingestion_service_never_imports_sqlmodel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    The IngestionService is the Hot Path. SQLModel (SQLAlchemy) must
    never appear here. This is the most critical boundary in the system.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;INGESTION_SERVICE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;IngestionService not yet created at &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;INGESTION_SERVICE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;violations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;forbidden&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sqlmodel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sqlalchemy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SQLModel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AsyncSession&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;imp&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;_get_imports&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INGESTION_SERVICE&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;imp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;module&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;imp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;names&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;forbidden&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;violations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;forbidden&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;violations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;violations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;_format_violation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;violations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;IngestionService imported SQLModel/SQLAlchemy.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Fix: Use asyncpg raw SQL only in services/ingestion.py.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  This is the Dual-Path contract. The hot path has zero ORM overhead.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;_get_imports&lt;/code&gt; function uses Python's stdlib &lt;code&gt;ast&lt;/code&gt; module to parse the file without executing it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_imports&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;tree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;imports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ImportFrom&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
            &lt;span class="n"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;module&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;names&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;line&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lineno&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;file&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;imports&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zero external imports. Zero server. Zero database. Just Python.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 8 Boundaries I Test
&lt;/h2&gt;

&lt;p&gt;My system has a 7-layer architectural contract. The tests cover the most common violations AI generates:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Layer Isolation — Import Guards&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MQTT workers never import LangGraph&lt;/li&gt;
&lt;li&gt;IngestionService never imports SQLModel&lt;/li&gt;
&lt;li&gt;Agents never write directly via ORM&lt;/li&gt;
&lt;li&gt;Routers never call IngestionService directly
&lt;strong&gt;2. Hot Path Integrity&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;IngestionService uses only raw SQL strings, never query builders&lt;/li&gt;
&lt;li&gt;Hot path tables never defined as SQLModel models
&lt;strong&gt;3. Event Contracts&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Redis Stream publishes use the canonical &lt;code&gt;DomainEvent&lt;/code&gt; envelope&lt;/li&gt;
&lt;li&gt;MQTT workers validate with Pydantic before publishing
&lt;strong&gt;4. Async Enforcement&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;No synchronous HTTP clients (&lt;code&gt;requests&lt;/code&gt;) in routers&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;time.sleep()&lt;/code&gt; in IngestionService&lt;/li&gt;
&lt;li&gt;Worker handlers are &lt;code&gt;async def&lt;/code&gt;
&lt;strong&gt;5. Trace ID Contract&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IngestionService.ingest()&lt;/code&gt; accepts &lt;code&gt;trace_id&lt;/code&gt; as a parameter&lt;/li&gt;
&lt;li&gt;All domain events include &lt;code&gt;trace_id&lt;/code&gt;
&lt;strong&gt;6. Redis Keyspace Convention&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;No bare Redis keys without a namespace prefix&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;checkpoint:{id}&lt;/code&gt;, &lt;code&gt;stream:{name}&lt;/code&gt;, &lt;code&gt;cache:{type}:{id}&lt;/code&gt;
&lt;strong&gt;7. Migration Integrity&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Zero DDL statements in application code
&lt;strong&gt;8. Full Dependency Topology&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Validates the complete forbidden import matrix in a single pass&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Result in Production
&lt;/h2&gt;

&lt;p&gt;When I give Claude Code full permission to refactor any file, the cycle is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Claude Code generates code
        ↓
pytest tests/test_architecture_fitness.py -v
        ↓ (0.4 seconds)
Red → Claude Code fixes
        ↓
Green → commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model can never violate architectural boundaries without me knowing immediately. Not because it's adversarial — but because it optimises for local consistency, not global contracts.&lt;/p&gt;

&lt;p&gt;After adding these tests, all architectural violations disappeared from code reviews. Claude Code started generating compliant code automatically because the &lt;code&gt;CLAUDE.md&lt;/code&gt; context file explicitly describes what the tests verify.&lt;/p&gt;




&lt;h2&gt;
  
  
  CLAUDE.md — The Second Mechanism
&lt;/h2&gt;

&lt;p&gt;The tests are enforcement. &lt;code&gt;CLAUDE.md&lt;/code&gt; is prevention.&lt;/p&gt;

&lt;p&gt;It's a file at the repo root that Cursor and Claude Code read before generating code. It contains the contracts in explicit language with ✅ correct vs ❌ wrong examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Hard Boundaries (Enforced by tests/test_architecture_fitness.py)&lt;/span&gt;

&lt;span class="gu"&gt;### 1. The Dual-Path Contract&lt;/span&gt;

services/ingestion.py → asyncpg ONLY
  ✅ await pool.acquire() → await conn.execute("INSERT INTO ...", ...)
  ❌ from sqlmodel import Session
  ❌ session.add() / session.commit()
  ❌ time.sleep() — use await asyncio.sleep()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The combination of failing tests + explicit documentation creates an environment where AI can refactor freely with structural guarantees.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Numbers
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Full suite:          18 tests
Execution time:      0.42 seconds
Dependencies:        0 (stdlib Python + pytest only)
Violations caught
before adding tests: 12+
Violations after:    0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;AI-assisted development is genuinely transformative for productivity. But it creates a new category of risk: &lt;strong&gt;silent architectural drift&lt;/strong&gt;. The model optimises for what it sees, not for what the global architecture requires.&lt;/p&gt;

&lt;p&gt;Architecture Fitness Tests are the answer. They're not hard to write — Python's &lt;code&gt;ast&lt;/code&gt; module does all the heavy lifting. And the return is immediate: full freedom to use AI on refactoring tasks without anxiety about what might have been broken.&lt;/p&gt;

&lt;p&gt;If you're building a distributed system with AI-assisted development, these tests are the first thing you should write — not the last.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The complete boilerplate (FastAPI + asyncpg + LangGraph + MQTT + pgvector + Prometheus + Grafana) with the 18 tests, the CLAUDE.md, and a 20-section ARCHITECTURE.md is available at &lt;a href="https://murinelo.gumroad.com/l/pdmfvr" rel="noopener noreferrer"&gt;murinelo.gumroad.com/l/pdmfvr&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>architecture</category>
      <category>ai</category>
      <category>fastapi</category>
    </item>
  </channel>
</rss>
