How to Get Better Output from Cursor and Claude Code
Practical tips for getting AI coding tools to produce better, more consistent code — starting with how you give them context.
If you use Cursor, Claude Code, or any LLM-based coding tool, you have probably noticed that the quality of output varies wildly. Sometimes it nails the implementation on the first try. Other times it generates code that looks plausible but misses the mark: wrong field names, invented APIs, inconsistent patterns, or subtly broken logic.
The difference between good and bad output usually is not the model. It is the context you provide. AI coding tools are pattern-matching engines. The better the patterns you feed them, the better the output. Give them vague instructions and you get vague code. Give them structured, specific context and you get code that actually fits your system.
Here are five practical tips that consistently improve output quality, with before-and-after examples for each.
Why Output Quality Varies
Before the tips, it helps to understand why output quality is inconsistent.
LLMs generate code by predicting the most likely next token given the context. When your context is a chat message like "build a user settings page," the model has to guess at your data model, API structure, component patterns, naming conventions, and state management approach. It fills in those gaps with generic patterns drawn from its training data, which may have nothing to do with your codebase.
When your context includes your actual data model, API contract, and component patterns, the model does not need to guess. It follows your patterns because those patterns are in the context window.
The principle is simple: reduce what the model has to invent, and you reduce the chance of it inventing something wrong.
Tip 1: Provide Structured Specs, Not Chat Messages
The most common way people use AI coding tools is conversational: "Build me a dashboard that shows project metrics." This forces the model to make dozens of assumptions about what "project metrics" means, how the data is structured, and what the UI should look like.
Instead, provide a structured spec that defines the requirements explicitly.
Before (chat prompt):
Build a dashboard that shows project metrics with charts and stuff.
After (structured spec):
## Feature: Project Dashboard
### Requirements
- Display a summary card for each project showing: name, spec count,
completion percentage, last updated date
- Show a bar chart of specs by status (draft, review, approved, archived)
- Include a filter dropdown to select time range (7d, 30d, 90d)
### Data Source
- GET /api/v1/projects/{id}/metrics
- Response includes: spec_count, status_breakdown, completion_pct, updated_at
### UI Pattern
- Follow the existing card grid layout used in ProjectListView
- Use the Chart component from @/components/ui/chart
- Match the existing color tokens: status-draft, status-review, etc.
The structured version eliminates guesswork. The model knows exactly which endpoint to call, what fields to expect, which components to reuse, and what visual pattern to follow.
Tip 2: Include Data Model Context
When AI tools generate code that interacts with your data, they need to know your data model. Without it, they invent field names. And invented field names are the number one source of bugs in AI-generated code.
Before (no model context):
Create a form to edit a specification.
The model might generate a form with fields like name, description, type, and status — reasonable guesses, but potentially wrong. Maybe your field is called title, not name. Maybe type is called spec_type. Maybe status is an enum with values the model does not know about.
After (with model context):
## Specification Model
| Field | Type | Notes |
|-------------|---------------------------------------------|--------------------|
| id | uuid | Read-only |
| title | string (max 255) | Required |
| description | string (nullable) | Optional, markdown |
| spec_type | enum: feature, api, data_model | Required |
| status | enum: draft, in_review, approved, archived | Default: draft |
| project_id | uuid (FK → projects.id) | Set on creation |
| created_at | datetime | Read-only |
| updated_at | datetime | Read-only |
Now the model knows the exact field names, types, constraints, and which fields are editable. The generated form will match your actual database schema.
Tip 3: Reference API Contracts
Frontend code needs to know what the backend expects and returns. When AI tools generate API calls without knowing your actual API contract, they guess at endpoint paths, request formats, and response shapes. This creates code that looks correct but fails at runtime.
Before (no contract):
Add a function to create a new spec in the project.
After (with contract):
## Create Specification
POST /api/v1/projects/{project_id}/specifications
### Request Body
```json
{
"title": "string (required)",
"description": "string | null",
"spec_type": "feature | api | data_model"
}
Response (201)
{
"id": "uuid",
"title": "string",
"description": "string | null",
"spec_type": "string",
"status": "draft",
"project_id": "uuid",
"created_at": "datetime",
"updated_at": "datetime"
}
Error Response (400)
{
"detail": [
{
"field": "title",
"message": "Title is required"
}
]
}
With the full contract, the model generates a function that sends the right fields, handles the right response shape, and parses errors correctly. Without it, you get code that might use `message` instead of `detail` for errors, or `name` instead of `title` in the request body.
## Tip 4: Give Examples of Expected Behavior
LLMs are exceptionally good at following examples. If you show them what correct output looks like for one case, they can extrapolate the pattern to similar cases.
**Before (no example):**
Write a custom hook for fetching notifications.
**After (with example from your codebase):**
````markdown
## Pattern: API Hooks
Follow the same pattern as the existing useProjects hook:
```typescript
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { apiClient } from "@/lib/api-client";
// Query key factory
const notificationKeys = {
all: ["notifications"] as const,
list: (filters: NotificationFilters) =>
[...notificationKeys.all, "list", filters] as const,
detail: (id: string) =>
[...notificationKeys.all, "detail", id] as const,
};
// Fetch hook
export function useNotifications(filters: NotificationFilters) {
return useQuery({
queryKey: notificationKeys.list(filters),
queryFn: () => apiClient.get<NotificationList>(
"/api/v1/notifications",
{ params: filters }
),
});
}
Use this exact pattern: query key factory at the top, typed API client, TanStack Query hooks with proper key management, and invalidation on mutations.
When you give the model a concrete example from your own codebase, the output follows your conventions: your query key pattern, your API client usage, your type naming, your file structure. Without it, the model uses whatever pattern it has seen most often in its training data, which is probably not yours.
## Tip 5: Use MCP for Persistent Context
Model Context Protocol (MCP) lets you connect external tools and data sources directly to your AI coding environment. Instead of manually pasting specs, data models, and API contracts into every prompt, you can configure an MCP server that provides this context automatically.
Here is a practical example of what an MCP server configuration might look like:
```json
{
"mcpServers": {
"intent-specs": {
"command": "npx",
"args": ["-y", "@anthropic/intent-mcp-server"],
"env": {
"INTENT_API_KEY": "your-api-key",
"INTENT_PROJECT_ID": "your-project-id"
}
}
}
}
```
With this configured, when you ask your AI tool to build a feature, it can pull the relevant spec, data models, and API contracts from Intent automatically. You do not need to copy-paste context into every prompt. The model always has access to the current, accurate state of your system design.
This is particularly powerful for teams. Every developer on the team gets the same context, which means the AI generates consistent code regardless of who is prompting it. The new hire gets the same quality output as the senior engineer because the context, not the prompt, is doing the heavy lifting.
## The Compound Effect
Each of these tips improves output quality on its own. Together, they compound.
A prompt with a structured spec, data model context, API contracts, a code example, and persistent MCP context gives the model almost everything it needs to generate code that works on the first try. You are no longer hoping the model guesses correctly. You are giving it the answers and asking it to assemble them.
Here is a side-by-side comparison of a typical interaction:
**Low-context prompt:**
```
Build the notification preferences page.
```
Likely result: Generic form with guessed field names, no API integration, wrong component library usage, inconsistent styling.
**High-context prompt (assembled from tips above):**
```markdown
Build the notification preferences page.
## Spec
(structured requirements with acceptance criteria)
## Data Model
(NotificationPreference schema with exact fields and types)
## API Contract
(GET and PATCH /notification-preferences with request/response shapes)
## Pattern Reference
(existing SettingsPage component as a template)
## Design Reference
(use SettingsLayout, FormSection, and ToggleGroup components)
```
Likely result: Working page that matches your codebase patterns, calls the right API, uses the right field names, follows your component conventions, and handles errors correctly.
## Practical Takeaways
Here is a summary of what to do:
1. **Write structured specs** with explicit requirements, not conversational prompts. Define what the feature does, what data it uses, and what patterns it follows.
2. **Include your data model** whenever the feature touches data. Field names, types, and constraints are the minimum. Relationships and enums are even better.
3. **Provide the API contract** for any endpoint the code will call or implement. Request format, response format, and error format.
4. **Show a code example** from your codebase that follows the pattern you want. One good example is worth a thousand words of instruction.
5. **Set up MCP** so this context is available automatically. Persistent context beats manual copy-paste every time.
The quality of AI-generated code is directly proportional to the quality of context you provide. The tools are already capable. The bottleneck is how well you tell them what to build.