Practical Prompt and Context Engineering

This guide goes over how to get good results out of AI: why it behaves the way it does, how to give it the context it needs, and how to scale that from a single prompt up to a whole feature. The theme running through this article is simple: treat the AI like a new hire who knows nothing about your company.

AI is advanced autocomplete

Think of AI as an advanced autocomplete. Its job is to guess, at any moment, the most probable word that comes next. Ask it “What is the capital of France?” and the most probable next word is “Paris.”

Now drop the last two words: “What is the capital?”

What’s the next word? You can’t really say. The capital of what? The capital you live in, the capital of some country, capital as in money? The question is ambiguous, and ambiguity like this is a big part of where AI starts to “hallucinate.” It doesn’t understand the question the way you do. It just works out, from the billions of documents it was trained on, the most probable thing to say next.

Prompt engineering is the skill of shaping your input so this statistical machine predicts the answer you actually want. That makes clear writing the most important skill you can bring to it. The clearer you can say what you want, the better the result you get back.

Onboard the AI like a new hire

Treat every AI session as a new hire who took the job without knowing a thing about your company. They’re book smart, having read millions of programming books, but they have no idea what you build or how you build it. You’d never drop them straight onto a jira ticket. You’d start broad and work down adding scope and detail one layer at a time so you don’t bury them:

  • What is the company building?
  • What projects are ongoing?
  • What is this team working on this quarter, this sprint?
  • What is this developer doing right now, for this ticket?

The same ladder applies to the code:

  • What is the overall goal of the codebase?
  • What languages, libraries, and tooling does it use?
  • Where do the major features live?
  • For this first jira ticket, where should the changes go?

“As a Java engineer…” is really about context

You’ve probably seen the persona trick for prompting AI: “As a Java engineer…” It’s tempting to credit the role-play, but the role-play isn’t what helps. The useful part is the fact hiding in that sentence, that the codebase is in Java. You’ve handed the model context it would otherwise have to guess at.

Follow that logic and you give it the rest too: the structure, the libraries, the conventions. The goal of a good prompt is to tell the AI everything you’d tell a new hire on their first ticket, and nothing more. You don’t explain how Java works or how a Spring Boot app is built in general. They learned that in school. What they’re missing is the context of your project.

Know who you’re talking to

Three things about this new hire AI are easy to forget.

First, every new chat session starts from zero. The AI doesn’t remember yesterday’s chat or last week’s work. You can spend an hour walking it through your auth flow, close the tab, open a fresh chat tomorrow, and it’ll cheerfully suggest the exact approach you ruled out the day before, because as far as it knows that conversation never happened. So you either re-onboard it every time or write the context down somewhere it can read. (We’ll come back to that in a second.)

Second, its memory is finite, even inside a single session. Like a person trying to keep too much in their head at once, cram too much into one chat and it starts to forget things or mix them up, and the part you actually care about gets buried under everything else it’s holding. This is why you should always start a fresh session for each new feature or task instead of running everything through one endless thread. This is context engineering, efficiency managing the live memory of an AI chat session as you go.

Third, it’s book smart but green. It has never seen your code. It can write solid TypeScript or design an API in the abstract, but it has no way of knowing that your team named the helper loadUserPrefs(), or how you handle errors, or which patterns you’ve standardized on. None of that is a flaw in the AI; it’s simply the half of the picture that only you have. The more of those specifics you give it up front, the less it has to work from generic assumptions.

Then match the depth of your brief to the size of the task. “Rename this variable everywhere” needs one sentence. “Design how we handle failover across regions” needs the whole backstory, or it’ll quietly invent constraints to fill the gaps.

A good prompt is a good jira ticket

Onboarding sets up the AI; the prompt is the jira ticket for the task in front of it. Write it the way you’d want a ticket written for you if you just got hired:

  • Say what “done” looks like. How will it be tested, what counts as finished, what will you check before you accept it. And if you want the answer in a specific shape, a table, a list, a word count, say so. The model can’t see the format in your head.
  • Point to the right place. You’d tell a new hire “look at the auth module,” not “go read the whole repo.” Point the AI at the right files, paste in the docs or code it needs, and it answers from facts instead of guesses.
  • Show how it was done before. Hand over a past example of the kind of work you want, the way you’d show someone a previous PR. One good example beats a paragraph of description.
  • Set the out of scope. “Don’t touch billing.” “Leave the migration alone.” Saying what not to do is as useful as saying what to do.
  • Name the house style. The unwritten rules a real hire soaks up over months: how you name things, the patterns you favor, the stuff you just don’t do here.
  • Let it ask first. A good hire asks what they’re missing instead of charging ahead. Tell the AI it can ask clarifying questions, and it’ll surface the gaps you forgot to fill.

Plan first, then execute

Because the model is always predicting the best-looking answer, it quietly fills gaps with assumptions. The single best habit for avoiding bad output is to not let it run straight at a solution.

Make it lay out a plan before it writes a line. Make it ask its open questions. Then go through the plan together and ask what it sees: what’s risky, what it would do differently, what it isn’t sure about. You catch the bad assumptions while they’re still cheap, not after they’re baked into a hundred lines of code in a pull request.

Most AI CLIs build this in as plan mode, so you don’t have to enforce it by willpower. Turn it on. In plan mode the agent reads, reasons, and proposes a plan but is blocked from changing files, so it can’t run off and edit on a bad assumption. You read what it proposes, and only when you’re happy with it do you switch to edit mode and let it make the changes.

It also helps to make it reason out loud, to think through the problem step by step and explain itself before it commits to an answer. This is rubber ducking, the old debugging trick where you explain your problem aloud to a rubber duck on your desk and the act of saying it out loud makes the flaw obvious. Articulating the steps is what surfaces the mistake, and it works the same way here: forcing the AI to walk through its reasoning exposes the shaky assumption before it turns into code.

The same discipline carries through the work. Correct it early and specifically, before a small misread compounds. Treat it as a conversation, and when a thread goes sideways it’s usually faster to reset and re-onboard than to argue with a confused one. And always check the output. (Human in the loop!)

The catch with prompts: you can’t do this every time

Notice what just happened. To get good results you’re now writing a wall of context for every task: the project, the stack, the structure, the conventions, the scope. It works, but retyping it every session is tedious and easy to botch.

You wouldn’t re-explain the company out loud to every new employee. You’d hand them a README. That’s what an AGENTS.md file is: a standing onboarding doc, covering what the project is, the stack, the structure, the rules, that the AI reads on its own. You stop pasting the same context over and over.

Keep it scoped and short

Keep the file short, well under a couple hundred lines, and include only the context for where it sits. A top-level AGENTS.md covers the broad strokes: what the project is about, the language, CI, linting, PR templates. Then you nest smaller AGENTS.md files inside folders for finer details of the section of the codebase, so each file carries only what’s relevant to its part of the tree instead of one giant file trying to explain the whole repo.

In practice it mirrors the codebase:

my-app/
├── AGENTS.md                 # what the project is about, language, CI, linting, PR template
├── api/
│   ├── AGENTS.md             # how the backend is structured, auth, error conventions
│   └── billing/
│       └── AGENTS.md         # the gotchas: don't touch the legacy invoice path, why
├── web/
│   ├── AGENTS.md             # component patterns, state management, styling rules
│   └── ...
└── infra/
    └── AGENTS.md             # deploy steps, environments, secrets handling

Work on a file under api/billing/ and the AI picks up the broad AGENTS.md from the top file plus the detailed billing AGENTS.md from the billing file. No more than it needs, right where it needs it. The less noise irrelevant to the scope of the problem being solved, the less confusion.

You also don’t have to cram everything into the AGENTS.md files themselves either. Treat it as an index that references other files and for anything deeper, link to the doc that already covers it. For example, put the basics inline, like the commands to lint, test, and build in an AGENTS.md file, then put “for the full testing strategy, see docs/testing.md”. The AI reads the short file every time and only pulls the heavier testing doc when a task calls for it. That keeps AGENTS.md lean and gives you one less place for docs to rot and most importantly, minimizes the context the AI has to remember at any time.

What goes in it: the codebase and the battle scars

What makes someone good on a team is two things, and a good AGENTS.md captures both. The first is knowing the codebase, the structure and stack we already covered. The second is the battle scars: the edge cases that burned them, the decisions and trade-offs the team made along the way, and why. A new hire earns those over months. You can hand them over on day one by writing them down, so the AI starts with the lessons instead of relearning them, or worse, re-breaking what you already fixed. An edge case can be a single line: “Invoices must be generated in UTC.” Keep it direct and meaningful.

Most important: keep agents.md current

Watch where the AI gets stuck, where it guesses wrong, where it trips on the same edge case twice, and write that down so the next chat session never hits it.

Outdated docs are worse than no docs. With no docs you at least go read the real code. Bad docs send you down a wild goose chase and leave you more lost than when you started, because you trust them, act on them, and get burned. The same is true for AI, except it can’t smell that something’s off the way a seasoned engineer eventually does. It’ll follow a stale AGENTS.md straight off a cliff, confidently. So treat the file as living. Every time you earn a new scar, a fresh edge case, a decision that changed, a path that’s now dead, add it right then. Treat your AGENTS.md file as a living document that grows and adapts with the codebase over time.

Writing it down is easier than it sounds, because you don’t have to do it by hand. Once you’ve worked through the problem, just tell the AI to do it: “add what we learned here in 1-2 lines to the AGENTS.md file.” It already has the full context of what went wrong and how you fixed it, so it can capture the scar in a sentence or two and drop it in the right place. Your only job is to glance at what it wrote and make sure it landed in the file that covers that part of the codebase, the nested billing one rather than the top-level file, for example.

Each fix you fold back in is one mistake it will never make again. Do this consistently and the onboarding gets sharper every week: the AI needs less hand-holding, wastes less time on dead ends, and gets more done per session. Get good at onboarding and you get good at AI.

Scaling up: planning architecture

Once the onboarding example clicks, it scales up from a single ticket to planning a whole feature.

You wouldn’t drop a hundred spec documents on a new hire and expect a plan back. You’d start small. What is this feature trying to do? Lay out its main parts. Take each part a level deeper, then those a level deeper again, until the whole thing is worked through. It’s the same context ladder as before, just pointed at software architecture instead of orientation.

And because its memory thins out when you overload it, don’t make one model carry the whole feature in its head. Concentrate on one thing per session, then spin up a fresh hire for the next part. Each goes deep on its slice with clean context while you stay the architect stitching the pieces together.

Good teams don’t just split work, they check it. Once a plan or a piece is drafted, put a fresh AI on it whose only job is to poke holes: ask questions, challenge assumptions, hunt the edge cases the first one missed. A reviewer coming in cold, without the author’s tunnel vision, catches what the author can’t, the same reason we review each other’s pull requests. It’s rubber ducking with a duck that talks back. Let your AI hires review other AI hires and a single confident guess becomes something that’s been tested.

Back to the capital of France

Remember “What is the capital?” The whole problem was missing context, and the model had to guess which one you meant. Every technique here is that same move at a different scale: stop making it guess.

A quick gut check before you hit send:

  • Did I give the context a new hire would need for this task, and only that?
  • Should it plan and ask questions before doing anything?
  • Is this context I’ll need again? If so, it belongs in AGENTS.md, not this prompt.
  • Have I checked the result instead of trusting it?

A template to start from

It all collapses into one habit: hand the AI a well-written ticket. You don’t have to reinvent that ticket every time. Start from a template and fill in the blanks. The bare-bones version is background, then problem, then how to solve it. That works, but it skips the two things that save you the most pain: where to look, and what “done” means. Here’s a fuller version that lines up with everything above:

# Context
<What this project/area is, and the background needed for THIS task only.
Link AGENTS.md or docs if deeper context lives there.>

# Problem / Goal
<What we're doing and why. The outcome, not the implementation.>

# Where to look
<The relevant files or modules, and a similar past example to follow.>

# Constraints & out of scope
<What it must not touch, and the patterns to follow.>

# Approach (optional)
<Your take on how, if you have one. Otherwise let it propose one.>

# Definition of done
<How we know it's finished: tests pass, behavior works, what you'll check.>

# Process
Lay out a plan and ask any questions before editing. Wait for my OK.

And a filled-in example:

# Context
We run a subscription billing service. Invoices are generated nightly in src/billing/.

# Problem / Goal
Customers on annual plans who upgrade mid-cycle aren't being charged a prorated
amount; they're billed the full new price immediately. We want a correct
proration credited against their next invoice.

# Where to look
Proration logic: src/billing/proration.ts. Invoice assembly: src/billing/invoice.ts.
There's a similar mid-cycle calculation for monthly plans in
src/billing/monthly.ts; follow that pattern.

# Constraints & out of scope
Don't touch the payment-gateway integration or the nightly job scheduler. Keep the
existing rounding rules (round to the cent, half-up). No new dependencies.

# Definition of done
- A unit test covering an annual mid-cycle upgrade with a prorated credit.
- Existing billing tests still pass.
- The credit shows on the NEXT invoice, not as an immediate charge.

# Process
Before writing any code, lay out a short plan and ask me any clarifying questions.
Wait for me to confirm before you start editing.

AI is a force multiplier

One last thing, because it’s easy to misread what all of this is. AI doesn’t do the work for you. You still do the work; you just do it at a different altitude. Your job shifts from writing every line yourself to guiding something that can, which moves your attention off the low-level details of the code and onto the higher-level parts: the planning, the architecture, deciding what good even looks like. The typing gets cheaper. The thinking gets more important, and so do the skills that were always the hard part: architecture, judgment, knowing a good design from a bad one, spotting what’s wrong in a review. Those are the things the AI leans on you for.

That’s why it’s a force multiplier, and a multiplier runs in both directions. Point it at vague thinking and thin context and it turns out vague, sloppy work faster than you ever could. Point it at clear thinking, a scoped plan, and the context it needs, and it turns out good work just as fast. The AI won’t make a bad engineer good; it just makes a good engineer faster and a careless one’s mess bigger. Everything in this guide is really about which side of that you land on.

Leave a Reply

Your email address will not be published. Required fields are marked *