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

推荐订阅源

C
CXSECURITY Database RSS Feed - CXSecurity.com
aimingoo的专栏
aimingoo的专栏
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
D
Docker
博客园 - 叶小钗
Recent Announcements
Recent Announcements
人人都是产品经理
人人都是产品经理
云风的 BLOG
云风的 BLOG
Vercel News
Vercel News
Hugging Face - Blog
Hugging Face - Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
美团技术团队
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
F
Fortinet All Blogs
博客园 - 三生石上(FineUI控件)
Microsoft Security Blog
Microsoft Security Blog
H
Heimdal Security Blog
有赞技术团队
有赞技术团队
The Cloudflare Blog
S
Secure Thoughts
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
T
The Blog of Author Tim Ferriss
M
MIT News - Artificial intelligence
Google DeepMind News
Google DeepMind News
Schneier on Security
Schneier on Security
N
News and Events Feed by Topic
小众软件
小众软件
C
Cybersecurity and Infrastructure Security Agency CISA
Application and Cybersecurity Blog
Application and Cybersecurity Blog
博客园 - Franky
T
Tenable Blog
The Last Watchdog
The Last Watchdog
腾讯CDC
量子位
Google DeepMind News
Google DeepMind News
Help Net Security
Help Net Security
雷峰网
雷峰网
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Cyberwarzone
Cyberwarzone
S
Securelist
Martin Fowler
Martin Fowler
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
罗磊的独立博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
P
Proofpoint News Feed
D
Darknet – Hacking Tools, Hacker News & Cyber Security
I
InfoQ
Spread Privacy
Spread Privacy
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
H
Hackread – Cybersecurity News, Data Breaches, AI and More

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 central artifact: spec.md and the field tests-first never had
jucelinux · 2026-05-17 · via DEV Community

Fourth article in the **Grounded Code* series. The previous articles established the cost, the mechanism, and the patterns to drop. This one introduces the artifact that holds the whole system together.*

I. The setup

The first three articles were diagnostic. The cost is 7.5x in tokens on the same feature. The mechanism is how agents read with grep instead of an IDE. The patterns that score badly are the ones that paid for themselves on the old axis and stopped paying on the new one.

This article goes positive. What does the agent actually need to stay anchored across turns, across sessions, across compactions, across the moment three weeks from now when a different developer asks the agent to extend the same feature?

The answer is a small markdown file that lives next to the code. It is the document the whole architecture turns around. It is where Grounded Code earns its name.

I want to make the article's three promises explicit, because they map directly to what I want you to walk away with:

  1. The full format of <feature>.spec.md.
  2. A complete example.
  3. The field tests-first never had, and why it matters more than I expected.

II. The full format

A spec is a short markdown file with frontmatter (the parseable contract) and a body (the prose rationale). It lives at src/<feature>/<feature>.spec.md, next to the implementation and the test.

The skeleton:

---
purpose: One sentence, present tense, what this feature does.
public_api:
  - functionName(arg: T): U
  - OtherType: { field: T }
invariants:
  - Statement that must always hold.
  - Another statement.
in_scope:
  - What this feature is responsible for.
out_of_scope:
  - What this feature explicitly does NOT do.
verification: |
  pnpm test src/payment/payment.test.ts
  pnpm typecheck
---

# <Feature> spec

A short prose narrative explaining the why of this feature.

## Why this exists

What problem this feature solves, in one paragraph.

## Edge cases worth knowing

A list of edge cases the implementation handles.

## Related specs

Links to specs of features this one interacts with.

Enter fullscreen mode Exit fullscreen mode

Six frontmatter fields, three body sections. The whole thing is usually 60 to 120 lines. Anything shorter is under-specifying. Anything longer means the feature is too big and should split.

Let me walk through the frontmatter fields.

purpose

One sentence, present tense, what the feature does. Fifteen to twenty-five words. This is what the agent sees first when it opens the spec, so it has to convey the feature without marketing language.

"Computes the final price after discounts, taxes, and currency conversion."

Not:

"A robust, extensible system for handling our complex pricing logic with support for various edge cases."

public_api

The exported names and their signatures. This is the surface the rest of the codebase depends on. If you add or remove a name here, downstream code is going to break. So this field doubles as a "things that must not be renamed without a migration" list.

You don't list every internal helper. Just the surface.

invariants

Statements that must always hold, written as plain English. The implementation enforces them, the tests verify them, the spec declares them. When all three agree, the system is consistent.

"The total returned by computeTotal is non-negative."

"If applyDiscount is called twice with the same code, the second call is a no-op."

in_scope and out_of_scope

In_scope is what the feature does. Out_of_scope is what it does not do, and crucially, what it should not be extended to do without an explicit decision.

I'm going to spend an entire section on out_of_scope below, because this is the field I keep finding to be more load-bearing than anything else in the spec. It's the one piece of the format that has no analog in tests-first development.

verification

The exact commands to run to verify the feature works. Copy-pasteable. This is what the agent runs as the gate before considering a change done.

verification: |
  pnpm test src/payment/payment.test.ts
  pnpm typecheck
  pnpm lint src/payment

Enter fullscreen mode Exit fullscreen mode

If verification involves running the app and checking something manually, say so explicitly. The agent will then know to surface the request rather than silently skip it.

The body

Three sections, in order.

Why this exists. One paragraph. The motivation. What changes if this feature did not exist. If the feature exists because of an external constraint (a regulation, a third-party API quirk, an incident), name it.

Edge cases worth knowing. A list. Each bullet states an edge case and its handling, with the justification. This is where lessons from production live. The things that would otherwise be encoded as comments scattered through the implementation get hoisted into one readable place.

Related specs. Links to the specs of features this one talks to. Relative paths, so the agent can follow them with one read.


III. A complete example

Here's the spec for a pricing feature, fully filled out:

---
purpose: Computes the final order total including discounts, taxes, and tip.
public_api:
  - computeTotal(order: Order, rates: Rates): Money
  - Money: { amount: PositiveInt; currency: Currency }
invariants:
  - The total is non-negative.
  - The total is in the currency of the order.
  - Discounts apply before taxes; tip applies last.
in_scope:
  - Discount application (percent and fixed-amount).
  - Sales tax based on shipping address.
  - Tip as percentage of pre-tax subtotal.
out_of_scope:
  - Payment authorization.
  - Currency conversion (rates are received as input).
  - Refund computation (see ../refund/refund.spec.md).
verification: |
  pnpm test src/total/total.test.ts
  pnpm typecheck
---

# Total spec

## Why this exists

Computing the total is non-trivial because of ordering: discounts before
taxes (legally required in our jurisdictions), tip after tax (customer
expectation), and rounding at each step (otherwise we accumulate cent-level
discrepancies that fail reconciliation).

## Edge cases worth knowing

- **100% discount:** total is zero, not negative. Tax on a zero subtotal
  is zero.
- **Tip on a fully-discounted order:** zero (tip is percentage of pre-tax
  subtotal which is zero).
- **Mixed-currency line items:** rejected upstream; this function assumes
  single-currency orders. The type system enforces this via the `Order`
  type's `currency` field.

## Related specs

- `../order/order.spec.md` for the upstream caller.
- `../refund/refund.spec.md` for the inverse computation.

Enter fullscreen mode Exit fullscreen mode

That's a sixty-line file. The implementation it describes is probably one hundred fifty lines. The tests are probably two hundred. The agent navigates all three with one glob (src/total/*) and rebuilds full understanding in two reads.

Notice what isn't in the spec. There's no marketing language. There's no implementation. There's no example of usage. The examples live in the test file, where they execute. The spec is a contract, not a tutorial.


IV. The field tests-first never had

I want to make a careful argument here, because TDD has earned its place over twenty years and "tests aren't enough" is not a casual claim.

Tests-first does real work. It forces the developer to state what passing looks like before coding. It catches regressions across changes. It documents behavior in an executable form. None of that goes away with spec.md. The verification field of the spec literally points at the test suite. The tests are the gate. The spec is the anchor. They do different jobs.

But there are two things tests structurally cannot do, no matter how well you write them:

1. Tests don't carry rationale. A test says expect(applyDiscount(order, '50OFF')).toEqual(...). It tells you what the system does in that case. It doesn't tell you why the discount applies before tax instead of after. It doesn't tell you whether the rounding was a deliberate decision or an accident of the implementation. When the agent has to decide whether a new requirement is a feature change or a bug fix, the tests cannot help. The rationale is somewhere else (a code comment, the head of the engineer who wrote it, a Slack thread from 2023). The spec's "Why this exists" section is where rationale belongs.

2. Tests don't carry the boundary. This is the one that surprised me. Tests state what is true about the feature. They do not state what is not part of the feature. Nothing in your test suite says "this function should not be extended to handle payment authorization." A reasonable agent, asked to extend the pricing function with a payment step, would do so. The test would not fail. The PR would even compile. And now a function that was supposed to be pure pricing computation is doing payment authorization, with all the failure modes that implies (latency, retries, error handling, observability).

The out_of_scope field is the explicit boundary. Listed, parseable, sitting at the top of the spec. When the agent reads the spec, it reads what the feature does NOT do, and the boundary is anchored. Asked to add payment authorization, the agent now responds with "that's listed as out_of_scope; should I create a new feature or expand the scope of this one?" That single question prevents a class of drift that no amount of test coverage can catch.

In the audit of the three coding-agent codebases, this is the field I kept seeing carry weight. opencode's specs explicitly list things the feature won't do. Claude Code's AGENTS.md documents at the codebase level use the same pattern. The boundary, once stated, holds.

The format calls this field load-bearing. After watching it work in practice, I think load-bearing is an understatement. The out_of_scope field is what stops a feature from quietly becoming five features.

There's a secondary effect that's almost as valuable. When you sit down to write out_of_scope and find the field hard to fill, that's a signal. It usually means the feature itself has no real boundary. The discipline of filling in out_of_scope is the discipline of deciding what the feature actually is.


V. What does not go in the spec

A short list, because the spec is small on purpose.

  • The implementation. The spec describes; it doesn't implement.
  • Examples of usage. Those go in the test file, where they're executable. The test name is the spec sentence; the test body is the example.
  • TODOs. Use the issue tracker. TODOs in a spec rot.
  • Diagrams. If a diagram would help, prefer the type system, then a single ASCII or Mermaid block. Don't reach for a separate image.
  • Marketing copy. The spec is for the agent and the engineer, not the website.

The spec is a contract document. Anything that doesn't help the agent or the engineer extend the feature correctly is noise.


VI. Honest limits

Two things I want to flag before someone tries this and is disappointed.

Specs cost time to write. The first spec for a feature takes maybe ten to fifteen minutes to fill in properly. That's real. The benefit shows up across all subsequent agent sessions, but the initial cost is real. For tiny features (a one-line utility, a single config constant), the cost outweighs the benefit. Skip the spec for those.

Specs need to be maintained. A stale spec is worse than no spec. If the implementation drifts away from the spec, the agent will trust the spec and produce wrong code. The discipline that makes this work is the discipline of updating the spec when the feature changes. The next article in the series (the loop) puts a step five at the end of every change for exactly this reason: consolidate the spec back to truth.

If you can't commit to updating the spec, don't write one. Stale documentation is the older, more familiar version of this problem, and the agent makes it worse, not better, by trusting confidently wrong content.


VII. What's next

The spec is the anchor. The next article is about the loop that runs on top of it: spec, plan, implement, verify, consolidate. Five steps, with the read-only plan mode being the one that saves the most time, and skipping step one being the most expensive failure mode I have seen in practice.

If you want to try writing a spec before the next article ships, pick a feature you'd extend tomorrow and fill in the frontmatter. Don't worry about the body yet. The frontmatter alone will tell you whether the feature has a coherent boundary. Half the value of the spec lands in the first ten minutes.


Next in the series: *"The five-step loop: spec, plan, implement, verify, consolidate."***