Skip to content

How to Define Tasks for AI Coding Agents: Goal, Context, Constraints, Done When Framework

I stared at my screen in frustration. The AI coding agent had just produced its third failed attempt at adding authentication to my Express app. Each iteration introduced new problems—wrong library, incompatible error handling, missing tests. I’d spent two hours going back and forth, and I still didn’t have working auth.

The problem wasn’t the AI. The problem was my task definition: “Add user authentication to the app.”

That’s it. Four words. No context, no constraints, no success criteria. I was expecting the AI to read my mind.

The GCCD Framework

Every task given to an AI coding agent should follow four components: Goal, Context, Constraints, and Done When. This structure eliminates ambiguity, provides necessary information upfront, and gives the agent clear success criteria.

Let me show you the transformation:

Bad Task Definition (too vague)
Add user authentication to the app.
Good Task Definition (GCCD framework)
## Goal
Add JWT-based user authentication to the Express API endpoints.
## Context
- `src/routes/` contains all API endpoints
- `src/middleware/` has existing middleware patterns
- Current auth RFC: /docs/auth-design.md
- Similar pattern in legacy app: github.com/org/old-app/auth.js
## Constraints
- Use jsonwebtoken package (already installed)
- Follow existing error response format in src/utils/errors.js
- Password hashing must use bcrypt with cost factor 12
- No new npm dependencies
## Done When
- POST /auth/login returns JWT on valid credentials
- POST /auth/register creates new user with hashed password
- All protected routes reject requests without valid JWT
- Unit tests for auth middleware pass with 90%+ coverage
- API documentation updated in /docs/api.md

Why Each Component Matters

Goal: What Exactly Needs to Change?

A vague goal forces the AI to make assumptions. “Improve performance” could mean anything—reduce bundle size, optimize database queries, implement caching, or add lazy loading.

Goal Examples
Bad: "Improve performance"
Good: "Reduce API response time from 2s to under 500ms"
Bad: "Fix the bug"
Good: "Fix the null pointer exception when user profile is missing"

The good goals are specific and measurable. The AI knows exactly what success looks like.

Context: What Information Does the Agent Need?

AI agents don’t have access to your mental context. They need to know:

  • Which files are relevant and what they do
  • Where to find documentation
  • Existing patterns in your codebase
  • Architectural decisions you’ve already made

When I skipped context in my authentication task, the agent spent its first two iterations exploring the codebase, making wrong assumptions about where auth logic should live. With context provided upfront, it went straight to implementation.

Constraints: What Rules Must Be Followed?

Without constraints, the AI will optimize for the goal in isolation—potentially breaking your existing patterns, adding dependencies you don’t want, or introducing security vulnerabilities.

Common constraints I use:

  • Must use existing authentication system
  • Cannot add new dependencies
  • Must maintain backward compatibility
  • Follow existing error handling patterns
  • Keep changes under 200 lines

Done When: How Do You Know It’s Complete?

This is where most task definitions fail. “Done when it works” is not a completion criteria. You need objective, verifiable conditions:

Done When Examples
- All tests pass
- Performance benchmark shows less than 500ms
- Code reviewed and approved
- Documentation updated
- No console errors in browser
- Lighthouse score above 90

The AI can check these conditions. You can check these conditions. There’s no ambiguity.

A Template You Can Use

task-definition.md
## Goal
[Specific, measurable change description]
## Context
- Files: [List relevant files]
- Docs: [Links to documentation]
- Patterns: [Existing code patterns to follow]
## Constraints
- [Must-do rule 1]
- [Must-do rule 2]
- [Must-not-do rule 1]
## Done When
- [Test criteria]
- [Review criteria]
- [Documentation requirements]

Common Mistakes I Made

Skipping Context: I’d tell the AI to “add caching” without mentioning we already have a Redis client configured. Result: the AI added a second caching layer with a different library.

Omitting Constraints: I asked for “input validation” without specifying we use Zod everywhere. Result: the AI used a different validation library, creating inconsistency.

Vague Done When: I said “done when working” for a refactoring task. Result: the AI made it work but broke three other features I didn’t specify.

Treating Components as Optional: I’d include Goal and skip the rest. Result: 50% success rate. With all four components, I’m at 90%+ first-attempt success.

When to Use GCCD

The framework applies to both planning and execution phases:

GCCD Application Flow
┌─────────────────────────────────────────────────────┐
│ Complex Task │
└─────────────────────┬───────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ GCCD for Planning Phase │
│ Goal: Understand what needs to be built │
│ Context: Project docs, existing architecture │
│ Constraints: Time, resources, tech stack │
│ Done When: Plan approved, tasks identified │
└─────────────────────┬───────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ GCCD for Each Execution Task │
│ Goal: Specific implementation │
│ Context: Relevant files, related code │
│ Constraints: Patterns, dependencies │
│ Done When: Tests pass, docs updated │
└─────────────────────────────────────────────────────┘

The ROI of Good Task Definition

Invest 2 minutes upfront to define tasks properly, and save hours of back-and-forth corrections. The math is simple:

  • Time spent on GCCD: 2 minutes
  • Average iterations with vague task: 4-5 (30+ minutes each)
  • Average iterations with GCCD: 1-2 (often first-attempt success)

For complex tasks, I spend 5-10 minutes on task definition. It’s the difference between an agent that produces 80% right solutions and one that consistently delivers exactly what I wanted.

Real Example: Refactoring a Legacy Component

Here’s a task definition I used recently:

refactor-user-profile.md
## Goal
Extract the user profile editing logic from the monolithic UserDashboard component into a dedicated UserProfileEditor component.
## Context
- `src/components/UserDashboard.tsx` - current location (847 lines)
- `src/components/` - where new component should live
- `src/hooks/useUserProfile.ts` - existing hook for profile operations
- `src/types/user.ts` - UserProfile interface already defined
- Pattern reference: `src/components/SettingsEditor.tsx` for similar extraction
## Constraints
- Maintain exact same UI behavior
- Keep the existing validation rules
- No changes to the useUserProfile hook
- All existing tests must still pass
- Use TypeScript strict mode
## Done When
- UserDashboard.tsx is under 500 lines
- UserProfileEditor.tsx exists and handles all profile editing
- All props are properly typed
- `npm test` passes with no new failures
- No TypeScript errors in `npm run typecheck`
- Visual regression tests pass (screenshots match)

The AI completed this in one iteration. Before GCCD, similar refactoring tasks took 3-4 iterations.

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