惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Y
Y Combinator Blog
Blog — PlanetScale
Blog — PlanetScale
H
Hackread – Cybersecurity News, Data Breaches, AI and More
WordPress大学
WordPress大学
B
Blog RSS Feed
A
About on SuperTechFans
V
Visual Studio Blog
J
Java Code Geeks
U
Unit 42
人人都是产品经理
人人都是产品经理
Martin Fowler
Martin Fowler
Recent Announcements
Recent Announcements
M
MIT News - Artificial intelligence
IT之家
IT之家
The Cloudflare Blog
T
The Blog of Author Tim Ferriss
C
Check Point Blog
博客园 - 叶小钗
P
Proofpoint News Feed
Microsoft Security Blog
Microsoft Security Blog
B
Blog
S
SegmentFault 最新的问题
Microsoft Azure Blog
Microsoft Azure Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
54 ADRs in 35 days: why I write the decision before the f...
Michel Faure · 2026-05-09 · via DEV Community

Comic strip 4 panels — Panel 1

The file that opens before the first commit

April 30, 2026, late afternoon. Rembrandt's /admin page has just betrayed me for the third time in two days. Blandine, an administrator, can't see the Attendance tab even though the "Attendance" checkbox is ticked in the rights grid she manages herself. Thirty minutes of digging earlier, I'd understood: the checkbox writes to dashboard_cards (display preference) and never touches can_access_emargement (the actual permission flag). Two semantic layers stacked in the same grid, treated as one.

Niran is in the room, hood loose at his back, a folded burger wrapper on the corner of his laptop. He's working on a financial analysis for Antoine, not watching my screen. I'm about to open app/admin/page.tsx and patch. Instead, I create an empty file: docs/adr/0040-admin-rbac-vs-dashboard-cards.md. Not a single line of code touched. A Markdown page.

This inversion of order — the ADR before the first commit, never after — is the single discipline that holds Rembrandt's architecture together. I want to describe why.

The retroactive ADR is a lie

The Architecture Decision Record, in the short form I keep, has four sections: chosen decision, rejected alternatives with their reason for rejection, positive and negative consequences, references. One page. The trap is to write it after the fact. Code is already pushed, the decision was made by reflex, the ADR documents what happened and gives it retrospective coherence. Anything that doesn't fit the final decision is smoothed over, rejected alternatives are written knowing they'll be rejected, negative consequences are cautious because we're already living them.

It's a polite lie. Worse, it's a useful one: it reassures the future reader who thinks they're reading a tested decision when they're reading the rationalization of a first reflex.

With Claude Code, the risk multiplies. I describe a problem, the agent returns a coherent architecture in minutes, I accept it because it stands up. I haven't tested the alternative. I haven't even noticed there was one. The retroactive ADR ratifies that non-choice. The pre-written ADR forbids it, because alternatives must be written before you know which one will win.

What the form forces

Here's the skeleton of 0040-admin-rbac-vs-dashboard-cards.md as it sits in production:

<!-- docs/adr/0040-admin-rbac-vs-dashboard-cards.md -->
# ADR-0040 — /admin page: RBAC / dashboard_cards / role separation
- Date: 2026-04-30
- Status: Accepted
- Decider: PBR

## Context
[three distinct semantic layers, Blandine incident, ~30 min diagnosis]

## Decision
1. Invariant `dashboard_cards ⊆ accessibleSlugs` (server validation)
2. Split UI: 3 ordered sections
3. Server action `toggleUserFlag` with strict whitelist
4. Fix `listUserRoles` (explicit SELECT)

## Rejected alternatives
1. Cosmetic patch (relabel) — doesn't address root cause
2. Merge dashboard_cards into RBAC — kills granularity
3. Postgres trigger as DB safeguard — TS↔SQL duplication cost too high

## Consequences
[positive, negative, deliberately deferred]

Enter fullscreen mode Exit fullscreen mode

The section that does all the work is Rejected alternatives. Three alternatives, each with a short technical reason for rejection. The cosmetic patch is rejected because it doesn't address the root cause. The merge is rejected because it removes granularity. The DB trigger is deferred because the matrix lives in TypeScript, and duplicating it in SQL creates its own drift. None of those sentences came naturally from the agent. Each came from a question I posed, in writing, inside the ADR: "what if we did this? — why not?". The written answer eliminated the option. The form did its work.

The Claude Code workflow

Three specific disciplines, without which the ADR is worse than nothing.

Phase 0 — grep existing ADRs. Before opening 0040, I grep dashboard_cards and permissions across docs/adr/. I find ADR-0014 (systematic RLS) which sets the RBAC scene and ADR-0017 (inscription source of truth) which spares me from reinventing a pattern. Without that grep, I'd have duplicated reasoning already done. Claude Code would have regenerated a local coherence, blind to sibling decisions.

Challenger agent on structurally significant changes. For ADR-0044 (DB ↔ code contract tests, shipped J1 on May 2nd), the challenger agent raised six objections, classified blocking / important / cosmetic. The "Response to challenger" section is part of the final ADR — it's the audit trail of adversarial pre-engagement. A decision that hasn't been attacked hasn't been tested. A decision that's been attacked and held up gets recorded.

Demand a technical reason for rejection. Alternatives discarded without a reason are a trap. "Too complex" rejects nothing. "Duplication cost between TS and SQL too high for marginal benefit vs app-side double validation" rejects something. I reread that section and anything that looks like an unstated intuition goes back as a question.

The rule

Before any project touching more than two files or changing a business invariant, open the ADR file. No code until the four sections hold together. The ADR can be incomplete, wrong, in need of revision — it exists. The next commit can contradict it. But it exists before, and that anteriority is what filters out decisions of convenience.

When the ADR is written after, it served no purpose. Better not to write it than to lie.

Coda

Fifty-four ADRs in thirty-five effective working days. Fifty-one published in the repo, three fiscal ones kept private (00110013). Roughly one and a half per day, marginal cost near zero with Claude Code. That cadence is not a trophy — it's a sign that the ADR format, hammered for twenty years inside heavy teams, becomes a daily instrument once writing is free.

Niran has finished his analysis. He shuts the laptop, folds the wrapper, says "I'm out" on his way past. The 0040 file is done. The code isn't written. The layer can be laid.


Companion code: rembrandt-samples/writing-adrs-claude-code/ — Rembrandt ADR template + Phase 0 grep checklist + adr-grep.sh script, MIT license.