<?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: Archrad</title>
    <description>The latest articles on DEV Community by Archrad (@archrad_architect).</description>
    <link>https://dev.to/archrad_architect</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%2F3846119%2F7a68e8b5-6407-467a-a5a8-30a672cd85b5.png</url>
      <title>DEV Community: Archrad</title>
      <link>https://dev.to/archrad_architect</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/archrad_architect"/>
    <language>en</language>
    <item>
      <title>ArchRad 0.3.0 — Two commands to find architecture violations in your existing stack</title>
      <dc:creator>Archrad</dc:creator>
      <pubDate>Fri, 17 Apr 2026 00:18:34 +0000</pubDate>
      <link>https://dev.to/archrad_architect/archrad-030-two-commands-to-find-architecture-violations-in-your-existing-stack-2eed</link>
      <guid>https://dev.to/archrad_architect/archrad-030-two-commands-to-find-architecture-violations-in-your-existing-stack-2eed</guid>
      <description>&lt;p&gt;All our early release of ArchRad has assumed one thing: that you already have an IR graph of your system to validate. You needed to author JSON by hand, learn the schema, map your services yourself before you could see a single violation.&lt;br&gt;
Based on the feedback, we have addressed that issue with release 0.3.0.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The cold start problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The feedback we kept hearing — from developers who found ArchRad on npm, from platform engineers who tried it in CI — was always the same: &lt;em&gt;the first step is too hard&lt;/em&gt;. Before you could run &lt;code&gt;archrad validate&lt;/code&gt;, you had to write an IR graph. Before you could write an IR graph, you had to understand the schema. Most people never got that far.&lt;/p&gt;

&lt;p&gt;That's the cold start problem. And &lt;code&gt;archrad init&lt;/code&gt; is the fix.&lt;/p&gt;


&lt;h2&gt;
  
  
  archrad init — zero hand-authored JSON
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;archrad init&lt;/code&gt; generates a valid IR graph from artifacts you already have. Starting with Docker Compose.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archrad init &lt;span class="nt"&gt;--from&lt;/span&gt; docker-compose.yml &lt;span class="nt"&gt;--output&lt;/span&gt; graph.json
archrad validate &lt;span class="nt"&gt;--ir&lt;/span&gt; graph.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two commands. No JSON authoring. No schema learning. If you have a &lt;code&gt;docker-compose.yml&lt;/code&gt;, you can see your architecture violations in under 60 seconds.&lt;/p&gt;

&lt;p&gt;Every service in your Compose file becomes a node in the IR graph. The node type is inferred from the image:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Image pattern&lt;/th&gt;
&lt;th&gt;node.type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;postgres, mysql, mariadb, timescale, cockroach&lt;/td&gt;
&lt;td&gt;&lt;code&gt;postgres&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;redis, memcached&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cache&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rabbitmq, kafka, nats, pulsar&lt;/td&gt;
&lt;td&gt;&lt;code&gt;queue&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nginx, traefik, caddy, kong, envoy&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gateway&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;elasticsearch, opensearch&lt;/td&gt;
&lt;td&gt;&lt;code&gt;search&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;minio, localstack&lt;/td&gt;
&lt;td&gt;&lt;code&gt;storage&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;unknown image + published ports&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;gateway&lt;/code&gt; (HTTP-facing fallback)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;unknown image, no ports&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;service&lt;/code&gt; (with warning)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;depends_on → edges&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Both list and object forms are supported. Each dependency becomes a directed edge in the IR graph.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connection URL detection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where it gets interesting. If a service has a &lt;code&gt;DATABASE_URL&lt;/code&gt;, &lt;code&gt;REDIS_URL&lt;/code&gt;, &lt;code&gt;AMQP_URL&lt;/code&gt;, &lt;code&gt;MONGODB_URI&lt;/code&gt;, or any of eight other connection URL environment variables, and the hostname in that URL matches another service in the compose file — &lt;code&gt;archrad init&lt;/code&gt; creates an edge.&lt;/p&gt;

&lt;p&gt;This is how it catches direct database access automatically. You didn't have to tell it. It found it in your existing configuration.&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-company/api:latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://postgres:5432/mydb&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:15&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running &lt;code&gt;archrad init&lt;/code&gt; on this file creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two nodes: &lt;code&gt;api&lt;/code&gt; (service), &lt;code&gt;postgres&lt;/code&gt; (postgres type)&lt;/li&gt;
&lt;li&gt;Two edges: &lt;code&gt;api → postgres&lt;/code&gt; from depends_on, and &lt;code&gt;api → postgres&lt;/code&gt; from DATABASE_URL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Running &lt;code&gt;archrad validate&lt;/code&gt; on the resulting IR fires &lt;code&gt;IR-LINT-DIRECT-DB-001&lt;/code&gt;. The API is calling the database directly. That is a finding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verbose mode&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;--verbose&lt;/code&gt; to see exactly what was inferred:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;archrad init: mapping:
  api            service   (image: my-company/api:latest)
  postgres       postgres  (image: postgres:15)
  redis          cache     (image: redis:7-alpine)
  → api → postgres  edge  (depends_on)
  → api → postgres  edge  (DATABASE_URL)
  → api → redis    edge  (depends_on)
archrad init: summary: 3 nodes, 3 edges, 0 warning(s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Everything else that shipped in 0.3.0
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;archrad yaml-to-ir — now accepts HTTPS URLs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--yaml&lt;/code&gt; now accepts a GitHub raw URL or any HTTPS URL, with optional &lt;code&gt;-H&lt;/code&gt; for authenticated fetches. Validate a blueprint hosted in your GitHub repo without downloading it first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archrad yaml-to-ir &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--yaml&lt;/span&gt; https://raw.githubusercontent.com/your-org/blueprints/main/api-gateway.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--out&lt;/span&gt; graph.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The lint rules that matter for platform engineers
&lt;/h2&gt;

&lt;p&gt;ArchRad ships 11 deterministic lint rules. The ones that fire most often in real-world systems:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IR-LINT-DIRECT-DB-001&lt;/strong&gt; — A service is calling a database directly, bypassing the service layer. This is the rule that fires when &lt;code&gt;DATABASE_URL&lt;/code&gt; in your compose file points straight to postgres with no service in between.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IR-LINT-MISSING-AUTH-010&lt;/strong&gt; — An HTTP entry point has no auth boundary. No auth node, no middleware, no &lt;code&gt;config.authRequired: false&lt;/code&gt; to document the intentional decision. This fires on every unauthenticated endpoint in the Petstore.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IR-LINT-MULTIPLE-HTTP-ENTRIES-009&lt;/strong&gt; — Multiple HTTP entry points with no gateway in front. Common in microservice-first architectures before a BFF or API gateway is introduced.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IR-LINT-NO-HEALTHCHECK-003&lt;/strong&gt; — No health or readiness endpoint defined. Kubernetes, ECS, and any load balancer needs this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IR-LINT-DEAD-NODE-011&lt;/strong&gt; — A node with no inbound or outbound edges. Orphaned service, probably left over from a previous topology.&lt;/p&gt;

&lt;p&gt;All rules are deterministic. No LLM in the validation loop. Every finding has a fix suggestion and a compliance impact statement.&lt;/p&gt;




&lt;h2&gt;
  
  
  CI integration
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;archrad validate&lt;/code&gt; exits 0 or 1. Drop it in any pipeline:&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="c1"&gt;# GitHub Actions&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Validate architecture&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;archrad init --from docker-compose.yml --output graph.json&lt;/span&gt;
    &lt;span class="s"&gt;archrad validate --ir graph.json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @archrad/deterministic
archrad init &lt;span class="nt"&gt;--from&lt;/span&gt; docker-compose.yml
archrad validate &lt;span class="nt"&gt;--ir&lt;/span&gt; archrad-graph.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apache-2.0. No telemetry. IR graphs never leave your machine.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/archradhq" rel="noopener noreferrer"&gt;github.com/archradhq&lt;/a&gt;&lt;br&gt;
npm: &lt;a href="https://npmjs.com/package/@archrad/deterministic" rel="noopener noreferrer"&gt;@archrad/deterministic&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you run it against your stack and find something interesting — or find a false positive — open an issue or drop a comment below. Every piece of feedback shapes 0.4.0.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>devops</category>
      <category>opensource</category>
      <category>platformengineering</category>
    </item>
    <item>
      <title>Detecting architecture drift during system design</title>
      <dc:creator>Archrad</dc:creator>
      <pubDate>Sat, 11 Apr 2026 02:43:57 +0000</pubDate>
      <link>https://dev.to/archrad_architect/detecting-architecture-drift-3pie</link>
      <guid>https://dev.to/archrad_architect/detecting-architecture-drift-3pie</guid>
      <description>&lt;p&gt;A team ships a feature. Weeks later, a security flaw surfaces. Not a bug in the code — a flaw in the architecture. The API gateway talks directly to the database. No auth boundary. A compliance gap that was baked in from day one.&lt;br&gt;
The fix takes months. Not because the code is hard to change. Because the architecture is.&lt;br&gt;
The real problem? Nobody caught it at design time. The architecture decision was made, the code was written, the system was deployed. By the time anyone looked at it through a compliance lens, it was too late to be cheap.&lt;br&gt;
AI coding agents make this worse. Cursor and Copilot write code faster than ever. But they have no idea what your architecture rules are. They'll generate a service that bypasses your auth layer, connects directly to a database it shouldn't touch, or creates a sync chain that kills your P99 latency. It passes tests. It ships.&lt;br&gt;
I built ArchRad to move architecture governance to where it should have been all along — design time.&lt;/p&gt;

&lt;p&gt;What it does&lt;br&gt;
ArchRad is a deterministic engine that validates your system architecture before you write code. You describe your architecture as an IR graph. ArchRad validates it against a set of lint rules — structural issues, missing auth, direct DB access, sync chain depth, isolated nodes — and blocks on violations.&lt;br&gt;
It ships as an MCP server. So Cursor, Copilot, and Claude Desktop can call it mid-session.&lt;br&gt;
I tested it cold this week. No system prompt. No hand-holding. Just a 6-node IR graph and a fresh Claude Desktop session.&lt;br&gt;
Here's what fired:&lt;br&gt;
⚠ IR-LINT-DIRECT-DB-ACCESS-002&lt;br&gt;
  api-gateway connects directly to user-db&lt;br&gt;
  Fix: introduce a service layer&lt;/p&gt;

&lt;p&gt;⚠ IR-LINT-MISSING-AUTH-010&lt;br&gt;
  api-gateway has no auth coverage&lt;br&gt;
  Fix: add auth node or set config.authRequired: true&lt;/p&gt;

&lt;p&gt;⚠ IR-LINT-ISOLATED-NODE-005&lt;br&gt;
  orphaned-analytics has no edges&lt;br&gt;
  Fix: remove or connect the node&lt;/p&gt;

&lt;p&gt;⚠ IR-LINT-NO-HEALTHCHECK-003&lt;br&gt;
  No HTTP node exposes /health or /healthz&lt;br&gt;
  Fix: add a health route&lt;br&gt;
Four violations. Six nodes. Caught before a single line of code was written.&lt;/p&gt;

&lt;p&gt;How to install&lt;br&gt;
npm install -g @archrad/deterministic&lt;br&gt;
Add to Claude Desktop config:&lt;br&gt;
json{&lt;br&gt;
  "mcpServers": {&lt;br&gt;
    "archrad": {&lt;br&gt;
      "command": "npx",&lt;br&gt;
      "args": ["-y", "@archrad/deterministic", "mcp"]&lt;br&gt;
    }&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
Restart Claude Desktop. Ask it to validate an IR graph. It calls archrad_validate_ir first try.&lt;/p&gt;

&lt;p&gt;CI integration&lt;br&gt;
Works on any platform. One command:&lt;br&gt;
npx @archrad/deterministic validate \&lt;br&gt;
  --ir ./architecture/ir.json \&lt;br&gt;
  --fail-on-warning&lt;br&gt;
Bitbucket, GitLab, Jenkins, Azure DevOps — all the same shell command. Exit 1 on violations. PR blocked.&lt;/p&gt;

&lt;p&gt;What's next&lt;br&gt;
The next step is ArchLora — a fine-tuned model that generates IR from plain English descriptions. Describe your system in plain English, get a validated IR back. Right now we support OpenAPI ingestion. Terraform and multi-repo ingestion are on the roadmap.&lt;br&gt;
The OSS engine is Apache 2.0. Free. No telemetry.&lt;br&gt;
GitHub: &lt;a href="https://github.com/archradhq/arch-deterministic" rel="noopener noreferrer"&gt;https://github.com/archradhq/arch-deterministic&lt;/a&gt;&lt;br&gt;
npm: &lt;a href="https://www.npmjs.com/package/@archrad/deterministic" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@archrad/deterministic&lt;/a&gt;&lt;br&gt;
If you've hit the same problem — security or compliance issues found late because architecture governance wasn't part of the design process — I'd like to hear about it&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>opensource</category>
      <category>ai</category>
      <category>enterprise</category>
    </item>
    <item>
      <title>Your architecture drifts before you write a single line of code</title>
      <dc:creator>Archrad</dc:creator>
      <pubDate>Wed, 08 Apr 2026 00:02:27 +0000</pubDate>
      <link>https://dev.to/archrad_architect/your-architecture-drifts-before-you-write-a-single-line-of-code-1cmj</link>
      <guid>https://dev.to/archrad_architect/your-architecture-drifts-before-you-write-a-single-line-of-code-1cmj</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;v0.1.5&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You have an architecture decision record. A Confluence page. Maybe a Miro board with boxes and arrows that everyone agreed on in the last design review.&lt;/p&gt;

&lt;p&gt;Then a sprint happens.&lt;/p&gt;

&lt;p&gt;A service that was never supposed to touch the database directly now has a &lt;code&gt;db.query()&lt;/code&gt; call buried in a helper. A dead node that was deprecated three months ago is still receiving traffic. Nobody noticed. The CI pipeline passed. The linter was happy. The tests are green.&lt;/p&gt;

&lt;p&gt;The architecture, however, is already wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  The gap that no tool fills
&lt;/h2&gt;

&lt;p&gt;We lint code. We type-check code. We test code. But we have never had a way to formally define an architecture and then &lt;em&gt;enforce&lt;/em&gt; it — continuously, deterministically, before a PR is merged.&lt;/p&gt;

&lt;p&gt;Code linters catch bad syntax. Architecture linters should catch bad structure. The two aren't the same problem, and a code linter cannot solve the architecture one.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Think of it this way: ESLint is to code what ArchRAD is to architecture blueprints. One enforces style and correctness at the expression level. The other enforces intent at the system level.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What ArchRAD does
&lt;/h2&gt;

&lt;p&gt;ArchRAD is a blueprint compiler and governance layer. You define your architecture as a formal Intermediate Representation — nodes, edges, metadata, allowed connections — and ArchRAD validates it against a deterministic rule engine.&lt;/p&gt;

&lt;p&gt;Two rules that ship out of the box:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;IR-LINT-MISSING-AUTH-010&lt;/code&gt; — flags any service edge missing an authentication boundary.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;IR-LINT-DEAD-NODE-011&lt;/code&gt; — flags any node with no inbound or outbound connections.&lt;/p&gt;

&lt;p&gt;Cold-start from an existing OpenAPI spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @archrad/deterministic ingest openapi ./openapi.yaml
npx @archrad/deterministic validate &lt;span class="nt"&gt;--ir&lt;/span&gt; ./graph.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Detect drift between your blueprint and generated code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @archrad/deterministic validate-drift &lt;span class="nt"&gt;--ir&lt;/span&gt; ./graph.json &lt;span class="nt"&gt;--target&lt;/span&gt; python &lt;span class="nt"&gt;--out&lt;/span&gt; ./out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the IR says service A cannot talk directly to the database, and the generated code does exactly that — ArchRAD tells you. Before it ships.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why deterministic matters
&lt;/h2&gt;

&lt;p&gt;Every other architecture tool gives you opinions. ArchRAD gives you constraints.&lt;/p&gt;

&lt;p&gt;The rule engine is graph-based and deterministic. The same IR, the same rules, the same inputs will always produce the same findings. No LLM guessing. No probabilistic output. This matters especially as AI coding agents become part of the workflow — agents need hard constraints, not soft suggestions.&lt;/p&gt;




&lt;h2&gt;
  
  
  MCP server — architecture governance inside your agent
&lt;/h2&gt;

&lt;p&gt;0.1.5 ships &lt;code&gt;archrad-mcp&lt;/code&gt; alongside the CLI. One install, two binaries. Add this to your Cursor or Claude Desktop config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"archrad"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"archrad-mcp"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your agent can now call six tools against the same deterministic engine your CI uses — &lt;code&gt;archrad_validate_ir&lt;/code&gt;, &lt;code&gt;archrad_lint_summary&lt;/code&gt;, &lt;code&gt;archrad_validate_drift&lt;/code&gt;, &lt;code&gt;archrad_suggest_fix&lt;/code&gt;, and more. When the agent proposes connecting service A directly to the database, ArchRAD returns &lt;code&gt;IR-LINT-DIRECT-DB-ACCESS-002&lt;/code&gt; — before the code is written, not after the PR is opened.&lt;/p&gt;

&lt;p&gt;Static remediation guidance ships for every built-in rule code. No generative output — &lt;code&gt;archrad_suggest_fix&lt;/code&gt; returns curated, deterministic text for each finding. 127 tests cover the guidance corpus.&lt;/p&gt;




&lt;h2&gt;
  
  
  CI in ten lines
&lt;/h2&gt;

&lt;p&gt;No separate Action needed — the CLI runs directly in any GitHub Actions workflow:&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;architecture drift&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&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;drift&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="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/setup-node@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;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;20'&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;npm ci&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="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;npx archrad validate-drift \&lt;/span&gt;
            &lt;span class="s"&gt;--ir ./graph.json \&lt;/span&gt;
            &lt;span class="s"&gt;--target python \&lt;/span&gt;
            &lt;span class="s"&gt;--out ./golden-export \&lt;/span&gt;
            &lt;span class="s"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PRs fail when generated code drifts from the IR. Add &lt;code&gt;--fail-on-warning&lt;/code&gt; to also gate on lint findings.&lt;/p&gt;




&lt;h2&gt;
  
  
  The OSS core is Apache-2.0
&lt;/h2&gt;

&lt;p&gt;The engine — IR, linter, validator, MCP server — is fully open source under Apache-2.0. No telemetry, no lock-in, works offline.&lt;/p&gt;




&lt;p&gt;Try it on your next architecture review:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;GitHub: &lt;a href="https://github.com/archradhq/arch-deterministic" rel="noopener noreferrer"&gt;github.com/archradhq/arch-deterministic&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Questions, feedback, or drift horror stories — drop them in the comments or open an issue on GitHub.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>opensource</category>
      <category>architecture</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Every CI pipeline checks code. Nobody checks architecture. Here's why that matters.</title>
      <dc:creator>Archrad</dc:creator>
      <pubDate>Sun, 05 Apr 2026 14:50:39 +0000</pubDate>
      <link>https://dev.to/archrad_architect/every-ci-pipeline-checks-code-nobody-checks-architecture-heres-why-that-matters-2274</link>
      <guid>https://dev.to/archrad_architect/every-ci-pipeline-checks-code-nobody-checks-architecture-heres-why-that-matters-2274</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally built this as a side project — sharing what I've learned along the way.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Your CI pipeline probably does a lot.&lt;/p&gt;

&lt;p&gt;It runs unit tests, scans for vulnerabilities, lints your API spec, builds and pushes a container image.&lt;/p&gt;

&lt;p&gt;What it almost certainly &lt;strong&gt;doesn't do&lt;/strong&gt; is check whether your architecture is still what you agreed to build.&lt;/p&gt;

&lt;p&gt;The problem isn’t that teams don’t care.&lt;br&gt;
It’s that architecture lives in docs — and CI can’t read docs.&lt;/p&gt;
&lt;h2&gt;
  
  
  The gap nobody talks about
&lt;/h2&gt;

&lt;p&gt;When a team agrees on a service boundary — no direct database access, auth required on every HTTP entry — that agreement lives in a Confluence doc or a Miro board.&lt;/p&gt;

&lt;p&gt;Nothing in your pipeline can read those. So nothing enforces them.&lt;/p&gt;

&lt;p&gt;A direct DB connection can merge. A missing auth boundary can ship. Not because nobody cared. Because &lt;strong&gt;nothing was gating on it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Code review is the current answer. But code review happens after implementation. The architecture decision has already been made and built. At that point you're reviewing a fait accompli.&lt;/p&gt;
&lt;h2&gt;
  
  
  The post-code tools don't close this gap
&lt;/h2&gt;

&lt;p&gt;ArchUnit, Spectral, Axivion, OPA — all solid tools. All operating on code, specs, or running systems that already exist.&lt;/p&gt;

&lt;p&gt;The intervention point is &lt;strong&gt;after the first commit.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;None of them can catch an architecture violation before a line of code is written.&lt;/p&gt;
&lt;h2&gt;
  
  
  What a pre-code gate looks like
&lt;/h2&gt;

&lt;p&gt;Your architecture is a graph — services are nodes, dependencies are edges. Express it as a machine-readable IR. Run a deterministic compiler on it before code generation begins.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use your existing OpenAPI spec — no IR authoring required&lt;/span&gt;
npx @archrad/deterministic ingest openapi &lt;span class="nt"&gt;--spec&lt;/span&gt; your-api.yaml &lt;span class="nt"&gt;--out&lt;/span&gt; graph.json
npx @archrad/deterministic validate &lt;span class="nt"&gt;--ir&lt;/span&gt; graph.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No security definitions in your spec → &lt;code&gt;IR-LINT-MISSING-AUTH-010&lt;/code&gt; fires automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⚠️  IR-LINT-MISSING-AUTH-010: HTTP entry node "payment-api" has no auth node
    or auth config in its immediate graph neighbourhood
    Fix: Add an auth/middleware node or set config.authRequired: false for
    intentionally public endpoints.
    Impact: Unauthenticated HTTP entry points are a compliance gap in regulated
    environments and a common attack surface.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Direct DB connection in the graph → &lt;code&gt;IR-LINT-DIRECT-DB-ACCESS-002&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⚠️  IR-LINT-DIRECT-DB-ACCESS-002: API node "payment-api" connects directly
    to datastore node "card-db"
    Fix: Introduce a service or domain layer between HTTP handlers and persistence.
    Impact: Harder to test, swap storage, or enforce invariants at a single boundary.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same graph. Same compiler version. Same findings. Every time.&lt;/p&gt;

&lt;p&gt;High level flow&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph IR (JSON/YAML)
        ↓
archrad validate  ← blocks on violations
        ↓
archrad export
        ↓
code + OpenAPI + Docker
        ↓
archrad validate-drift ← blocks on divergence
        ↓
rest of CI (tests, security scans)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Machine-readable JSON output. CI-gateable. Blocks the PR.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limits
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cold start is real.&lt;/strong&gt; You have to get your architecture into graph IR format. OpenAPI ingestion helps for teams that already have specs. IaC ingestion — Terraform, CloudFormation — is roadmap, not shipped.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This doesn't prove semantic correctness.&lt;/strong&gt; Drift detection means "code matches what the IR would generate" — not that your code is correct or matches production behavior. That's your tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No round-trip.&lt;/strong&gt; Once you edit generated code there's no built-in path back to the IR. Think of it as scaffold plus contract validation, not full lifecycle sync.&lt;/p&gt;

&lt;h2&gt;
  
  
  The CI pipeline this enables
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph.json committed to repo
        ↓
archrad validate --fail-on-warning   ← blocks PR if architecture violated
        ↓
archrad validate-drift               ← blocks PR if code drifted from IR
        ↓
Spectral                             ← lint generated OpenAPI spec
        ↓
Snyk / Semgrep                       ← scan generated code
        ↓
Merge → deploy → operate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ArchRad runs first because structural violations should block before other tools waste time scanning code that shouldn't exist yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;OSS under Apache-2.0. No IR authoring needed to get started:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Ingest your existing OpenAPI spec&lt;/span&gt;
npx @archrad/deterministic ingest openapi &lt;span class="nt"&gt;--spec&lt;/span&gt; your-api.yaml &lt;span class="nt"&gt;--out&lt;/span&gt; graph.json
npx @archrad/deterministic validate &lt;span class="nt"&gt;--ir&lt;/span&gt; graph.json

&lt;span class="c"&gt;# Or run against the built-in ecommerce fixture&lt;/span&gt;
npx @archrad/deterministic validate &lt;span class="nt"&gt;--ir&lt;/span&gt; fixtures/ecommerce-with-warnings.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 &lt;a href="https://github.com/archradhq/arch-deterministic" rel="noopener noreferrer"&gt;github.com/archradhq/arch-deterministic&lt;/a&gt;&lt;br&gt;
📦 &lt;code&gt;npm install -g @archrad/deterministic&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Closing question:&lt;/strong&gt; does your CI pipeline gate on architecture today?&lt;/p&gt;

&lt;p&gt;And if not — is that a tooling problem, a process problem, or just accepted as inevitable?&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>opensource</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Architecture as Code isn't enough — here's why it needs a compiler</title>
      <dc:creator>Archrad</dc:creator>
      <pubDate>Sat, 28 Mar 2026 16:05:20 +0000</pubDate>
      <link>https://dev.to/archrad_architect/architecture-as-code-isnt-enough-heres-why-it-needs-a-compiler-3j2a</link>
      <guid>https://dev.to/archrad_architect/architecture-as-code-isnt-enough-heres-why-it-needs-a-compiler-3j2a</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally built this as a side project — sharing what I learned along the way.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Architecture decisions live in Confluence docs and Miro boards. They get reviewed once, approved, and then slowly become fiction as implementation diverges. A cache layer gets added. A direct DB call sneaks in. The agreed service boundary dissolves over three sprints.&lt;/p&gt;

&lt;p&gt;There's no CI step for architecture. There's no compiler that says "this violates what you agreed to build." There's just a diagram that nobody updates.&lt;/p&gt;

&lt;p&gt;I spent a long time thinking this was a discipline problem. Teams just need to be more rigorous, right? But I've come to think it's actually a tooling problem. &lt;strong&gt;Architecture has never been a machine-readable artifact.&lt;/strong&gt; Diagrams are for humans. Docs are for humans. Nothing in your pipeline can read them — so nothing in your pipeline can gate on them.&lt;/p&gt;




&lt;h2&gt;
  
  
  The idea this builds on
&lt;/h2&gt;

&lt;p&gt;Neal Ford and Mark Richards recently published a piece on O'Reilly Radar previewing their upcoming book &lt;em&gt;Architecture as Code&lt;/em&gt;. They describe "fitness functions" — automated governance checks for architectural concerns, like unit tests but for architecture. The tools they reference (ArchUnit, NetArchTest, PyTestArch) are solid implementations of this idea.&lt;/p&gt;

&lt;p&gt;But they all operate on &lt;strong&gt;code that already exists&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What I've been building is the pre-code version of that idea: a deterministic compiler that runs the fitness function on the architecture graph &lt;strong&gt;before any code is written&lt;/strong&gt;. The intervention point is different — and so is the cost of fixing what you find.&lt;/p&gt;




&lt;h2&gt;
  
  
  The insight: architecture needs an IR
&lt;/h2&gt;

&lt;p&gt;In compiler design, an IR (intermediate representation) sits between source code and target output. It's the stable, machine-readable artifact that validation, optimization, and code generation all operate on. Same input, same output, every time.&lt;/p&gt;

&lt;p&gt;What if architecture had the same thing?&lt;/p&gt;

&lt;p&gt;Not a diagram. Not a doc. A structured graph — nodes and edges — that a deterministic compiler can read, validate, and compile to runnable code. Something CI can gate on before a service repo even exists.&lt;/p&gt;

&lt;p&gt;That's what I've been building: &lt;strong&gt;a deterministic compiler for system architecture&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it looks like in practice
&lt;/h2&gt;

&lt;p&gt;You describe your architecture as a graph. Here's a minimal example — a payment API talking directly to a database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"graph"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"nodes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"payment-api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Payment API"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/payments"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user-db"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"database"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"User DB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"engine"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"postgres"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"edges"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"payment-api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user-db"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You run &lt;code&gt;archrad validate&lt;/code&gt; on it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⚠️  IR-LINT-DIRECT-DB-ACCESS-002: API node "payment-api" connects
    directly to datastore node "user-db"
    Fix: Introduce a service or domain layer between HTTP handlers
    and persistence.
    Impact: Harder to test, swap storage, or enforce invariants
    at a single boundary.

⚠️  IR-LINT-NO-HEALTHCHECK-003: No HTTP node exposes a typical
    health/readiness path (/health, /healthz, /live, /ready)
    Fix: Add a GET route such as /health for orchestrators and
    load balancers.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These findings are &lt;strong&gt;deterministic&lt;/strong&gt;. Same graph, same compiler version, same findings — every time. You can run this in GitHub Actions, fail the PR, attach the JSON output to a ticket, and show it to an auditor.&lt;/p&gt;

&lt;p&gt;Now fix the graph — add a service layer node between the API and the database, add a health route — and re-validate. Clean gate. Export unlocks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archrad &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nt"&gt;--ir&lt;/span&gt; graph.json &lt;span class="nt"&gt;--target&lt;/span&gt; python &lt;span class="nt"&gt;--out&lt;/span&gt; ./my-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Out comes a FastAPI project with OpenAPI, Dockerfile, docker-compose, and Makefile. Scaffold tied to a validated IR.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where does the IR come from?
&lt;/h2&gt;

&lt;p&gt;This is the first question everyone asks. Three paths:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Path A — Author YAML directly.&lt;/strong&gt; Lighter than JSON, same result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archrad yaml-to-ir &lt;span class="nt"&gt;--yaml&lt;/span&gt; graph.yaml &lt;span class="nt"&gt;--out&lt;/span&gt; graph.json
archrad validate &lt;span class="nt"&gt;--ir&lt;/span&gt; graph.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Path B — Ingest an existing OpenAPI spec.&lt;/strong&gt; If your team already has one, each operation becomes an HTTP node automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archrad ingest openapi &lt;span class="nt"&gt;--spec&lt;/span&gt; openapi.yaml &lt;span class="nt"&gt;--out&lt;/span&gt; graph.json
archrad validate &lt;span class="nt"&gt;--ir&lt;/span&gt; graph.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Path C — ArchRad Cloud.&lt;/strong&gt; Type a natural language prompt in the browser, the planner converts it to a structured graph, download &lt;code&gt;graph.json&lt;/code&gt;. The OSS compiler takes it from there — no cloud dependency on the blocking path.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;graph.json&lt;/code&gt; commits to your repo like any other source file. From that point everything is local, deterministic, and offline.&lt;/p&gt;




&lt;h2&gt;
  
  
  "But won't the IR file rot just like a Confluence doc?"
&lt;/h2&gt;

&lt;p&gt;This is the right objection. Someone raised it when I posted about this on Reddit. I didn't have a complete answer then — so I shipped one.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;archrad validate-drift&lt;/code&gt; re-exports from the IR in memory and diffs it against what's on disk. If generated code has been modified without updating the IR, CI fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archrad validate-drift &lt;span class="nt"&gt;-i&lt;/span&gt; graph.json &lt;span class="nt"&gt;-t&lt;/span&gt; python &lt;span class="nt"&gt;-o&lt;/span&gt; ./out

&lt;span class="c"&gt;# ❌ DRIFT-MODIFIED: app/main.py&lt;/span&gt;
&lt;span class="c"&gt;#    File differs from deterministic export for this IR&lt;/span&gt;
&lt;span class="c"&gt;# archrad: drift detected — regenerate with `archrad export`&lt;/span&gt;
&lt;span class="c"&gt;#    or align the IR.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The IR becomes the authority. If code diverges from it, you know immediately — in local dev or in CI.&lt;/p&gt;

&lt;p&gt;This doesn't fully solve the deeper problem the commenter raised — deriving IR from IaC or service mesh config is the right long-term answer and it's on the roadmap. But it closes the gap where generated code drifts silently from the IR that produced it.&lt;/p&gt;




&lt;h2&gt;
  
  
  How it fits into a real pipeline
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph.json committed to repo
        ↓
CI: archrad validate --fail-on-warning   ← blocks PR if architecture violated
        ↓
CI: archrad validate-drift               ← blocks PR if code drifted from IR
        ↓
CI: Spectral (lint generated openapi.yaml)
        ↓
CI: Snyk / Semgrep (scan generated code)
        ↓
Merge → deploy → operate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ArchRad runs first because structural violations should block before other tools waste time scanning code that shouldn't exist yet. Findings output as &lt;code&gt;--json&lt;/code&gt; for machine consumption — attach to PR comments, tickets, audit logs.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this does NOT solve (honest scope)
&lt;/h2&gt;

&lt;p&gt;I want to be direct about the limits:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not semantic correctness.&lt;/strong&gt; Drift detection means "code matches what the IR would generate." It doesn't prove the code is correct or matches prod behavior. That's your tests and your runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not full compliance attestation.&lt;/strong&gt; The OSS layer catches structural and architecture heuristics — missing health routes, direct DB access, sync chain depth, high fan-out. Deeper policy (PCI, HIPAA, SOX rule packs) lives in ArchRad Cloud. OSS findings are supporting evidence for internal programs, not a substitute for external attestation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not a round-trip.&lt;/strong&gt; Once you edit generated code, there's no built-in path back to the IR. Think of it as scaffold + contract validation, not full lifecycle sync.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The IR authoring cold start is real.&lt;/strong&gt; Getting from "existing system" to a graph IR is work. The OpenAPI ingestion path helps for HTTP surfaces. IaC ingestion is on the roadmap. Hand-authoring YAML works for greenfield.&lt;/p&gt;




&lt;h2&gt;
  
  
  Can you add your own rules?
&lt;/h2&gt;

&lt;p&gt;Yes — the lint rules are a typed visitor registry. Adding a custom rule is writing one function and appending it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// my-org-rules/require-timeout.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ruleRequireTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ParsedLintGraph&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;IrStructuralFinding&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;findings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IrStructuralFinding&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nodeById&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;findings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ORG-001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;warning&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lint&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Node "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" has no timeout configured`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;nodeId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;fixHint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Add config.timeout to this node.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;findings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// In your pipeline:&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LINT_RULE_REGISTRY&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@archrad/deterministic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;LINT_RULE_REGISTRY&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ruleRequireTimeout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fork or wrap the package, write visitors that return &lt;code&gt;IrStructuralFinding[]&lt;/code&gt;, append to the registry. Same pattern the built-in rules use. Org-specific policy packs with more depth are a Cloud feature — but the extension point is open and documented.&lt;/p&gt;




&lt;h2&gt;
  
  
  The open-core structure
&lt;/h2&gt;

&lt;p&gt;The deterministic engine — validate, architecture lint, export, drift detection — is fully open source under Apache-2.0:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/archradhq/arch-deterministic" rel="noopener noreferrer"&gt;github.com/archradhq/arch-deterministic&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @archrad/deterministic
archrad validate &lt;span class="nt"&gt;--ir&lt;/span&gt; graph.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Want to try it without writing any IR? The repo ships fixtures that hit every lint rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @archrad/deterministic validate &lt;span class="nt"&gt;--ir&lt;/span&gt; fixtures/ecommerce-with-warnings.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ArchRad Cloud adds natural language authoring, org policy packs, compliance frameworks, and AI remediation — built on the same deterministic contract. The OSS compiler is what makes the cloud product's claims auditable. You can verify what the gate does before you buy anything.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why the timing matters
&lt;/h2&gt;

&lt;p&gt;The O'Reilly &lt;em&gt;Architecture as Code&lt;/em&gt; book and the broader "architecture governance" conversation are converging right now. Academic papers on deterministic compilation for structured pipelines are appearing. The industry is waking up to the idea that architecture needs to be machine-readable.&lt;/p&gt;

&lt;p&gt;The gap I'm filling is specific: &lt;strong&gt;the moment between architecture decision and first commit&lt;/strong&gt;. Every existing tool — ArchUnit, Spectral, Snyk, Checkov — operates on artifacts that already exist. This is the only tool I know of that operates before any of those artifacts exist, on the architecture graph itself, with a deterministic gate you can put in CI.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's your current approach?
&lt;/h2&gt;

&lt;p&gt;I'm genuinely curious how other teams handle this. Do you enforce architecture through code review alone? Have you tried fitness functions or architectural tests? What worked, what failed?&lt;/p&gt;

&lt;p&gt;Drop a comment — especially if you have a technical objection. Those tend to become features.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>opensource</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
