FORM NOT VOID, MIND NO CORE

Chapter 13 The Anti-Regression Contract: Ensuring Every Change Is an Improvement

2026.08.10

In Chapter 11, we built the "test grid," which uses unit tests and integration tests to powerfully ensure our software's "functional correctness." In Chapter 12, we established a "garbage collection" mechanism, which uses periodic cleanup to ensure the "structural health" of our codebase.

Now, we face the last and more subtle challenge: how do we ensure the "non-functional quality" of our software?

These quality attributes are usually not reflected in "whether the function works," but in "what it feels like to use the function." They include:

  • Performance: Has the response time of an API unconsciously degraded from 50ms to 500ms through a series of seemingly "harmless" modifications?
  • Resource Consumption: Has the memory usage of a background task silently grown from 100MB to 1GB?
  • Bundle Size: Has the JavaScript bundle size of the frontend application increased by 200KB due to the introduction of a seemingly harmless utility library, slowing down the initial load for all users?
  • Security: Did a refactoring inadvertently introduce a new XSS vulnerability?
  • Accessibility: Did a UI component change make it unusable for screen reader users?

These "non-functional" metrics are extremely susceptible to "slow, hard-to-detect degradation" during rapid iteration. Each individual modification might only cause a 1% impact on performance, completely within the acceptable range. But after a hundred such modifications, the overall quality of the system may have degraded by over 60%, turning a "nimble athlete" into a "halting fat man."

This "boiling frog" degradation is very difficult for traditional unit tests to catch, because unit tests only care about "right or wrong," not "fast or slow," "economical or wasteful," "big or small."

In this chapter, we will learn how to establish an "Anti-Regression Contract." This "contract" transforms our expectations for non-functional quality from vague "feelings" and "hopes" into quantifiable, automated, non-negotiable "baselines." We will nail these baselines into our development process like rivets, ensuring that every modification must prove itself to be an "improvement," or at least "not a regression."

13.1 Locking Down Proven Baselines

To prevent "regression," we first need a clear, recognized benchmark to define what "no regression" means. This benchmark is the "baseline."

A baseline is a "snapshot" of our system's various non-functional indicators at a "healthy" point in time. For example, at the release of version v1.2.0, we measure and record:

  • The average response time for the GET /api/users endpoint is 45ms.
  • The peak memory usage for the NightlyReportJob task is 128MB.
  • The bundle size of the frontend homepage's main.js is 350KB.

These numbers constitute the "performance baseline" for our v1.2.0 release. From this moment on, any new code submission must be compared against this baseline. If a modification causes the response time for GET /api/users to become 60ms, we say a "performance regression" has occurred.

How to Establish and Automate Baseline Comparison?

This process can be fully automated through the CI/CD pipeline and some specialized tools.

1. Performance Testing

  • What to do: We need to write a special kind of "performance test case." Unlike unit tests that run once, these tests perform hundreds or thousands of "warm-up" and "loop" calls to a key function or API, then measure its average execution time, P95/P99 percentile latency, throughput, and other metrics.
  • Tools to use:
  • Backend: JMeter, k6, Gatling for API stress testing. For code-level benchmarking, each language has its own libraries, such as Go's testing.B, Java's JMH, and Python's pytest-benchmark.
  • Frontend: Lighthouse CI, Playwright combined with custom measurement scripts.

2. Resource Monitoring

  • What to do: In the CI environment, run your application or task and use system tools to monitor its CPU usage, memory consumption, disk I/O, etc., during execution.
  • Tools to use: In Docker containers, you can get this via docker stats or by reading the cgroups pseudo-filesystem. In CI scripts, you can use commands like ps, top to take snapshots before and after the task runs for comparison.

3. Bundle Analysis

  • What to do: After the build step of the frontend project, automatically analyze the size of generated static assets (JS, CSS).
  • Tools to use: webpack-bundle-analyzer, source-map-explorer and other tools can generate detailed reports telling you which part of the bundle is contributed by which library.

Commit Hash Locking Strategy

Now, here is the most critical question: our CI pipeline generates new performance data on every run. What should it compare against?

The simplest and most brute-force method is to compare against the latest build result of the main (or master) branch. This is called a "floating baseline."

This method has a fatal flaw: it allows "slow regression" to happen.

  • Day 1: Your PR increases the endpoint latency from 50ms to 51ms (+1ms). It does not exceed the threshold. Merged. Now the main branch baseline is 51ms.
  • Day 2: Another PR increases the latency from 51ms to 52ms (+1ms). It does not exceed the threshold either. Merged. Now the baseline is 52ms.
  • ...
  • Day 30: After 30 "tiny" regressions, the endpoint latency has become 80ms. The system has unconsciously slowed down by 60%.

To solve this problem, we need to introduce the "Commit Hash Locking Strategy," also known as the "Fixed Baseline."

The workflow is as follows:

  1. Set a "Golden Commit": We no longer use the "latest" state of the main branch as the baseline. Instead, at an important project milestone (e.g., after a major release, at the moment of optimal performance), select a specific Commit Hash (e.g., a1b2c3d) and declare it as our current phase's "performance benchmark commit."
  2. Store Baseline Data: Run a complete performance test and analysis pipeline against this commit a1b2c3d, and store all the resulting baseline data (api_latency: 50ms, bundle_size: 350KB...) in a dedicated location (e.g., a JSON file, or a dedicated performance monitoring service), associated with the Commit Hash a1b2c3d.
  3. Comparison in CI: Now, when any new Pull Request runs, the "performance comparison" step in its CI pipeline will: a. Get the current Commit Hash, e.g., e4f5g6h. b. Run performance tests to get e4f5g6h's performance data. c. No longer compare against the latest state of the main branch, but always compare against the baseline data locked to a1b2c3d.
  4. Regression Judgment: The CI pipeline calculates the percentage change of the new data relative to the "golden baseline." We can set a strict threshold, e.g., ±5%. If any metric's regression exceeds this threshold, the CI build automatically fails.
  5. Baseline Update: This "golden baseline" is not set in stone forever. When we consciously make major, positive performance optimizations and confirm that the new performance data is better than the old one, we can manually and solemnly update the "golden commit" pointer to this new, better Commit Hash. This is a deliberate, reviewed "improvement," not an unconscious "regression."

In this way, we establish an "absolute ruler." We have locked down the standard of "good." Any code modification by AI (or humans) must prove its innocence before this ruler. The "salami-slicing" degradation of "regressing a little each time" has nowhere to hide.

13.2 Embedding Anti-Regression Instructions in Prompts for AI Self-Audit

Building an automated "baseline detection" pipeline is our "hardware" safeguard. But it is a "post-hoc," expensive detection method. Waiting for CI to run for over ten minutes only to tell us a regression has occurred is still not efficient enough.

We need to "shift left" the awareness of "anti-regression" to the moment AI generates code. We need to embed the "anti-regression" gene into our Prompts, making AI conduct a "self-audit" before writing every line of code.

This is like installing a real-time "performance and quality scanner" in AI's brain.

How to Build an "Anti-Regression Prompt"?

This kind of Prompt usually contains three core elements:

  1. Clear "Non-Functional" Constraints: Directly tell AI your specific requirements for performance, resource consumption, etc.
  2. Require "Self-Assessment": Instruct AI to analyze and state the potential impact of its solution on non-functional quality before providing the code.
  3. Provide "Alternative Solutions": If AI believes it cannot complete the task without affecting quality, require it to propose alternatives and explain the trade-offs.

[Prompt Template 13.1: Performance Anti-Regression Instruction]

Your Question (when asking AI to refactor a data processing function):

Context: I need to refactor the following data processing function.

# [Paste the old, reasonably performing function code]

Task: Refactor this function to improve its readability and add a new filtering logic [...describe new logic...].

ANTI-REGRESSION CONTRACT (CRITICAL):

  1. Performance Baseline: The current function processes 1 million records in approximately 500ms on our standard hardware. Your new implementation must not be significantly slower. Ideally, it should be faster.
  2. Memory Baseline: The current function has a peak memory usage of around 200MB. Your new implementation must not increase this footprint. Avoid loading the entire dataset into memory if possible.
  3. Self-Assessment Requirement: Before you write the final code, you must provide a brief "Performance & Memory Impact Analysis" section. In this section, explain how your proposed changes will affect performance and memory usage, and why you believe they comply with the baselines.
  4. Provide Alternatives: If you believe the new filtering logic inherently requires a trade-off (e.g., more memory for faster speed), you must present at least two options: one that prioritizes speed, and one that prioritizes memory, and explain the trade-offs.

The power of this Prompt lies in:

  • Quantified Baseline: It does not say "please make it faster," but gives specific, measurable numbers like 500ms and 200MB. This gives AI a clear optimization target.
  • Forced Thinking: The Self-Assessment Requirement forces AI to "think" and "state" before "acting." This activates the knowledge weights in AI's brain related to algorithm complexity, memory management, and data flow processing.
  • Exposed Trade-offs: The Provide Alternatives requirement returns the "decision power" to you. It turns AI from a "code generator" into a "solution consultant," presenting the pros and cons of different choices, and it is you who makes the final decision that fits the business needs.

[Prompt Template 13.2: Frontend Bundle Anti-Regression Instruction]

Your Question (when asking AI to add a new feature, like a "date picker"):

Context: I need to add a date picker component to our user profile page.

ANTI-REGRESSION CONTRACT (CRITICAL):

  1. Bundle Size Baseline: We have a strict policy to keep our main vendor bundle size under 250KB (gzipped). Any new third-party library added must be carefully evaluated.
  2. Library Evaluation Requirement: If you suggest using a third-party date picker library, you must first perform a cost-benefit analysis. This analysis must include:
  • The library's estimated gzipped bundle size (you can use sites like bundlephobia.com for this).
  • Whether the library is "tree-shakeable".
  • A comparison with at least one other lightweight alternative.
  1. Prioritize Native/Existing Solutions: Before suggesting any new library, first consider if the required functionality can be achieved using native browser APIs (like <input type="date">) or existing libraries already in our project ([e.g., day.js]).

This Prompt will completely change AI's "lazy" habit. By default, AI might directly recommend moment.js or a fully-featured but huge UI library. Under the constraints of this "contract," however, it is forced to:

  1. First think, "Can I avoid adding a new dependency?"
  2. If a new library is necessary, it will act like a senior frontend architect, researching and comparing the "cost-effectiveness" of different libraries, then presenting you with a data-backed, professional selection report.

By embedding these "anti-regression clauses" in your Prompts, you shift the "quality assurance" checkpoint to the very beginning of the entire development process. You are no longer passively waiting for CI to give you a red "X." Instead, you are proactively guiding AI toward the path most beneficial to quality during the "seedling stage" of its thoughts.

13.3 When AI Accidentally Crosses the Boundary, How to Quickly Correct It Through the Feedback Loop

Even with our automated "hardware" baseline and front-loaded "software" Prompt constraints, AI can still occasionally "make mistakes." It might generate performance-breaking code due to a misunderstanding of a complex scenario.

At this point, our CI pipeline faithfully captures this "boundary-crossing" behavior. The build fails, and a detailed "regression report" is generated.

Now, our task is to use this report to form an efficient "feedback-correction" loop with AI. This process is very similar to the "test-driven fix" in Section 11.3, except this time our "driving force" is not the failing unit test log, but the regression report from "performance testing" or "bundle analysis."

[Steps of the Feedback Loop]

  1. CI captures the regression: Your PR build fails. The failing step is "PerformanceBenchmark."
  2. Extract "evidence": You open the CI log and find the "regression report" generated by the tool.
Performance Regression Detected!
Endpoint: GET /api/users
Baseline (a1b2c3d): 45ms (p95)
Current (e4f5g6h): 65ms (p95)
Regression: +44.4% (Threshold: 5%)
  1. Reverse feed the report: You feed this report, together with the code AI submitted last time, back to AI as new context.
  2. Issue the "fix" instruction:

[Prompt Template 13.3: Fix Instruction Based on Regression Report]

Your Question:

Context: Your previous code submission caused a critical performance regression and was rejected by our CI quality gate.

Your Role: Act as a Senior Performance Engineer on-call. Your task is to analyze the regression report, identify the root cause in the code you wrote, and provide a fix.

Regression Report (from CI):

[Paste the detailed regression report from above]

Your Previous Code (that caused the regression):

// [Paste the code AI submitted last time that caused the performance issue]

Your Task:

  1. Root Cause Analysis: Pinpoint the exact line or logic in your previous code that caused the response time to increase from 45ms to 65ms. Explain why it caused the slowdown (e.g., "This introduced an N+1 query problem because...").
  2. Propose a Fix: Provide a new version of the code that resolves the performance issue and brings the response time back within the 5% threshold of the 45ms baseline.
  3. Confirm Understanding: Start your response by acknowledging the regression and stating your goal: "I understand my previous code caused a performance regression. My goal now is to fix it."

This process is like collaborating with a real, professional colleague.

  • You did not blame it: "The code you wrote is too slow!"
  • You did not guess the cause: "Is it because you used map instead of forEach?"
  • You calmly and objectively presented the "facts" (the CI report) and "context" (the code it wrote) before it, then gave it an expert role of "solving the problem" and clearly defined the "success" criteria ("bring this number back to around 45ms").

The performance of AI after receiving such feedback is usually excellent. Because the problem is defined extremely clearly, it can focus all its "attention" on the specific, quantified difference of "45ms -> 65ms," and then reason backwards about which part of its code is most likely responsible for that extra 20ms of latency.

By establishing this rapid "CI captures -> Human feedback -> AI fixes" closed loop, we turn every "regression" into an opportunity for "learning" and "reinforcement." Guided by you, AI not only fixes the problem but also indirectly "strengthens" its own "understanding" of "which operations are expensive."

[Prompt Snippets] Ready-to-Use Anti-Regression Clauses

To help you seamlessly integrate the "Anti-Regression Contract" into your daily Prompts, here are some modular "clause snippets" that you can plug and play. You can add them as a "standard footer" to all your "code modification" type requests.


Standard Anti-Regression Clause: Before providing the solution, you must perform a self-check to ensure your changes do not violate our core quality principles:

  1. No Performance Degradation: The new code must not be demonstrably slower or use significantly more memory than the code it replaces.
  2. No Bundle Size Increase: Do not introduce new third-party dependencies unless absolutely necessary and explicitly approved. State the size impact if you do.
  3. No Security Vulnerabilities: Sanitize all inputs and encode all outputs. Check for common OWASP Top 10 risks.
  4. Maintain Test Coverage: If you add new code, you must also provide the corresponding unit tests to maintain our 85% coverage threshold.

If you believe a trade-off is necessary, you must declare it explicitly.

Database Interaction Specific Clause

Database Interaction Clause: When modifying any code that interacts with the database, you must ensure:

  1. No N+1 Queries: Analyze your data access pattern. If your code is in a loop, ensure you are not issuing a new database query inside each iteration.
  2. Efficient Indexes: All WHERE, JOIN, and ORDER BY clauses must be supported by appropriate database indexes. If you are unsure, state which columns you think need an index.
  3. Transaction Safety: For operations that involve multiple writes, ensure they are wrapped in a single, atomic database transaction.

Frontend UI Component Specific Clause

UI Component Clause: When creating or modifying a UI component, you must verify:

  1. Accessibility (a11y): The component must be fully keyboard navigable, and all interactive elements must have appropriate ARIA attributes.
  2. No Re-rendering Loops: The component must not trigger excessive re-renders. Analyze your use of useEffect, useMemo, and useCallback to prevent this.
  3. Responsiveness: The component must display correctly on both mobile and desktop viewports.

Embedding these precise, standardized constraints -- like legal provisions -- as a "fixed format" for communication between you and AI has profound significance. It is not just about "reminding" AI, but about continuously and subtly "training" your AI session.

Over time, AI will "learn" your high standards. It will raise the weight of these constraints in its "session memory." Eventually, you might find that even if you forget to add these clauses on occasion, AI will proactively include a "performance impact analysis" when providing solutions.

At that moment, you have truly internalized your "quality outlook" into AI's "behavioral habits." You no longer need to supervise it constantly, because you have successfully built an unyielding, automatic defense line in its "brain" as well.