Skip to main content
← Back to course

Scoping for Velocity: Vertical Slices & YAGNI

Scope is the most expensive decision you make on a project, and you make it before you write a line of code. Get it wrong and you pay interest on it every sprint. The senior move isn't "build less" as a vibe — it's a discipline: ship vertical slices and aggressively apply YAGNI (You Aren't Gonna Need It).

The mental model: cut down, not across

A horizontal slice builds one layer fully — every Supabase table, every RLS policy, the whole design system — before anything is usable end to end. You can't demo it, can't deploy it, can't learn from it. A vertical slice cuts top to bottom through one narrow user path: route → server action → database → response. It's small, but it's real — it runs in production and produces signal.

A left-to-right flow showing a vertical slice cutting top to bottom: route to server action to database to response.

For a coaching check-in feature, the first vertical slice is: an authenticated coach submits one check-in and reads it back. Not threads, not notifications, not charts. One path, all the way down.

// app/checkins/actions.ts
'use server'
import { createClient } from '@/lib/supabase/server'
import { z } from 'zod'

const CheckIn = z.object({ note: z.string().min(1).max(2000) })

export async function submitCheckIn(formData: FormData) {
  const parsed = CheckIn.safeParse({ note: formData.get('note') })
  if (!parsed.success) return { error: 'Invalid input' }

  const supabase = await createClient()
  const { data: { user } } = await supabase.auth.getUser()
  if (!user) return { error: 'Unauthorized' }

  const { error } = await supabase
    .from('check_ins')
    .insert({ user_id: user.id, note: parsed.data.note })
  return error ? { error: error.message } : { ok: true }
}

That's a complete slice: validated input, auth check, a writable table behind RLS, a typed result. It deploys to Vercel today and tells you whether the core loop is even worth the rest of the roadmap.

YAGNI in practice

YAGNI is a forecasting claim: the cost of building speculative capability now — plus carrying it through every future refactor — usually exceeds the cost of adding it later when the need is real. The trap is that premature abstraction feels like senior engineering.

Concrete calls on this stack:

  • No generic "settings" table until two real settings exist. One sensible default beats a config system nobody configures.
  • Don't reach for an event queue or background jobs when a synchronous server action covers v1's load.
  • Skip the multi-provider auth abstraction. Use Supabase Auth directly; wrap it only when a second provider actually lands.
  • One RLS policy per real access pattern, not a permissions framework for roles you haven't shipped.

The pitfalls that bite at scale

Vertical doesn't mean sloppy. The slice is narrow, but it must be production-grade within its boundary — RLS enabled, input validated with Zod, errors surfaced not swallowed. A thin slice with select * and no row security isn't lean, it's a liability.

Schema is the one place to think slightly ahead. Code is cheap to change; a check_ins table that five features now depend on is not. You don't need every column up front, but pick names and a primary key you won't regret. Migrations are reversible; a bad foreign-key shape that's already in prod is a weekend.

Watch for scope creep disguised as a refactor. "While I'm in here, let me make this reusable" is feature creep wearing an engineer's hat. Park it in a LATER.md, keep the slice shippable, and let real usage tell you which abstraction is worth extracting.

⌨️ Exercise

Take a feature you're building. Write its thinnest vertical slice as one sentence: "An authenticated <user> can <one action> and see the result." Implement it as a single Next.js server action backed by one Supabase table with RLS on and Zod validation — nothing else. Then open a LATER.md and move three things you were tempted to build into it. Deploy the slice to a Vercel preview and confirm the round trip works before you write the second slice.