Skip to content

Blog

Ask what it intends to build before you let it code

This version is for engineers. For a plain-language version, read Before an AI builds your app, ask what it plans to build.

A coding agent is an AI tool that takes instructions in plain language, reads your codebase, writes and edits files, and runs commands such as tests and builds. Popular ones include Claude Code, OpenAI Codex, and Cursor.

“Build me a webpage that shows the catalog, with sections for pricing, stories, and a signup.”

That is a fair first sentence. A lot of people treat it as the whole request. They paste it into a coding agent and wait for an app.

The problem is the same whether the request is a small utility or an enterprise system. There is more than one reasonable way to build it. The model inside the agent samples from those ways. Run the same prompt twice and you can get two working applications with different screens, different module boundaries, and a different schema. That variance is a property of the model, and a sharper prompt narrows it without removing it.

So the job is not to write the perfect prompt and hope. The job is to make the agent show you the paths it could take, at each layer of the application, and to choose one yourself before the code exists. Be a participant in the outcome. Do not be the person who reviews it afterward.

The example

One application runs through this whole piece.

A small shop in New Jersey sells a dozen products online, to customers in the United States and in London. A visitor browses the products and prices, reads three short customer quotes, and buys one. Buying means entering a name, an email, and a phone number, then paying. The shop wants customer accounts so a returning buyer is recognized.

That is a small utility by any measure. An enterprise system has the same three layers below, with more screens, more rules, more tables, and more people who will live with the decisions. The questions do not change. The cost of skipping them grows.

Three layers, three sets of choices

Every application here has three places where the agent will make decisions for you.

  1. The front end. The UI: screens, layout, and the components they are built from.
  2. The middle layer. The business logic: the rules of the domain, and the modules allowed to carry them out.
  3. The back end. Persistence: the schema, the keys, and the column types.

Deployment sits underneath all three. It gets a short section at the end, because it follows from the architecture.

First, ask for options

At every layer, the first proposal is one draw. Accept it and you never see the alternatives, and your real preferences usually live in the difference between them.

Ask for three. Three layouts. Three ways to split the modules. Three schemas. Then interrogate the one the agent recommends. Why this one? What gets harder in six months? What did you assume about the user, the traffic, the team?

You are not trying to catch the model out. You are reviewing decisions while they are still a paragraph. A paragraph is cheap to change. A schema with production rows on it is not.

Before any of that, let the model interview you. What is the app for, who uses it, what does done look like? Answer, and when the honest answer is “I have not decided,” say so and ask for options. Then turn the interview around.

Layer 1: the front end

In the shop, the front end is the catalog page, the customer quotes, and the checkout form.

Wireframes before markup. A wireframe is a low-fidelity sketch: boxes, labels, and the order of content. No color, no type, no polish pretending to be a product. Ask for wireframes of the screens that matter, and more than one of each.

The catalog shows why. One option is a single long page: products, quotes, checkout. Another is a product grid with a separate checkout. A third is a page per product. All three satisfy “a page with sections.” A dozen products wants the grid. One service would want the long page. Pick one, and say what you rejected so the next pass does not drift back to it.

Ambiguous nouns get the same treatment. “Stories” might mean three quotes. The agent might build a blog, with authors, dates, and a route per post. Name the one you mean.

A component library first, in the framework you ship. A component is a reusable piece of UI: a button, a product card, a form field, a section with a heading. A component library is that set, agreed before any page is assembled. If the app uses Next.js, SvelteKit, Astro, or anything else, ask the agent to build the library in that framework first, then compose the screens from it.

When the wireframe and the shipped screen are made of the same components, design and implementation have little room to diverge. Without the library, the agent invents a button each time it needs one. The catalog says Buy, the checkout says Pay, and a page added next month says Request a demo with a different radius and padding, because nobody said those were one component with three labels.

A prompt that works:

Stories means three customer quotes, not a blog. Show me three wireframes for the catalog: one long page, a product grid with a separate checkout, and a page per product. Recommend one. Then build a component library in our framework, with one button component, before you build any page.

Layer 2: the middle layer

In the shop, the middle layer decides what happens when someone buys, and which part of the code is allowed to do it.

The rules

Business logic is the part people describe loosely and the agent fills in most freely. “Let people sign up and pay” has several valid readings. Pay first and create the account afterward. Create the account first and pay later. Create an account as a silent side effect of checkout. Each is a reasonable implementation, and they are different businesses.

Ask the agent to write the sequence out before it codes it. For the shop:

  1. The visitor picks a product.
  2. The visitor enters a name, an email, and a phone number. Validate them here.
  3. If the email matches an existing account, attach the order to it. Otherwise create the account.
  4. Take payment last.
  5. Mark the order paid only when the payment provider confirms it.

Step 5 carries a rule that is easy to miss. Payment providers retry their confirmation callbacks. If the handler is not idempotent, one purchase can be recorded twice. Ask the agent how it guarantees a single order per payment, and make it point to the code that does.

Who may carry them out

Organization is where the files sit. Architecture is which modules may call which other modules, and through what interface.

Ask for a modular monolith by name unless there is a real reason to distribute the system. Monolith: one deployable application, not a set of services calling each other over the network. Modular: inside that application, the code is divided into modules with explicit public interfaces. Each module owns its data. Nothing reaches into another module’s tables or internals.

For the shop, the modules might be catalog, accounts, orders, and payments. Payments may ask accounts whether this customer exists, through a function accounts exposes. Payments may not write to the accounts tables. When billing logic changes, signup does not break, because the only connection is an interface you can read.

Two other shapes appear when you do not ask. One is the big ball of mud, where anything can import anything. It is fast on day one and miserable on day ninety, because a change to pricing breaks checkout through a dependency nobody knew about. The other is microservices from the start. That adds network calls, distributed failure, separate deploys, and data consistency problems you now have to solve. You need those when you have the traffic, the team size, or the failure isolation requirement that justifies them. A shop with a dozen products has none of the three.

Then the folders. Ask for two layouts of the source tree, with one sentence for what lives in each folder and one for what must not. Organizing by module (catalog, accounts, orders, payments) matches the architecture. Organizing by technical layer (components, services, models) scatters each module across the tree. Prefer a tree you can explain in the same words you use for the product. An agent working in a clear tree puts the next feature in the right module. An agent working in a pile puts it in whatever file it last opened.

A prompt that works:

Write the checkout sequence as numbered steps before you code it, including what is validated at each step and how a retried payment callback is handled. Use a modular monolith with catalog, accounts, orders, and payments modules. Payments may call the accounts interface. Payments may not write to accounts tables. Show me the module interfaces and the folder tree before you implement them.

If the agent says “modular monolith” but cannot name the modules and their interfaces, you do not have an architecture. You have a vibe with folders.

Layer 3: the back end

In the shop, the back end holds the customers, the products and their prices, and each order.

This is the layer where a casual prompt does damage you live with longest. The agent will produce tables that look fine and column types that are slightly wrong. Slightly wrong survives, because by the time you notice, the table has customers in it and changing it means a migration.

Say what a row means. Before any table exists, ask for one sentence per table. “One row is one customer.” “One row is one price, in one currency, for one product.” “One row is one order, placed by one customer.” If you cannot say the sentence, the table is confused. Split or rename it until you can.

Then go down into the columns. People skip this because it looks like paperwork. It decides whether the data is usable next year.

Primary and foreign keys. The agent will usually reach for an auto-incrementing integer or a random UUID (version 4). An integer is compact and readable, and it leaks your row count if the ID is ever exposed: order 18421 tells a competitor roughly how many orders you have. A UUIDv4 does not leak the count, and its randomness scatters inserts across the B-tree index. On a small table you will not notice. On a large one, every insert lands on a random page, and write performance and cache locality suffer.

UUIDv7 is time-ordered. It is still globally unique, and new rows land near the end of the index, the way new pages go at the end of a notebook. If that is what you want for primary keys and the foreign keys that reference them, say “UUIDv7.” Otherwise you get whatever the agent defaulted to.

Money. Never store money in a floating-point type. Floats are approximations. 19.99 can come back as 19.989999, and across a thousand orders the totals no longer reconcile with the payment provider. Ask for one of two things and make the agent say which: a fixed-precision decimal such as numeric(12,2), or an integer count of the minor unit, 1999 cents. Store a currency code next to every amount, using ISO 4217 such as USD or GBP, because $ is more than one currency and the shop sells in London. Quantities are integers. A price and a count are both numbers, and they are not the same type.

Phone numbers. A phone number is an identifier, not a quantity. It can start with 0 or +. A New Jersey number is ten digits. A London number starts with 0 locally and +44 internationally. A numeric column drops the leading zero and cannot store the plus, so the London customer’s number becomes a different number. Store it as text, normalized to E.164: a plus, the country code, and the digits, with no spaces or dashes. Then state which countries you support. “United States and the United Kingdom” is a specific promise, and it is the one this shop needs.

Dates, currencies, names. Store timestamps with a time zone, normalized to UTC, as timestamptz in Postgres. An order placed at 11 p.m. in New Jersey is already the next morning in London, and you will otherwise argue about which day it happened. A name is not first-plus-last everywhere, and some customers have one name. You do not have to solve every locale. You do have to name the cases you care about and make the agent show you the column it chose.

A prompt that works:

Before creating tables, give me one sentence for what a row means in each. Use UUIDv7 primary keys and matching foreign keys. Store prices as integer cents with an ISO 4217 currency code. Store phone numbers as text in E.164, and support US and UK numbers. Store timestamps as timestamptz in UTC. Show me the schema before you write a migration.

Where it will run

Deployment is how the running application is hosted and how a change gets from your machine to production. One process you can restart, or several services updating on different schedules. A database on the same host, or a managed database.

Some of this can wait. The part tied to the architecture cannot. A modular monolith wants one deployable and one database. A set of services wants a set of deploys, service discovery, and someone on call for each. Ask the agent what it assumed, in a paragraph. If that paragraph describes infrastructure you do not have and do not want to operate, say so while it is still a paragraph. The shop needs one application and one Postgres database.

The order, layer by layer

This is the sequence I want before the agent writes the application.

  1. Outcome. State it in a few sentences: who it is for, and what they can do when it works.
  2. Interview. Let the agent ask you questions. Answer them. Where you have not decided, ask for options.
  3. Front end. Ask for several wireframes. Pick one. Build a component library in the framework you will ship, then compose the screens from it.
  4. Middle layer. Write the business rules as numbered steps, including validation and retries. Ask for two architectures and two folder trees. Name the modules and their interfaces.
  5. Back end. Ask for the tables, with a sentence for what a row means. Specify the types for keys, money, phone numbers, timestamps, and anything else that will hurt to migrate after the first real data lands.
  6. Deployment. Ask where it runs and what a normal release looks like.
  7. Review. Read all of it. Question what it proposed. Then let it write the application.

The last step is the one people skip, because the agent is fast and the idea is exciting. That speed is the reason to pause. The model will gladly scaffold an entire product from one prompt, and you will spend the next month deleting decisions you never made.

Let the model grill you. That part is real. Then grill it back, layer by layer: the screens and the components, the rules and the modules, the tables down to the column types, and where it runs. That is the intent. If you cannot point at it, you do not have a request yet. You have a wish, and the agent will build a nearby one.