<?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: errrt</title>
    <description>The latest articles on DEV Community by errrt (@errrt).</description>
    <link>https://dev.to/errrt</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4094309%2F1f0aaed8-b2f2-4031-9438-e0ab655f3a8e.jpg</url>
      <title>DEV Community: errrt</title>
      <link>https://dev.to/errrt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/errrt"/>
    <language>en</language>
    <item>
      <title>Why CLAUDE.md Is Not Enough to Protect Your Architecture</title>
      <dc:creator>errrt</dc:creator>
      <pubDate>Tue, 25 Aug 2026 13:59:10 +0000</pubDate>
      <link>https://dev.to/errrt/why-claudemd-is-not-enough-to-protect-your-architecture-2bl5</link>
      <guid>https://dev.to/errrt/why-claudemd-is-not-enough-to-protect-your-architecture-2bl5</guid>
      <description>&lt;p&gt;Files such as &lt;code&gt;CLAUDE.md&lt;/code&gt; and &lt;code&gt;AGENTS.md&lt;/code&gt; are useful for explaining a project to coding agents.&lt;/p&gt;

&lt;p&gt;They can describe decisions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Supabase Auth instead of Firebase&lt;/li&gt;
&lt;li&gt;UI components must not access the database directly&lt;/li&gt;
&lt;li&gt;Authentication code requires manual review&lt;/li&gt;
&lt;li&gt;Keep dependencies pointing inward&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem is that these are still instructions, not enforcement.&lt;/p&gt;

&lt;p&gt;An agent can understand a rule and still violate it during a large change. The generated code may compile and pass its tests while quietly introducing an architectural dependency that the project was supposed to avoid.&lt;/p&gt;

&lt;p&gt;Humans do this too. The problem is not specifically AI-generated code. AI agents simply make it easier to produce larger changes faster, which makes architectural drift easier to miss.&lt;/p&gt;

&lt;h2&gt;
  
  
  From instructions to executable rules
&lt;/h2&gt;

&lt;p&gt;I wanted the most important architecture decisions to behave more like lint rules.&lt;/p&gt;

&lt;p&gt;That led me to build &lt;a href="https://github.com/errrt/archlint" rel="noopener noreferrer"&gt;ArchLint&lt;/a&gt;, a small open-source CLI that checks Git changes against architecture rules stored in the repository.&lt;/p&gt;

&lt;p&gt;The basic flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI agent or developer changes code
                  ↓
               Git diff
                  ↓
               ArchLint
                  ↓
           PASS or BLOCKED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ArchLint does not need to know whether the change came from Claude Code, Codex, Cursor, or a human contributor. It evaluates the resulting Git diff.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small example
&lt;/h2&gt;

&lt;p&gt;A project can define its rules in &lt;code&gt;.archlint.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no-firebase&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;forbidden_dependency&lt;/span&gt;
    &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;firebase&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Use&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Supabase&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Auth&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;only."&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db-boundary&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;import_boundary&lt;/span&gt;
    &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src/components/**"&lt;/span&gt;
    &lt;span class="na"&gt;deny&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src/db/**"&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UI&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;components&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;must&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;not&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;access&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;database&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;directly."&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;protect-auth&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;protected_path&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src/auth/**"&lt;/span&gt;
    &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;warning&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These rules express three different decisions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A forbidden package must not be introduced.&lt;/li&gt;
&lt;li&gt;UI components must not import database code.&lt;/li&gt;
&lt;li&gt;Changes to authentication code should be highlighted for review.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The configuration lives beside the code, so it can be reviewed and versioned like any other architectural decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking a change locally
&lt;/h2&gt;

&lt;p&gt;From the root of a Git repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx archlint-ai init
npx archlint-ai check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first command creates a starter configuration. The second checks staged, unstaged, and untracked changes.&lt;/p&gt;

&lt;p&gt;No account, API key, dashboard, or global installation is required.&lt;/p&gt;

&lt;p&gt;A successful check looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✓ 3 rules passed

NO ARCHITECTURE DRIFT DETECTED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a rule is broken, ArchLint reports the rule, file, evidence, and message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[no-firebase]

src/auth/firebase.ts:1

Use Supabase Auth only.

Evidence: firebase
Severity: ERROR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Blocking architectural drift in pull requests
&lt;/h2&gt;

&lt;p&gt;The same check can run in GitHub Actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ArchLint&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;archlint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx archlint-ai check --base origin/${{ github.event.repository.default_branch }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a pull request introduces an error-level violation, the command exits with a non-zero status and the check fails.&lt;/p&gt;

&lt;p&gt;At that point, the team has two choices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fix the implementation so it follows the existing architecture.&lt;/li&gt;
&lt;li&gt;Change &lt;code&gt;.archlint.yml&lt;/code&gt; because the architecture decision itself has intentionally changed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second option is important. Architecture rules should not be permanent by accident. They should be explicit and reviewable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why inspect the Git diff?
&lt;/h2&gt;

&lt;p&gt;ArchLint primarily checks additions in the current diff rather than scanning the entire repository.&lt;/p&gt;

&lt;p&gt;This has two practical benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A team can adopt it without fixing every existing architectural problem first.&lt;/li&gt;
&lt;li&gt;Pull requests are evaluated based on the new drift they introduce.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also keeps the tool focused on one question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Did this change make the architecture worse?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why deterministic checks first?
&lt;/h2&gt;

&lt;p&gt;An LLM could review a diff and decide whether it violates an architectural principle. That may become useful for rules that cannot be expressed structurally.&lt;/p&gt;

&lt;p&gt;However, many important constraints do not require an LLM:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do not add this dependency.&lt;/li&gt;
&lt;li&gt;Do not import this layer from that layer.&lt;/li&gt;
&lt;li&gt;Flag every change under this path.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For these rules, deterministic checks are faster, cheaper, easier to understand, and produce repeatable results.&lt;/p&gt;

&lt;p&gt;ArchLint v0.1 therefore focuses on deterministic rules. It defines an experimental provider-neutral interface for future semantic checks, but the current package does not send repository code to an LLM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current limitations
&lt;/h2&gt;

&lt;p&gt;ArchLint is still an early release.&lt;/p&gt;

&lt;p&gt;The current dependency and import checks focus on JavaScript and TypeScript syntax. Import-boundary rules are path-based, and the project does not yet attempt to understand every framework or programming language.&lt;/p&gt;

&lt;p&gt;That is intentional. I wanted to start with a small tool that solves a clear problem before expanding the rule system.&lt;/p&gt;

&lt;p&gt;The next rules should be driven by real projects rather than guesses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture instructions still matter
&lt;/h2&gt;

&lt;p&gt;ArchLint is not intended to replace &lt;code&gt;CLAUDE.md&lt;/code&gt;, &lt;code&gt;AGENTS.md&lt;/code&gt;, architecture decision records, or code review.&lt;/p&gt;

&lt;p&gt;Those documents explain the reasoning and help agents make better choices.&lt;/p&gt;

&lt;p&gt;ArchLint handles the smaller set of decisions that are important enough to enforce automatically.&lt;/p&gt;

&lt;p&gt;A useful separation is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Documentation explains the architecture.
ArchLint protects its critical boundaries.
Code review handles context and judgment.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project is available on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/errrt/archlint" rel="noopener noreferrer"&gt;https://github.com/errrt/archlint&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It can be tried with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx archlint-ai init
npx archlint-ai check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I would especially like feedback from developers using coding agents on real repositories:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which architecture rule would you want to enforce first?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>opensource</category>
      <category>devtools</category>
    </item>
  </channel>
</rss>
