<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Junkyu Jeon</title>
    <description>The latest articles on DEV Community by Junkyu Jeon (@jakay).</description>
    <link>https://dev.to/jakay</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3881762%2F9a9db767-95ae-450b-bb0c-4b74fd31e375.jpg</url>
      <title>DEV Community: Junkyu Jeon</title>
      <link>https://dev.to/jakay</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jakay"/>
    <language>en</language>
    <item>
      <title>Why AI Coding Goes Off the Rails — And How to Take Back Control</title>
      <dc:creator>Junkyu Jeon</dc:creator>
      <pubDate>Thu, 16 Apr 2026 06:44:54 +0000</pubDate>
      <link>https://dev.to/jakay/why-ai-coding-goes-off-the-rails-and-how-to-take-back-control-3nmb</link>
      <guid>https://dev.to/jakay/why-ai-coding-goes-off-the-rails-and-how-to-take-back-control-3nmb</guid>
      <description>&lt;h2&gt;
  
  
  The Honeymoon Phase
&lt;/h2&gt;

&lt;p&gt;You remember the moment. You opened Cursor, or Bolt, or Claude for the first time, typed something like "Build me a landing page with a hero section and a pricing table," and watched as fully functional code materialized in front of you. In minutes, not hours.&lt;/p&gt;

&lt;p&gt;You kept going. "Add a contact form." Done. "Make it responsive." Done. "Add dark mode." Done. You felt like a 10x developer. The possibilities felt limitless. You started telling friends about it. You stayed up late building things you'd been putting off for months.&lt;/p&gt;

&lt;p&gt;This phase is real. It's not a trick. AI coding tools genuinely are that powerful for getting something off the ground. The problem is what happens next.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Things Start Breaking
&lt;/h2&gt;

&lt;p&gt;It's subtle at first. Around the time your project hits 20-30 files, something shifts. The AI that was nailing everything starts... stumbling.&lt;/p&gt;

&lt;p&gt;You notice it in small ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It forgets decisions it made earlier.&lt;/strong&gt; You established a pattern for how components fetch data, but the AI starts using a completely different approach in new files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It contradicts its own patterns.&lt;/strong&gt; Half your files use one error-handling style, the other half use another. Neither is wrong, but the inconsistency makes everything harder to follow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It breaks existing features while adding new ones.&lt;/strong&gt; You ask for a new settings page and suddenly your authentication stops working.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Its fixes create new problems.&lt;/strong&gt; You point out a bug, it patches it, and now something else is broken. You fix that, and the original bug comes back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It goes in circles.&lt;/strong&gt; Fix this, breaks that. Fix that, breaks this. You spend an hour and end up back where you started, except now the code is messier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this sounds familiar, you're not alone. This is the experience of almost every developer who uses AI tools on a project beyond a certain size. And it's not because the AI got dumber or because you're doing something wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Happens: The Context Problem
&lt;/h2&gt;

&lt;p&gt;Here's the thing most people don't understand about AI coding tools: &lt;strong&gt;they have a limited working memory.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI models operate within something called a &lt;strong&gt;context window&lt;/strong&gt; — the amount of text they can "see" and think about at any given time. Think of it like a desk. When your project is small, all your files fit on the desk. The AI can see everything at once — every component, every function, every decision you've made together.&lt;/p&gt;

&lt;p&gt;But as your project grows, files start falling off the desk. The AI can only look at a slice of your codebase at a time. It literally &lt;strong&gt;cannot see&lt;/strong&gt; what's in the other files. It doesn't know what patterns were established. It doesn't remember what constraints exist. It doesn't recall the naming conventions you agreed on three sessions ago.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It's like asking someone to renovate your house, but they can only see one room at a time. They might pick a great paint color for the kitchen — that clashes horribly with the living room they can't see.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And here's the part that really stings: &lt;strong&gt;each conversation starts fresh.&lt;/strong&gt; When you open a new chat or start a new session, the AI has zero memory of your previous interactions. It doesn't know about the bug you spent two hours fixing yesterday. It doesn't know about the architectural decision you made last week. Every time, it's meeting your project for the first time.&lt;/p&gt;

&lt;p&gt;This isn't a flaw that'll be fixed in the next update. It's a fundamental characteristic of how these models work. And once you understand it, the frustrating behavior makes perfect sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Snowball Effect
&lt;/h2&gt;

&lt;p&gt;Here's where it gets really painful. When things break, the natural instinct is to tell the AI: "Fix it." And the AI obliges — it patches the immediate symptom. But because it can't see the full picture, it's not fixing the root cause. It's applying a band-aid.&lt;/p&gt;

&lt;p&gt;Each band-aid adds complexity. And that complexity makes it even harder for the AI to understand the codebase on the next request. So the next fix is even more likely to be a band-aid. And the cycle continues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Something breaks&lt;/li&gt;
&lt;li&gt;AI patches the symptom&lt;/li&gt;
&lt;li&gt;The patch adds complexity&lt;/li&gt;
&lt;li&gt;The added complexity causes something else to break&lt;/li&gt;
&lt;li&gt;Go to step 1&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your codebase becomes a patchwork of contradictory patterns, redundant logic, and fragile workarounds. Eventually the AI is spending more tokens trying to understand the mess than actually solving the problem you asked about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is why vibe coding hits a wall.&lt;/strong&gt; Not because AI is bad at coding. Not because you're not technical enough. But because the approach of "just keep prompting" doesn't scale. The more you build, the less effective each prompt becomes, until you're fighting the tool instead of building with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Shift: From Prompting to Harness Engineering
&lt;/h2&gt;

&lt;p&gt;When people hit this wall, their first instinct is to write better prompts. More detailed instructions. Longer explanations. "Be very careful not to break the auth system when you add this feature."&lt;/p&gt;

&lt;p&gt;This helps a little. But it's treating the symptom, not the disease. You can't prompt your way out of a structural problem.&lt;/p&gt;

&lt;p&gt;The real unlock is something we call &lt;strong&gt;harness engineering&lt;/strong&gt; — designing the environment and structure that the AI works within.&lt;/p&gt;

&lt;p&gt;Think about it this way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prompt engineering&lt;/strong&gt; is telling the AI &lt;em&gt;what&lt;/em&gt; to do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Harness engineering&lt;/strong&gt; is setting up &lt;em&gt;where&lt;/em&gt; and &lt;em&gt;how&lt;/em&gt; the AI works.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A "harness" is everything that surrounds the AI: the project structure, the rules it follows, the context it receives, the guardrails that catch its mistakes. It's the difference between dropping a builder in an empty field and saying "build a house," versus handing them blueprints, a materials list, and a building code.&lt;/p&gt;

&lt;p&gt;When you invest in the harness, the AI performs dramatically better — even with the exact same prompts you were using before. Because the environment compensates for the AI's limitations.&lt;/p&gt;

&lt;p&gt;Here's what harness engineering looks like in practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; / &lt;code&gt;.cursorrules&lt;/code&gt; files&lt;/strong&gt; that give the AI persistent context about your project — your tech stack, your conventions, your constraints. Things the AI would otherwise forget between sessions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean folder structure&lt;/strong&gt; so the AI only needs to see the relevant files for any given task, keeping more of the important context on that "desk."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Module boundaries&lt;/strong&gt; that limit the blast radius of AI changes. If the AI makes a mistake in the settings module, it shouldn't be able to break authentication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test suites&lt;/strong&gt; that catch when the AI breaks something, before you even notice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory systems&lt;/strong&gt; that preserve decisions across sessions — so the AI doesn't have to rediscover your architecture every time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these require you to be a senior engineer. They require you to think like an &lt;strong&gt;architect&lt;/strong&gt;, not a prompter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Steps to Take Back Control
&lt;/h2&gt;

&lt;p&gt;You don't need to rebuild everything from scratch. Here are concrete things you can do today to improve how AI works with your project:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Set Up a CLAUDE.md or .cursorrules
&lt;/h3&gt;

&lt;p&gt;This is the single highest-leverage thing you can do. Create a file that tells the AI everything it keeps forgetting: your tech stack, your folder structure, your naming conventions, your patterns, your constraints. The AI reads this at the start of every session, giving it the context it would otherwise lack.&lt;/p&gt;

&lt;p&gt;We wrote a full guide on how to do this well: &lt;a href="https://bivecode.com/blog/claude-md-guide" rel="noopener noreferrer"&gt;How to Make AI Tools Understand Your Codebase&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Break Your Project Into Modules
&lt;/h3&gt;

&lt;p&gt;Smaller, self-contained pieces mean less context needed per task. When your auth logic is in its own module with clear boundaries, the AI can work on it without needing to understand your entire app. Feature-based folder structure is a great starting point — see our guide on &lt;a href="https://bivecode.com/blog/structure-ai-project" rel="noopener noreferrer"&gt;how to structure your AI project&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Give AI One Job at a Time
&lt;/h3&gt;

&lt;p&gt;Instead of "Build the auth system with social login, password reset, 2FA, and admin panel," try:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Add a basic email/password login page"&lt;/li&gt;
&lt;li&gt;"Add password reset functionality"&lt;/li&gt;
&lt;li&gt;"Add Google OAuth login"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each focused request is more likely to succeed because it requires less context and produces smaller, more reviewable changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Add Tests Before Adding Features
&lt;/h3&gt;

&lt;p&gt;Tests are your safety net. When the AI changes something, tests tell you immediately if it broke something else. You don't need 100% coverage. Even basic tests for your critical paths — login works, data saves correctly, pages load — will catch the most damaging regressions.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Review Before Accepting
&lt;/h3&gt;

&lt;p&gt;This is the hardest habit to build. AI-generated code comes out fast and looks professional, so it's tempting to accept it without reading. Don't. Spend 30 seconds scanning each change:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does it follow your existing patterns?&lt;/li&gt;
&lt;li&gt;Did it change files it shouldn't have touched?&lt;/li&gt;
&lt;li&gt;Does the logic actually make sense, or does it just look right?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't need to understand every line. But you should understand the &lt;em&gt;shape&lt;/em&gt; of the change.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Version Control Religiously
&lt;/h3&gt;

&lt;p&gt;Commit every working state. Every one. When the AI inevitably breaks something, you can roll back to the last good version instead of trying to untangle the damage. &lt;code&gt;git commit&lt;/code&gt; is your undo button. Use it early and often.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mindset Shift
&lt;/h2&gt;

&lt;p&gt;Here's the most important thing to internalize: &lt;strong&gt;you're not a prompter. You're an architect.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The AI is the builder. It's fast, it's capable, and it works tirelessly. But builders need blueprints. They need someone who understands the whole structure, who can see across all the rooms at once, who makes sure the kitchen paint works with the living room.&lt;/p&gt;

&lt;p&gt;That's you.&lt;/p&gt;

&lt;p&gt;The best AI-assisted developers don't write the most code. They don't write the fanciest prompts. They design the best &lt;strong&gt;structures&lt;/strong&gt; for AI to work within. They set up the harness so well that the AI almost can't fail.&lt;/p&gt;

&lt;p&gt;And that's a skill worth developing — because as AI tools get better, the people who know how to direct them effectively will build things the rest of the world didn't think were possible.&lt;/p&gt;




&lt;p&gt;If your project has already gone off the rails and you're not sure how to get it back on track, &lt;a href="https://bivecode.com/rescue" rel="noopener noreferrer"&gt;we can help&lt;/a&gt;. We specialize in taking AI-built codebases and giving them the structure they need to keep growing.&lt;/p&gt;

&lt;p&gt;And if you want to prevent this from happening on your next project, start with our guide on &lt;a href="https://bivecode.com/blog/structure-ai-project" rel="noopener noreferrer"&gt;how to structure your AI project from day one&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://bivecode.com/blog/why-ai-coding-goes-off-rails" rel="noopener noreferrer"&gt;bivecode.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Cursor Rules vs CLAUDE.md: When to Use Which (And How They Work Together)</title>
      <dc:creator>Junkyu Jeon</dc:creator>
      <pubDate>Thu, 16 Apr 2026 06:39:12 +0000</pubDate>
      <link>https://dev.to/jakay/cursor-rules-vs-claudemd-when-to-use-which-and-how-they-work-together-5hg</link>
      <guid>https://dev.to/jakay/cursor-rules-vs-claudemd-when-to-use-which-and-how-they-work-together-5hg</guid>
      <description>&lt;p&gt;If you're using AI coding tools seriously, you've probably heard of both &lt;code&gt;.cursorrules&lt;/code&gt; and &lt;code&gt;CLAUDE.md&lt;/code&gt;. They solve the same fundamental problem: &lt;strong&gt;giving AI persistent context about your project.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Without them, every AI session starts from scratch. The AI doesn't know your tech stack, your folder conventions, your naming patterns, or the quirks that make your project unique. You end up repeating the same instructions over and over.&lt;/p&gt;

&lt;p&gt;Both files fix this. But they work differently, they're read by different tools, and they have different strengths. Most developers pick one and ignore the other. That's a mistake — they're most powerful when used together.&lt;/p&gt;

&lt;h2&gt;
  
  
  What .cursorrules Does
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;.cursorrules&lt;/code&gt; is Cursor's project-level configuration file. When you open a project in Cursor, it automatically reads this file and applies the rules to every AI interaction within that project.&lt;/p&gt;

&lt;p&gt;Key characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tool-specific&lt;/strong&gt; — only Cursor reads it. Other AI tools ignore it completely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instruction-focused&lt;/strong&gt; — best for telling the AI what to do and what not to do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Applies to all interactions&lt;/strong&gt; — every chat, every inline edit, every Cmd+K generation follows these rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supports glob patterns&lt;/strong&gt; — you can scope rules to specific file types or directories.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A typical &lt;code&gt;.cursorrules&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are an expert in TypeScript, React, Next.js App Router, and Tailwind CSS.

Key Principles:
- Write concise, technical TypeScript code
- Use functional and declarative programming patterns
- Prefer iteration and modularization over duplication
- Use descriptive variable names with auxiliary verbs (isLoading, hasError)

Naming Conventions:
- Components: PascalCase (UserProfile.tsx)
- Hooks: camelCase with "use" prefix (useAuth.ts)
- Utilities: camelCase (formatDate.ts)
- Constants: SCREAMING_SNAKE_CASE

DO NOT:
- Use `any` type
- Use default exports (except for pages)
- Put business logic in components
- Use inline styles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What CLAUDE.md Does
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; is Claude's project context file. Claude Code, Claude in the terminal, and other Claude-powered tools read it automatically when they enter your project directory.&lt;/p&gt;

&lt;p&gt;Key characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tool-specific&lt;/strong&gt; — Claude tools read it. Cursor doesn't (unless you explicitly include it in context).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context-focused&lt;/strong&gt; — best for explaining your project's architecture, decisions, and constraints. Not just rules, but &lt;em&gt;why&lt;/em&gt; those rules exist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hierarchical&lt;/strong&gt; — you can have a root &lt;code&gt;CLAUDE.md&lt;/code&gt; plus subdirectory-specific ones (e.g., &lt;code&gt;src/components/CLAUDE.md&lt;/code&gt;). Claude merges them based on what files it's working with.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supports project commands&lt;/strong&gt; — you can define shortcuts for common tasks like building, testing, and deploying.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A typical &lt;code&gt;CLAUDE.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Project Overview&lt;/span&gt;
Next.js 15 SaaS app for invoice management.
&lt;span class="p"&gt;-&lt;/span&gt; Frontend: React 19, TypeScript, Tailwind CSS
&lt;span class="p"&gt;-&lt;/span&gt; Backend: Next.js API routes, Drizzle ORM
&lt;span class="p"&gt;-&lt;/span&gt; Database: PostgreSQL (Supabase)
&lt;span class="p"&gt;-&lt;/span&gt; Auth: Better Auth with Google OAuth
&lt;span class="p"&gt;-&lt;/span&gt; Payments: Stripe
&lt;span class="p"&gt;-&lt;/span&gt; Deployment: Vercel

&lt;span class="gh"&gt;# Architecture&lt;/span&gt;
Feature-based folder structure:
src/
  app/         # Pages and API routes
  features/    # Feature modules (auth/, billing/, invoices/)
  components/  # Shared UI components
  lib/         # Utilities, DB client, API helpers

&lt;span class="gh"&gt;# Important Constraints&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Auth middleware runs on Edge Runtime — no Node.js APIs
&lt;span class="p"&gt;-&lt;/span&gt; All monetary values stored as integers (cents), displayed as decimals
&lt;span class="p"&gt;-&lt;/span&gt; Invoices use optimistic locking — always check version before update

&lt;span class="gh"&gt;# Commands&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Build: npm run build
&lt;span class="p"&gt;-&lt;/span&gt; Test: npm run test
&lt;span class="p"&gt;-&lt;/span&gt; Dev: npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Key Differences
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;.cursorrules&lt;/th&gt;
&lt;th&gt;CLAUDE.md&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read by&lt;/td&gt;
&lt;td&gt;Cursor only&lt;/td&gt;
&lt;td&gt;Claude tools only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primary strength&lt;/td&gt;
&lt;td&gt;Coding rules and patterns&lt;/td&gt;
&lt;td&gt;Project context and architecture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tone&lt;/td&gt;
&lt;td&gt;Imperative ("Do this, don't do that")&lt;/td&gt;
&lt;td&gt;Descriptive ("Here's how the project works")&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hierarchy&lt;/td&gt;
&lt;td&gt;Single file at project root&lt;/td&gt;
&lt;td&gt;Root + subdirectory files, merged contextually&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;Enforcing code style and patterns&lt;/td&gt;
&lt;td&gt;Explaining architecture and constraints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Project commands&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;Supported (build, test, lint)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glob scoping&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;Via subdirectory files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  When to Use Which
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Use .cursorrules when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Your team primarily uses Cursor as their AI coding tool&lt;/li&gt;
&lt;li&gt;You want to enforce strict coding patterns across all AI interactions&lt;/li&gt;
&lt;li&gt;Your rules are mostly about code style: naming, imports, error handling patterns&lt;/li&gt;
&lt;li&gt;You want rules that apply to inline edits and quick generations, not just chat&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use CLAUDE.md when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Your team uses Claude Code or Claude-powered tools&lt;/li&gt;
&lt;li&gt;You need to document complex architecture that requires explanation, not just rules&lt;/li&gt;
&lt;li&gt;Your project has non-obvious constraints (edge runtime limitations, data format decisions, etc.)&lt;/li&gt;
&lt;li&gt;You want project commands integrated into the AI workflow&lt;/li&gt;
&lt;li&gt;Different parts of the codebase need different context (use subdirectory CLAUDE.md files)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use both when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Team members use different AI tools (some on Cursor, some on Claude)&lt;/li&gt;
&lt;li&gt;You want consistent AI behavior regardless of which tool is used&lt;/li&gt;
&lt;li&gt;You want the best of both: Cursor's pattern enforcement + Claude's architectural context&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Use Them Together
&lt;/h2&gt;

&lt;p&gt;Here's the approach that works best: &lt;strong&gt;put architectural context in CLAUDE.md, put coding rules in .cursorrules, and keep them in sync.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What goes in CLAUDE.md only:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Project overview and tech stack&lt;/li&gt;
&lt;li&gt;Architecture decisions and their reasoning&lt;/li&gt;
&lt;li&gt;Non-obvious constraints ("monetary values are stored as cents because...")&lt;/li&gt;
&lt;li&gt;Common tasks with step-by-step instructions&lt;/li&gt;
&lt;li&gt;Build/test/deploy commands&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What goes in .cursorrules only:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The persona prompt ("You are an expert in...")&lt;/li&gt;
&lt;li&gt;Cursor-specific behaviors (how to handle inline edits, tab completions)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What goes in both (keep synced):
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Naming conventions&lt;/li&gt;
&lt;li&gt;Folder structure&lt;/li&gt;
&lt;li&gt;Import patterns&lt;/li&gt;
&lt;li&gt;Error handling approach&lt;/li&gt;
&lt;li&gt;"Do not" rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yes, there's some duplication. That's the cost of supporting multiple tools. The alternative — having rules in one file and not the other — means your AI behavior is inconsistent depending on which tool you're using. That's worse.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keeping them in sync
&lt;/h3&gt;

&lt;p&gt;The practical approach: treat &lt;code&gt;CLAUDE.md&lt;/code&gt; as the source of truth for architecture and constraints, and &lt;code&gt;.cursorrules&lt;/code&gt; as the source of truth for coding patterns. When you update a shared rule (like naming conventions), update both files. It takes 30 seconds and prevents drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Making them too long
&lt;/h3&gt;

&lt;p&gt;Both files compete for the AI's attention window. A 2,000-line rules file means the AI is spending context on your instructions instead of on understanding the code it's working with. Aim for 200-500 lines max for each file.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Being vague
&lt;/h3&gt;

&lt;p&gt;"Write clean code" is useless in both files. "Use named exports for all non-page files" is actionable. Be specific enough that there's only one way to interpret the rule.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Never updating them
&lt;/h3&gt;

&lt;p&gt;Your project evolves. Your rules should too. If you switched from REST to tRPC three months ago but your CLAUDE.md still describes REST patterns, the AI will generate REST code. Review your files monthly.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Not including examples
&lt;/h3&gt;

&lt;p&gt;AI tools are pattern matchers. A rule like "use the repository pattern for database access" is vague. A rule with a 5-line code example of what that looks like in your project is crystal clear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started Today
&lt;/h2&gt;

&lt;p&gt;If you have neither file, here's the 30-minute setup:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create CLAUDE.md&lt;/strong&gt; — write your project overview, tech stack, folder structure, and top 5 constraints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create .cursorrules&lt;/strong&gt; — write your coding conventions, naming rules, and "do not" list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy shared rules&lt;/strong&gt; — naming conventions, folder structure, and import patterns should appear in both.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test it&lt;/strong&gt; — ask both tools to create a new component. Does the output match your conventions? If not, your rules aren't specific enough.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you already have one file, creating the other takes 15 minutes — most of the thinking is already done.&lt;/p&gt;




&lt;p&gt;For a deeper dive into CLAUDE.md specifically, check out our &lt;a href="https://bivecode.com/blog/claude-md-guide" rel="noopener noreferrer"&gt;CLAUDE.md setup guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://bivecode.com/blog/cursor-rules-vs-claude-md" rel="noopener noreferrer"&gt;bivecode.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>The Vibe Coding Security Checklist: 7 Things to Check Before You Ship</title>
      <dc:creator>Junkyu Jeon</dc:creator>
      <pubDate>Thu, 16 Apr 2026 06:36:05 +0000</pubDate>
      <link>https://dev.to/jakay/the-vibe-coding-security-checklist-7-things-to-check-before-you-ship-4e66</link>
      <guid>https://dev.to/jakay/the-vibe-coding-security-checklist-7-things-to-check-before-you-ship-4e66</guid>
      <description>&lt;p&gt;Here's a stat that should keep you up at night: &lt;strong&gt;24.7% of AI-generated code contains security vulnerabilities.&lt;/strong&gt; Nearly 45% of those hit the OWASP Top 10 — the most common, most exploitable categories of web security flaws.&lt;/p&gt;

&lt;p&gt;This isn't because AI is bad at coding. It's because AI optimizes for making things &lt;em&gt;work&lt;/em&gt;, not making them &lt;em&gt;safe&lt;/em&gt;. When you ask it to "add a user login," it builds a functional login. It doesn't think about session fixation, brute force protection, or what happens when someone puts a SQL statement in the email field.&lt;/p&gt;

&lt;p&gt;If you've built an app with Cursor, Bolt.new, Lovable, or any AI coding tool and you're about to ship it to real users — run through this checklist first. Each item takes 5-15 minutes. All of them together could save you from a breach that kills your product.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Secrets Are Not in Your Code
&lt;/h2&gt;

&lt;p&gt;This is the most common and most dangerous mistake in AI-built apps. AI tools love to hardcode API keys, database URLs, and secrets directly in the source code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API keys in JavaScript/TypeScript files (search for &lt;code&gt;sk-&lt;/code&gt;, &lt;code&gt;pk_&lt;/code&gt;, &lt;code&gt;key_&lt;/code&gt;, &lt;code&gt;secret&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Database connection strings in source code&lt;/li&gt;
&lt;li&gt;JWT secrets hardcoded in auth files&lt;/li&gt;
&lt;li&gt;Third-party service credentials (Stripe, SendGrid, AWS) in client-side code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Search your codebase for exposed secrets&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"sk_live&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;sk_test&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;secret&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;api_key&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;apiKey"&lt;/span&gt; src/

&lt;span class="c"&gt;# Move all secrets to environment variables&lt;/span&gt;
&lt;span class="c"&gt;# .env.local (never committed to git)&lt;/span&gt;
&lt;span class="nv"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres://...
&lt;span class="nv"&gt;STRIPE_SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk_live_...
&lt;span class="nv"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-secret-here

&lt;span class="c"&gt;# .gitignore (make sure this exists)&lt;/span&gt;
.env
.env.local
.env.production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Critical:&lt;/strong&gt; If secrets were ever committed to git, they're in the history even after removal. Rotate them immediately — generate new keys and revoke the old ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. All User Input Is Validated
&lt;/h2&gt;

&lt;p&gt;AI-generated code almost never validates input properly. It trusts whatever the user sends, which opens the door to injection attacks, crashes, and data corruption.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Form inputs used directly without validation&lt;/li&gt;
&lt;li&gt;API route parameters used without type checking&lt;/li&gt;
&lt;li&gt;Database queries built from user input (SQL injection risk)&lt;/li&gt;
&lt;li&gt;File uploads without type/size restrictions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Use Zod for input validation in API routes&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CreateUserSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;CreateUserSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safeParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// ... safe to use&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validate on the server, always. Client-side validation is for UX — server-side validation is for security. Never trust the client.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Authentication Is Actually Protecting Routes
&lt;/h2&gt;

&lt;p&gt;AI tools often generate auth that &lt;em&gt;looks&lt;/em&gt; right but has gaps. The login page works, but the API routes behind it? Wide open.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API routes that should require authentication but don't check for a session&lt;/li&gt;
&lt;li&gt;Middleware that checks auth on some routes but misses others&lt;/li&gt;
&lt;li&gt;Admin-only endpoints accessible to regular users (broken access control)&lt;/li&gt;
&lt;li&gt;Direct object references — can user A access user B's data by changing an ID in the URL?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Quick test:&lt;/strong&gt; Open your browser's dev tools, find an API call your app makes, copy it as a cURL command, remove the auth cookie/token, and run it. If it still returns data, your route isn't protected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Every protected API route should start with this&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unauthorized&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// For user-specific data, always filter by the authenticated user&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// NOT from URL params&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. No Sensitive Data in Client-Side Code
&lt;/h2&gt;

&lt;p&gt;Everything in your frontend JavaScript is visible to anyone who opens dev tools. AI tools frequently put things on the client side that should stay on the server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API keys in &lt;code&gt;.env&lt;/code&gt; files prefixed with &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; that shouldn't be public&lt;/li&gt;
&lt;li&gt;Business logic that reveals pricing algorithms, discount rules, or internal calculations&lt;/li&gt;
&lt;li&gt;Error messages that expose database schema, file paths, or internal system details&lt;/li&gt;
&lt;li&gt;User data from other users leaking into API responses (overfetching)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only prefix env variables with &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; if they're truly meant to be public&lt;/li&gt;
&lt;li&gt;Move business logic to server-side API routes or server components&lt;/li&gt;
&lt;li&gt;Return generic error messages to the client, log detailed errors on the server&lt;/li&gt;
&lt;li&gt;Shape API responses to include only what the requesting user needs to see&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Rate Limiting Exists
&lt;/h2&gt;

&lt;p&gt;Without rate limiting, anyone can hammer your API thousands of times per second. This leads to DDoS vulnerability, brute force attacks on login, and runaway costs on pay-per-use services (like AI APIs).&lt;/p&gt;

&lt;p&gt;AI-generated code almost never includes rate limiting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to protect:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Login/signup endpoints&lt;/strong&gt; — prevent brute force (5-10 attempts per minute)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI generation endpoints&lt;/strong&gt; — prevent cost abuse&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password reset&lt;/strong&gt; — prevent email bombing (2-3 per hour)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;General API&lt;/strong&gt; — prevent abuse (100-1000 requests per minute)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Using Upstash Redis for serverless rate limiting&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Ratelimit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@upstash/ratelimit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@upstash/redis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ratelimit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Ratelimit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEnv&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;limiter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Ratelimit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slidingWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1 m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-forwarded-for&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;127.0.0.1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ratelimit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Too many requests&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// ... handle request&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Dependencies Are Not a Liability
&lt;/h2&gt;

&lt;p&gt;AI tools add npm packages liberally. Each dependency is code you didn't write running in your app. Some may have known vulnerabilities, be abandoned, or even be malicious.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check for known vulnerabilities&lt;/span&gt;
npm audit

&lt;span class="c"&gt;# See what you've got installed&lt;/span&gt;
npm &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;--depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;span class="c"&gt;# Check for unused dependencies&lt;/span&gt;
npx depcheck
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fix critical and high vulnerabilities. Remove packages you don't use.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. HTTPS, Headers, and CORS Are Configured
&lt;/h2&gt;

&lt;p&gt;The boring infrastructure stuff that AI never sets up but attackers always check.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Essential security headers&lt;/strong&gt; (add to &lt;code&gt;next.config.ts&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;securityHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-Content-Type-Options&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nosniff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-Frame-Options&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DENY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-XSS-Protection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1; mode=block&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Referrer-Policy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;strict-origin-when-cross-origin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/(.*)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;securityHeaders&lt;/span&gt; &lt;span class="p"&gt;}];&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;CORS:&lt;/strong&gt; If your API is only used by your own frontend, don't enable CORS at all. If you need CORS, whitelist specific origins — never use &lt;code&gt;*&lt;/code&gt; in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 15-Minute Security Sprint
&lt;/h2&gt;

&lt;p&gt;If you can only do one thing from this list, here's the priority order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Secrets audit&lt;/strong&gt; (5 min) — grep for exposed keys. Highest-impact, easiest-to-exploit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth check&lt;/strong&gt; (5 min) — test your API routes without authentication. Find the gaps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Input validation&lt;/strong&gt; (5 min) — add Zod to your most critical endpoint.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These three catch the majority of real-world attacks on AI-built apps.&lt;/p&gt;




&lt;p&gt;A security breach doesn't just break your app. It breaks trust. Users whose data gets leaked don't come back.&lt;/p&gt;

&lt;p&gt;AI made it possible to build an app in a weekend. But shipping it without a security review is like driving a car without brakes — it works fine until it doesn't, and when it fails, it fails catastrophically.&lt;/p&gt;

&lt;p&gt;Spend the hour. Run the checklist. Ship with confidence.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://bivecode.com/blog/vibe-coding-security-checklist" rel="noopener noreferrer"&gt;bivecode.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
