AI Vibe Coding with Cursor and Copilot: Prompts, Workflows and Where It Goes Wrong

Key takeaways

AI tools write code quickly but also produce broken code, lose project context and drift in style. The post compares Cursor, Copilot, Claude, Windsurf and v0, then covers @-context, project rules files, step-by-step prompting and security reviews so you still check what the AI writes.

Introduction

“Vibe coding” means describing what you want in natural language and letting an AI tool such as Cursor, GitHub Copilot or Claude write the first draft of the code. It is fast for boilerplate and small features, but the AI also produces code that does not run, loses track of the project structure, drifts in style between requests and occasionally introduces security holes. This post compares the main tools, shows prompt patterns and workflows for new projects, bug fixes and refactoring, and ends with the failure modes you need to review for.

The tools in this space change every few months, so specific model names, context sizes and prices are left out on purpose. What stays stable is the workflow: give the model the right context, ask for small changes, and verify each one as if a new colleague had written it.

What is Vibe Coding?

Traditional Coding vs Vibe Coding

Traditional Coding:

1. Google search → Stack Overflow
2. Read documentation
3. Write code
4. Debug
5. Refactor

Vibe Coding:

1. Request to AI: "Create a TODO app with React"
2. AI generates code
3. Feedback: "Add dark mode"
4. AI immediately modifies

The second list is shorter, but steps are hidden inside it. “AI generates code” still has to be read, run and tested, and the debugging step has not disappeared; it has moved to after the code exists instead of while you write it. That shift is the core trade-off: you type less and review more, and reviewing code you did not write is a different skill from writing it.

Core of Vibe Coding

Natural Language-Based Development:

Traditional:
for (int i = 0; i < arr.size(); i++) {
    if (arr[i] % 2 == 0) {
        result.push_back(arr[i]);
    }
}
Vibe Coding:
"Filter only even numbers from the array"
→ AI generates the loop (which you still review)

Advantages of Vibe Coding

AdvantageDescriptionCaveat
SpeedBoilerplate and repetitive code are drafted quicklyReview time does not disappear
LearningExposure to patterns and APIs you had not usedOnly if you read and question the output
ConsistencyRules files and prompts can enforce conventionsWithout them, style drifts between requests
FocusMore time for design and reviewDesign mistakes are still yours

Main AI Tools

Cursor

Features:

  • VS Code-based AI editor (existing VS Code extensions and settings mostly carry over)
  • Indexes the codebase so chat and edits can draw on files you did not open
  • Multi-file editing and an agent mode that can run terminal commands
  • Lets you choose between several model providers

Usage:

Cmd+K (Mac) / Ctrl+K (Windows)
→ "Convert this function to TypeScript"
Cmd+L (Mac) / Ctrl+L (Windows)
→ Chat mode conversation
@filename
→ Include specific file context

Practical Example:

Prompt: "@app.py Add JWT authentication to this Flask app"
AI Response:
1. Install flask-jwt-extended
2. Add JWT configuration
3. Create login endpoint
4. Apply protected route decorator
→ Review each change before accepting it

The review step in that example is not a formality. A generated JWT setup commonly hard-codes the signing secret in the source file, sets a very long or no token expiry, or leaves the login endpoint without rate limiting. None of these break the demo, which is exactly why they survive when changes are accepted without reading the diff.

GitHub Copilot

Features:

  • Inline autocomplete as you type
  • Chat, and an agent mode for multi-file changes
  • Runs inside existing editors (VS Code, JetBrains IDEs, Visual Studio, Vim/Neovim)

Usage:

// Function to save TODO list to localStorage
function saveTodos(todos) {
  // Press Tab for AI to autocomplete
  localStorage.setItem('todos', JSON.stringify(todos));
}

Practical Example:

# Binary search implementation
def binary_search(arr, target):
    # Tab → AI generates full implementation
    left, right = 0, len(arr) - 1
    while left <= right:
        mid = (left + right) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

Completion works best for code like this: well-known algorithms and idioms that appear many times in public code, where the function name and a comment pin down the intent. It is weakest where the right answer depends on something outside the current file, such as your project’s error-handling conventions or an internal API, because the model fills the gap with the most common pattern it has seen rather than yours. Note that this binary search silently assumes arr is sorted; the completion will not add that precondition unless you ask.

Claude (Anthropic)

Features:

  • Large context window, useful for pasting long files, logs or specifications
  • Strong at explaining code, design discussions and reviews
  • Available as a chat app, through the API, and as the Claude Code terminal agent; it is also one of the models selectable in Cursor and Copilot

Usage:

Prompt:
"Design an e-commerce system with microservices architecture.
- User service
- Product service
- Order service
- Payment service
Include API, database schema, and Docker Compose file for each service"
→ Full system design + implementation code generation

A request this large returns something that looks complete, and that is its danger. Treat the output as a draft design to argue with, not as an implementation. Ask follow-up questions about the parts that are hard in real systems, such as how an order and a payment stay consistent when one service fails, before generating any code.

Windsurf

Features:

  • VS Code-based AI editor in the same category as Cursor
  • Agent-style “flows” that combine chat, multi-file edits and commands
  • Has offered a free tier; check current plans, since pricing in this category changes often

v0 (Vercel)

Features:

  • Specialized in UI component generation
  • Natural language → React components, typically with Tailwind CSS and shadcn/ui
  • Live preview

Usage:

Prompt: "Create a profile card component with dark mode support"
→ Generates React component + Tailwind CSS
→ Check with the live preview
→ Copy code → Paste into project

Prompt Engineering

How to Write Effective Prompts

Bad Prompt:

"Create login functionality"

Good Prompt:

"Implement login functionality with Next.js 14 App Router.
Requirements:
- Email/password login
- JWT token-based authentication
- Persist login state (localStorage)
- Error message on login failure
- Input validation with Zod
- Styling with Tailwind CSS
File structure:
- app/login/page.tsx (login page)
- app/api/auth/login/route.ts (API)
- lib/auth.ts (JWT utility)
"

The good prompt works because it removes decisions the model would otherwise make silently: which router, which validation library, which files to create. Every unstated decision is filled with a guess, and guesses are where generated code diverges from your project. It also shows why the prompt is still your responsibility: storing a JWT in localStorage makes it readable by any script that runs on the page, so a single XSS bug leaks the token. The AI will implement the requirement exactly as written; if the requirement is a questionable design, you get a well-implemented questionable design. An httpOnly cookie is the usual safer choice.

Prompt Patterns

1. Role Assignment

"You are a senior backend developer.
Design a RESTful API with Node.js and PostgreSQL."

2. Provide Context

"The current project is based on Express.js,
and uses Prisma ORM.
Add a Post model to the existing User model and set up a 1:N relationship."

3. Provide Examples

"Create API responses in the following format:
{
  "success": true,
  "data": { ... },
  "error": null
}

4. Specify Constraints

"Use TypeScript,
minimize external libraries,
and add JSDoc comments to all functions."

Of these, providing context and examples tend to matter most in practice. A role line changes tone more than correctness, while pasting the existing schema.prisma or one existing endpoint gives the model something concrete to imitate, which is the most reliable way to get code that matches the rest of the project.

Multi-Turn Conversation Strategy

Step 1: Big Picture

"Design a blog system architecture"

Step 2: Specification

"Write the database schema for the User model with Prisma"

Step 3: Implementation

"Implement the signup API endpoint"

Step 4: Improvement

"Add email duplicate check and hash passwords with bcrypt"

Long conversations degrade. After many turns the model may start contradicting earlier decisions or reintroducing code you removed, because older messages compete with newer ones for attention. When that happens, start a new chat with a short summary of the decisions made so far and the current files attached, rather than arguing with the old thread.


Practical Workflows

Workflow 1: Starting a New Project

Traditional Method:

1. Think about project structure
2. Set up boilerplate
3. Implement basic features

Vibe Coding:

In Cursor:
1. "Set up a blog project with Next.js 14 + TypeScript + Tailwind + Prisma"
   → Project structure generation
2. "Add User, Post, Comment models to Prisma schema"
   → Database design
3. "Create a component that shows recent posts on the homepage"
   → UI implementation
4. "Add dark mode support"
   → Theme switching

For scaffolding, prefer the framework’s own generator (create-next-app and similar) and let the AI work on top of it. Generated project setups tend to mix configuration styles from different framework versions, which produces builds that work today and confusing errors after the next upgrade. Commit after each step so a bad suggestion is one git restore away.

Workflow 2: Bug Fixing

Traditional Method:

1. Analyze error logs
2. Search for the error
3. Modify code
4. Test

Vibe Coding:

In Cursor:
1. Copy error message
2. Cmd+L → "Fix this error"
3. AI analyzes cause + suggests solution
4. Automatically modifies code

“Fix this error” with only the message attached invites a fix for the symptom. The pattern I see most often is a TypeError: Cannot read properties of undefined answered with an added ?. or a null check, which makes the error go away while the real bug, the value being undefined in the first place, stays. Include the stack trace, the relevant code, and what you expected to happen, and ask for the cause before asking for the fix.

Workflow 3: Refactoring

Traditional Method:

1. Analyze code
2. Plan refactoring
3. Modify

Vibe Coding:

In Cursor:
"@app.py Refactor this file as follows:
1. Separate functions into smaller units
2. Add type hints
3. Add docstrings
4. Improve error handling
5. Performance optimization"
→ AI provides full refactoring + explanations

Five kinds of change in one request produce a diff that is hard to review, and behavior changes hide inside it. Refactoring is only safe when you can show behavior did not change, so have tests in place first (the AI is good at writing characterization tests for existing code), then request one kind of change at a time and run the tests after each.


Productivity Comparison

How much time an AI tool saves depends heavily on the task. Scaffolding a CRUD app, writing tests for existing functions or converting code between languages are areas where a first draft appears in minutes. Debugging a race condition, designing a data model or working in a large legacy codebase with implicit conventions benefit much less, because the hard part is understanding the problem, not typing the code.

The practical difference between tools is mostly about context. Completion works best inside the file you are editing, suggesting the next line or function. Chat and agent modes in Cursor, Copilot and Claude can take several files or a whole folder into account, which matters for changes that touch models, routes and tests at once. Whatever the tool, the time you save on writing is partly spent on reviewing, so a realistic measure is “time until the change is merged and working”, not “time until code appears”.


Best Practices

Use AI as a Pair Programmer

❌ Bad Usage:
"Create entire project" → Copy-paste → Done
✅ Good Usage:
"Design User model"
→ Review and feedback
→ "Add password hashing"
→ Review and learn
→ Repeat

Context Management

In Cursor:
@filename - Include specific file
@foldername - Include entire folder
@docs - Include documentation
@web - Include web search results
Example:
"@app.py @models.py Connect these two files
and add user authentication functionality"

More context is not automatically better. Attaching an entire large folder can push the relevant file out of focus and makes responses slower. Attach the files the change actually touches plus one example of the pattern to follow.

Incremental Improvement

Step 1: "Implement basic functionality"
Step 2: "Add error handling"
Step 3: "Write test code"
Step 4: "Optimize performance"
Step 5: "Add documentation"

Leverage Code Review

In Cursor:
"@app.py Review this code.
Analyze from the following perspectives:
1. Security vulnerabilities
2. Performance issues
3. Code smells
4. Improvement suggestions"
→ AI provides detailed review

An AI review is a useful second pass, not a replacement for linters, type checkers and tests. It tends to produce a long list mixing real issues with style preferences, and it can confidently flag correct code. Treat each finding as a hypothesis to verify.

Use as a Learning Tool

"Explain this code step by step"
"Explain why this method is better"
"Are there other approaches?"
"What are the pros and cons of this pattern?"
→ Learn deeply through conversation with AI

Troubleshooting

AI Generates Incorrect Code

Problem:

AI-generated code doesn't work

Solution:

1. Copy error message
2. "Fix this error" + error message
3. AI provides corrected code
Or:
"Explain why this code doesn't work"
→ Identify cause then request fix

A specific form of incorrect code is the invented API: a method that does not exist on the library version you use, or a package name that is not published. If the model keeps proposing the same wrong call after two rounds, stop and give it the real documentation or the library’s type definitions instead of asking again. Unfamiliar package names deserve a check on npm or PyPI before installing, since attackers have registered names that models tend to hallucinate.

Insufficient Context

Problem:

AI doesn't understand project structure

Solution:

In Cursor:
1. Include full structure with @foldername
2. Write project description in README.md
3. Tell AI "First understand the project structure"

Inconsistent Code

Problem:

AI generates code with different styles each time

Solution:

Create a project rules file (.cursor/rules/, or the older .cursorrules in the project root):
"""
Coding rules:
- Use TypeScript
- Functional programming style
- Follow ESLint + Prettier
- Add JSDoc to all functions
- Handle errors with try-catch
"""
→ AI follows rules

Rules files are instructions, not enforcement; the model follows them most of the time, not always. Keep them short and concrete, and let the real enforcement happen in tooling: ESLint, Prettier and the type checker in CI will catch the cases where the rules were ignored. Copilot has an equivalent in .github/copilot-instructions.md, and Claude Code reads a CLAUDE.md file.

Security Issues

Problem:

AI generates code with security vulnerabilities

Solution:

"Analyze security vulnerabilities in this code"
"Review based on OWASP Top 10"
"Add SQL Injection defense code"
→ Modify to security-hardened code

Asking the same model to review its own output catches some problems but shares its blind spots. Back it with tools that do not guess: a secret scanner before commits (API keys pasted into prompts end up in generated code more often than people expect), dependency audits (npm audit, pip-audit), and static analysis such as CodeQL or Semgrep. Queries built with string concatenation, disabled TLS verification and permissive CORS settings are the recurring issues worth searching for by hand.


Conclusion

AI-assisted coding changes where the effort goes: less typing, more specifying and reviewing.

Key Summary:

  1. Tools: Cursor (codebase-wide context in its own editor), Copilot (inline completion and chat in your existing editor), Claude (long-context design, review and terminal agent)
  2. Prompts: Write specifically and give real context
  3. Workflow: Small steps, commit often, test each change
  4. Learning: Grow by analyzing AI code

Cautions:

  • Don’t blindly trust AI; review every diff
  • Check security vulnerabilities with tools, not only prompts
  • Use code only after you understand it

Getting Started:

1. Install Cursor (cursor.com) or enable Copilot in your editor
2. Start with a simple project
3. Practice prompts
4. Gradually attempt complex tasks

Further Reading:


Frequently Asked Questions (FAQ)

Q. How do I stop the AI from writing code in a different style every time?

A. Put your conventions in a project rules file (.cursor/rules/ in Cursor, .github/copilot-instructions.md for Copilot) covering language, style, linting and error-handling rules, so they apply to every request, and give it project context with @filename or @foldername references instead of relying on it to guess the structure. Keep linters and formatters in CI as the actual enforcement.