Skip to main content
Documentation

Building an app

The end-to-end methodology for building a whole product on DeepSpace - research, spec, design, de-risk, plan, build, verify, ship.

On this page

This is the methodology for building a complete app end to end - a new product, a clone of an existing product, or any multi-feature build. It is written for coding agents and for humans running long builds alike; the rules are the same either way. Skip it for single-feature additions and bug fixes.

The core law: research and de-risking come before building; verification comes before "done." The order below is load-bearing - phases overlap and you will loop back, but never skip forward. Account setup, scaffolding, and the feature catalog are covered in installation and the quickstart (scaffold whenever you need a repo to work in; research needs no code) - this page sequences the work around them.

1. Research before any code#

If the app follows a reference product (a clone, "like X but…", a screenshot), study the real thing first - never build "like X" from memory.

  • Reverse-engineer it end to end: the full feature surface, the core loop, pricing and tiers, the data model - and the real mechanics underneath. Drive the live product headlessly (Playwright, or capture with npx deepspace test screenshot <url> <out.png>) and watch the network traffic while you drive - the API calls and request/response shapes reveal how it actually works: the pipeline, the providers, sometimes the prompts. Get to the secret sauce, or say plainly what stayed a black box - never guess it. The public web and any materials the user gave you fill the rest.
  • Ask for access to gated surfaces. If key surfaces sit behind a login, ask the user for access or screenshots - don't create accounts on someone else's product without their say-so.
  • Save a reference corpus - screenshots of every surface, notes, captures - in a stable folder (docs/refs/ or similar). Every later phase diffs against this corpus; if you don't have enough screenshots, go take more.
  • Write findings into a small docs wiki. Note where each claim came from, and mark inferences as hypotheses, never as facts.
  • Check what the platform already gives you first. Walk the feature installer (npx deepspace add) and the integration catalog (npx deepspace integrations list) so you don't hand-build what exists. Nothing outside the catalogs is a blocker - any external API works with the user's own key (npx deepspace secrets set KEY=...). Classify each capability: SDK primitive, catalog integration, or wire-it-yourself - the wire-it-yourself ones are your prime de-risking targets (step 4).

For a from-scratch product, the same step applies to the domain: study two or three real products in the space before inventing features. A feature invented from guesswork ("users probably want a live feed") gets thrown away - ground it or cut it.

2. Decide everything before code#

Drive the spec until it passes the zero-questions test: a designer and a build agent could execute it with zero clarifying questions back. A feature left as a noun ("analytics", "AI assistant") is a hole - resolve its input, source of truth, edit path, and empty, loading, and failure states.

Spec the whole product the research describes - don't quietly plan an MVP slice; staging is the user's call, not a default. The counterweight is the realism gate: for each feature, ask who clicks it and why - cut what only sounds good in theory, and say what you cut.

Surface the genuinely ambiguous product decisions to the user now, in one batch with a recommendation on each - not one at a time, and not mid-build. If the user is away, write the batch down, adopt your recommendations, and keep moving. Record decisions in a decisions file; once locked, don't relitigate without new evidence.

3. Pin the design source#

Every build has exactly one design source of truth, chosen up front. Which path is the user's call - if they haven't said, ask, folded into the step-2 question batch:

  • A design prototype from a design-generation tool. When handing off to one, give it the complete product spec but no layout prescription of your own: features and specs in full, no pages, screens, or navigation. UI requirements the user stated are the exception - pass those through. Prescribing layout produces rigid, generic design; withholding product info produces wrong design. Never feed the tool the reference product's own screenshots or brand - that only makes it copy them. Generation tools do well on conventional product and marketing layouts and badly on unusual shells (multi-pane workspaces) - prefer one of the two paths below for those.
  • The reference product's screenshots - for exact-parity clones. Treat the corpus as the bible; parity means every button, section, and option, not the general vibe. Parity covers structure and features, never identity: don't ship the original's name, logo, brand assets, or copy.
  • Self-design against studied references - screenshot two or three genuinely good real products first (ones the user names, or find your own), and note which section or component pattern you're borrowing from which product. Those screenshots join the reference corpus: design against them and keep diffing against them through the build. Studying a reference once and then recreating UI from memory produces the same slop as never studying it.

A vetted design is copied, not "improved" - idle edits are how good designs degrade. Behavior a static frame can't show (live data, motion) is yours to design deliberately. When self-designing, follow the design workflow - direction, style tile, patterns, and the anti-AI gate.

4. De-risk the load-bearing bets#

For anything the app stands on that you haven't proven - a risky integration, the core generation or data pipeline, a cost assumption, a quality bar - run a small, timeboxed spike before building the feature on top of it. Prove the recipe with real calls and real output:

npx deepspace integrations invoke <provider>/<endpoint> --body '{...}'   # real call, real output, real cost
bash

Let the result decide the design. list and info discovery is free; invoke is billed - stay inside any budget the user granted (see external APIs). Experiments are cheap; rebuilding a wrong foundation is not.

5. Plan top-down, then build in phases#

Design the whole system first - architecture, module boundaries, the data model (collections plus RBAC), the SDK surfaces each subsystem uses, folder structure, and conventions (naming, where logic lives, error handling) - then recurse into each part until nothing load-bearing is vague. The depth test: a fresh agent could implement each part from its doc alone, with zero questions back. The plan docs are the build contract. Scale planning to the build: recursion stops when ambiguity is gone, not at a page count - never let the plan grow heavier than the code it guides.

Build order and parallel work#

Build the shared foundation first - schemas, RBAC, worker routes, theme tokens - through one writer. Two hands making different implicit decisions in the foundation silently corrupt everything built on it. Features come after, on top.

Building on a single thread is the default. Parallelize across sub-agents only when the work splits cleanly, and then under five conditions:

  1. Exclusive file ownership per stream - no two agents ever touch the same file, or the same decision.
  2. Conventions inlined in every brief - never just "see the plan".
  3. The absolute working directory pinned in every brief.
  4. Cross-stream changes routed through you - never agent-to-agent edits.
  5. Spike any shared recipe on one target before fanning it out - a broken shared instruction multiplies across every parallel worker.

Reading work - research, experiments, reviews - parallelizes freely; it's parallel writing that needs these rules.

One isolated checkout per line of work#

Each stream gets a durable, resumable branch and an isolated checkout: a DeepSpace workspace (npx deepspace workspace new -t "<what this is for>") or, under GitHub source, an ordinary Git branch and worktree - which applies is the app's source mode (npx deepspace app source --json).

DeepSpace workspace sync reports path overlap with live peers, but the warning is advisory; keep exclusive file ownership in the briefs regardless. Commit and publish through the selected source's normal flow as you go. Parallel checkouts each get their own dev server on a deterministic port - see dev workflow.

6. Verify like a user - green gates are a false green#

tsc clean and passing tests say nothing about whether the app works or looks right - the classic failure is every automated gate green and the live app broken on the first click. Before calling anything done, run all six gates:

  1. Type-check and tests (npx deepspace test run) - necessary, never sufficient.
  2. Inspect the visual surfaces that changed. Use targeted screenshots when layout or design parity matters; assertions remain the primary verification.
  3. Diff against the design source - side by side with the reference corpus or prototype, surface by surface.
  4. Live-smoke the core loop on the deployed app - drive it headlessly against the deploy, signed in as a fresh test account, and do the thing the app exists for, as a user would.
  5. Multi-user features get multiple real sessions (npx deepspace test accounts list, and a two-user spec - see testing) - never verify collaboration with a single tab.
  6. Exercise the failure states - denied, expired, empty, offline. A raw exception reaching a user is a defect.

Report with evidence - the screenshot, the output, the live URL - and keep a hard line between built-and-verified, built-but-unverified, and not built. Never blur them. Assume the first pass is wrong and budget for verify-fix cycles; close the loop on a check that produces a real pass or fail, not on "looks done."

7. Review as you build, walk the spec, ship#

  • Review periodically, not only at the end - after each major feature or big chunk of work: review the new code, and step back to the system level (is a mess accumulating? is a refactor or simplification due?). Catching drift mid-build is cheap; untangling it at the end is not.
  • Independent review by a fresh context that didn't write the code - mandatory for money, auth and permissions, anything that fans out to users, and anything hard to reverse. Brief reviewers to flag only what genuinely affects correctness or the stated requirements - a reviewer sent to "find problems" invents them. No way to spawn a fresh context? Degrade honestly: re-review against the spec with deliberately fresh eyes, and say the review wasn't independent.
  • Adjudicate findings yourself. Review agents exaggerate: verify each finding against the actual source, discard what you can't ground, and reject over-engineered fixes - keep the real issue, apply the simplest correct change. Loop review, fix, re-review until a pass finds nothing of value.
  • Money paths get the hardest review, and their invariants get pinned with a test: amounts resolved server-side, never client-trusted; entitlements checked on every gated request; failures fail closed. Never hand-roll Stripe - see payments.
  • Walk the spec feature by feature against the research and decisions docs and confirm each feature is built and verified. This completeness walk - not your own sense of progress - decides "done." A self-graded "mostly there" is not a completeness check.
  • When everything looks 100% done, run a whole-system design review - a high-level pass over the finished codebase: refactor where it simplifies, remove dead code, split what grew too big, make it leaner and more robust. This is what keeps the codebase maintainable. Then re-run the step-6 gates - a refactor isn't done until verification confirms nothing broke.
  • Every cut or deferral is communicated with a reason - never a stubbed "coming soon", a silently skipped hard part, or a substituted reference. If a named resource is missing, ask; never quietly swap.

Landing and deploying#

  • Land before you deploy when DeepSpace-source workspace work is meant for trunk - an intentional workspace deploy must first sync its exact HEAD. Deploy semantics per source mode (commit-first vs ships-the-working-tree) are source control's contract. Every deploy appends a release; rollback works only while its bundle is retained. See deployment.
  • Pre-launch, with no users: deploy autonomously on green gates plus a live smoke. Rehearse risky changes with deploy --env staging.
  • Then hand the user the live URL with a short what-to-test-first list. The user driving the live product is the final gate - taste doesn't automate. Park any taste calls you couldn't settle for that moment.

Long builds: state lives on disk, not in context#

Context gets compacted; anything not written down gets forgotten or relitigated. On resume, run npx deepspace status for present facts, read npx deepspace activity from the cursor you retained, and re-open the state files below.

  • A task list - the running to-do. While it's non-empty there is always a next action; this is what prevents stopping halfway.
  • A state and decisions file - current phase, exact next step, locked decisions. The first thing to re-read on resume.
  • A lessons file - append every gotcha or wrong assumption the moment you learn it, and carry it into the next task or sub-agent brief so no mistake repeats within the build.
  • Commits, not memory, are the undo. Commit before every risky pass (reviews, refactors, redesigns) so there's something to go back to. Publish through deepspace push or workspace sync for DeepSpace source, or ordinary Git for GitHub source, so the work survives this machine. Commit code; keep planning and coordination docs out of the repo (gitignore the docs folder) and out of any published repo.

Decide vs ask#

The solo-project default: decide and keep moving. Make the small calls (copy, naming, layout details, component choices) and log them. Ask only for what's irreversible or outward-facing:

  • Spending real money beyond an explicit budget.
  • Logins or OAuth flows only the user can complete.
  • Buying a domain.
  • Publishing or announcing to real users.
  • Brand or positioning stakes.
  • A load-bearing bet that failed de-risking.

Don't pause a build to check in - a working product is the floor. And know when to stop: a fix that has failed several times in a row means come back with what you tried and what you observed, not another lap of the same loop.

When you report, lead with the result - point-wise, in plain terms, with zero assumed context - and be dead honest about what's verified versus not.

Where builds go wrong#

Recurring failure modes and the rule that prevents each. (The review and deferral reflexes live in step 7.)

FailureRule
Declaring done on green gates while the live app is brokenDone = the core loop driven on the deploy, as a fresh user, with evidence
Stopping with features unbuilt, or after one dead endKeep a task-list file; keep going until the walk-the-spec check passes; route around blockers
Grading your own parity ("mostly matches")Diff item by item against the pinned reference corpus, never memory
Ignoring or losing an explicit instruction (especially across compaction)Pin user-stated requirements in the spec and decisions file; re-verify against them before done
Inventing features or facts by guessworkGround load-bearing choices in real calls (invoke), real docs, real data - or ask
Fake or synthetic data anywhere (UI stats, demos, fixtures)Real data or clearly labeled placeholder; demos generated by the real pipeline
Patching symptoms; deleting a feature to kill its bugRoot-cause first; fix at the layer that generates the bad data
Fixing only the reported instanceTreat each report as a class; sweep the whole surface for its siblings
Shipping scaffold chrome or template-shaped UIThe scaffold is a placeholder - design against studied real references (product polish)
Test data leaking into the live appClean up seeded data in finally; audit for orphans before handoff
"Improving" a proven design or pipeline while replicating itReplicate exactly; diverge only where the user granted latitude
Wrong or stale working directory (sibling checkouts exist)Pin the absolute cwd everywhere; verify the folder is the canonical, synced one
Hours of work living only in an uncommitted worktreeCommit as you go and publish through the app's selected source; a GitHub deploy may ship dirty bytes, but that is not durable source
Two streams silently editing the same filesOne isolated worktree per line of work; for DeepSpace workspaces, read the advisory overlap report before landing

Next steps#

  • Dev workflow - the local runtime and per-worktree ports for parallel checkouts.
  • Testing - the suites and the multi-user fixture behind the verification gates.
  • External APIs - the catalog, real-call spikes, and billing modes.
  • Deployment - what a deploy ships, releases, and rollback.