SDD - Spec Driven Development

Spec-Driven Development (SDD) for AI-Assisted App Building

Purpose

When building apps with AI assistance, use Spec-Driven Development (SDD) to avoid letting the AI make implicit architecture decisions.

SDD means: define how the feature should work and how it should be implemented (at a high level) before generating code.


Why this matters

If you only describe the UI outcome ("build this screen") without defining implementation details, the AI will fill in the gaps—often inconsistently from screen to screen.

This can lead to:

  • Components that do too much

  • Mixed concerns (UI + data fetching + business logic everywhere)

  • Random patterns per feature

  • Low reuse and hard scaling

  • Difficult maintenance and refactors


Anti-pattern: “Vibe Coding” (Frontend example)

Prompt:

“Create a dashboard screen showing user stats and recent activity using this Figma design.”

What’s usually undefined

  • Data fetching: on load? pagination? caching?

  • Loading behavior: spinner vs skeleton? partial loading?

  • Error handling: retries? blocking UI or non-blocking?

  • Empty states: what to show with no data?

  • Component boundaries: where does logic live?

  • State strategy: local vs global vs cached/server state?

  • Refresh behavior: pull-to-refresh? auto refresh interval?

Outcome

The AI decides these things for you—often differently each time.


Recommended approach: Spec-Driven Development (Frontend example)

Use a structured spec per feature so the implementation is consistent.

Feature: Dashboard Screen

1) Scope

  • Display user stats (e.g., total posts, followers)

  • Display recent activity list (last 10 items)

2) Data layer

  • Data source: Supabase (REST or RPC)

  • Fetch timing: on screen load

  • Cache strategy: in-memory (e.g., React Query or equivalent)

  • Refetch triggers: pull-to-refresh

3) State management

  • Server state: handled by React Query (or equivalent)

  • UI state: local UI flags (loading, error, refreshing)

4) UI states

  • Loading

    • Show skeleton for stats cards

    • Show 5 placeholder items for activity list

  • Error

    • Show error message + retry button

  • Empty

    • Show “No activity yet” message

  • Success

    • Render stats cards

    • Render activity list

5) Component structure (example)

  • DashboardScreen

    • StatsCards

    • ActivityList

      • ActivityItem

Rule: child components should be pure UI (no data fetching inside children).

6) Data fetching rules

  • Fetching happens only in DashboardScreen

  • Pass data down as props

7) UX details

  • Pull-to-refresh triggers refetch

  • Smooth loading transitions (avoid layout shift)

  • Errors do not break layout


Key difference: SDD vs vibe coding

With Spec-Driven Development

  • You define how data flows

  • You control component boundaries

  • You enforce consistency across screens

With vibe coding

  • Each screen is built differently

  • Logic gets duplicated

  • Small changes become large refactors

Even with tools like Supabase handling auth and backend, frontend complexity is often where apps break if implementation details aren’t specified.


“Grill-me” skill (ask clarifying questions first)

Before building, use a step where the AI is instructed to:

  • Ask all necessary questions

  • Avoid assumptions

  • Confirm missing decisions (data fetching, states, boundaries, refresh, caching)

This prevents rushing into implementation and reduces future rework.


Do not delegate engineering decisions to AI

AI can help write code, but the team must:

  • Make the implementation decisions

  • Understand what is being built

  • Review architecture before generating or merging code


SDD is not the same as a PRD

SDD is not (only) a business requirements document. It focuses on software implementation details, such as:

  • data flow

  • component boundaries

  • state management approach

  • error/loading/empty UX

  • layering and reuse rules

Treat it as a distinct step in the process.


Suggested workflow: Ask → Plan → Build

Use a staged workflow to improve consistency.

  1. Ask mode (clarify)

  • Define what you want

  • Ask the AI to list questions and identify missing decisions

  • Do not allow assumptions

  1. Plan mode (design + tasks)

  • Have the AI propose an execution plan and architecture

  • Review, correct, and refine the plan before coding

  1. Build mode (implementation)

  • Generate code based on the agreed spec + plan

  • Keep the AI constrained to the defined decisions

Optional guidance from the thread:

  • Use higher-intelligence models for the Ask/Plan phases

  • Use a more cost-balanced model for Build/Execution

  • Invest time upfront in team rules, guardrails, and agents aligned to your stack


Baseline engineering knowledge recommended (especially for low-code devs)

To make good decisions while using AI, study core software concepts:

  • Software layers (UI, service layer, data layer)

  • Design patterns (when/why, not necessarily how to implement)

  • Service layer vs UI components

  • Model classes

  • Interfaces and why to use them

  • Code reuse principles

  • Functions and separation of concerns

Why this matters:

  • AI may duplicate code unnecessarily

  • AI may over-engineer with layers you don’t need

  • Humans must enforce simplicity and maintainability


Common AI issue: duplicated functions

It’s common for AI-generated code to recreate the same logic multiple times.

Rule of thumb: identify repeated logic and extract shared utilities/modules early.


Project note: shared code for Supabase Edge Functions

If using Supabase Edge Functions, the team reported success using a shared folder for reused code across functions (used in the LOOP project).

Document internally:

  • Where the shared folder lives

  • What belongs there (shared utilities, types, helpers)

  • Team conventions for imports and reuse


Summary

To build scalable AI-assisted apps:

  1. Define first (spec + decisions)

  2. Plan next (architecture + tasks)

  3. Generate/build last (constrained code output)

This keeps architecture intentional, reduces duplication, and improves long-term maintainability.


Was this article helpful?
© 2026 LowCode Internal Docs