DEV Community

Cover image for Building With AI When You Don't Know the Architecture: A Survival Guide
Jaxon Greaves
Jaxon Greaves

Posted on

Building With AI When You Don't Know the Architecture: A Survival Guide

I have spent the better part of the last five years watching the relationship between developers and their tools mutate at a pace that most of us are still struggling to internalize, and if there is one pattern that repeats itself in almost every codebase I get pulled into for a review, it is this: a junior developer or a solo founder leaned heavily on an AI coding assistant to ship something functional, the thing genuinely worked in the demo, and then six weeks later the entire system started buckling under its own weight because nobody, including the AI, was thinking about architecture at the time the first line of code was written.

This is not a criticism of AI tools, and it is definitely not a criticism of the people using them, because I think leaning on AI to move fast is one of the most rational decisions a resource-constrained developer can make in 2026. The problem is not the tool.

The problem is that most people never learned how to ask an AI system to think architecturally, because architectural thinking was never explicitly taught to them in the first place, and AI assistants are extremely good at answering the question you asked while being completely indifferent to the question you should have asked.

So this guide is my attempt to hand you the mental checklist that I use, refined over years of both writing production systems by hand and increasingly delegating large portions of that work to AI pair programmers.

I am going to be deliberately thorough here rather than punchy, because the whole point of architecture is that it rewards patience and punishes shortcuts, and a survival guide that reads like a listicle would betray the subject matter.

Why "vibe architecture" collapses later rather than immediately

The dangerous thing about building a system with an AI assistant when you do not understand architecture yourself is that the failure mode is deferred.

A missing index does not matter until your table has real data in it. A tightly coupled service does not matter until you need to change one piece without breaking three others. An unvalidated boundary between your frontend and backend does not matter until a malicious or simply careless user sends a payload your AI-generated validation logic never anticipated.

AI coding assistants, no matter how capable, optimize heavily for the request in front of them, and if your request was "build me an endpoint that lets users upload a profile picture," you will get exactly that, and you will not get a conversation about file size limits, storage lifecycle, CDN invalidation, or the fact that unauthenticated uploads to a public bucket are a liability waiting to happen, unless you specifically steer the conversation there.

This is the core insight the rest of this guide is built around: your job, even when you do not have a formal architecture background, is to become extremely good at asking the AI the second-order questions, because it will rarely volunteer them unprompted.

The checklist I actually use

I want to give you something concrete rather than abstract advice, so here is the sequence of questions I walk through before I let an AI assistant, or myself, write a single line of implementation code for any nontrivial feature.

1. What is the actual unit of state, and where does it live?

Before anything else, identify what data your feature creates, reads, updates, or deletes, and where that data is going to be persisted.

A staggering number of AI-assisted prototypes fail because state was scattered across component-local variables, browser local Storage, and a database table simultaneously, with no single source of truth.

When you prompt an AI assistant, be explicit about this. Instead of asking "build a shopping cart feature," ask something closer to the following.

Design a shopping cart system where the cart state is persisted
server-side in a carts table keyed by user_id, with cart items
stored in a related cart_items table. The frontend should only
hold a cached, optimistic copy of this state, and every mutation
should go through a single API layer that reconciles the cart on
the server before returning the authoritative state to the client.

Notice how that prompt does the architectural thinking for the AI instead of hoping the AI does it for you. This is the single highest-leverage habit you can build.

2. What are the boundaries, and what crosses them?

Every system has boundaries: frontend and backend, your service and a third-party API, one microservice and another, the browser and your server. Boundaries are where bugs, security holes, and cascading failures are born, because data crossing a boundary needs to be validated, authenticated, and often transformed.

When you do not understand architecture yet, the mistake is treating your whole application as one undifferentiated blob of code, which is exactly how AI assistants will treat it too if you let them, because you never told them otherwise.

A useful habit is to literally draw the boundaries before you prompt anything, even if the drawing is just a text list.

Boundaries in this system:

  1. Browser <-> API server (needs auth token validation, input sanitization)
  2. API server <-> Postgres database (needs parameterized queries, connection pooling)
  3. API server <-> Stripe API (needs idempotency keys, webhook signature verification)
  4. API server <-> Redis cache (needs TTL strategy, cache invalidation on writes)

Once you have that list, you can prompt the AI boundary by boundary, and at each boundary you explicitly ask it: what can go wrong here, and how do we defend against it. This single exercise will catch more architectural mistakes than almost anything else I know of for people early in their career.

3. What happens when this fails, not just when it succeeds?

AI assistants, left to their own devices, write the happy path beautifully and the failure path almost as an afterthought, often as a generic try/catch with a console log that would tell you nothing in production.

Force the conversation toward failure modes explicitly. Here is an example of the kind of follow-up prompt that changes the shape of the generated code substantially.

For the payment processing function you just wrote, walk through
every external call it makes and tell me: what happens if that
call times out, what happens if it returns a 5xx error, what
happens if it succeeds but the response is malformed, and whether
retrying this operation is safe or whether it could cause a
duplicate charge.

This is the kind of question a senior engineer asks instinctively during code review, and it is exactly the kind of question a junior developer or solo founder has to ask deliberately until it becomes instinct for them too.

4. Is this piece of logic going to need to change independently of its neighbors?

This is the essence of coupling, and it is the architectural concept that takes the longest to develop an intuition for.

A rough heuristic that I give people who are still building that intuition is this: if you can imagine a believable future business requirement that would force you to change function A without touching function B, but your current code structure makes that impossible without touching both, you have a coupling problem.

Below is a small, concrete illustration in TypeScript of the difference between tightly coupled logic that an AI assistant will often produce on a first pass, and the same logic restructured to isolate the parts that are likely to change independently.

`// Tightly coupled version: notification logic is welded to order logic.
// Changing how you send emails means touching your order processing code.
async function completeOrder(orderId: string) {
const order = await db.orders.findById(orderId);
order.status = "completed";
await db.orders.save(order);

// Email logic is buried inside order logic
const emailBody = Hi ${order.customerName}, your order ${order.id} shipped!;
await sendgrid.send({
to: order.customerEmail,
subject: "Order shipped",
body: emailBody,
});
}`

`// Decoupled version: order completion emits an event,
// and notification logic subscribes to that event independently.
async function completeOrder(orderId: string) {
const order = await db.orders.findById(orderId);
order.status = "completed";
await db.orders.save(order);

await eventBus.emit("order.completed", { orderId: order.id });
}

// Lives in a completely separate module, can change independently
eventBus.on("order.completed", async ({ orderId }) => {
const order = await db.orders.findById(orderId);
await notificationService.send({
to: order.customerEmail,
template: "order-shipped",
data: { orderId: order.id, name: order.customerName },
});
});`

The second version costs you a small amount of upfront complexity, in the form of an event bus, but it buys you the ability to add SMS notifications, delay emails for fraud review, or swap email providers entirely, without ever touching your order completion logic again.

When you prompt an AI assistant, you can explicitly ask for this shape by saying something like "structure this so that order completion and notification delivery are decoupled through an event, because I expect the notification requirements to change independently of the order logic."

5. What is the smallest piece of this I can validate before committing to the rest?

Architecture mistakes compound, and the earlier you catch one, the cheaper it is to fix. Rather than asking an AI assistant to generate an entire feature end to end in one shot, break the request into the smallest architecturally meaningful slice, validate it, and only then extend outward.

For a new feature involving a database schema, that might mean asking the AI to first generate just the schema and migration, explaining your reasoning back to you, before a single API route or UI component exists.

`-- Ask the AI to generate and justify this in isolation first
CREATE TABLE subscriptions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
plan_id UUID NOT NULL REFERENCES plans(id),
status TEXT NOT NULL CHECK (status IN ('active', 'canceled', 'past_due')),
current_period_end TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX idx_subscriptions_user_id ON subscriptions(user_id);
CREATE INDEX idx_subscriptions_status ON subscriptions(status);`

If you review this schema and it does not match your mental model of the domain, you have caught the mistake before it propagated into an API layer, a frontend, and a set of tests that all assumed the wrong shape.

Prompting patterns that force architectural thinking out of the AI

Beyond the checklist itself, I want to give you a handful of prompting patterns that consistently produce better architectural outcomes, because the way you phrase a request to an AI assistant materially changes the quality of the design it produces, in the same way that the way you phrase a question to a senior engineer in a design review changes the quality of their answer.

Ask for tradeoffs explicitly, rather than asking for "the best" way to do something, because there is rarely a single best way, and an AI assistant asked for "the best" approach will often give you a confident-sounding answer that hides the tradeoffs it silently chose on your behalf.

A better prompt sounds like "give me two or three ways to structure this caching layer, and for each one tell me what I am gaining and what I am giving up."

Ask the AI to argue against its own suggestion, because this single technique surfaces edge cases and weaknesses that a straightforward request never will. Something like "you just proposed storing session tokens in localStorage, now argue for why that is a bad idea and what the alternative would look like" tends to produce a genuinely more honest and complete picture than the original proposal alone.

Ask for the migration path, not just the end state, because junior developers and solo founders often ask for the final architecture without asking how to get there safely from whatever exists today, and an AI assistant will happily describe a beautiful target state while leaving you to figure out, usually the hard way, how to move your production data and running system into that state without downtime or data loss.

A closing thought on what architecture actually is

If you take one idea away from this guide, let it be this: architecture is not a fixed set of diagrams or a checklist you complete once and forget. It is the ongoing discipline of asking what happens next, what happens when this breaks, and what happens when the requirements change, before you commit to a particular shape of code.

AI assistants are extraordinarily good at generating that shape of code once you have answered those questions for them, or at least pointed them firmly in the direction of asking those questions themselves. The developers who thrive while leaning heavily on AI tooling in the coming years will not be the ones who know the most design patterns by name.

They will be the ones who have internalized the habit of interrogating their own systems relentlessly, and who have learned to turn that interrogation into prompts precise enough that the AI has no choice but to think architecturally alongside them.

Building without a formal architecture background is not a disqualifying weakness anymore. It is simply a starting point, and the checklist above is meant to be the scaffolding you lean on until asking these questions becomes as automatic as writing the code itself.

Top comments (0)