如何撰寫讓 AI 應用程式生成工具能實際建置的規格
How to Write a Spec an AI App Builder Can Actually Build From
文章提出一套供 AI 應用程式生成工具使用的規格寫法,涵蓋用戶、核心工作、畫面清單、資料模型、導覽、狀態和限制,並建議先定義資料模型。作者以習慣追蹤應用程式示範,建議先生成基本架構,再逐個畫面提出小而可測試的修改要求;這套流程亦適用於其他生成工具或交由開發者實作。
TL;DR
- AI app builders fill every gap in your prompt with a guess. A spec is just a way of making fewer gaps.
- The seven things that matter most: users, core jobs, screen list, data model, navigation, states, constraints.
- Write the data model before the screens. Most "the AI built the wrong thing" moments are really "I never said what a record looks like."
- Don't ask for the whole app and then fix it. Generate the skeleton, then iterate one screen at a time with small, testable asks.
- There's a full example spec below you can copy and adapt.
If you've tried any AI app generator, you've probably had this experience. You type "build me a habit tracker app," hit enter, and get something that looks impressive for about ninety seconds. Then you notice it has a social feed you didn't want, no way to edit a habit, and a settings screen that doesn't do anything.
The model didn't fail. It did exactly what you asked, which was almost nothing. Every detail you left out, it had to invent. And its inventions are the average of every habit tracker it has ever seen, not the one in your head.
This post is about closing that gap with the same thinking you'd put into a brief for a human developer, written down more explicitly.
Why vague prompts produce generic apps
A short prompt isn't a simple request. It's a request with a huge number of unstated decisions. "Habit tracker" leaves open:
- Who is this for? A student? Someone in recovery? A team?
- Is a habit daily, weekly, or custom?
- Can you miss a day without losing your streak?
- Is there an account, or is everything local?
- What happens on first launch when there's no data?
A human developer would come back with twenty questions. Most AI tools just pick defaults. Your job is to answer those questions up front.
The seven parts of a buildable spec
Here's the structure I use. You don't need every section to be long. You need every section to exist.
1. Users
One or two sentences per user type. Who they are and what context they're in when they open the app.
Primary user: someone trying to build 1 to 3 new habits, opening the app once or twice a day, usually for under 30 seconds.
That last bit ("under 30 seconds") is doing a lot of work. It tells the generator that check-ins need to be one tap, not a form.
2. Core jobs
What the user is trying to get done. Keep it to three to five. If you have twelve, you have a different problem.
Write them as verbs ("log today's check-in," "add a new habit"), not features ("calendar view"). Jobs tell the model why a screen exists.
3. Screen list
Name every screen and give it one line of purpose. This is the single highest-leverage section. If a screen isn't on the list, it probably shouldn't exist.
4. Data model
The entities, their fields, and how they relate. Plain English is fine, but be specific about types and required fields. Screens, forms, and the backend all depend on this, so mistakes here cause the most rework.
5. Navigation
How screens connect. Tabs or a stack? What's the first screen after login? Where does "back" go from a detail screen? Mobile navigation has a lot of conventions, and stating which one you want saves a round of corrections.
6. States
For every screen that shows data: what does it look like when it's empty, loading, errored, or full? Empty states especially. Every new user sees them first, and generators often skip them unless asked.
7. Constraints
The stuff that's easy to forget: platforms, auth method, offline needs, accessibility, things you explicitly don't want. "No social features" is a perfectly good constraint.
A worked example: a small habit app
Here's a complete spec for a deliberately small app. It's about 400 words, which is roughly the size I'd aim for on a first generation.
# App: Streakly (working name)
## Users
- Primary: an adult building 1 to 3 habits. Opens the app 1 to 2 times a day,
usually for under 30 seconds. Wants a quick check-in, not a dashboard.
## Core jobs
1. Check in on today's habits in one tap each
2. Add, edit, or archive a habit
3. See progress for the current week and the current streak
## Screens
1. Sign in: email + magic link only
2. Today (home tab): list of active habits with a check-in button each
3. Habit detail: name, schedule, current streak, last 4 weeks as a grid
4. Add / edit habit: form (name, schedule, reminder time)
5. Progress (tab): this week across all habits, one row per habit
6. Settings (tab): reminder toggle, sign out, delete account
## Data model
- User: id, email, created_at
- Habit: id, user_id, name (required, max 40 chars),
schedule ("daily" or list of weekdays), reminder_time (optional),
archived (bool, default false), created_at
- CheckIn: id, habit_id, date (one per habit per day), created_at
- Streak is computed from CheckIns, not stored.
## Navigation
- Bottom tabs: Today, Progress, Settings
- Today -> Habit detail (push)
- Habit detail -> Edit habit (modal)
- "+" button on Today -> Add habit (modal)
- After sign in, land on Today
## States
- Today, no habits: friendly message + "Add your first habit" button
- Today, all done: show a short "All done for today" banner
- Habit detail, no check-ins yet: show empty grid, streak = 0
- Any network error: inline retry message, keep last loaded data visible
## Constraints
- iOS and Android, portrait only
- Must work with large text sizes (Dynamic Type / font scaling)
- No social features, no sharing, no gamification badges
- Check-in must be a single tap with immediate visual feedback
- Archived habits are hidden from Today but kept in the database
A few things worth pointing out:
-
"Streak is computed, not stored." Without this, a lot of generators will add a
streakcolumn and then forget to update it. - "One per habit per day." That's a uniqueness rule. It prevents double check-ins, and it's the kind of thing you only remember after you see the bug.
- The "no" list. Telling the model what to leave out is as useful as telling it what to include.
Before and after
Here's the same idea expressed two ways.
Before:
Build a habit tracking app with streaks, reminders, and nice charts.
Make it modern and clean.
After (the condensed version for a chat-style prompt):
Build a mobile habit tracker for adults building 1 to 3 habits who open
the app for under 30 seconds at a time.
Screens: Sign in (email magic link), Today (home tab, list of habits with
one-tap check-in), Habit detail (streak + last 4 weeks grid), Add/Edit
habit (modal form: name, schedule, reminder time), Progress tab (this week,
one row per habit), Settings tab (reminders toggle, sign out).
Data: Habit (name max 40 chars, schedule daily or weekdays, optional
reminder time, archived flag). CheckIn (one per habit per day). Compute
streaks from check-ins, don't store them.
Empty state on Today: message plus "Add your first habit" button.
No social features. Portrait only. Support large text sizes.
"Nice charts" and "modern and clean" mean nothing specific. The "after" prompt is longer but leaves little room to wander, and it swaps "charts" for a 4-week grid, which is more concrete.
Iterate screen by screen, not app by app
Even a great spec won't produce a perfect app in one pass. The trick is how you handle the second pass.
The tempting move is to write a giant follow-up: "fix the Today screen, also the colors are off, also add dark mode, also the detail screen should show a chart." That usually breaks something that was working.
What works better:
- Generate the skeleton first. All screens, navigation, and data model. Don't polish anything yet.
- Check the data model before anything else. If the entities are wrong, every screen built on top of them will be wrong too.
- Pick one screen and make one or two changes. Be concrete: "On Today, make the check-in button a 44pt circle on the right side of each row, and fill it when checked."
- Test that screen on a device before moving on.
- Repeat for the next screen.
Small asks are easier to apply and to verify. If something breaks, you know which change did it.
This is the workflow I use most days, because I work on RapidNative, which turns a plain-English description into a React Native + Expo project with screens, navigation, styling, and a Supabase backend. You can select a single screen and describe a change to just that screen, which pairs well with the loop above. The output is a standard Expo project (Expo SDK 54, React Native 0.81, Expo Router 6, NativeWind 4, TypeScript) that you can export whenever you want. But nothing in this post depends on a specific tool. The same spec and the same loop apply whatever builder you're using, or if you're handing the spec to a human developer.
Common mistakes that cost you a regeneration
A few patterns I see in prompts that reliably lead to rework:
- Describing the look before the function. "Glassmorphism, purple gradient" before saying what the screen does. Get structure right first, style later.
- Implicit auth. If you don't say how sign-in works, you'll get whatever the default is. Say "email magic link" or "no account, local only."
- No empty states. Your first test user will see an empty app. Design for it.
- Changing the data model mid-iteration without saying so. If you add a field, mention which screens should show it.
Wrapping up
What an AI app builder gives you is capped by what you give it. Users, jobs, screens, data, navigation, states, constraints. Write them down (twenty minutes, usually), generate a skeleton, then iterate one screen at a time.
What's the one section of a spec you always forget until the generated app makes it painfully obvious? For me it's empty states. I'd love to hear yours in the comments.
來源:DEV Community · dev.to