Skip to content
Marc Allington
Marc Allington
AI

Your best engineer's prompt is a team standard

Marc AllingtonMarc Allington · 17 min read
Your best engineer's prompt is a team standard

Give two developers the same .NET project and the same coding agent. One asks for an API endpoint that creates an order, with validation. The other asks for a minimal API endpoint returning TypedResults, validation through the validator the project already uses, a ProblemDetails body on failure, an idempotency key on the request, the EF Core write inside one SaveChangesAsync, and an integration test on WebApplicationFactory proving that a repeated key returns the original response rather than a second order.

The second prompt gets better code, and Anthropic's analysis of around 400,000 Claude Code sessions says why. It rated each user's expertise at the task from three signals: how precisely they framed the instruction, what they asked the agent to verify, and whether they corrected the agent or the agent corrected them. An expert-rated prompt set off twelve agent actions and 3,200 words of output against five actions and 600 words for a novice, and expert sessions reached verified success more than twice as often. Most of that gain came between novice and intermediate. Anthropic

The second developer is working from a list in their head.

Two prompts for the same endpoint: one sentence on the left, six tagged lines on the right

If you run the team, that list is the cheapest standard you'll ever set. Write it down as the files the agent loads before it touches your code, ship them to the team as one plugin, and every prompt on the team carries it whether the person typing knew the files existed or not: a new starter's first change starts from the same baseline as the senior's, and the review comment you've written forty times becomes a rule you write once. My own changes have gone through a file like that for the last 18 months, and most of its rules started as review comments I was tired of repeating.

The one that taught me this was DateTime.UtcNow. The first three changes that touched anything dated came back from the plan reviewer with the same note: the plan reads the clock directly, so the test can't control time, so the test for "expires after 30 days" is either missing or sleeps. I wrote the comment three times in a fortnight. Each time the builder fixed that change and the next change did it again, because the agent had copied the nearest file, and the nearest file was one I'd written before TimeProvider existed.

So the fourth time I didn't write the comment. I wrote one line in the rules file: time comes from the injected TimeProvider, never from DateTime.Now or UtcNow, and a test that needs the clock uses FakeTimeProvider. Then I fixed the file the agent kept copying. The reviewer hasn't raised it since, not because the agent learned anything, but because the rule is in its context on every change and the example it copies now does the right thing.

Three review comments about the clock become one line in the rules file and a fixed example, and the comment stops

Every rule in the file has a review comment behind it that I got tired of writing.

The engineer's prompt is a list, and a list can be shared

Look at what the second prompt contains: the result type the project uses, where validation lives, what an error response looks like, what the service does with a retried request, where the transaction boundary sits, and what test proves it. Every item is a decision the engineer made once, years ago, and now makes by reflex.

Written down, it's a standard. Loaded into the agent's context, it's a standard every prompt on the team carries, whether or not the person writing the prompt knows it exists. That's the knowledge base: the files the agent loads before it writes any code in your repository, holding the rules it has to follow and the examples it should copy.

Don't expect it to do all the work. An agent reads before it writes and copies what it reads, so in a codebase that already uses these patterns consistently, a new endpoint will follow the nearest existing one without being told. What the agent can't copy is anything that isn't in the file it opened: the rate-limit policy a new endpoint needs, the idempotency key, the outbox for a new side effect, the unique constraint for a race that didn't exist before. It also copies whichever file it happened to open, and in a real codebase that's sometimes the old one. The knowledge base is for what the codebase can't show: which example is the canonical one, the policies no single file contains, and the patterns the project hasn't met yet.

The knowledge base starts with the platform as it is now

A model's instinct for how to write C# comes from the volume of C# in its training data, and most of that targets older versions of the platform. The best measurements of the effect are on Python, but the mechanism is the same. An ICSE 2025 study of seven models found that where a library had deprecated an API and replaced it, the models still reached for the old one in 25 to 38% of completions. Wang et al. A 2026 benchmark of 270 real library changes found that, given only a description of the change, only 42.55% of the examples that adopted the new API ran in the target environment, and the figure rose to 66.36% when the full documentation was in the context. Ashik et al. The model writes current code when current knowledge is in front of it.

.NET gives you a new version every November. .NET 10 and C# 14 arrived in November 2025 with extension members and the field keyword Microsoft Learn, and .NET 11 is due in November 2026. InfoQ So the first file in the knowledge base is the platform baseline: the target framework and language version, TreatWarningsAsErrors with the analysis level set in the project file and rule severities in .editorconfig, the packages you've standardised on, and a short list of superseded patterns the model still reaches for:

  • a hand-written exception middleware, when IExceptionHandler has been in ASP.NET Core since .NET 8

  • Swashbuckle generating the OpenAPI document, when the .NET 9 templates do that with Microsoft.AspNetCore.OpenApi and Swashbuckle or Scalar only needs to draw the UI

  • a retry loop with Thread.Sleep, when AddStandardResilienceHandler is one line

  • new HttpClient() inside a method, when IHttpClientFactory has been the answer since .NET Core 2.1

  • lock (syncObj) on a plain object, when .NET 9 introduced System.Threading.Lock and C# 13 taught lock to use it

  • a Dictionary built at startup and read forever, when FrozenDictionary exists for exactly that

That last list pays back fastest. I still see each of these in generated code, each one compiles, and only the developer who already knows the replacement would send it back.

Security rules the agent can't misread

Veracode ran 80 coding tasks with known security pitfalls across more than 100 models. Given a secure and an insecure way to write the code, the models picked the insecure one 45% of the time. C#, Python and JavaScript sat in a 38 to 45% band, and larger models did no better than smaller ones. Veracode Its 2026 report put the average security pass rate at 56%, so the failure rate has barely moved. Veracode 2026 In Stanford's user study, the people who produced secure code with an assistant were the ones who trusted it less and put more into the prompt. Stanford The security knowledge has to come from the context, because the defaults are a coin toss.

So the security section of the knowledge base holds rules the agent follows and the reviewer checks, each one a sentence the agent can't misread:

  • CORS names its origins. SetIsOriginAllowed(_ => true) never appears next to AllowCredentials().

  • Secrets come from user secrets locally and Key Vault in every deployed environment. Nothing in appsettings.json gets anyone in.

  • SQL goes through LINQ or the parameterised FromSql. Interpolated strings into FromSqlRaw fail review.

  • Every endpoint requires authorisation by default. Anonymous access is an opt-in, by name, with a reason next to it.

  • Token validation checks lifetime, issuer and audience, and there is no test-only switch that turns any of them off.

  • Form posts carry antiforgery tokens.

The fourth rule went in last, after a build left an endpoint open and neither the reviewer nor the verifier caught it until I read the diff. The developer who has never hit any of these now ships the fix for all of them, because the fix is in the context rather than in their memory.

Where the senior's review comments go

You can't prompt for what you don't know is missing, and the agent, left to itself, writes for the happy path and hides the rest. Columbia's DAPLab built fifteen or so applications with five coding agents and found the agents would rather ship code that runs than code that's right, repeatedly suppressing errors instead of surfacing them to the user. DAPLab In C# that arrives as catch (Exception) around a database write, logging the failure and returning 200.

The rest is a handful of short policies, and the policies are where most of your senior developer's review comments end up.

Unhandled exceptions reach a single IExceptionHandler that returns ProblemDetails, and domain exceptions map to status codes in one place. Nothing is caught that can't be handled where it's caught.

ASP.NET Core has had rate limiting since .NET 7, with fixed window, sliding window, token bucket and concurrency policies partitioned by a key you choose. The rate-limit policy names which one each class of endpoint gets, and an endpoint on a shared resource doesn't ship without one.

Data access is AsNoTracking on reads, a projection or Include instead of a query per row, and async all the way down with no .Result.

Anything that happens after the save goes through the outbox, never Task.Run. That one went in after two builds sent an email inside Task.Run and the verifier caught both.

The front end gets loading, empty and error states, validation on client and server from one set of rules, labels and focus order that work with a keyboard and a screen reader, and one set of design tokens rather than a different spread of utility classes on every page.

The agent writes none of this unless the policy is in context.

Done means the tests the knowledge base says to write

Anthropic's second expertise signal was what the user asked the agent to verify. The knowledge base answers that for everyone with a definition of done the agent reads before it starts.

Any change that alters behaviour begins with a failing test. An endpoint gets an integration test on WebApplicationFactory against a real database in a container, with EF Core left alone rather than mocked. The test's name says what the behaviour is, and its arrange block uses the project's builders rather than hand-built entities. Builds run at warnings-as-errors and the analyzers pass. Finally the agent reports the test output rather than describing it.

Tests written to a shared shape are tests a reviewer can read in seconds, and a build that fails on a warning is a rule the model can't argue with.

A shaping step asks the questions the rules file can't

What to verify depends on the change, and that's the half of the senior engineer's prompt a standing document can't supply. So the knowledge base ends with a step that runs before the model sees the prompt.

Don't make it a prompt rewriter. A tool that turns "add an endpoint that creates an order" into a longer prompt with the right nouns pasted in moves the boilerplate problem up a level: the developer copies a prompt instead of copying code, still doesn't know why the items are there, and can't tell when the code that comes back is wrong.

Make it an interview, and make it a .NET one. The generic interview already exists. GitHub's Spec Kit has a clarify step that marks ten categories of a spec clear, partial or missing and asks up to five questions before planning, and AWS's Kiro asks about scope, constraints and edge cases before it writes requirements. Spec Kit Kiro Run either against "add an endpoint that creates an order, with validation" and you get asked whether the functional scope is clear and what's out of it, whether there are non-functional requirements such as performance or security, and what the testable acceptance criteria are.

They'd be the same questions for a Java service or a React front end, and they leave the developer to supply the answers from whatever they know. A .NET engineer with ten years in asks about that endpoint specifically:

  • Which validator does this project use, and where does it run? Don't add a second way.

  • What happens when the client retries? There's an idempotency key or there's a duplicate order.

  • Two requests for the same customer at once: a unique constraint, a row version, or an accepted race?

  • The transaction boundary needs naming: one SaveChangesAsync, or two aggregates.

  • What does failure look like to the caller: which domain exceptions map to which status codes through the registered IExceptionHandler?

  • The authorisation policy and the rate-limit policy, both by name.

  • Does anything happen after the save? Then it goes through the outbox, not Task.Run.

  • Which existing endpoint is the nearest thing to this, and which test proves the retry case?

A generic tool can't ask those, because it doesn't know it's looking at an EF Core write behind a minimal API and has no stack knowledge to form the question with. Spec Kit will recommend an answer to each question it asks. It can't recommend an idempotency key it doesn't know to ask about. The shaping tool gets both from the knowledge base. It reads the codebase first, so the questions are about this project's validator and this project's nearest endpoint. It has the rules in context, so a yes to "shared resource" attaches your rate-limit policy by name. And its instruction is to ask what a senior .NET engineer would ask about this change, in order of what's most likely to go wrong in production, and to recommend the answer the rules imply.

The answers become a short brief: goal, scope, the rules that apply, the test that proves it, the file to copy. The developer reads it and agrees with it, and a reviewer can read it in a minute. A fresh session generates the build prompt from it, so the build starts from the brief and not from the conversation that produced it.

Two things keep it in use. It has to be skippable for copy and config changes, or people route around it. And it has to stay short: a handful of questions, not a form.

It also shouldn't be a command the developer has to remember, so mine isn't. Claude Code has a UserPromptSubmit hook that fires when a prompt is submitted, before the model processes it, and the hook can add context alongside the prompt or block it and tell the developer why. Anthropic That's where I run the shaping step. A prompt that describes a change and carries no brief gets stopped with the first question, before the model sees it. The hook keeps the interview's state, so the developer's next prompts are read as answers until the brief exists, and then the prompt goes through with the rules for the files it names attached. The hook only sees the prompt text and the files it mentions, so it can't know the change needs an idempotency key, and the interview still has to ask. What the hook guarantees is that the interview happens.

Worked examples and task-specific skills beat one long file

Models copy what they're shown more reliably than they follow what they're told. The Stanford participants who gave the assistant helper functions to build on got more secure code back than those who described what they wanted. Stanford Anthropic's own guidance for Claude Code says the same: keep the always-loaded instructions short and specific, and put the detail where it's needed. Anthropic

Past a certain length the model skims a single file, so the knowledge base has three layers. A short rules file that loads on every session, a few hundred words covering the baseline and the rules above. A skill for each kind of change the team makes often, such as a new endpoint, a new migration or a new page, each carrying a worked example lifted from your own codebase and the checks specific to that kind of change. And the examples themselves, kept current: when the engineer writes the best version of an endpoint the project has, that file becomes the one the skill points at.

I ship the three layers and the hook as one plugin, cf, and every machine on a project installs it. A plugin can't carry a file that loads on its own, so cf injects the rules file through a session-start hook, and the skills and the prompt hook ship beside it. Claude Code plugins bundle skills and hooks together, and when a developer enables one its hooks merge with their own settings rather than replacing them, so the team installs a single thing and nobody's copy drifts from the standard. Anthropic In a larger firm, managed settings go a step further: an administrator sets what every developer's Claude Code loads and can restrict hooks to the managed ones, so the standard isn't something a developer opts into. Anthropic

The rules file, skills and examples stacked inside one plugin, with a prompt hook as a gate in front

What the gates check against

A knowledge base is guidance, and the model treats guidance as guidance. I've written before about why a rule with no exceptions belongs in a hook or a gate rather than a prompt. Every change goes through the same gates That still holds with a knowledge base in place.

The analyzers fail the build. A reviewer agent that never saw the planning reads the plan against the rules file before any code exists. The verifier reads the diff against the same file and has to show evidence for every finding. A human still approves. Without the knowledge base, the reviewer is checking against whatever happens to be in the reviewer's head.

The pipeline from prompt to approval, with the hook, the plan reviewer and the verifier each reading from the rules file

The cost of skipping this shows up in the telemetry. Faros AI's 2026 data across 22,000 developers found that as teams moved from low to high AI adoption, median time in code review rose five times over and incidents per pull request rose 243%. Faros AI Google's 2025 DORA report called AI an amplifier: it makes strong teams better and makes struggling teams struggle faster. Google Cloud Which side a team lands on, I'd say, depends on whether its standard is written down and the gates check against it.

Review findings have somewhere to go. When the reviewer bounces three plans in a week on the same missing rule, the senior puts the rule into the knowledge base and it stops bouncing.

Sources

Comments

Sign in or create an account to join the conversation.

Be the first to comment.

Enjoyed this? Get new articles by email.

Subscribe