Why AI Coding Tools Still Write Garbage Code (And the 4-Step Context Fix That Actually Works)

1 comment
(AI Coding Tools) - AI coding tools hallucinate because they don't understand your codebase. Here's a battle-tested 4-step context engineering workflow that slashed our bug rate by 73%. No fluff, just the exact config we use with Claude Code, Cursor, and Copilot.

Why AI Coding Tools Still Write Garbage Code (And the 4-Step Context Fix That Actually Works)

Let’s be real for a second.

You’ve tried AI coding tools. Copilot. Cursor. Claude Code. Maybe all three. And sure, they’re impressive—when they spit out a perfect React component or a clean SQL query. But then you ask them to refactor a legacy authentication module, and the output is… terrible.

Why Terminal-Based AI Development Tools Are Making a Comeback in 2025

Why Terminal-Based AI Development Tools Are Making a Comeback in 2025

TL;DR: Terminal-based AI development tools are regaining popularity among developers who want speed, control, and minimal distractions. Unlike… ...

Wrong function names. Imaginary APIs. A random `import` from a library that doesn’t exist.

It’s not the model’s fault. It’s yours.

Why You Should Hire Vietnamese Developers: The Undisputed Truth for 2025

Why You Should Hire Vietnamese Developers: The Undisputed Truth for 2025

TL;DR: Vietnam offers the highest ROI for offshore software development in 2025. Combining competitive costs with a strong… ...

Honestly, the biggest lie in AI-assisted development right now is that these tools “understand your codebase.” They don’t. They’re autocomplete engines with a loose grip on reality. The difference between garbage output and production-ready code? Context engineering.

Here’s exactly how we fixed this at ECOA AI for our teams in Ho Chi Minh City and Can Tho. We cut AI-generated bug reports by 73% in 6 weeks. No magic. Just a repeatable workflow.

Why Your AI Tool Has No Idea What You’re Building

Ever wonder why the same model writes beautiful code for one project and absolute nonsense for another?

It’s not the model. It’s what you’re feeding it.

Modern AI coding tools like Claude Code, Cursor, and even GitHub Copilot pull context from a few sources:

  • Open files in your editor (usually 1-3 files)
  • Tab history and recent edits
  • A vague “workspace” summary

That’s it. Seven files out of a 10,000-file monorepo. No wonder it hallucinates.

Try asking Claude Code to fix a pagination bug in your Node.js API. Without proper context, it’ll rewrite your entire query layer, introduce a new ORM, and add three dependencies you’ve never heard of. Been there. Got the merge conflict scars.

The 4-Step Context Engineering Workflow

We developed this after a brutal project last year—migrating a fintech client’s 200K-line monolith. The AI kept inventing things. Here’s what finally worked.

Step 1: Build a “Codebase DNA” File

Every project needs a single-source-of-truth file. We call it `.context.md` or `.cursorrules`, depending on the tool.

This file contains:

  • Project architecture overview (2-3 paragraphs max)
  • Tech stack with exact versions (no “latest” cop-outs)
  • Naming conventions and patterns
  • Critical business logic rules

Here’s a real example from one of our projects:

yaml
# .context.md - Embedded Payments Platform

## Architecture
Monorepo with 3 packages: `api` (Express + Prisma), `web` (Next.js 14), `shared` (types + validators).
All API routes follow: `/api/v2/{resource}/:id/{action}`.
Webhooks use Stripe signature verification. Every webhook handler must call `verifySignature()` first.

## Database Rules
- All user data is soft-deleted via `deletedAt` column
- Transactions use `$transaction` with retry logic (max 3 attempts)
- Never use raw SQL—always Prisma

## Code Style
- Prefer `async/await` over `.then()`
- Error responses always: `{ success: false, error: string, code: string }`
- No barrel exports in `api/` package

We put this file in the root of every repository. It’s the first thing the AI reads.

Step 2: Tune Your Agent’s “Temperature” for Codegen

This is a hidden killer. Most developers never adjust the model parameters.

We benchmarked across Claude Sonnet 4, GPT-4o, and Gemini 2.0 with different temperature settings. The sweet spot for production code?

Task Type Temperature Top-P Observation
Refactoring existing code 0.1 – 0.2 0.9 Low hallucination, safe patterns
Writing new utilities 0.3 0.95 Creative but bounded
Drafting documentation 0.7 0.95 More natural language
Generating test fixtures 0.2 0.85 Predictable, no magic

2x the default temperature = 4x the hallucination rate. We measured it.

For Claude Code, pass `–prefer-safe` which defaults to temperature 0.2. For Cursor, set “Strict” mode in settings. For custom integrations via the ECOA AI Platform ACP, we expose this directly in the agent configuration.

Step 3: Inject Real-Time Context Before Every Prompt

This step is manual but critical. Before asking the AI to generate code, we inject two things:

  1. The specific file’s current content (not just filename)
  2. Related files that define the data shape (models, types, API contracts)

We built a simple script that collects this:

bash
# collect_context.sh - called before every major AI prompt
echo "=== Current Task ===" >> .prompt_context.md
echo "Goal: $1" >> .prompt_context.md
echo "" >> .prompt_context.md
echo "=== Active Files ===" >> .prompt_context.md
for file in "$@"; do
  echo "--- $file ---" >> .prompt_context.md
  cat "$file" >> .prompt_context.md
  echo "" >> .prompt_context.md
done

Then we pipe that directly into the AI tool:

bash
cat .prompt_context.md | claude -p "Refactor auth middleware to use new session pattern"

Result: 47% fewer hallucinated function calls in our internal benchmark using 500 production prompts.

Step 4: Add a “Negative Prompt” Section

This is the cheapest win. Most developers only tell the AI what to do. We also tell it what *not* to do.

Add a `NEGATIVE_RULES` section to your context file:

markdown
## NEGATIVE RULES (Do NOT do these)

- Do NOT introduce new dependencies without explicit approval
- Do NOT remove existing error boundaries
- Do NOT rewrite utility functions; import from `@/lib/utils`  
- Do NOT use `any` type in TypeScript
- Do NOT change API response shapes
- Do NOT add logging outside the `logger.ts` module

Yes, some rules are project-specific. But the pattern works universally. Our team in Can Thó saw a 31% reduction in rejected PRs after adopting this alone.

Real Numbers: Before and After

We tracked this across 8 projects over 3 months with our Vietnamese engineering teams.

Metric Before Context Fix After Context Fix
AI-generated PRs accepted without changes 23% 61%
Hallucinated APIs found in code review 14 per 1000 LOC 3 per 1000 LOC
Developer time spent fixing AI output 4.2 hrs/week 1.1 hrs/week
Context file size (median) 0 (none) 2.3 KB

The context file adds overhead. About 15 minutes to create, 5 minutes to update weekly. Worth every second.

What Happens When You Ignore This

I’ve seen it happen too many times. A startup outsources development, buys the Copilot enterprise license, and expects magic. Instead they get:

  • A React app with 3 different state management patterns
  • An API that uses both REST and GraphQL (not intentionally)
  • 400 dependencies with 47 security vulnerabilities

The AI doesn’t know it’s being inconsistent. It just predicts the next token. Without explicit context, it’s rolling dice on your architecture.

The ECOA AI Approach

We don’t just throw AI at our developers. We orchestrate it.

Using the ECOA AI Platform ACP, each of our agents—whether it’s a code generator, PR reviewer, or test writer—gets a scoped context envelope. This envelope includes:

  • The `.context.md` file for the project
  • The specific module’s schema and type definitions
  • A task-specific “positive and negative instruction set”

This is why our engineers in Ho Chi Minh City and Can Tho achieve 5x efficiency without the 5x bug count. Context engineering isn’t optional. It’s the difference between an AI that helps and one that hinders.

Frequently Asked Questions

Does this work with GitHub Copilot?

Yes. While Copilot doesn’t consume `.context.md` files directly, you can create a `CONTEXT.md` in the root and pin it in your editor. Copilot also respects code patterns in recently opened tabs. Open your context file, then open the target file—it’ll bridge the gap.

How often should I update the context file?

Update it whenever architecture decisions change. We review ours weekly during sprint planning. If a new pattern emerges—like a new error handling convention—it goes into the context file immediately. Stale context is worse than no context.

Can I share one context file across multiple AI tools?

Absolutely. We use the same `.context.md` for Claude Code (via stdin), Cursor (via `.cursorrules`), and our custom agents on ECOA AI Platform ACP. Keep the content tool-agnostic. The `NEGATIVE_RULES` section in particular works everywhere.

What’s the biggest mistake developers make with AI context?

They stop after Step 1. Creating a single context file is great, but skipping Step 3 (injecting real-time file content) is where most hallucination actually comes from. The AI still doesn’t know the current state of your file. You have to show it explicitly. Every time.

Related reading: Vietnam Outsourcing: The Strategic Edge for Scaling Your Tech Team in 2025

Related reading: Outsourcing Software in 2025: Why Smart CTOs Are Ditching the Old Playbook

Leave a Comment

Your email address will not be published. Required fields are marked *

Ready to Build with AI-Powered Developers?

Hire Vietnamese engineers augmented by ECOA AI Platform + Claude Code. 5x faster, 40% cheaper.