Home / Blog / AI in Architecture

Prompting an Architecture Assistant: What Good Instructions Look Like

Type "make this better" into a general chat window and you get a paragraph you can skim, shrug at, and ignore. Type "make this better" into an architecture assistant that proposes changes to your model, and you get something with teeth: a structured patch, sitting in a review screen, waiting for someone to click approve. The stakes of a vague instruction are not the same in both places, and it took us a while, watching people use Mooodels, to understand exactly why.

In a chat interface, a vague prompt costs you a re-roll. In a model-editing assistant, a vague instruction costs you a review — you now have to read every add, rename, and connect operation in a proposed ModelPatch and work out which ones the AI guessed correctly and which ones it guessed wrong, element by element, before you can approve anything. That review is real work, and it is work a well-written instruction mostly eliminates. This article is about writing the instructions that eliminate it: what specificity actually buys you here, which patterns of phrasing reliably produce a patch worth approving, and what to do — concretely — when the result still isn't right.

Why vagueness is more expensive against a model than in a chat window

The core difference is what a vague instruction has to become before it can do anything at all. A chat model answering "make this better" can stay vague indefinitely — it can hedge, generalize, offer three options, and the cost of that vagueness lands entirely on the reader's attention. An architecture assistant proposing a ModelPatch cannot do that. A patch is a list of concrete operations: add this element, rename that one, connect these two, delete this relationship. There is no vague version of an add operation. Somewhere between your instruction and the patch, every ambiguity you left in the instruction gets resolved into a specific, concrete guess — which element counts as "this," what "better" means as an operation, which of six plausible relationships gets touched and which don't.

That resolution has to happen whether you like it or not, because the assistant can't propose an ambiguous patch — the patch format doesn't have a slot for "probably something like this." So the ambiguity doesn't disappear when you write a loose instruction; it just moves. It moves from your keyboard, where it was cheap to fix, into the assistant's inference, where it becomes a guess you can't see being made, and then into your review, where you have to reconstruct what was guessed and check it against what you actually meant. A vague instruction doesn't save you the work of being specific — it just relocates that work to a slower, more error-prone stage of the process, and hands part of it to someone else's judgment instead of yours.

This is worth sitting with, because it cuts against a fairly natural intuition. With a general-purpose chat assistant, being loose and iterating on the output feels efficient — you get a draft fast, then steer. With a model-editing assistant, the equivalent move — firing off something loose and expecting to steer the result into shape by editing the proposal — tends to be slower than just writing the specific instruction the first time, because now you're not steering a paragraph, you're auditing a set of graph operations against a model you have to hold in your head at the same time.

The asymmetry in one line: a vague prompt to a chat model produces a disappointing sentence you can reread in two seconds; a vague instruction to a model-editing assistant produces a patch you have to carefully review, operation by operation, for what it guessed.

What "specific" actually buys you

It helps to see the contrast directly, because the difference between a weak and a strong instruction is rarely about politeness or length — it's about how many decisions you left for the assistant to make on your behalf.

Weak instructionWhat the assistant has to guessStrong instruction
"Make this better"What "this" refers to, what "better" means, which elements are in scope"Add an API gateway between Portal and PermitAPI, route all Portal traffic through it, and remove the direct connection"
"Clean up the model"Which elements are considered clutter, whether cleanup means renaming, deleting, merging, or re-tagging"Delete the three unused test elements tagged 'sandbox' and merge the two 'Customer API' duplicates into the one owned by the Payments team"
"Add authentication"Which element needs it, what kind of authentication element or relationship this implies, whether existing connections change"Add an IdentityProvider element, connect PermitAPI to it with an 'authenticates via' relationship, and mark PermitAPI's existing 'direct login' relationship as deprecated"
"Simplify the integration layer"Which elements count as the integration layer, what a simpler version looks like, what's allowed to disappear"Replace the four point-to-point connections between Portal, PermitAPI, BillingService, and NotificationService with a single connection from each to the existing MessageBus element"

Notice what the strong versions have in common: they don't just add adjectives, they remove decisions. "Make this better" leaves three separate ambiguities open at once — scope, meaning, and target. "Add an API gateway between Portal and PermitAPI, route all portal traffic through it, and remove the direct connection" leaves essentially none. It names the new element, names its type implicitly through context, names both endpoints, states the routing change, and states explicitly what should be removed. An assistant reading that instruction isn't inferring your intent from a thin signal — it's translating an already-decided architectural change into the patch format.

That's the real difference specificity buys you: not politeness, not a better-behaved AI, but a shorter distance between what you typed and what the patch actually contains. The shorter that distance, the less review you owe the result.

Pattern one: name existing elements so they resolve unambiguously

Mooodels keeps the AI assistant scoped to a relevant slice of the model rather than the whole repository, both for efficiency and because a narrower context is a stronger check against a hallucinated relationship or a misidentified element. That design choice has a direct consequence for how you should write instructions: name the elements you mean, using the name they actually carry in the model, not a description of them.

Compare two ways of referring to the same target:

Weak: "connect the payment thing to the new gateway"

Strong: "connect PaymentAPI to the new APIGateway element"

"The payment thing" might resolve correctly if there's exactly one plausible candidate in the assistant's current context slice. It might not, if there's a PaymentAPI, a PaymentService, and a PaymentGatewayLegacy all sitting in the same neighborhood of the model — which, in any system that's been through a migration or two, is a completely normal thing to find. When the reference is ambiguous, the assistant has to pick one, and it will pick the one that seems most plausible given the surrounding context — which is a guess, not a lookup, however good the guess is. When you name the element exactly, there's nothing to guess. The assistant resolves the name against the model, finds the element, and the patch references the one you meant.

This matters more, not less, as a model grows. In a five-element toy model, "the payment thing" probably resolves fine by elimination. In a real portfolio model with hundreds of applications, several of which share a domain word in their name, elimination stops working and pattern-matching starts — and pattern-matching is exactly the mechanism that produces a technically-plausible, actually-wrong patch. Naming elements precisely is the cheapest insurance available against that failure mode, and it costs you nothing extra to do — you already know which element you mean, you're just typing the name instead of a description of it.

The same logic applies to relationship types and existing structure. "Connect them" is weaker than "connect them with a 'depends on' relationship," if your profile distinguishes relationship kinds — which an ArchiMate or C4 profile will. Say the kind of relationship you want when the model has more than one kind available, for the same reason you'd name the element: it removes a guess rather than asking the assistant to infer your intent from context.

Pattern two: describe the end state, not a sequence of clicks

The second pattern is almost the opposite instinct from pattern one, and it's easy to get backwards if you're used to giving a human colleague step-by-step instructions. With a model-editing assistant, you generally get a better result describing what the model should look like when you're done than describing the sequence of edits to get there.

This is because the assistant isn't a macro recorder replaying clicks — it's reasoning about a target state and proposing the operations needed to reach it from the current one. Framing your instruction as a to-do list of individual edits doesn't help it do that; if anything, it can constrain it into a worse sequence than the one it would have found on its own, because you've implicitly told it to follow your ordering rather than figure out the cleanest path.

Weaker (procedural): "First add a new element called APIGateway.
Then draw a connection from Portal to APIGateway.
Then draw a connection from APIGateway to PermitAPI.
Then find the old connection from Portal to PermitAPI and delete it."

Stronger (end state): "Portal should route through a new APIGateway
element to reach PermitAPI, instead of connecting to it directly.
Remove the old direct connection."

Both instructions describe the same intended change. The end-state version is shorter, harder to get internally inconsistent, and easier to check against the resulting patch — you're comparing "does the model now look like this" against a single mental picture, rather than replaying a sequence of steps and hoping each one landed correctly. It's also more robust to small differences in how the assistant chooses to sequence the actual patch operations, which don't matter to you as long as the end state is correct.

End-state framing has a second, quieter benefit: it forces you to actually decide what you want before you write the instruction. "First do this, then do that" can be written while you're still figuring out the change as you type. "The model should end up looking like this" requires the decision to already be made. That's a feature, not friction — an instruction written after the decision is made is reliably better than one written while the decision is still being worked out live, for a human reviewer as much as for an AI assistant.

Pattern three: say what should not change

The third pattern is the one people skip most often, and it's usually the one that would have prevented the review headache after the fact. An instruction that describes the change you want, without saying anything about what should stay untouched, gives the assistant license to interpret "and while we're at it" more broadly than you intended — not out of any tendency to overreach, but because a genuinely ambiguous instruction has more than one internally consistent completion, and the assistant has to pick one.

Take the API gateway example again, but imagine PermitAPI also has three other callers besides Portal — an internal admin tool, a batch job, and a partner integration. "Add an API gateway between Portal and PermitAPI, route all portal traffic through it, and remove the direct connection" is precise about Portal. It says nothing about the other three callers. A careful assistant will probably leave them alone, because you scoped the instruction to Portal specifically — but "probably" is exactly the word you want to eliminate from a patch review, not lean on.

Closing that gap costs one clause:

"Add an API gateway between Portal and PermitAPI, route all portal
traffic through it, and remove the direct connection. Leave the
admin tool, batch job, and partner integration connected to
PermitAPI directly — they are out of scope for this change."

This is where being explicit about what should not change stops being a nicety and becomes the thing that makes the patch fast to review. If the resulting patch touches only Portal's connection and leaves the other three alone, you can approve it in ten seconds because it matches what you asked for exactly — including the boundary. If it had touched a fourth connection you didn't mention, you'd have no clean way to tell, from the instruction alone, whether that was a considered judgment call or an overreach, and you'd have to go figure it out by inspecting the model yourself. Naming the boundary turns an implicit assumption you were carrying in your head into an explicit constraint the assistant can be held to — and a patch that violates an explicit constraint is much easier to spot as wrong than one that violates an assumption nobody wrote down.

This pattern matters even more on anything described with words like "clean up," "simplify," or "consolidate," because those words describe a direction, not a boundary. "Consolidate the duplicate customer elements" is directionally clear and boundary-free — nothing in it tells the assistant which of several plausible-looking "duplicates" you actually meant, or which one should survive as the canonical element and which should be merged into it. "Consolidate CustomerRecord and CustomerProfile into CustomerRecord, keeping CustomerRecord's ID and properties, and update every relationship that pointed to CustomerProfile to point to CustomerRecord instead" has a direction and a boundary. It says what changes, what survives, and implicitly what shouldn't be touched — anything not connected to either of those two elements.

From a specific instruction to a reviewed patch Instruction names elements, end state, boundary Model slice relevant elements resolved by name Proposed patch add · connect · disconnect Validated + previewed rules checked, diff shown Approve applied to model Reject refine, re-request back to a sharper instruction
A specific instruction resolves cleanly against the model's relevant slice into a small, checkable patch. A rejected patch feeds back into a sharper instruction — not a manual edit of the proposal.
An AI-proposed change from a specific instruction, applied to the model
The patch that resulted from naming both endpoints, stating the routing change, and scoping it to Portal specifically — small enough to review at a glance.

When the result isn't quite right: reject and refine, don't hand-edit

Even with all three patterns applied, a proposed patch will sometimes come back slightly off — it picked the wrong of two similarly-named elements, it used a relationship type that's technically fine but not what you'd have chosen, it scoped a delete one element too broadly. The instinct at that point, especially for anyone used to fixing a first draft by hand, is to open the proposed patch and edit it into correctness: uncheck one operation, tweak a property, approve the rest.

Resist that instinct. The better move, almost always, is to reject the patch outright and rewrite the instruction to close the gap that produced the wrong guess, then re-request.

There are two reasons this isn't just a stylistic preference. The first is about the model's integrity. A patch is a coherent, self-consistent set of operations reasoned about together — the assistant chose to add this element and connect it to that one and remove this other connection as a single considered change. Hand-editing one operation out of that set doesn't necessarily leave the rest coherent; the connection you kept might have been proposed on the assumption that the element you just removed from the patch was also going to exist. Editing a patch into partial correctness can produce a model state nobody — human or AI — actually reasoned through end to end. Rejecting and re-requesting keeps the guarantee that whatever gets applied was reasoned about as a whole, once, by whichever party (human, in the rewritten instruction, or assistant, in the new patch) is doing the reasoning at that step.

The second reason is more practical: hand-editing a wrong proposal doesn't fix the actual problem, which is upstream, in the instruction. If "the payment thing" resolved to the wrong element once, it will resolve to the wrong element again next time you use the same loose phrasing, on a different change, possibly one where the mistake is far less obvious in review. Patching the current instance by hand treats the symptom. Rewriting the instruction — naming the element precisely this time — fixes the actual gap and pays off on every future instruction that would otherwise have hit the same ambiguity.

In practice this means treating a wrong proposal as diagnostic information rather than as raw material. If the assistant connected the wrong element, that tells you exactly which name was ambiguous — add the qualifier. If it scoped a change more broadly than intended, that tells you exactly which boundary you forgot to state — add the sentence that states it. If it chose a relationship type you didn't want, name the relationship type you do want next time. Each rejection, read this way, makes the next instruction better rather than just making the current patch marginally less wrong.

Instruction: "Connect the reporting service to the customer database"

Proposed patch: connects ReportingService to CustomerDB_Legacy
→ wrong database; CustomerDB_v2 is the current one

Don't: manually edit the patch to point at CustomerDB_v2 and approve

Do: reject, then re-request as —
"Connect ReportingService to CustomerDB_v2 (not CustomerDB_Legacy,
which is being decommissioned)"

That rewritten instruction is better in a way that outlasts this one request — it's now on record, in whatever conversation history or instruction log you keep, that CustomerDB_Legacy is the decommissioned one and CustomerDB_v2 is current. The next instruction involving that database, from you or from a colleague using the same model, benefits from that clarification even if nobody remembers writing it down explicitly.

Putting the patterns together

None of these patterns are complicated on their own, which is part of why they're easy to skip under time pressure. Put together, though, they change the shape of the whole interaction. A weak instruction produces a patch that requires careful, element-by-element review because you genuinely don't know what got guessed. A strong instruction — one that names its elements precisely, describes the end state rather than a sequence of edits, and states its boundary — produces a patch you can check against a single sentence you already wrote, which is a much faster kind of review because you're confirming rather than reconstructing.

The underlying discipline is the same one that makes any delegated task go well, human or AI: the person doing the delegating carries the burden of removing ambiguity, because ambiguity left in doesn't disappear, it just becomes someone else's guess. What's different about an architecture assistant that proposes a ModelPatch instead of a paragraph is how visible and how consequential that guess becomes — it's not a sentence you can skim past, it's a set of graph operations sitting between you and a model your organization actually depends on. Writing the instruction that removes the guess, rather than reviewing the guess after the fact, is consistently the faster path in Mooodels, and it's the habit worth building first.

A short checklist before you hit send

Most of what makes an instruction to an architecture assistant work well is not a special skill — it's the same clarity you'd already bring to writing a change request for a human colleague who's never seen your system before and can't ask a follow-up question. The difference is that here, that clarity is checked immediately, against a real model, in the form of a patch you either approve or don't.

See the model this article describes, working in a real editor.

Try the live demo