Spec-Driven Development
Spec-Driven Development β€Ί Module 2: Authoring Machine-Readable Specs
Module 2: Authoring Machine-Readable Specs

Chapter 6: Setting Hard Boundaries: The Art of Explicit Scope Exclusion

By Muhammad Riaz

The "Helpful AI" Paradox

Large Language Models are fine-tuned via Reinforcement Learning from Human Feedback (RLHF) to be helpful, polite, and thorough. In conversational assistant contexts, these traits make models pleasant to interact with.

In automated software development, however, this eagerness to please becomes a distinct engineering liability: The "Helpful AI" Paradox.

When you prompt an AI coding assistant to build a feature without specifying hard boundaries, the model assumes that "completeness" requires building everything adjacent to the request.

  • Ask it to build a user profile form, and it generates a custom avatar image-cropping tool, installs an unrequested file-upload SDK, and writes an inline email validator.

  • Ask it to handle a simple Redis cache lookup, and it builds a custom full-blown micro-framework for cache invalidation, fallback tiers, and telemetry reporting.

This behavior is not a bug; it is an intrinsic property of autoregressive language generation. An LLM predicts the most statistically probable continuation of your request based on its massive training corpusβ€”where full-stack tutorials and open-source frameworks bundle UI, business logic, abstractions, and dependencies together.

To keep AI agents on task, human software architects must master The Art of Explicit Scope Exclusion.

The Three Pillars of Scope Boundaries

Explicit Scope Exclusion is the discipline of defining what the AI is strictly forbidden from doing. It constructs a virtual sandbox around the AI agent, restricting its execution across three dimensions:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                       THE THREE BOUNDARY PILLARS                            β”‚
β”‚                                                                             β”‚
β”‚  1. DEPENDENCY BOUNDARIES  ──> Prohibiting unapproved libraries/packages    β”‚
β”‚  2. FILE SYSTEM BOUNDARIES  ──> Restricting file read/write permissions      β”‚
β”‚  3. ARCHITECTURAL BOUNDARIES──> Locking design patterns & abstractions      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

1. Dependency Boundaries: Eliminating Package Sprawl

One of the quickest ways an unguided AI degrades a production codebase is through Dependency Sprawl β€”installing third-party packages to solve minor problems easily handled by standard libraries or existing codebase utilities.

The Risk

Every new package added to package.json, requirements.txt, or Cargo.toml introduces:

  • Security Surface Area: Vulnerabilities in transitive dependencies (e.g., supply chain attacks).

  • Bundle Size Bloat: Increasing cold-start times or client download overhead.

  • Maintenance Overhead: Future framework upgrade conflicts.

Practical Specification Patterns

In your .spec.md file, replace vague instructions with explicit dependency rules:

<!-- BAD: Vague Dependency Instruction -->
"Use a standard library to handle date formatting and JWT parsing."

<!-- GOOD: Explicit Dependency Boundary -->
## Dependency Boundaries (STRICT)

- **ZERO New Dependencies:** The agent MUST NOT run `npm install`, `pip install`, or add any packages to `package.json`.

- **Approved Utilities Only:**

- Date manipulation MUST use native `Intl.DateTimeFormat` or existing app utility `src/utils/date.ts`.

- JWT verification MUST reuse existing utility `src/auth/jwtVerifier.ts`.

- **Violation Action:** Any generated code introducing a new package requirement will be automatically rejected during CI verification.

2. File System Boundaries: Locking Down Directory Access

When an AI agent runs in an autonomous CLI environment (such as Claude Code, Cursor Agent, or custom multi-agent runners), it possesses file system read and write privileges across your repository.

Without strict directory boundaries, an agent tasked with updating an API route might refactor shared global middleware, overwrite database seeds, or alter configuration files in adjacent directories.

The File Impact Map as a Sandbox

In your specification and planning artifacts, define strict file system permissions using three categories:

## File System Boundaries (File Impact Map)

### Permitted Writes (Agent MAY create or edit):

- `src/modules/billing/services/refund.service.ts`

- `tests/modules/billing/refund.service.test.ts`

### Read-Only Context (Agent MAY read, MUST NOT edit):

- `src/modules/billing/types/stripe.types.ts`

- `src/config/database.ts`

### Strictly Forbidden Paths (Agent MUST NOT read or edit):

- `src/core/auth/*`

- `prisma/migrations/*`

- `.env*`

- `.github/workflows/*`

When an agent is prompted with explicit path restrictions, modern tools enforce these boundaries at the tool-calling level, blocking the LLM from executing file-write commands on forbidden targets.

3. Architectural Boundaries: Preventing "Over-Engineering"

AI models tend to introduce abstractionsβ€”such as Abstract Factory patterns, generic wrapper interfaces, or deeply nested inheritance hierarchiesβ€”when simple, idiomatic code is required.

Architectural boundaries lock the AI into your project’s established conventions.

Real-World Case Study: Over-Engineered Error Handling

A developer asked an AI agent to add input validation to a user registration form.

  • What the Unbounded AI Built: Created a custom 400-line validation engine with dynamic rule-chaining, custom decorators, and a global event-emitter bus to handle validation errors.

  • What the Codebase Actually Needed: A 15-line Zod schema check matching existing form controllers.

How to Spec Architectural Boundaries

Use the "Anti-Pattern Exclusion" section in your .spec.md:

## Architectural Boundaries & Anti-Pattern Exclusions

- **AP-1 (No Custom Abstractions):** DO NOT create new abstract base classes, generic wrapper types, or custom event buses.

- **AP-2 (Schema Standard):** Input validation MUST use existing Zod schemas in `src/schemas/auth.schema.ts`. DO NOT write custom regex or manual field-by-field validation checks.

- **AP-3 (Error Handling Consistency):** Errors MUST be thrown using the standard application exception classes (`AppError`, `ValidationError`) defined in `src/errors/`. DO NOT use generic `throw new Error()` or return custom error objects.

Crafting "Negative Directives" (The Art of Anti-Prompts)

In prompt engineering, instructions that tell a model what not to do are known as Negative Directives.

Due to how attention mechanisms operate in Transformers, negative directives work best when structured as Explicit Counter-Examples: stating what is forbidden and providing the required alternative immediately.

Weak Boundary (Vague) Strong Boundary (Explicit Counter-Example)
"Don't write complex code." Forbidden: Custom caching algorithms. Required: Call cache.get(key) from src/services/redis.ts.
"Don't change global styles." Forbidden: Edits to src/styles/globals.css. Required: Use existing Tailwind CSS utility classes inline.
"Avoid hardcoding settings." Forbidden: String literals or API keys in application logic. Required: Import values from src/config/env.ts.

Practical Exercises: Boundary Enforcement

  1. Conduct a Dependency Audit: Open your current project's package manifest (package.json, requirements.txt, etc.). Identify how many packages were added by AI tools over the last month that duplicate existing functionality or native capabilities.

  2. Draft a Sandbox Profile for a Feature: Take a feature currently on your sprint board. Create a 10-line text block titled Scope Exclusion Checklist that explicitly lists: - 2 forbidden npm/pip packages. - 3 forbidden directory paths. - 1 architectural pattern to avoid.

  3. Run a Stress Test: Pass a prompt to your AI tool specifying a feature, accompanied by your Scope Exclusion Checklist. Observe whether the AI respects the constraints or attempts to bypass them.

Next Steps

Now that you know how to lock down scope, dependencies, and file access, we must address system integrity at the data level.

In Chapter 7, we will explore Contracts, Schemas, and System Invariants β€”learning how to define airtight API contracts, JSON schemas, and invariant assertions that force AI tools to write integration-ready code on the first attempt.