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

推荐订阅源

D
DataBreaches.Net
人人都是产品经理
人人都是产品经理
爱范儿
爱范儿
WordPress大学
WordPress大学
T
Tor Project blog
Jina AI
Jina AI
美团技术团队
酷 壳 – CoolShell
酷 壳 – CoolShell
V
V2EX
雷峰网
雷峰网
博客园 - 聂微东
B
Blog RSS Feed
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 【当耐特】
Microsoft Azure Blog
Microsoft Azure Blog
S
SegmentFault 最新的问题
Recent Announcements
Recent Announcements
有赞技术团队
有赞技术团队
GbyAI
GbyAI
aimingoo的专栏
aimingoo的专栏
U
Unit 42
博客园 - Franky
Last Week in AI
Last Week in AI
阮一峰的网络日志
阮一峰的网络日志
Microsoft Security Blog
Microsoft Security Blog
F
Fortinet All Blogs
罗磊的独立博客
云风的 BLOG
云风的 BLOG
G
Google Developers Blog
大猫的无限游戏
大猫的无限游戏
Engineering at Meta
Engineering at Meta
C
Check Point Blog
Martin Fowler
Martin Fowler
The Cloudflare Blog
N
Netflix TechBlog - Medium
小众软件
小众软件
T
Tailwind CSS Blog
T
The Blog of Author Tim Ferriss
月光博客
月光博客
博客园 - 司徒正美
Stack Overflow Blog
Stack Overflow Blog
J
Java Code Geeks
Blog — PlanetScale
Blog — PlanetScale
P
Proofpoint News Feed
H
Help Net Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
The Exploit Database - CXSecurity.com
S
Securelist
I
Intezer
Spread Privacy
Spread Privacy

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 Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
The ten principles: locality, contracts, quarantine
jucelinux · 2026-05-17 · via DEV Community

Sixth and final article in the Grounded Code series. The previous five built the diagnosis and the workflow. This one names the principles that hold the rest together.

I. The setup

We've covered the cost (article 1), the mechanism (article 2), the patterns to drop (article 3), the artifact (article 4), and the workflow (article 5). This article finishes the picture with the principles that hold it all together. Ten of them, in three clusters.

I want to make the framing explicit, because the clusters are doing real work. Each one answers a different question about how code stays useful when the primary reader has changed:

  • Locality answers: where should things be findable?
  • Contracts answers: how does truth get encoded so the agent can trust it?
  • Quarantine answers: where do exceptions live so they don't leak into the rest of the code?

These aren't arbitrary categories. They map directly to the three axes on which re-derivation cost is paid. When the agent re-derives, it's usually because (1) the thing it needs isn't where it expected, (2) the contract it needs to honor isn't visible from the file it's reading, or (3) an exception to a pattern is invisible until it bites. The principles below remove cost on each axis.

I'll walk through all ten, with code where it helps, and flag the two cases where the audit corrected me.


II. Locality (1 to 4)

Where things should be findable, and findable with one glob.

1. Co-locate by feature, not by layer

Every file related to one feature lives in one directory. The agent finds everything about a feature with a single glob.

src/payment/
├── payment.ts              implementation
├── payment.test.ts         tests as specifications
├── payment.spec.md         contract + verification
├── payment.types.ts        public types (if separated)
├── paymentInternals.ts     local helpers
└── paymentValidation.ts    validation if non-trivial

Enter fullscreen mode Exit fullscreen mode

The opposite of this is layered layout: src/controllers/payment.ts, src/services/payment.ts, src/repositories/payment.ts. To extend the payment feature, the agent has to grep three directories, read three files, and reconstruct the feature mentally from fragments.

The only top-level layers that survive are ones the agent should rarely touch: bootstrap/ for unavoidable side effects, lib/ for genuinely cross-cutting utilities, config/, possibly vendor/. Everything else is a feature directory.

2. The triplet per feature: implementation, test, spec

Every feature carries three files with parallel names:

  • <feature>.<ext> is the implementation.
  • <feature>.test.<ext> is the test, with names that read as specifications of behavior, not as descriptions of which function is being tested.
  • <feature>.spec.md is the markdown contract with frontmatter (article 4 covers the format).

The glob src/<feature>/<feature>.* returns all three. The agent never has to guess where any of them live.

The three files reference each other. The spec's verification block names the test commands. The test names match invariants stated in the spec. The implementation's exported names match the public_api field of the spec. When the three drift, that's a regression, and a quick visual diff catches it.

3. Imports use full paths with explicit extensions

// Don't:
import { foo } from '@utils/foo'

// Do:
import { foo } from '../utils/foo.ts'

Enter fullscreen mode Exit fullscreen mode

Path aliases save the human a few keystrokes and cost the agent a resolution hop on every import. The agent reads '../utils/foo.ts' and knows exactly where the file is. It reads '@utils/foo' and has to consult tsconfig.json to resolve the alias before it can navigate.

There's one allowed exception: workspace package boundaries in a monorepo (@org/shared-types). That's not ergonomic sugar; it's the package boundary itself.

4. Files under roughly 300 lines for what the agent will modify

A soft limit. Above 300 lines, the agent reads in slices and loses context between them. Before you reach the limit, split into co-located files with disambiguating suffixes (payment.ts, paymentCore.ts, paymentValidation.ts). All start with payment, which keeps the glob clean and signals that they're internal to the feature.

The audit correction. I initially framed this as a hard rule. The triangulation across the three coding-agent codebases showed pi violating it deliberately: a 3,110-line orchestrator file with an explicit defense in its AGENTS.md. The defense is that this is an orchestrator, it's meant to be read top to bottom, and splitting it would cost more in cross-file inference than the length costs in slicing.

The rule survives as a soft preference for feature files. Orchestrators, generated code, large data tables, exhaustive switches can be longer. Make the choice deliberate, name it in AGENTS.md or CLAUDE.md, and trust the type system to keep the larger file coherent.


III. Contracts (5 to 7)

How invariants get encoded so the agent can trust them without re-derivation.

5. Spec before code (the five-step loop)

For every non-trivial change, the spec gets written or updated first. Article 5 covers the full loop (spec, plan, implement, verify, consolidate). The principle here is the discipline behind it: don't write code until the contract is on disk.

The cost the principle removes is drift. Without a spec, the agent's implicit understanding of what it's building lives only in the prompt history, and the prompt history doesn't survive compaction or session boundaries. With a spec, the agent re-grounds in seconds at any point.

6. Types carry contracts; prose docs do not

Push invariants into the type system. Brand types, discriminated unions, exhaustive switches with never checks. A file that violates a type-encoded invariant fails to compile. A file that violates a prose-encoded invariant passes silently.

// Don't:
/** count must be positive */
function take<T>(items: T[], count: number): T[] { ... }

// Do:
type PositiveInt = number & { readonly __brand: 'PositiveInt' }
const positiveInt = (n: number): PositiveInt => {
  if (n <= 0 || !Number.isInteger(n)) throw new RangeError()
  return n as PositiveInt
}
function take<T>(items: T[], count: PositiveInt): T[] { ... }

Enter fullscreen mode Exit fullscreen mode

The agent reads the type and knows the contract. It doesn't have to remember to read the docstring, and it can't accidentally call take(items, -1).

The same idea applies to state machines (discriminated unions like 'idle' | 'loading' | 'ready' | 'error'), to immutability (readonly everywhere you don't actually mutate), and to exhaustiveness (switch with never checks so adding a case forces the agent to handle it everywhere).

Types are also the bridge to the spec. The public_api field of the spec lists the same names the implementation exports. When they agree, the system is consistent. When they drift, the type checker catches it before the agent does.

7. Comments are the incident-and-rationale channel

A comment exists to prevent regressions or to explain non-obvious decisions. That is the entire job description. Anything else competes with the code's own self-description and creates drift.

Write comments for:

  • A bug or near-bug that motivated a workaround. "This branch was added after incident-2024-08-12; do not remove without re-reading the post-mortem."
  • A counter-intuitive decision. "Lazy require to avoid circular dependency with telemetry.ts."
  • A load-bearing exception to a project rule. The eslint-disable line above a deliberate side effect, with a sentence saying why.
  • A constraint imposed by a dependency that isn't grep-able locally.

Don't write comments for:

  • What the code does. The function name and types do that.
  • Plans for the future ("TODO: refactor this once X ships"). Use the issue tracker.
  • Boilerplate prose ("This method returns a string.").
  • Visual section dividers (// ===== HELPERS =====). The directory layout does that.

The test for whether a comment belongs: if removing it would not confuse a future reader, it shouldn't have been written. Comments are load-bearing only when they encode something the code can't say itself.


IV. Quarantine (8 to 10)

Where exceptions to patterns are contained, so they don't compound across the codebase.

8. Side effects are quarantined and labeled

Top-level side effects (anything that runs at import time and isn't a pure declaration) live in one place: bootstrap/, src/main.ts, or equivalent. A lint rule (no-top-level-side-effects or similar) blocks them everywhere else.

When an exception is genuinely needed, the deviation is explicit:

// eslint-disable-next-line no-top-level-side-effects
// MDM checks must run before module evaluation; see incident-2024-08-12
startMdmRawRead()

Enter fullscreen mode Exit fullscreen mode

The agent respects the disable comment because it's load-bearing and declared. Without the comment, the agent will "clean up" the disable on its next pass, which is exactly the kind of confident wrong fix that quarantine prevents.

The principle scales beyond side effects. Anything you want the agent to find in one place, but never anywhere else, gets the same treatment: a quarantine directory, a lint rule, and explicit exceptions that name themselves.

9. Concrete dependencies; uniform DI only if it's truly uniform

Functions receive what they actually use, not interfaces they could hypothetically use. For real circular dependencies, use a lazy require with a one-line comment:

// Lazy require: telemetry.ts imports state.ts which imports this file
const getTelemetry = () => require('./telemetry.js')

Enter fullscreen mode Exit fullscreen mode

For tests, prefer integration. Mock only what's genuinely external (network, filesystem, clock, randomness). A mock that stands in for first-party code is a maintenance burden that grows with the codebase.

The audit correction. I framed this as "no DI container" early on. The triangulation showed opencode using Effect's Layer pattern across the entire codebase, and it works. The cost is paid once, then amortized. The rule survives as "drop ad-hoc, heterogeneous DI," not as "drop DI."

The deeper principle is the one I named in article 3: heterogeneity is the disease, not abstraction. A codebase with five different DI patterns charges re-derivation cost on every feature. A codebase with one DI pattern applied uniformly everywhere charges it once. If you have DI, make it uniform, or drop it. The version to drop is ad-hoc DI that wraps first-party code without a multi-implementation justification.

10. Build-time feature flags when both branches don't need to coexist

import { feature } from 'bun:bundle'

const adminPanel = feature('ADMIN_PANEL')
  ? require('./adminPanel.ts')
  : null

Enter fullscreen mode Exit fullscreen mode

The false branch is dead-code-eliminated. The agent reads the source and sees exactly what's in the build, with no need to correlate with a runtime flag service.

When flags genuinely need to flip without a rebuild (kill switches, gradual rollouts), isolate them behind a single function like isEnabled(flagName) that the agent can grep in one place. Avoid scattering process.env.X === 'true' checks throughout the code; each one is a different signal that has to be correlated separately.

A note on the audit. Of all the principles, this one diverged most across the three codebases. Claude Code uses Bun's build-time feature(). opencode uses runtime config. pi uses environment-gated branches. There isn't an agent-friendly consensus here, and probably the pattern is dominated by deployment constraints rather than agent ergonomics. I'm including it because the underlying idea (the agent reads what gets shipped) holds even when the implementation varies.


V. What the list doesn't include

A few things you may notice missing.

No dependency injection rule. It's folded into principle 9, with the audit correction.

No folder convention beyond per-feature. I want the agent to discover by globbing the feature directory, not by remembering a folder convention. The fewer rules the agent has to know up front, the cheaper the first read.

No linter or formatter rule. Project-specific. Encode in lint config, not in prose. The agent respects lint rules that block compilation. It does not respect rules that exist only in a style guide nobody enforces.

No "no any" rule. True, but it's a TypeScript-specific application of principle 6. Each language has its own surface.

The principles list deliberately stays in the agnostic core. Stack-specific guidance lives in overlays, which is what the next phase of the series is about.


VI. What's still in motion

The series so far has been honest about scope. I want to keep being honest at the close.

The three codebases I audited are all coding agents. Claude Code, opencode, pi. Some of what converges across them may be specific to that subculture. The principles that handle agent reading mechanics (locality, contracts, quarantine of side effects) probably travel. The principles around feature granularity and uniform DI may need adjustment for general-purpose application backends and frontends. The overlays will be where that adjustment lives.

Three things I was wrong about. Article 3 named them, and I'm repeating the headlines here because they're load-bearing:

  1. The 300-line file limit isn't a hard rule. It's a soft preference for feature files. pi proved this.
  2. "No DI" isn't right either. Heterogeneous DI is the problem; uniform DI is fine. opencode proved this.
  3. Build-time feature flags diverged across all three codebases. There's no agent-friendly consensus, and I'm holding the principle as provisional.

What I expect to be wrong about going forward. Specific predictions, so I can be tested on them:

  • The triplet-per-feature rule will need adjustment for serverless, where a feature is often a single function with no separate test file in the traditional sense.
  • The 300-line soft limit will probably need to grow as model context windows enable longer effective slices. Whether that means 500 or 1,000 is empirical.
  • Some principle I currently believe will turn out to be specific to TypeScript codebases and not survive the move to Rust, Python, or Go.

If you spot any of these or others I haven't seen, that's the kind of feedback the series is asking for. The thesis improves when it gets falsified in public.


VII. What's next

The agnostic core is done. The series so far covered:

  1. The cost (the manifesto, with the 7.5x receipt).
  2. The mechanism (how agents read).
  3. The patterns to unlearn (anti-patterns from the human era).
  4. The central artifact (<feature>.spec.md and out_of_scope).
  5. The workflow (the five-step loop).
  6. The principles (this one).

The next phase is overlays: short stack-specific documents that answer the three questions the agnostic core deliberately leaves open. What counts as a feature in this stack? Where do irreducibly cross-cutting concerns live? How does the deploy unit align with the feature unit?

Overlays for React, backend TypeScript, serverless, and fullstack are in draft. I'm holding them until the agnostic core has had enough public exposure to surface what's wrong with it. There's no point shipping overlays on top of principles that might still shift.

If you want to apply any of this before the overlays land, the five-minute experiment from the manifesto is still the cheapest way in. Pick a feature, co-locate its files, write the spec, run the agent twice (once on the old layout, once on the new), and compare the token receipts. The number you get back is the argument the series doesn't have to make.


VIII. The close

Six articles, written across a stretch of months, with the framing shifting more than once as the audit corrected my early intuitions. The version of Grounded Code I'd defend now is narrower than the version I'd have published if the audit hadn't happened, and the narrower version is the one I trust.

A few things I want to leave you with.

The principles aren't commandments. They're observations about what reduces re-derivation cost on the three coding-agent codebases I studied. If a principle here costs you more than it pays back in your context, drop it. The framework survives the loss of any individual principle. What it doesn't survive is forgetting the central question: when the agent extends feature X, can it find everything with one glob, read it in a small number of files, and trust the types plus the spec for the contract?

The architecture is for an agent. The code is still for you. Everything in this series adds a layer that makes agents fast and accurate. None of it subtracts from human readability. Where the two trade off, the cost to the human is small (slightly more boilerplate, slightly less clever abstraction) and the gain to the agent is large (one grep instead of three, one file instead of seven). If you ever feel the framework pushing you toward code that's worse for humans, push back. Something is being misapplied.

This is a working name. I've been calling the approach Grounded Code throughout the series because the central image (each feature carrying its own ground in the form of a spec) keeps holding. If a better name surfaces, I'll switch. The point isn't the brand. The point is the question.

If you've made it through all six articles, thank you for the time. The series exists because the question seems worth working on in public, and the conversation it generates is the only way I'll know which parts hold and which I've gotten wrong. Send pushback. Send falsifications. Send the experiment results from your own codebase. The thesis gets better when it gets tested by people who didn't write it.


The series so far: [1. Manifesto] [2. The primary reader changed] [3. What to unlearn] [4. The central artifact] [5. The five-step loop] [6. This one].

Next phase: stack overlays. When they're ready.