Scaffold a SaaS starter app in a new project: {{language}} on the backend, {{database}} for persistence, and {{addon}} wired in from the start. Core pieces: email-plus-password auth with session cookies, an organizations table so every user belongs to a team, role checks (owner, member) enforced in middleware, and a billing page stubbed with plan tiers read from config. Routes: /signup, /login, /app (protected shell), /app/settings, /app/team with an invite-by-email flow that stores pending invites. Handle the ugly cases: invite accepted by an existing account, last owner trying to leave a team, and expired sessions redirecting back to the original URL after login. Generate migrations, a .env.example listing every required variable, and a README section on first-run setup. Run the migration and boot the app in the terminal to prove it starts clean, then summarize the folder structure you created.What Windsurf is good at
Windsurf is an AI-native IDE (a VS Code fork) built around Cascade, an agent that combines repo-wide context with the ability to edit multiple files and run terminal commands in a single flow. You describe an outcome, Cascade plans the steps, makes the edits, executes the build or tests, and iterates on what it sees.
Compared with Cursor, Windsurf leans harder on automatic context: Cascade decides which files matter instead of waiting for you to @-mention them, which suits developers who want to delegate a whole task rather than steer each edit. Compared with Copilot, it is an agent first and an autocomplete second. Compared with terminal agents like Claude Code, you keep a full editor UI, with diffs you review and accept per file.
It fits developers working in medium-sized repos who want task-level delegation with a visual review step before anything is committed.
How to prompt Windsurf
Cascade works best when you hand it a complete task with verifiable finish conditions, not a fragment. Aim for 60 to 160 words: state the goal, the constraints that matter, the edge cases you already know about, and how the agent should prove the work is done.
Things that pay off with Windsurf specifically:
- Let it find context, but pin the anchors. Cascade pulls in relevant files on its own, so you rarely need to paste code. Do name the one or two files or functions that must be the starting point when precision matters. - Demand execution, not description. Cascade can run commands in its terminal. End prompts with "run the tests and fix failures" or "boot the dev server and confirm no errors", otherwise you may get plausible code that was never exercised. - Ask for a file list. Requesting "name each file you touched" at the end turns review from archaeology into a checklist. - Use rules for house style. If you repeat an instruction across prompts, it belongs in a rules file instead.
Keep one task per flow. Cascade handles multi-step work well inside a single task, but stacking unrelated asks in one message produces half-finished edits that are harder to unwind than two clean flows.
All 20 Windsurf prompts
App 4 prompts
In Cascade Write mode, scaffold a React to-do app in this workspace: a Vite project with components/TaskList, components/TaskForm, and a useTasks hook that persists to localStorage under the key todo.tasks. Behaviors: add on Enter, toggle complete with a checkbox, inline edit on double click, delete with an undo toast, and a filter bar for all, active, and done. Show a short hint in the empty state and disable submit on blank titles. Keep all state in the hook; components stay presentational. Run npm run dev in the Windsurf terminal so I can check the preview, then write a smoke test for useTasks covering add, toggle, and reload persistence. Show me the per-file diff before touching anything outside src/.
Build a blog with CMS-backed content in this workspace. Model: post with title, slug, rich text body, publishedAt, and an author reference. Pages: an index listing published posts newest first with pagination, /blog/[slug] rendering the rich text, and a 404 for unknown slugs. Add an RSS feed at /rss.xml and per-post title and description meta tags. Drafts must never appear in production builds; enforce the publishedAt filter in every query that touches posts. Check package.json for an existing CMS or ORM and ask before picking one; if none exists, store content in {{database}} tables with a minimal editing route. Seed two posts, run the production build from the Windsurf terminal, and confirm the slug route, the feed, and the 404 respond correctly.Build an internal admin panel under /admin. Views: a searchable table of {{resource}} records with column sorting and pagination, backed by the existing {{resource}} endpoints, a detail drawer that edits one record with optimistic updates and rollback on failure, and a read-only audit log. Gate the whole route tree behind an isAdmin check, redirecting anonymous users to login; no per-component guards. Destructive actions require a typed-confirmation modal, and bulk actions must report partial failures row by row. Do not add a UI kit: inventory the components this repo already exports and compose those. Boot the app from the Windsurf terminal, click through create, edit, and delete on a seeded record, and summarize any endpoint gaps the panel exposed, like missing filters or absent audit events.Backend 4 prompts
Have Cascade draft a short plan first, naming the modules it will create, then execute it. Goal: a {{language}} REST service over {{database}} exposing /api/{{resource}}. Handlers: create, fetch a single record, PATCH updates, delete, and a listing route accepting page, limit (never above 100), and sort. A body failing validation gets a 400 whose payload is an array of per-field messages; an unrecognized id gets 404; inserting a duplicate into a unique column gets 409. Every query must go through this repo's ORM or prepared statements; SQL assembled from strings is banned. Include a schema migration plus a seeder inserting sample rows. In the editor terminal, run the integration suite until it passes; required cases: complete CRUD flow, malformed body, id that matches no row, and a page request beyond the final page. Finish with the created-file list and the command that boots the server locally.Build a TypeScript GraphQL API with Apollo Server backed by {{database}}. Schema: a {{resource}} type, a query for one {{resource}} by id, a cursor-paginated list query, and create, update, delete mutations returning a payload with a userErrors field instead of throwing. Put SDL in src/schema.graphql, resolvers under src/resolvers/, and a dataloader per relation to prevent N+1 lookups. Validate input at the resolver boundary and map {{database}} failures to typed errors. Ask before installing packages. Then start the server from the Windsurf terminal and run an introspection query plus one create-then-fetch round trip to prove the wiring. Finish by listing every file you created and the nullability decisions you made so I can review them.Add JWT authentication to this API. Endpoints: POST /auth/register hashing passwords with argon2 or bcrypt, POST /auth/login returning a short-lived access token plus an httpOnly refresh cookie, POST /auth/refresh rotating the refresh token, POST /auth/logout revoking it. Store refresh token hashes in {{database}} with a revoked flag; never store raw tokens. Sign with a secret read from env and fail startup loudly when it is missing. Middleware attaches req.user, returns 401 for expired tokens and 403 for valid but forbidden ones. Handle replayed refresh tokens, clock skew tolerance, and login rate limiting. Write integration tests covering the full register, login, refresh, logout cycle, run them in the Windsurf terminal, and show me the combined diff with the test output.Design a Postgres schema for {{resource}} management as numbered migration files, not a dump. Tables: users, one per {{resource}}, and a join table with a composite primary key, all carrying created_at and updated_at with a trigger keeping updated_at current. Conventions: snake_case identifiers, text over varchar, timestamptz everywhere, and foreign keys with an explicit ON DELETE choice justified in a SQL comment per relation. Add indexes only for access patterns visible in code: read the repository layer first and cite the call site behind each index. Every migration gets a matching rollback file. Apply the migrations to the local database from the Windsurf terminal, run one insert, update, and select round trip, and paste the psql \d output for each table.Features 3 prompts
Add search for {{resource}} records to this codebase, end to end. Backend: a GET /api/search endpoint taking q, limit, and offset, matching against name and description fields case-insensitively, returning results plus a total count. Use the database's native text search if available, otherwise ILIKE with a trigram or prefix index, and add that index in a migration. Debounce the frontend input at around 300ms, cancel stale requests with AbortController so out-of-order responses never overwrite newer ones, and show three states: searching, results with the match count, and a no-results message echoing the query. Empty or whitespace-only queries should clear results, not hit the API. Escape user input so % and _ cannot act as wildcards. Verify with a test covering ranking of exact match above partial match, then tell me which files changed and how to try it.Integrate an LLM chatbot into this app. Server: a POST /api/chat route that streams tokens over server-sent events, reads the provider key from env only, and rejects bodies over 4000 characters with a 413. Client: a ChatPanel component that renders history, appends streamed chunks, offers a stop button that aborts the fetch, and retries once on transient failure. Never log message contents. Add {{addon}} for rate limiting only if I confirm it. Use Cascade Chat mode first to propose the file layout and a provider abstraction, wait for my approval, then switch to Write mode to implement. Verify by starting the dev server in the Windsurf terminal and sending one scripted curl request that proves the stream ends with a done event.Add email notifications to this codebase. Build an EmailService with a provider interface so transports swap cleanly, a console transport for development, and templates as files under emails/ with subject and body separated. Triggers: welcome on signup, password reset with a one-hour token link, and a weekly digest stub behind a feature flag. Sending happens off the request path: reuse the repo's job queue if one exists, otherwise an outbox table in {{database}} drained by a worker; ask me before adding {{addon}}. Handle missing addresses, unsubscribed users, and template render failures without crashing the caller. Before editing, have Cascade grep for the real signup and reset handlers so hooks land in the right functions, then run the test suite and list changed files.Frontend 3 prompts
Build a dashboard UI in this project using {{language}} and the existing component conventions. Layout: a fixed sidebar with nav links, a top bar with a date-range picker, and a main grid of four stat cards above two charts and a recent-activity table. Fetch data through a single useDashboardData hook so widgets stay decoupled from the API layer. Every widget needs three states: skeleton while loading, an inline retry on fetch failure, and a zero-data message that tells the user what action creates data. The table should sort by column header and paginate client-side up to 500 rows. Keep the grid responsive: cards collapse to one column below 768px and charts stay legible at 320px wide. When done, run the dev server, confirm no console errors, and list the components you added with their file paths.Create a landing page at src/pages/index with these sections in this order: hero with one headline and a single primary CTA, a three-item feature grid, a code snippet block with a copy button, a social proof placeholder, and a footer with nav links. Constraints: semantic header, main, and footer landmarks, system font stack, no carousel, no external scripts, and images below the fold lazy loaded with explicit width and height. Build mobile first at 360px, then adapt at 768px and 1200px. Use {{addon}} for styling only if the repo already includes it; otherwise CSS modules. Have Cascade start the dev server, open the preview at each breakpoint, and flag markup problems it can see, like missing alt text or unsized images, before I review.Build a pricing page with three tiers defined in one pricing.ts config: name, monthly price, annual price, feature list, CTA target, and a recommended flag. Render a monthly-annual toggle that recomputes prices without reload, style the recommended tier from the flag rather than hardcoded markup, and include a comparison table that collapses to stacked cards below 640px. Add a small FAQ accordion using button elements with aria-expanded. Format currency through Intl.NumberFormat; no hardcoded symbols. Empty feature arrays hide the list instead of rendering blank bullets. Check whether the repo already exports Button or Card components and reuse them. Start the preview from the Windsurf terminal, verify toggle math in both billing modes, and list any new design tokens you introduced.
Refactor 3 prompts
Refactor the large component I point you at without changing its rendered output or observable behavior. First map it: list every piece of state, every effect, and which JSX blocks depend on each, so we agree on the seams before you cut. Then extract in this order: pure helper functions to a sibling utils file, data fetching into a custom hook, and self-contained JSX regions into child components with explicit props, no prop drilling more than two levels (lift to context if deeper). Keep each extraction as its own small edit so the diff stays reviewable. Preserve memoization semantics: anything previously wrapped in useMemo or useCallback must keep equivalent dependencies. After every extraction, run the existing tests and typecheck in the terminal. Stop and ask before touching anything with side effects on mount. End with a before-and-after line count and the new file list.
Migrate this repo from JavaScript to TypeScript incrementally. Add tsconfig.json with allowJs and checkJs enabled and strict off, so nothing breaks on day one. Convert in dependency order: leaf utilities, then shared modules, then entry points. Rename one directory at a time; after each directory, run tsc --noEmit and the existing tests in the Windsurf terminal and report the error count before continuing. Never reach for any; prefer unknown plus narrowing, and give every exported function an explicit signature. Wrap untyped third-party modules behind typed shims in src/types/. When the tree compiles, flip strict on and list which strict flags still fail, grouped by directory, so I can decide the cleanup order.
Find and fix the slow database queries in this repo. First, in Cascade Chat mode, list every query touching the largest tables and rank them by suspicion: missing indexes, SELECT star in hot paths, N+1 loops in list endpoints, unbounded result sets. Wait for my approval on that list. Then fix in order: covering indexes as migration files, batched IN queries or joins replacing per-row lookups, LIMIT with keyset pagination on any endpoint returning whole tables, and filtering pushed down into SQL. For every change, run EXPLAIN ANALYZE in the Windsurf terminal before and after, and include both plans in the summary. No denormalization and no caching in this pass. End with the migration list plus each query you left alone and why.
Testing 3 prompts
Write unit tests for the module I have open (or the path I give you). Start by reading the file and listing its public functions with the branches each one contains, then propose the test matrix before writing anything. Cover: normal inputs, boundary values (empty arrays, zero, max lengths), error paths that should throw or return failure objects, and any async rejection paths. Mock only true externals like network and clock, keep pure logic unmocked. Match the test runner already configured in this repo, follow its existing describe/it naming style, and put files next to the source or in the repo's test directory, whichever pattern already exists. Run the suite with coverage in the terminal, report uncovered lines, and add cases until each listed branch is hit. Do not modify the source under test to make testing easier without flagging it first.
Set up Playwright end-to-end tests. Create e2e/ with a fixtures.ts for auth state and specs for the three paths that matter most in this app; read the route files first and tell me which paths you picked. Prefer getByRole locators over CSS selectors, adding data-testid only where roles are ambiguous. Each spec seeds and cleans its own data through the API, never through the UI, so specs stay independent. Configure Playwright's webServer option so the suite boots the app itself. Run it headed once from the Windsurf terminal while I watch, then headless; paste any flaky failure output. Close with a list of user paths you noticed but did not cover, so I can decide what comes next.
Audit this repo for missing error handling and fix it in layers. Start with a Cascade search for bare awaits, empty catch blocks, and promise chains without rejection paths; group findings by file and show me before editing. Then add a shared asyncHandler around route handlers, one error middleware mapping known error types to status codes and hiding stack traces outside development, and a timeout plus typed failure result on every external call: network, filesystem, {{database}}. On the UI side, wrap the route tree in an error boundary with a retry action. Every fix ships with a test that forces the failure: reject the promise, sever the connection, throw inside a child component. Run the suite from the Windsurf terminal and report each forced-failure test you added.Prompting patterns that work in Windsurf
Plan before edits
Ask Cascade to outline its steps or map the code before it changes anything, then confirm the plan in one reply. This costs a single turn and stops the agent from committing to a wrong structure across ten files. It pays off most on refactors and scaffolds.
Finish-line command
End every prompt with the command that proves success: run the suite, boot the server, execute the migration. Cascade reacts to its own terminal output, so a failure it can see gets fixed inside the flow instead of landing on you.
Anchor the entry point
Automatic context retrieval is good but not psychic. Naming the one file or function where work must start removes Cascade's main failure mode: confident edits in a plausible but wrong module. One anchor per task is usually enough.
Offload conventions to rules
Move stable preferences, your test runner, import style, directory layout, into a rules file once. Cascade reads rules on every flow, so each prompt can spend its words on the task's edge cases instead of restating house style.
Common mistakes
Accepting the whole flow unread
Cascade batches edits across many files, and the accept-all button is tempting after a long run. Step through the per-file diffs; the one bad edit is usually in a file you did not expect the agent to touch.
Prompts with no definition of done
A prompt that stops at 'add the feature' lets Cascade decide what finished means, often without executing anything. Include the command that must pass and the edge cases that must be handled; both change the output immediately.
Pasting code the agent already has
Dumping large source blocks into the chat duplicates what Cascade reads from disk and can drift out of sync with the real file. Reference paths and symbol names instead, and reserve pasting for external material like API responses or error logs.