How to Stop Cursor from Hallucinating: 5 Production Rules Every AI Engineer Needs
If you use Cursor, Claude Code, or GitHub Copilot on a non-trivial codebase, you've probably encountered the AI coding drift problem.
The model:
- Invents methods that don't exist in your framework version.
- Couples database queries or third-party APIs directly inside HTTP route handlers.
- Generates loose types like
anyorobjectto bypass TypeScript or Pydantic errors. - Writes brittle unit tests that mock everything without testing actual boundary failures.
When building production systems, manually fixing AI-generated drift can quickly erase the productivity gains from AI-assisted development.
The solution isn't simply "better conversational prompting."
It's explicit engineering rules that constrain the coding agent before it writes the code.
Here are five rules you can add to your .cursor/rules/ directory.
1. Bounded Context & Layer Isolation
Prevent the AI from mixing database access, business logic, and HTTP concerns:
- Route handlers MUST only perform request validation and delegate to application services.
- Domain logic MUST remain independent of database ORMs and external APIs.
- External API clients and third-party SDKs MUST be encapsulated behind dedicated adapters.
- Database queries MUST NOT be placed directly inside HTTP route handlers.
This gives the agent explicit boundaries between the presentation, application, domain, and infrastructure layers.
Without these constraints, an AI agent will often choose the shortest path to a working implementation—even when that implementation creates unnecessary coupling.
2. Hermetic Unit Testing
AI-generated tests can look comprehensive while providing very little protection.
A common pattern is to mock almost every dependency and then assert that the mocked functions were called.
The test passes, but the actual boundary failure was never tested.
Give the agent explicit testing constraints:
- Unit tests MUST be hermetic: no real network calls and no unintended external filesystem dependencies.
- Tests MUST cover valid inputs, invalid inputs, and important boundary conditions.
- Avoid mocking internal domain logic.
- Mock external boundary adapters where appropriate.
- Tests MUST verify observable behavior rather than implementation details.
- Every bug fix SHOULD include a regression test when practical.
For example, don't only test that an API call succeeds.
Also test what happens when the external service:
- Times out
- Returns malformed data
- Returns an unexpected status code
- Returns an empty response
The goal isn't maximum mock coverage.
It's meaningful behavioral coverage.
3. Fail-Fast Input Boundaries
AI-generated applications often assume that incoming data is trustworthy.
That's particularly dangerous at API boundaries.
Tell the agent exactly how input should be handled:
- All incoming payloads MUST be validated using strict schemas such as Pydantic or Zod.
- Avoid implicit type coercion when strict validation is required.
- Invalid input MUST be rejected at the application boundary.
- Domain-specific failures MUST use typed exceptions rather than generic errors.
- Do not pass unvalidated request dictionaries through application layers.
This creates a clear boundary:
External input → Validation → Application logic → Domain logic
Instead of allowing malformed data to travel through the entire application before something eventually fails.
4. Hallucination & Assumption Defense
One of the most frustrating problems with AI coding assistants is confident guessing.
A model may generate an import, method, parameter, or dependency that looks perfectly reasonable but doesn't actually exist in your installed version.
Add rules that explicitly prohibit this behavior:
- NEVER assume an API, method, parameter, or configuration option exists without verification.
- Prefer APIs already used by the existing codebase when implementing new functionality.
- Do not invent dependencies or speculative import paths.
- Do not use deprecated APIs when a supported alternative exists.
- If an implementation depends on an unverified assumption, explicitly identify the assumption before proceeding.
- If the requested approach introduces architectural or security risks, explain the trade-off and propose a safer alternative.
The important principle is:
The project's installed dependencies and existing code are the source of truth—not the model's memory.
This is particularly useful when working with rapidly changing frameworks and libraries.
5. Idempotent State Mutations
AI-generated APIs can also overlook what happens when clients retry requests.
Consider an endpoint that creates an order or processes a payment.
If the client sends the request, experiences a timeout, and retries it, you don't want the server to process the operation twice.
For state-changing operations, give the agent explicit constraints:
- State-changing endpoints SHOULD support idempotency when duplicate requests could cause unintended side effects.
- Financial, order, and payment operations MUST define an idempotency strategy.
- Idempotency keys MUST be persisted and associated with the resulting operation.
- Concurrent state transitions MUST use appropriate transactional or locking mechanisms.
- Do not rely on application-level checks alone when atomic database guarantees are required.
The exact implementation will depend on your database and architecture, but the important thing is that the agent is forced to consider retry and concurrency behavior instead of generating only the happy path.
Why These Rules Matter
AI coding assistants are extremely good at generating code.
But they don't automatically know:
- Your architecture
- Your dependency versions
- Your domain boundaries
- Your testing philosophy
- Your security requirements
- Your tolerance for technical debt
Without explicit constraints, the model tends to optimize for producing code that looks plausible and solves the immediate request.
That's where coding drift begins.
Compare:
"Implement authentication."
with:
Use the existing authentication service.
Do not access the database from route handlers.
Validate all external input with the existing schema system.
Do not introduce new dependencies.
Add tests for expired tokens and invalid credentials.
The second instruction gives the agent a much smaller—and more useful—solution space.
Start With Constraints, Then Generate Code
You don't need an enormous system prompt containing every possible engineering rule.
Start with the constraints that matter most to your project:
- Architecture boundaries
- Dependency verification
- Strict input and type validation
- Boundary-focused testing
- State and concurrency safety
Then adapt them to your framework and codebase.
The goal isn't to make your AI coding assistant less autonomous.
It's to make its autonomy bounded by engineering constraints.
Open-Source Developer Prompt Vault
I've collected these types of rules, along with additional prompts for architecture, refactoring, testing, security, and development workflows, in an open-source repository:
Developer Prompt Vault:
https://github.com/AymaneWebDEV/developer-prompt-vault
The repository contains reusable Markdown rules and templates that you can adapt to your own AI-assisted development workflow.
If you're building AI-powered applications with FastAPI, I've also put together a separate starter architecture covering authentication, streaming APIs, rate limiting, Docker, and automated testing:
FastAPI AI Agent Starter Kit:
https://nexusbuilds.gumroad.com/l/fastapi-ai-starter-kit
What Rules Have Helped You?
What constraints have had the biggest impact on your Cursor, Claude Code, or Copilot workflows?
I'd especially be interested in rules around architecture, testing, dependency verification, and preventing AI-generated technical debt.
Top comments (2)
Rule 2 is the one that actually changes outcomes. The mock-everything pattern is worse than having no test: it pins the call graph, so the suite asserts that
repo.savewas called and passes while the boundary where the bug lives is never touched — and then a legitimate refactor reads as a regression. Forcing hermetic tests plus boundary coverage is what stops that.On the layer rules, the phrasing that stuck for me is a prohibition plus the alternative, because a bare prohibition gets routed around: "no DB access in route handlers" produces a thin wrapper parked in the handler folder, whereas "DB access lives only in the adapter; handlers may call exactly one application service" closes that door and is mechanical enough for the agent to self-check.
Do these hold on a codebase the model hasn't seen before, or only once the rules directory has accumulated a few in-repo examples of the pattern? My experience is that a rule with no nearby example gets acknowledged on the first file and quietly ignored by the third.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.