FORM NOT VOID, MIND NO CORE

Chapter 36: Practical Case One: Building an API Gateway with Auth and Rate Limiting from Scratch

2026.08.30

36.1 Breakdown and Blueprint Design

Scenario: Build the core logic of an API gateway with authentication (Auth) and rate-limiting (Rate Limit) mechanisms from scratch.

This scenario condenses the most classic methodology question in AI-assisted programming: placing these two cross-cutting concerns -- "authentication" and "rate limiting" -- in the correct architectural positions while keeping them decoupled. It is the best practice drillground for the "structural milestones" and "rebuild at first sign of chaos" discipline.

Phase One: Breakdown (systematic dimensionality reduction).

Following the "systematic dimensionality reduction" principle from Chapter 10, we build a dependency tree to clarify the absolute sequence of implementation:

Phase One (lay the foundation): JWT Auth Middleware (core, independent, do first)
Phase Two (build load-bearing walls): Rate-Limit Middleware (depends on Phase One infrastructure)
Phase Three (run utilities): Route Binding and Middleware Assembly (horizontal integration)

Key Decision: Phase One's milestone is only "complete the JWT Auth middleware" -- absolutely do not touch rate-limiting logic yet. Split the two cross-cutting concerns into two independent structural milestones, each verifiable on its own.

Blueprint Design Points (written into CONTEXT.md):

  • Tech Stack Constraints: Node.js + Express (or equivalent framework); JWT parsing library; rate-limiting driver with reserved interface;
  • Data Models: No database needed (middleware is pure logic), but must define Token payload structure (user_id, exp, iat) and the data contract for rate-limit counters;
  • API Contracts: Middleware inputs (request object) and outputs (request object + authenticated user context / 429 response); custom exception formats (401 AppError, 429 RateLimitError);
  • Milestone Dependency Tree: M1 JWT Auth Middleware → M2 Rate-Limit Middleware → M3 Gateway Route Assembly and Integration Acceptance.

Directional Tip: Before having the AI write any code, hand it the CONTEXT.md and acceptance criteria first. An example of the M1 acceptance criteria:

Acceptance Criteria (M1: JWT Auth Middleware):
1. Parse the Bearer Token in the Authorization header;
2. Verify signature and expiration time; return 401 if expired;
3. On success, inject user_id into the request context;
4. All failure cases uniformly throw a 401 AppError;
5. Sensitive fields in the Token payload (e.g., secret key) must not leak into logs.

36.2 Milestone Progression and Acceptance

Milestone M1: JWT Auth Middleware.

  • Issue Instruction: Feed the acceptance criteria to the AI as hard constraints (acceptance-driven development);
  • Coding: AI generates the middleware code;
  • Acceptance: Functional test (test requests with valid/expired/forged tokens one by one), architectural test (does the middleware maintain single responsibility, does it sneak in unnecessary libraries?), security test (no hardcoded secrets, Token payload doesn't leak sensitive info);
  • Conclusion: PASS → git commit to solidify → update CONTEXT.md (record M1 completion, confirm API contract) → clear session.

Milestone M2: Rate-Limit Middleware.

  • Issue Instruction: Clarify technical constraint -- "the rate-limiting logic must remain as an independent middleware, never coupled with the auth middleware";
  • Coding: AI generates the rate-limiting logic;
  • Acceptance: Functional test (requests exceeding the threshold within a time window return 429), architectural test (still decoupled?), boundary test (concurrent scenarios, counter reset);
  • Conclusion: PASS → commit → update blueprint.

Milestone M3: Route Assembly and Integration Acceptance.

  • Mount both middlewares onto the gateway route in the correct order;
  • Integration Acceptance: Authentication failure short-circuits before entering rate-limiting logic; rate-limit counting treats authenticated and unauthenticated requests consistently; end-to-end test of the full chain passes.

36.3 A Deliberate Architecture Drift: Identify → Elevate → Rebuild

Deliberately cause an architecture drift (this is the full version of the Chapter 10 practical drill):

How the drift occurs: Issue a deliberately vague instruction to the AI -- "add rate-limiting functionality" (without providing any technical constraints). The AI will very likely determine that "the simplest way to implement this": hardcode a memory-based rate-limiting logic directly into the current auth middleware file. Auth and rate-limiting become tightly coupled, and the file begins to bloat.

Identification Signals (danger radar alarm):

  • Foundation tampering: The AI modified the already-solidified auth middleware file (M1, an already-accepted milestone);
  • Size out of control: A single middleware file rapidly bloats.

Elevated Instruction (First Step Correction): Don't blame the details; point out the structural flaw in architect language:

"Your recent implementation hardcodes the rate-limiting logic inside the auth middleware, breaking the Single Responsibility Principle and coupling two cross-cutting concerns. Please extract the rate-limiting logic into a separate middleware to maintain module decoupling."

Stop Implementation (Second Step): If the AI falls into the "self-consistency trap" -- introducing an abnormally large and incompatible third-party rate-control framework to "decouple," or even breaking the original route-binding mechanism so the gateway won't start -- stop immediately after two consecutive failed corrections. Do not debate a third time.

Complete Rebuild (Third Step):

# Inspect the branch and working tree without performing recovery
git status --short
git log --oneline -5

Protect needed tracked, untracked, and ignored files, then verify the full M1 target hash and choose a recovery path in Section 10.4: consider reset only on a personal unshared branch; use revert for shared erroneous commits. A stash is not an undo and does not protect ignored files by default; Git does not restore databases or external systems. After recovery, rerun M1 acceptance to verify authentication before implementing rate limiting.

After Rebuild: Rewrite the Prompt with detailed blueprint instructions for the rate-limiting mechanism -- "use an in-memory counter to implement sliding-window rate limiting, maintain middleware-isolated state, return a unified 429 format". Then clear the now-contaminated conversation (/clear) and feed the new blueprint to a fresh AI instance.

Result: The new round of code generation precisely implements the functionality while maintaining perfect architectural elegance.

Retrospective: Mindset at Each Decision Point

Decision PointMindset
Why only auth in Phase One, absolutely no rate-limiting?Systematic dimensionality reduction -- lay foundations when laying foundations; don't let the AI leap across levels
Why write acceptance criteria before coding?Acceptance-driven development -- acceptance criteria are the best instruction and also your red line
Why escalate architecturally instead of micromanaging after a drift?Point out structural flaws in architect language; give the AI one or two chances to self-correct
Why stop after two failed corrections?The AI has fallen into the self-consistency trap; continuing to argue only pollutes the context
Why choose controlled recovery followed by rebuilding?Compare total costs including protection and verification per Section 7.5, and recover per Section 10.4
Why restart the conversation after rebuilding?Cut off the AI's historical negative memories; start fresh with clean context