Skip to content

How to Use AI Coding Assistants Effectively: Best Practices for Developers

The Divide

I noticed something strange in developer communities. Some developers say AI coding tools like Claude Code and Cursor have “honestly been working well” for them. Others complain about hallucinations, broken code, and wasted time.

The Reddit thread revealed the pattern. A developer posted about their success with AI coding, and the comments exposed the real difference:

“If you understand software development and how to write code - and work closely with your AI tools to ensure the code is solid and doing what you want it to do - that’s a far cry from vibe coding.”

This hit the core issue. The struggling developers weren’t using AI wrong technically. They were treating it as an autonomous code generator instead of a collaborator.

What “Vibe Coding” Looks Like

I’ve seen this pattern many times. A developer gives AI a vague request:

Implement user authentication with JWT

Then accepts the output without review. The code compiles. Tests pass (maybe). The developer commits and moves on.

Two weeks later, issues surface:

  • Password reset doesn’t work for edge cases
  • Token refresh has race conditions
  • Error messages leak sensitive information
  • No rate limiting on login endpoints

The developer blames the AI: “It gave me broken code.”

But the real problem: the prompt was too vague, and the output was never reviewed properly.

The Five Practices That Work

The successful developers in the thread shared common patterns. Here’s what they do differently.

Practice 1: Define Tasks Clearly

Vague prompts produce vague code. The successful OP emphasized “being very explicit about what you want.”

Compare these prompts:

// BAD: Vague, assumes context
Fix the bug in auth.ts
// GOOD: Specific, complete context
In auth.ts, the login function throws "Cannot read property 'id' of undefined"
when the user table has a null email column. Add null check before accessing
user.id and return 401 Unauthorized if email is missing. Keep existing
error handling pattern (try-catch with logging).

The good prompt specifies:

  • The exact symptom (null reference error)
  • The root cause (null email column)
  • The expected fix (null check, 401 response)
  • Constraints to maintain (existing error handling)

Practice 2: Provide Context Upfront

AI tools need the same context you’d give a human developer. The successful OP explicitly mentioned “giving the right context” as a key factor.

Before asking for code, I provide:

Context:
- This is a React app using TypeScript
- We use React Query for data fetching (see src/hooks/useQuery.ts for pattern)
- Error handling follows the pattern in src/utils/errors.ts
- All API calls go through src/api/client.ts
Task:
Add a hook to fetch user profile data using the existing patterns

This mirrors how I’d onboard a new team member. I wouldn’t hand them a ticket without background. Same principle applies to AI.

Practice 3: Ask for Planning Before Coding

The most powerful technique from the Reddit thread: “ask it to think through and plan the approach before writing code.”

I structure requests like this:

I need to add rate limiting to our API. Before implementing:
1. Identify which endpoints need rate limiting
2. Suggest where to add the middleware
3. Recommend rate limit values (requests per minute)
4. Outline how to handle rate limit exceeded errors

The AI responds with a plan:

Let me plan this first:
1. Rate limiting needed on: /api/auth/* (auth endpoints prone to abuse)
2. Add middleware in src/middleware/rateLimiter.ts, apply in app.ts
3. Suggest: 100 req/min for auth endpoints, 1000 req/min for others
4. Return 429 with Retry-After header, log blocked requests
Should I proceed with this plan?

This catches issues early. I can course-correct before the AI writes 200 lines of wrong code.

Practice 4: Keep Scope Small

Large requests produce unreliable output. The successful Reddit user emphasized “keeping the scope small.”

Instead of one big request:

// TOO LARGE
Build a complete REST API for user management with authentication,
validation, and CRUD operations

I break it into steps:

// Step 1: Define the model
Create a User model with: email (string, required, unique),
passwordHash (string), createdAt, updatedAt. Use existing Mongoose
pattern from models/Post.ts.
// Step 2: Add basic CRUD (after reviewing model)
Create userController with create, read, update, delete methods.
Follow the pattern in postController.ts. Return 404 for not found.
// Step 3: Add routes (after reviewing controller)
Add routes for /api/users using Express Router. Apply existing
authMiddleware to protect create, update, delete.

Each step is verifiable. If step 2 is wrong, I catch it before step 3 compounds the problem.

Practice 5: Review Output Rigorously

The top commenter’s insight: “work closely with your AI tools to ensure the code is solid.”

I review every line of generated code:

  • Does it handle edge cases?
  • Is error handling complete?
  • Are there hardcoded values?
  • Does it follow project patterns?
  • Are there security issues?

Treat AI output like code from a junior developer - useful, but needs review.

Why This Approach Works

The key insight from the Reddit thread:

“I wouldn’t hand requirements to AI that don’t meet the same standards as what I’d hand off to a software engineer.”

This reframes AI coding from “magic code generator” to “collaborative tool requiring management.”

When I shift perspective:

  • Lower expectations of autonomy = fewer disappointments
  • More preparation = better results
  • Active review = higher quality output
  • Iteration = refinement over time

The developers who struggle expect AI to replace human judgment. The developers who succeed use AI to amplify human judgment.

Common Mistakes I’ve Made

Mistake 1: Autopilot Mode

Here's a feature spec, implement it completely.

This almost always produces code that looks right but has subtle issues. The AI doesn’t ask clarifying questions; it makes assumptions. Those assumptions are often wrong.

Fix: Stay in the loop. Review after each small piece. Guide the AI like I’d guide a developer.

Mistake 2: Ignoring Project Context

Add a caching layer

Without mentioning our existing cache implementation, the AI might add Redis when we already use Memcached, or create a new caching pattern instead of extending the existing one.

Fix: Always provide relevant files and patterns.

Add caching using our existing cache.ts module, following the
pattern in userService.ts

Mistake 3: Skipping the Planning Phase

Sometimes I just want code fast:

Just write the code.

The AI jumps straight to implementation without considering alternatives or edge cases.

Fix: Always ask for a plan first. Even a brief outline reveals potential issues.

Mistake 4: Accepting Output Without Testing

The code compiles but has runtime errors. The tests pass but miss edge cases. The feature works but breaks existing functionality.

Fix: Test every piece of AI-generated code before moving on. Run the application. Check logs. Verify behavior.

A Mental Model That Helps

I think of AI coding assistants as junior developers who:

  • Have broad knowledge but no project context
  • Work fast but need supervision
  • Don’t ask enough questions
  • Need explicit instructions
  • Produce useful first drafts that need refinement

This framing helps me set appropriate expectations and use the right level of oversight.

Quick Reference: Prompt Structure

When I start a new AI coding session, I use this structure:

[Context]
Project: [brief description]
Relevant files: [list with brief descriptions]
Patterns to follow: [specific files or conventions]
[Task]
Specific request with:
- What to do
- Constraints
- Expected behavior
[Planning]
Before coding, outline:
- Files affected
- Step-by-step approach
- Edge cases to handle

Example:

[Context]
Project: Express.js API with TypeScript
Relevant files:
- src/models/User.ts (Mongoose model)
- src/controllers/authController.ts (auth pattern)
- src/middleware/errorHandler.ts (error handling)
Patterns: Follow existing controller structure, use async/await
[Task]
Add password reset functionality:
- POST /auth/forgot-password (accepts email, sends reset token)
- POST /auth/reset-password (accepts token + new password)
- Tokens expire in 1 hour
- Use bcrypt for hashing (already in project)
[Planning]
Outline your approach before implementing.

Summary

In this post, I showed why some developers succeed with AI coding assistants while others struggle. The key difference: successful users treat AI like a junior developer who needs clear instructions, proper context, and active oversight.

The five practices that work:

  1. Define tasks clearly with specific requirements
  2. Provide context upfront like onboarding a new team member
  3. Ask for planning before coding to catch issues early
  4. Keep scope small for verifiable output
  5. Review output rigorously before committing

The developers who struggle expect AI to replace human judgment. The developers who succeed use AI to amplify human judgment.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments