Writing a Task Codex Can Act On
A good Codex task is specific, scoped to one logical change, includes examples if needed, and describes success criteria -- not a vague wish list.
Learning objectives
- Write a task description that Codex can disambiguate and execute
- Scope a task to a single, reviewable change
- Provide enough context that Codex succeeds without a retry loop
ToolDix original visual
Frame
Name the outcome and constraints.
Build
Try one bounded workflow.
Review
Keep evidence, revise, and share.
The anatomy of a good task
A good task has four parts: a clear goal, a scope boundary, success criteria, and any examples or constraints Codex needs to follow your project's conventions.
Goal: "Add a cache layer to database queries" is clear; "make the database faster" is not. Codex needs to know what you're doing, not what outcome you want. Outcomes are ambiguous (do you want caching, pagination, query optimization, or all three?), but actions are concrete.
Scope: "In the database.ts module, wrap all SELECT queries in a cache function" is scoped; "improve our data layer" is not. Codex should know which files to touch and which to leave alone. A good scope is something you could merge as a single PR.
Success criteria: "The cache should return stale data within 30 seconds if a fresh query would take longer than 100ms" tells Codex what "working" means. Without this, Codex might write a cache that never evicts, or one that's slower than the original queries.
Conventions and constraints: "Follow the pattern in auth.ts for initialization" or "don't import lodash" gives Codex guardrails. Your project probably has patterns; naming it saves Codex from inventing its own style.
Scope and clarity matter more than the model
Good task, bad task
Bad task: "Add error handling."
Why it's bad: Error handling can mean try-catch blocks, error codes, logging, graceful degradation, user-facing messages, or recovery logic. Codex will guess and probably guess wrong.
Good task: "In payment-processor.ts, wrap the stripe.charge() call in a try-catch block. If it fails, log the error with the full error object and the customer ID, then return a 500 status with an error message. Follow the pattern in invoice-handler.ts for the error format."
Why it's good: Codex knows the file, the specific function, what kind of error handling (try-catch), what to do in the catch block (log and return), and an example of your convention to follow.
Bad task: "Refactor the auth module to be more maintainable."
Why it's bad: "Maintainable" is subjective. Does Codex refactor to smaller functions? Use more comments? Change variable names? Add types? Extract constants? It has no objective measure.
Good task: "In auth.ts, extract the JWT validation logic into a separate validateToken() function. It should accept a token string and return {valid: boolean, decoded?: JwtPayload}. Use TypeScript types to match the existing JwtPayload interface. Tests should verify that valid tokens decode correctly and invalid tokens return {valid: false}."
Why it's good: Specific files, specific logic to extract, specific return type, a naming convention (validate, Payload), and concrete test cases.
Bad task: "Optimize this code for performance."
Why it's bad: Codex doesn't know which performance metric matters (latency? memory? throughput?), which code, or what "optimized" means (1x faster, 10x faster?).
Good task: "The filterCustomers() function in queries.ts is O(n²) because of nested loops. Refactor it to O(n log n) using a hash map. The function should return the same result but process 10,000 customers in under 500ms on a standard laptop. Write a performance test that verifies this."
Why it's good: Specific algorithm problem identified, specific data structure hinted (hash map), concrete performance target (500ms for 10k items), and testable.
A worked example: building a task step by step
You notice that your API routes have inconsistent return types for errors. Some return {error: string}, others return {message: string}. You decide to standardize on {error: string, code: string}. Here's how to write the task:
First draft: "Make error returns consistent."
Too vague. Codex could change format, add data, or do something you didn't intend.
Second draft: "In the /routes directory, make all error responses return {error: string, code: string} instead of {message: string}."
Better, but Codex might not know what code should be (HTTP status code? a domain-specific error code?).
Final draft: "In /routes, update all error responses to use the format {error: string, code: string}, where code is one of the values in the ErrorCode enum from types.ts. Keep the HTTP status code the same; just change the response body. Update the TypeScript return types in each route to match. Reference routes/health.ts for the pattern."
This task is specific, scoped, and includes a reference. Codex knows exactly what format to use, where to find the enum, which types to update, and has a concrete example. It can succeed on the first try.
Common mistake
Writing tasks that try to combine multiple logical changes. "Add a cache layer and switch from polling to WebSockets" is two tasks. Codex will either do both poorly or pick one and ignore the other. Break it into "Add a cache layer to reduce database hits" and "Switch from polling to WebSocket subscriptions for real-time updates" -- two separate tasks, two separate diffs, easier to review and revert if one doesn't work out.
Sources and license context
These references informed the lesson. ToolDix adds its own explanation, workflow, and practice rather than reproducing source material. Every link below leaves ToolDix and opens the publisher's own site in a new tab.
- OpenAI: Codex CLI (GitHub Repository) (opens github.com in a new tab)External · github.com (Publisher terms apply)
- OpenAI: Prompt Engineering for Code (opens platform.openai.com in a new tab)External · platform.openai.com (Publisher terms apply)
Keep going
Read these next on ToolDix.
Original lessons that build on what you just read.