7 Tips to Make Your AI Agent More Predictable A developer shared seven tips for making AI coding agents more predictable, based on lessons from building a link-sharing platform with Codex GPT 5.6 Sol and Figma MCP. The tips include writing clear one-sentence prompts, providing examples, and forcing step-by-step reasoning, citing an MIT study that found AI agents increase code written by 180% but shipped code by only 30%. After months of building with AI coding tools, I found the difference between generated code that works and generated code that ships comes down to how you communicate with the AI. I have been sharing these lessons in a talk called "It's Dangerous to Code Alone Take This: Developer's AI Survival Guide" and people keep asking me to write them down. How big is the gap? An MIT study across 100,000+ developers https://www.forbes.com/sites/josipamajic/2026/06/10/ai-coding-agents-write-180-more-code-but-ship-only-30-more-software/ found that AI agents boosted code written by ~180%, while code that actually shipped to production rose by only ~30%. To demonstrate these tips, I built a link-sharing platform so my teammates can share resources without juggling multiple QR codes. I generated the frontend with Codex GPT 5.6 Sol and Figma MCP, and I am adding an AWS Blocks backend to swap out local mocks with real cloud infrastructure. You can find all the prompts in this repository https://github.com/salihgueler/some-useful-links . All of these tips are applicable to greenfield projects as well. I can't promise you it is going to have the 42 effect : Each model reacts to prompts differently. The clearer you get, the faster you achieve your goal. You also need to remember that now we don't only have models, we also have effort levels. If you are not mindful about which model you are running with which effort level, you will have a hard time getting the results you want. Here are some general rules I follow you can check the BUILD PROMPT.md and the AWS Blocks skill in the repo for the full picture : Describe what you want. Works for simple, well-defined tasks. Here is my frontend build prompt. One sentence, clear outcome: Build "Some Useful Links" — a LinkTree-style web application using Next.js with SSR enabled. The complete visual design specification is in DESIGN SPEC.md and reference screenshots are in the design-previews/ folder. Implement the design pixel-perfectly. And for the backend migration to AWS Blocks: Replace the local JSON mock services in src/lib/services/local/ with AWS Blocks implementations. The service registry index.ts is the only file that should change in the existing codebase. Frontend must not be modified. Both are zero-shot: one clear task, no ambiguity. Show examples. Input, output. Input, output. The model picks up the shape. Research shows the format matters more than whether the examples are perfectly correct. In my BUILD PROMPT.md , I use this for the service registry pattern. I show the AI what an implementation swap looks like: js // src/lib/services/index.ts import { LocalAnalyticsStore } from './local/analytics-store.local'; import { LocalLinkStore } from './local/link-store.local'; import { LocalVisitTracker } from './local/visit-tracker.local'; // SWAP POINT: Replace these with cloud implementations export const analyticsStore = new LocalAnalyticsStore ; export const linkStore = new LocalLinkStore ; export const visitTracker = new LocalVisitTracker ; When I ask the AI to build the AWS Blocks backend, it sees this pattern and knows the target: create a BlocksAnalyticsStore , BlocksLinkStore , and BlocksVisitTracker that implement the same interfaces, then swap them in index.ts . I do not need to explain the concept of dependency injection. The example IS the explanation. Force the model to reason step by step before acting. For debugging, architecture decisions, or anything multi-step, this cuts logical errors significantly. I use this when asking the AI to plan the AWS Blocks migration: Before writing any code, analyze the existing service interfaces in src/lib/services/interfaces/. For each interface method, determine: 1. Which AWS Blocks building block maps to it DistributedTable, KVStore, FileBucket, etc. 2. What the key schema should be to support the query patterns 3. Whether the method needs authentication check if the frontend calls it from an admin route or a public route Write your analysis as a numbered plan. I will review it before you start implementing. The AI produces a plan I can review before it writes a single line of code. Without this step, it would just start building and often pick the wrong storage pattern for a given query. Use words like MUST , NEVER , ALWAYS , and STRICTLY FORBIDDEN . Avoid weak phrasing like "Please try to," "It is preferred," or "Usually we do." Here is a comparison from my project. The frontend BUILD PROMPT.md sets boundaries like this: All backend infrastructure must be local mocks — no cloud dependencies. The mocks must be documented clearly enough that another AI agent or developer can swap them for any cloud provider without restructuring. And the AWS Blocks skill sets boundaries for the backend side: Backend lives in aws-blocks/index.ts . Frontend imports from 'aws-blocks' workspace package . The client.js is auto-generated — never edit it. Both are direct, absolute, and leave no room for interpretation. Telling the AI what NOT to do is often more effective than listing everything it should do. Frontend AGENTS.md : - Do not add cloud SDKs, deployment configuration, external persistence, or source-controlled credentials unless the task explicitly requires them. - Import services from @/lib/services ; components and route handlers must not import src/lib/services/local/ directly. - Keep unrelated refactors and generated-file churn out of focused changes. Backend AWS Blocks skill : - Adding to an existing project? Scaffold into a temp dir, copy only aws-blocks/ folder, then manually merge workspace config, scripts, and dependencies. The scaffolder overwrites root package.json, tsconfig.json, vite.config.ts, .gitignore. - Never edit index.cdk.ts, index.handler.ts, or client.js — these are auto-generated. You need to steer the agent in the correct direction. AI tools have dedicated files for this purpose: AGENTS.md CLAUDE.md .kiro/steering/ .kiro/skills/ Some of these are loaded every session steering files , giving the AI persistent rules. Others are loaded on demand skills , giving the AI specialized knowledge only when it needs it. Both keep your context window lean. For the frontend, I have an AGENTS.md at the root that covers the full Next.js application: AGENTS.md Project Overview Some Useful Links is a local-first, multi-page link-sharing application built with Next.js App Router, React, strict TypeScript, and Tailwind CSS. Commands npm install npm run dev npm run validate:links npm run lint npm run build Architecture - src/app: App Router pages, layouts, route handlers - src/components: UI grouped by admin, analytics, layout, links, share - src/lib/services/interfaces: Backend-neutral service contracts - src/lib/services/local: Local JSON-backed implementations - src/lib/services/index.ts: The only provider registration and swap point I also have two Kiro steering files that enforce cross-cutting rules regardless of the task: TypeScript rules .kiro/steering/typescript.md — enforces strict typing and build validation: TypeScript Project Instructions Workflows & Validation - Pre-completion Check: Before completing any task or reporting success, you MUST run npm run build in the terminal. - Do not consider a task finished if the build command returns errors. Fix the errors first. Coding Conventions - Strict typing is enforced. You are strictly forbidden from using the any type. - Always define and apply the exact, correct types and interfaces for all variables, function parameters, and return values. - You are STRICTLY FORBIDDEN from using @ts-ignore. If unavoidable, use @ts-expect-error with a detailed comment. Agent behavior rules .kiro/steering/agent.md — controls what the AI can and cannot do on its own: Agent Behavior Rules File and Folder Boundaries - DO NOT create any new Markdown files unless explicitly instructed by the user. - STRICTLY FORBIDDEN to auto-generate changelogs or documentation. - You MUST update the existing README.md if your changes alter the project's public API or architecture. Technology Boundaries - You MUST ALWAYS use the Strands Agents library with TypeScript for any agent development. - You are STRICTLY REQUIRED to use Claude Haiku 4.5 from Amazon Bedrock for all agent models. - You MUST ALWAYS use React and Vite with TypeScript for web development. - NEVER write or generate unit tests unless the user explicitly commands it. Security Boundaries - NEVER commit or hardcode sensitive information client secrets, API keys, client IDs, resource IDs . Git Boundaries - Commits MUST stay under 150 lines of source code. - Every commit: single-sentence summary, blank line, detailed explanation max 20 lines . - You MUST append Kiro to the author name using: git commit --author=" Git Username Kiro < User Email " These prevent the common annoyances: AI generating unwanted test files, committing giant diffs, sneaking any types past the compiler, or littering the repo with markdown files nobody asked for. Steering files are always loaded. But what about capabilities that are only needed sometimes? You do not want to load everything upfront because that wastes context. A Skill is a reusable, discoverable capability. The AI loads it only when it becomes relevant to the current task. The most important part of a Skill is the name and description. That is how the AI decides whether to use it. My AWS Blocks skill activates with this frontmatter: --- name: building-aws-blocks-apps description: "Builds fullstack TypeScript applications on AWS using" @aws-blocks/blocks. Use when working with any Building Block KVStore, DistributedTable, Agent, AuthBasic... , ApiNamespace, BlocksStack, or the create-blocks-app CLI. --- The main SKILL.md is the overview: decision guides, project structure, quick start. Detailed reference lives in separate files: .kiro/skills/aws-blocks-development/ ├── SKILL.md Overview + decision guide under 200 lines ├── CORE-ARCHITECTURE.md Scope, ApiNamespace, JSON-RPC, CORS ├── TROUBLESHOOTING.md Common errors and fixes └── blocks/ ├── auth-basic.md AuthBasic patterns ├── distributed-table.md DistributedTable patterns ├── api-namespace.md ApiNamespace deep dive └── ... 20+ block files When the AI needs to implement authentication, it loads blocks/auth-basic.md . When it needs to set up a database, it loads blocks/distributed-table.md . It does not carry all 122 KB of reference material in every conversation. Keep the root file under 200 lines . My AGENTS.md is 127 lines. The AWS Blocks SKILL.md is the overview under 200 lines , with detailed reference files loaded on demand. Use the "Router Pattern": a root file that points to detailed references when needed. The AWS Blocks skill does exactly this with its block reference table. Project Context tells the AI where it is: "This is a Next.js 14 App Router project using Tailwind CSS." Actionable Rules tell the AI what to do: "Import services from @/lib/services ; components must not import src/lib/services/local/ directly." Keep these separate. Context helps the AI orient itself. Rules constrain its behavior. Your AI sees everything in a stack: Performance degrades at 25% capacity, not 100%. You do not have the full context window available. The degradation starts much earlier than you think. Long sessions lead to the model "forgetting" earlier decisions, fixing one thing and breaking two others. Hallucinations increase as context fills up and your original constraints stop being followed. I have seen this firsthand. On the frontend side, I asked my AI to follow the service registry pattern from my AGENTS.md . After 15 turns of unrelated work, it started importing directly from src/lib/services/local/ , exactly what I told it not to do. On the backend side, I had a session where I was building multiple API methods with AWS Blocks. After building the analytics endpoints, I asked it to add authentication. It generated a whole custom auth system instead of using the AuthBasic block that was in the skill file. The context was too full for it to reference back. Vibe coding skips everything we know about building software: planning, analysis, design, testing, maintenance. All of it gone. Vibe coding works for prototyping and tiny fixes. But for anything beyond that, you need structure. Spec-Driven Development SDD is a methodology where detailed, unambiguous requirements are written and agreed upon before any actual coding begins. The spec is the contract between you and your AI. BUILD PROMPT.md