You get a feature request. Maybe a ticket, maybe just a Slack message. You open your IDE, create a branch, and start building. Claude Code writes the code, you review it, things move fast.
Then, three hours in, you discover you’ve made five architectural decisions you never consciously made. The subscription entity is attached to the User because Claude assumed that. The tier check happens in the controller because that’s where it fit. There’s no audit trail because you never said you needed one.
The code works. It just doesn’t solve the right problem.
This is not a Claude problem. It’s a design problem. When AI does the coding, the design phase becomes the highest-leverage thing you can do as a developer. Vague input produces vague code. And yet it’s exactly the phase most developers skip.
This post is about fixing that.
What you’re building toward
By the end of a proper design phase, you should have nine documents that give Claude Code — or any developer — everything needed to implement without guessing:
What you’re building and why
- Problem statement — one paragraph: the problem, who it affects, why now
- Acceptance criteria — in Given/When/Then format; this also becomes Claude’s definition of done
- Out-of-scope — what you’re explicitly not building in this iteration
How it fits the system
- ADR for each non-trivial architectural choice
- C4 Container diagram — where does this fit in the larger system
- Sequence diagram for the main flow
What gets built
- API contract — endpoints, request/response schemas, error codes
- Data model — EF Core entities and relationships
- Ordered implementation plan with acceptance criteria per step
- Test scenarios — which tests must pass before you’re done
Nine documents sounds like a lot. But you’re not writing them from scratch. You’re producing them in four focused Claude Code sessions, each building on the last.
Session 1: Problem clarification
Don’t open your solution. Don’t write a prompt that starts with “build me”. Start with this:
I want to add a feature to our ASP.NET Core application.
Don't write any code yet. Don't propose a solution yet.
Ask me questions — one at a time — until you fully understand:
- What problem we're solving and for whom
- How often this situation occurs
- What "done" looks like from the user's perspective
- What we're explicitly NOT building in this iteration
When you have enough, write a one-page problem statement and ask me to confirm it.
Claude will ask five to eight targeted questions. Not generic questions like “what’s the scope?” — specific ones that force you to articulate what you’ve been assuming.
For a subscription tier feature (Free / Pro / Enterprise with different API rate limits), Claude might ask:
- What happens to a user who exceeds their limit mid-month — do they get blocked immediately or at the next billing cycle?
- If a user downgrades, do they lose access to Pro features instantly or at the end of their current period?
- Who can change a user’s subscription — just the user, or also an admin?
- Do we need an audit trail of tier changes?
These are the decisions that will shape the entire implementation. Getting them answered before writing code prevents you from discovering them in a code review.
After the questions, Claude writes a problem statement:
## Problem statement
Our ASP.NET Core API currently has no concept of subscription tiers.
All authenticated users have identical access to all endpoints and resources.
As we move toward a paid product, we need to differentiate access based on
subscription tier: Free, Pro, and Enterprise.
The primary need is rate limiting per tier (requests/minute) and feature
gating (certain endpoints only available to Pro and Enterprise).
Admins can change a user's tier at any time; the change takes effect immediately.
Downgraded users lose access to higher-tier features at the moment of downgrade.
We need an audit trail of all tier changes, including who made the change.
Out of scope: billing integration, payment processing, trial periods,
team/organization subscriptions.
You read it, correct anything that’s wrong, and confirm it. This takes fifteen minutes. It prevents two days of rework.
Session 2: Architecture exploration and ADR
Now you have a confirmed problem statement. Open a new session — or continue the same one — and bring in your codebase:
Read the solution structure. Here is the confirmed problem statement:
[paste problem statement]
Propose THREE different architectural approaches for implementing subscription
tiers. For each approach:
- Name the main components involved
- Explain how it fits (or conflicts) with our existing patterns
- List the trade-offs
Do not recommend one yet. I want to see the options first.
The “do not recommend yet” instruction matters. If you ask Claude for the best approach, it gives you one. If you ask for three options, you see the trade-off space — and you make the call.
For subscription tiers in a typical ASP.NET Core API, Claude might surface:
Option A: Store the tier on the existing UserProfile entity. Simple, no extra join, but mixes identity with billing concepts. Hard to extend later.
Option B: A separate Subscription entity with its own table. Clean separation, supports history, fits a future billing integration. One extra join per request.
Option C: A dedicated microservice or module with its own database. Maximum separation, maximum complexity. Overkill for the current scope.
You choose Option B. Then:
We're going with Option B — a separate Subscription entity.
Write an ADR documenting this decision. Include:
- Context: why we needed to make this decision
- Options considered: all three, with their trade-offs
- Decision: Option B and the reasoning
- Consequences: what becomes easier, what becomes harder
Save it as docs/adr/001-subscription-tier-storage.md
Claude produces a real ADR in under a minute. Something that used to take three hours of writing now takes thirty seconds of prompting and five minutes of reviewing.
The ADR isn’t for Claude. It’s for the developer — including future you — who needs to understand why this choice was made, six months from now.
Session 3: Technical design
With the architecture locked, you can generate the design documents. These are the artifacts a developer needs to actually build without asking questions.
API contract:
Based on the ADR and problem statement, design the REST API contract
for subscription tier management.
Output a markdown table with: HTTP method, path, request body,
response body, HTTP status codes including errors.
Follow the existing patterns in our controllers/endpoints.
The output maps directly to what you’ll implement:
| Method | Path | Request | Response | Status codes |
|---|---|---|---|---|
GET | /api/subscriptions/me | — | SubscriptionDto | 200, 401 |
PUT | /api/admin/users/{id}/subscription | UpdateSubscriptionRequest | SubscriptionDto | 200, 400, 401, 403, 404 |
GET | /api/admin/users/{id}/subscription/history | — | SubscriptionHistoryDto[] | 200, 401, 403, 404 |
Data model:
Design the EF Core entities needed for this feature.
Show the C# class definitions.
Include relationships with existing entities (User, UserProfile).
Add data annotations where appropriate.
Claude generates the entities with the right relationships, indexes, and constraints — matching your existing conventions because it read your codebase first:
public class Subscription
{
public int Id { get; set; }
public int UserId { get; set; }
public SubscriptionTier Tier { get; set; }
public DateTime EffectiveFrom { get; set; }
public DateTime? EffectiveTo { get; set; }
public int ChangedByUserId { get; set; }
public string? ChangeReason { get; set; }
public User User { get; set; } = null!;
public User ChangedByUser { get; set; } = null!;
}
public enum SubscriptionTier
{
Free = 0,
Pro = 1,
Enterprise = 2
}
Sequence diagram:
Generate a Mermaid sequence diagram for the admin changing a user's subscription tier.
Include: HTTP client, API layer, authorization check, SubscriptionService,
repository, audit trail write, response.
sequenceDiagram
participant Client
participant API as SubscriptionsEndpoint
participant Auth as AuthorizationService
participant Svc as SubscriptionService
participant Repo as SubscriptionRepository
participant DB as Database
Client->>API: PUT /api/admin/users/42/subscription
API->>Auth: RequireRole("Admin")
Auth-->>API: Authorized
API->>Svc: UpdateTierAsync(userId: 42, tier: Pro, changedBy: adminId)
Svc->>Repo: GetCurrentAsync(userId: 42)
Repo-->>Svc: Subscription { Tier: Free }
Svc->>Repo: CloseCurrentAsync(subscription)
Svc->>Repo: CreateAsync(new Subscription { Tier: Pro, ... })
Repo->>DB: INSERT + UPDATE
DB-->>Repo: OK
Svc-->>API: SubscriptionDto
API-->>Client: 200 OK
This diagram goes into the ADR or a dedicated design document. It’s the conversation you used to have in a whiteboard session, now produced in sixty seconds and versioned in your repository.
Session 4: Implementation plan and test scenarios
The final session produces the documents that drive the actual build:
Based on all the documents we've created — the problem statement, ADR,
API contract, data model, and sequence diagram — write an ordered
implementation plan.
Each step should:
- Be completable in one focused Claude Code session
- Build on the output of the previous step
- Have clear acceptance criteria
Then write five test scenarios (mix of unit and integration tests)
that must all pass before this feature is considered complete.
The implementation plan Claude produces:
## Implementation plan: Subscription tiers
### Step 1: Data model and migration
Create Subscription entity, SubscriptionTier enum, and EF Core migration.
✓ Migration runs without errors
✓ Subscription table created with correct columns and foreign keys
### Step 2: Repository and service layer
Create ISubscriptionRepository, SubscriptionRepository, ISubscriptionService,
SubscriptionService. Implement GetCurrentAsync and UpdateTierAsync.
✓ Unit tests pass for both happy path and not-found cases
### Step 3: API endpoints
Implement GET /api/subscriptions/me and PUT /api/admin/users/{id}/subscription.
Register endpoints in program.cs, add authorization attributes.
✓ Endpoints return correct status codes
### Step 4: Rate limiting middleware
Implement ISubscriptionTierProvider. Wire into existing rate limiting middleware.
✓ Free tier users receive 429 when exceeding limit
✓ Pro tier users have higher limit applied correctly
### Step 5: Integration tests
Write integration tests using WebApplicationFactory for all three endpoints.
✓ All five test scenarios pass
✓ dotnet test exits 0
Each step can be handed directly to Claude Code as a session prompt. The plan replaces the mental overhead of figuring out what to do next — that decision was already made, in the design phase, when it was cheap to change.
What you don’t let Claude decide
Claude is excellent at generating options, structuring decisions, and producing documents from constraints you define. It is not a product manager, a compliance officer, or a team lead.
There are decisions that have to come from you:
Business priorities. What gets built first, which tier gets which limits, what counts as a “Pro” feature — these are business decisions. Claude will invent plausible-sounding answers if you don’t provide them. Don’t let it.
Security and compliance requirements. If your product handles financial data, healthcare records, or anything regulated, the requirements don’t come from Claude’s training data. They come from your legal team or compliance documentation. Provide them explicitly.
Performance thresholds. Ask Claude “what’s a reasonable rate limit for a free tier?” and it will give you a number. That number has no basis in your actual infrastructure, your traffic patterns, or your cost model. You set the thresholds.
Team conventions that aren’t documented. If your team has patterns that only exist in people’s heads — not in the codebase, not in CLAUDE.md — Claude doesn’t know about them. This is the biggest source of “technically correct but wrong” code. The design phase is a good moment to surface those conventions and write them down.
The output you end up with
After four sessions — probably two to three hours total — you have:
- A confirmed problem statement
- Explicit acceptance criteria with an out-of-scope definition
- An ADR documenting the key architectural choice
- A C4-style context for where this fits
- An API contract in a markdown table
- EF Core entity definitions
- A Mermaid sequence diagram for the main flow
- An ordered implementation plan with per-step criteria
- Five test scenarios that define done
That’s everything a developer needs to build without guessing. Including you, next week, when you’ve forgotten half the decisions you made this week.
The next time you get a feature request, resist the reflex to open your IDE. Open a terminal. Start a Claude Code session. Type: “Don’t write any code yet. Ask me questions until you understand the problem.”
What used to happen in your head — or didn’t happen at all — now happens on paper. The implementation gets faster. The code fits. And the decisions you made are documented, for the team and for Claude.
The design phase isn’t overhead. With AI, it’s the job.