How to Write Prompts: Communicating Effectively with Codex
📚 Series Navigation: The previous article 12 · Slash Commands and Shortcuts got your fingers in the right places—using
/to switch modes, clear context, and check status. This article moves to a different level: now that you know where to press, you must know what to say. How clearly you express a requirement makes a world of difference to Codex's output.
They say "whether an AI programming tool is powerful depends on the model"—I beg to differ.
To be honest: given the same GPT-5 model and repository, an experienced developer gets it done in three prompts, while a novice goes through five rounds of revisions and ends up frustrated. The model is already powerful; what limits it is usually the prompt you feed it. If you throw a vague "fix this bug" prompt with zero information, it has to guess—which file, what error, and what the fix should look like. If it guesses wrong, you stare at the diff grumbling "this AI is useless," but the problem isn't the AI.
I fell into this trap last year. A Node service returned a 500 error, and I typed "the login endpoint is broken, fix it" and hit Enter, without attaching logs. Codex searched the project, picked a bug it assumed was the issue, and edited three files—none of which resolved the actual root cause. The issue was an environment variable I forgot to mention, which Codex had no way of knowing. That was when I realized: the ceiling of Codex is largely defined by how I ask questions.
This article does not teach you to memorize templates; it helps you understand one thing—what Codex needs to know to avoid running off course. Understand this, and writing prompts will feel natural.
After reading this article, you will get:
- A comparison table of "Vague vs. Clear Prompts" to reduce revisions immediately
- A four-part framework for writing requirements: Goal, Scope, Constraint, and Verification—without which Codex will guess the details
- How to split large tasks into manageable steps that Codex can handle and you can review easily
- How to use
/goal(Goal mode) to lock down success criteria so that it "does not stop until the goal is achieved" (requires enablingfeatures.goalsfirst, detailed in Section 05) - A hands-on experiment comparing two prompts for the same requirement to see the difference firsthand
⚠️ All specific commands, parameters, and default behaviors mentioned below are based on the Codex official documentation; model names and interface layouts are subject to change and should match what is actually displayed on your local system.
01 Why Vague Prompts Fail
Let's dissect the login endpoint failure. "The login endpoint is broken, fix it" lacks critical details for Codex:
- Which login endpoint? It has to search the entire project.
- How is it broken? What errors are returned, and under what conditions does it replicate? Codex has no idea.
- What is the expected behavior? It has to guess based on "general login patterns."
As explained in 06 · Running Your First Task, Codex operates in an agent execution loop (agent loop)—calling models, reading files, modifying files, and running commands, cycling through "think → act → review." The official docs note that it "runs shell commands in a loop, edits code, runs checks, and attempts to verify its work." But if the input to the first "think" step is garbage, the loop runs in the wrong direction.
Analogy: Writing a delivery address. If you write "deliver to the apartment complex" on your order, the driver has to search building by building, likely delivering it to the wrong door. If you write "Apartment 1503, Unit 2, Building 8, next to the green shoe rack at the door," they find it easily. The more precise the address, the less the driver searches; the vaguer the address, the more they guess. Prompts for Codex work the same way—the precision of your prompt determines if it runs off course.
Let's compare vague and clear prompts using the official documentation guidance:
| Scenario | ❌ Vague Prompt | ✅ Clear Prompt |
|---|---|---|
| Fixing bugs | "The login endpoint is broken, fix it" | "Users report a 500 error when calling POST /api/login after session timeout. Write a reproducing test case first, locate token refresh logic in src/auth/, fix it, and run tests to verify they pass." |
| Writing tests | "Add tests to parser.py" | "Write tests for parse_date in parser.py, covering empty strings and invalid formats. Avoid mocking, and run pytest to confirm they pass." |
| Adding features | "Add an export feature" | "Review how export_csv is implemented in report.py, add export_json following the same pattern, and do not introduce new dependencies outside already installed libraries." |
| Reading code | "Why is this module written like this?" | "Review the Git history of the transform module, and summarize how its interfaces evolved to the current state." |
See the pattern? Clear prompts do one thing: feed details to Codex that it would otherwise have to guess. When it doesn't have to guess, it doesn't run off course.
💡 Summary in one sentence: Vague prompts fail because they leave information gaps for Codex to guess; clear prompts explain the details it needs in advance.
02 The Four-Part Framework: Goal, Scope, Constraint, and Verification
Instead of guessing what details to provide, use a simple framework—Goal, Scope, Constraint, and Verification. This is the checklist I run in my head before prompting: if any part is missing, Codex will make assumptions for you.
Analogy: Instructions for a general contractor. A reliable contractor confirms four things before starting—what the final result should look like (Goal), which rooms to renovate and which to leave alone (Scope), what restrictions exist like not touching load-bearing walls (Constraint), and how to verify the work is done (Verification). With all four, they can execute and clean up; missing one, they guess, often yielding undesirable results. Prompting Codex is like giving it this checklist.
Let's break down these four parts:
| Element | Question It Answers | How to Provide | Missing Impact |
|---|---|---|---|
| Goal | What needs to be done? | "Return 0 for empty lists," "Change export format to JSON" | It guesses what you want, leaving the direction to chance |
| Scope | Where to edit (and where not to)? | Name files/functions: "Only edit average in stats.py" | It searches blindly and modifies unrelated files |
| Constraint | What restrictions apply? | "Do not introduce new libraries," "Maintain backward compatibility" | It uses its own preferences, which may not fit your requirements |
| Verification | How to define success? | "Write two test cases and run them," "Ensure build exit code is 0" | It stops when it "looks done," leaving you to find bugs |
Among these, "Verification" is the one beginners omit most often, yet it is the most critical:
Codex performs significantly better when it can verify its own work. Include reproduction steps, verification commands, and lint or pre-commit checks.
Why is verification critical? Because without a test, "appearing to work" is Codex's only signal to stop. If you do not provide a validation step, it stops based on "feeling," leaving you to catch the bugs. But if you provide a test command that returns pass/fail—test cases, lint checks, or build scripts—the loop closes automatically: it edits → runs checks → reviews results → edits again if failed, without requiring your supervision.
My prompts now follow this checklist closely. When adding email validation to a Python project, I prompted:
Add a validate_email function in src/validators.py (Goal).
Only modify this file, do not touch others (Scope).
Use the standard library re module; do not introduce external libraries (Constraint).
Write three tests: valid for user@example.com, invalid for invalid, and invalid for user@.com,
and run pytest to confirm they pass (Verification).With all four elements defined, Codex succeeded in one go—locating the file, writing the function, writing the three tests, running them, and presenting the passing tests. It didn't need to guess anything because I locked down what to do, where to edit, what to use, and how to verify.
💡 Summary in one sentence: Run through "Goal, Scope, Constraint, Verification" before prompting; if any part is missing, Codex will make assumptions for you. Provide tests to let it close the loop automatically.
03 Scope and Constraints: Feed Files Directly Instead of Describing Them
For "Scope" and "Constraint," you rarely need long descriptions—simply feed the files to it directly, which is the most practical shortcut.
The rule: never describe what you can feed directly. Codex reads raw files much better than your explanations of them.
First, reference files in the context. The Prompting guide states: when prompting, include references to files and images that Codex can use. The easiest way is referencing file paths in your prompt:
Referencing the types defined in src/types/user.ts, add type annotations to UserServiceThis is much more effective than saying "there is a user type file in the project, find it." The IDE extension makes this even easier: it automatically includes your active files and selected code block in the context. If you select lines in VS Code, Codex knows you mean those lines without you describing them.
Analogy: Inserting a USB drive vs. pointing at a shelf. Referencing files or letting the extension read selections is like plugging a data drive directly into Codex's workbench—the data is ready instantly. Telling it "the files are somewhere on the third shelf" forces it to search the archives, risking errors. (The MCP USB analogy in Article 02 referred to "capabilities"; this refers to "data," which follows the same concept.)
Second, paste error logs in full. Practice this: instead of writing "it returned an undefined error," paste the complete stack trace:
Running tests returned this error; locate the cause:
TypeError: Cannot read properties of null (reading 'userId')
at getUserProfile (src/services/user.ts:42:18)
at async ProfileController.getProfile (src/controllers/profile.ts:15:20)Pasting the full trace provides filenames, line numbers, and call stacks (user.ts:42), letting Codex navigate directly to the issue. Summarizing deletes these coordinates, forcing Codex to guess.
Third, attach screenshots for UI/visual issues. Codex supports image inputs—paste or drag images directly into the chat box, or pass them on the command line (check the official docs for flags). Mockups, error screenshots, and architecture diagrams are much more precise than describing "move the button left" in text.
Feeding files vs. describing them:
| Data | ❌ Describing | ✅ Feeding |
|---|---|---|
| File contents | "There is a file handling auth" | Reference src/auth/session.ts in prompt |
| Active code block | "That login block" | Select it in the editor; extension feeds it |
| Error stack trace | "It returned an undefined error" | Paste the full stack trace |
| UI layouts | "The button is misaligned" | Attach a screenshot / mockup |
💡 Summary in one sentence: Never describe what you can feed directly—reference paths, select code blocks, paste full stack traces, and attach screenshots. Codex reads raw data much better than explanations.
04 Splitting Large Tasks: Manageable for Codex and Reviewable for You
The checklist covers how to write a single prompt. But some tasks are large—"implement user auth," "migrate the project from JS to TS"—and throwing them in one prompt causes Codex to run off course in a corner, presenting a massive, broken diff.
The official guidelines clarify:
Codex performs significantly better when you split complex tasks into smaller, focused steps. Small tasks are easier for Codex to test and easier for you to review. If you are unsure how to split a task, ask Codex to draft a plan first.
This contains two rules: first, splitting tasks benefits you too—small diffs are easier to review; reviewing 30 lines of code takes a glance, while reviewing 300 lines across 8 files is painful, leading you to click approve blindly (causing my login failure). second, ask Codex to draft a plan if you cannot split it yourself—which matches the "draft a plan first, do not edit immediately" rule in Article 06.
Analogy: Eating an elephant one bite at a time. You cut it into small pieces, chewing and swallow one by one; if a piece tastes bad, you spit it out. Swallowing it whole will choke you. Splitting large tasks into steps ensures every edit is small enough to be verified easily.
Let's look at how to split a user auth task:
Large Task: Implement user authentication
Steps (edit one by one):
Step 1: Design database schemas (users & tokens tables); explain the plan first.
Step 2: Implement registration (hash passwords with bcrypt); write tests to verify.
Step 3: Implement login (issue JWTs); write tests to verify.
Step 4: Implement token validation middleware; write tests to verify.
Step 5: Implement logout; write tests to verify.Notice two details: every step includes verification (running tests to verify)—applying the "Verification" checklist rule to each step; and Step 1 drafts a plan first—database schemas affect the entire system, so verifying the design draft first is much cheaper than fixing finished tables.
If you don't know how to split a task, use these two options:
/plan(Plan Mode): Prompts Codex to explore and draft a plan, generating an execution plan before editing. Use this when you are unsure where to start—let it write the steps and review them before proceeding.- Instructing "do not edit yet": If you don't want to switch modes, write: "Explain which files you need to edit and your plan first; do not modify any code yet."
Choosing when to plan:
| Task | Action |
|---|---|
| Fixing typos, adding log lines, renaming variables | Run directly; do not plan or split simple tasks |
| Adding check to a function, writing a test | Single prompt + checklist; run in one step |
| Multi-file edits, unfamiliar code, architectural changes | Run /plan first, review, and execute step-by-step |
| "Implement XX system," migrations, large refactoring | Split into 5-8 verifiable steps, running them in sequence |
The rule of thumb: "Can you describe what the diff looks like in one sentence? If yes, run directly; if not, split and plan." Planning simple tasks is unnecessary.
💡 Summary in one sentence: Split large tasks into small, verifiable steps that are easy to test and review; use
/planto let Codex write the steps first if you are unsure; run simple tasks directly without planning.
05 Locking Success Criteria: /goal (Goal Mode)
While writing validation steps in a regular prompt works, it has a limitation: it only applies to the current turn—if the tests fail, Codex may return control to you and wait for your next prompt. If you want it to "work continuously, editing in rounds until tests pass," use Goal Mode (/goal).
How it differs from regular prompts: regular prompts check validation "once"; /goal pins the validation as the success criteria for the task. The docs state:
When setting a goal, the goal text serves as both the starting prompt and the completion criteria. Codex uses it to decide its next actions and determine when the task is complete.
The prompt is both the task description and the success criteria—Codex checks it after every edit cycle, continuing execution if it fails, and stopping only when successful. This is ideal for multi-step, complex tasks with clear validation rules.
To use it, type /goal followed by your goal in the chat. Write goals so Codex can verify them objectively—the docs suggest including specific outputs, quantitative metrics, or testable criteria. Official examples:
/goal Migrate this repository from JavaScript to TypeScript, ensuring it compiles in strict mode without any explicit any types/goal Reduce home page Time to Interactive (TTI) to under 1 second"Strict mode compile without any types" and "TTI under 1 second" are binary testable criteria. If you write vague goals like "ensure high code quality" or "improve user experience," Goal Mode cannot verify them, as there is no objective success metric.
Details on /goal execution:
- Can't find
/goal? Enable the feature first. Addgoals = trueunder[features]in~/.codex/config.tomlor runcodex features enable goals.
# ~/.codex/config.toml
[features]
goals = true- Not sure how to write the goal? The docs suggest: run
/planfirst to outline the task, and convert it to a goal; you can also ask Codex to interview you to define success criteria. - Adjust goals mid-run. You can send messages to add constraints ("use this library," "avoid this path") while it runs; to check progress without interrupting the execution, use side chats (side chats).
- Long runs. The docs remind you: pause long-running goals before disconnecting, resuming or editing them once you reconnect.
Comparing regular validations and /goal:
| Feature | Regular Validation | Goal Mode (/goal) |
|---|---|---|
| Lifespan | Active turn only | Entire task, runs until success |
| Best for | Simple requirements, 1-2 steps | Multi-step, complex tasks with clear success criteria |
| Success Criteria | "Run these tests" | Objective, binary quantitative metrics |
| Requirements | None | Requires features.goals = true |
💡 Summary in one sentence: Use
/goalto let Codex run in cycles until success—writing objective, binary success criteria; use/planto outline goals first, and enablefeatures.goalsin configuration.
06 Hands-on: Comparing Vague and Clear Prompts
Let's run a comparison experiment. It only requires a three-line playground file.
Platform Note: The
mkdircommand works on Mac / Linux; on Windows PowerShell, runmkdirandcd, and createstats.pymanually in File Explorer, pasting the two lines of code inside.
Step 1: Create a playground file with a bug (Mac / Linux)
mkdir prompt-demo
cd prompt-demo
echo 'def average(nums):
return sum(nums) / len(nums)' > stats.pyThe bug: if an empty list [] is passed, len(nums) is 0, causing a division-by-zero error. Let's use this as our test case.
Step 2: Start Codex in the directory
codexExpected behavior: The Codex TUI launches, displaying the input box.
⚠️ Start
codexinside theprompt-demofolder; Codex treats the active folder as its workspace, reading files from where it was launched.
Step 3: Test a vague prompt, and observe its assumptions
@stats.py help me modify this functionExpected behavior: Codex guesses your goal—adding type annotations or docstrings—without realizing you want to fix the empty list division error, leaving the outcome to chance. This is what happens when "Goal and Verification" are omitted: it makes assumptions for you.
Step 4: Test a clear prompt using the checklist
The average function in @stats.py has a bug: passing an empty list causes a division-by-zero error.
The expected behavior is to return 0 for empty lists (Goal).
Only modify this function, do not edit other files (Scope); use plain Python, do not introduce external libraries (Constraint).
Fix it, and write two tests: average([]) should return 0, and average([2, 4]) should return 3. Run them to confirm they pass (Verification).Expected behavior: Codex follows a clear execution path—handling empty lists → adding a check like if not nums: return 0 → writing the two tests → running them → displaying the passing results. It doesn't guess because Goal, Scope, Constraint, and Verification are locked down.
Step 5: Exit and verify modifications
Exit Codex (check UI for exit keymaps) and check the file:
cat stats.py(On Windows PowerShell: type stats.py)
Expected behavior: stats.py contains the empty list check (like if not nums: return 0). Matching your Step 4 requirement confirms you have the hang of writing clear prompts.
Compare the two approaches:
| Metric | Step 3: ❌ Vague Prompt | Step 4: ✅ Clear Prompt |
|---|---|---|
| Goal | Omitted; model guesses | Set to return 0 for empty lists |
| Scope | Omitted; searches entire file | Restricted to average function |
| Constraint | Omitted; might import libraries | Restricted to plain Python |
| Verification | Omitted; stops when it "looks done" | Two test cases + execution check |
| Outcome | You review diff grumbling "not what I wanted" | It follows your instructions; succeeds in one run |
💡 Summary in one sentence: Vague prompts leave details for Codex to guess, while clear prompts define Goal, Scope, Constraint, and Verification. Run this comparison once to see the difference.
07 The Prompting Pipeline
Let's trace the lifecycle of a requirement, from prompt submission to execution:

Keep these two decision points in mind: task size (plan and split large tasks, do not swallow whole) and verification (provide test commands to close the loop automatically; otherwise, it stops on assumptions, leaving you to verify everything).
💡 Summary in one sentence: The correct path for a requirement is "split/plan large tasks → write prompts using the checklist → provide verification commands to close the loop → review diffs to apply." If it fails, check if you skipped any of these steps.
08 Summary
This article explained how to write prompts that Codex can execute accurately:
| Technique | Rule of Thumb | Example / Action |
|---|---|---|
| The Checklist | Goal, Scope, Constraint, Verification. Define details so it doesn't guess | "Modify average (Scope) to return 0 on empty lists (Goal) using plain Python (Constraint); write tests and run them to confirm (Verification)" |
| Feed, Don't Explain | Feed files and data directly | Reference paths, select code blocks, paste full stack traces, and attach screenshots |
| Split Large Tasks | Break tasks into small, verifiable edits | Run /plan to write steps, and execute them one by one |
| Lock Success Criteria | Run loops until tests pass | Run /goal with binary quantitative metrics (requires features.goals enabled) |
You should now be able to write clear prompts using the checklist, feed files and data directly, split large tasks, and use Goal Mode for continuous execution. Prompt design is the core skill for working with Codex—no matter how advanced the features are, vague prompts yield poor results.
Think about this: since writing clear rules is critical, do you have to repeat project conventions (like "never introduce external libraries" or "place tests in
tests/") in every prompt? Is there a way to let Codex "remember" them across sessions? (Hint: Article 11AGENTS.mdhas the answer.)
The next article 14 · Common Workflows: this article covered general prompt design guidelines; the next article applies them to common daily coding tasks—exploring legacy codebases, fixing bugs, refactoring, and writing tests—providing a playbook for each task. Let's look at the plays next.