Skip to content
advanced Phase 19 · Full Stack Project

Project Planning & Architecture

Design the architecture, data models, API contracts, and project structure for a full stack app.

1h
0 problems
Topic Progress 0%

Defining Project Scope

Defining Project Scope

Before writing a single line of code, you need to define exactly what you are building, who it is for, and what constraints apply. A well-defined scope prevents feature creep, aligns stakeholders, and gives your team a clear target.

MoSCoW Prioritization

MoSCoW separates features into four priority tiers so you know what must ship in the MVP and what can wait.

Must Have (MVP - ship without these the product is broken)
  - User registration and login with email/password
  - CRUD operations for core domain (tasks, projects, etc.)
  - Responsive UI that works on mobile and desktop
  - Error handling with user-friendly messages
  - Input validation on both client and server

Should Have (important but not blocking launch)
  - Search and filter functionality
  - Pagination for list views
  - File upload for avatars and attachments
  - Email notifications for key events
  - Dark mode toggle

Could Have (nice-to-haves that add polish)
  - Real-time updates via WebSockets
  - Social login (Google, GitHub)
  - Activity feed / audit trail
  - Dashboard with charts and analytics

Won't Have (explicitly out of scope for now)
  - Native mobile apps
  - Payment processing
  - Multi-language internationalization
  - Offline-first architecture

Writing User Stories with Acceptance Criteria

Each feature should be expressed as a user story with clear acceptance criteria. This turns vague ideas into testable requirements.

Story: As a registered user, I want to create a new project
  so that I can organize my tasks into logical groups.

Acceptance Criteria:
  GIVEN I am logged in
  WHEN I click "New Project" and fill in name + description
  THEN a project is created and I see it in my project list

  GIVEN I am logged in
  WHEN I submit a project with no name
  THEN I see a validation error "Project name is required"

  GIVEN I submit a project name over 100 characters
  THEN the input is rejected with "Name must be 100 characters or fewer"

Effort Estimation with Story Points

Use Fibonacci story points (1, 2, 3, 5, 8, 13) to estimate relative complexity, not time. This accounts for uncertainty better than hour-based estimates.

Feature                  Points   Reasoning
─────────────────────────────────────────────
Auth (register/login)      5     Standard flow, JWT, bcrypt, validation
Project CRUD               5     CRUD + ownership checks + pagination
Task management            8     Nested under projects, status workflow, assignment
Team invitations           5     Email flow, role-based access, invite tokens
Search & filter            3     Full-text search, filter by status/assignee
File uploads               3     S3 integration, multer, image processing
Comments on tasks          2     Simple nested CRUD, no threading
─────────────────────────────────────────────
Total                     31

At a team velocity of 8 points per 2-week sprint:
  31 / 8 ≈ 4 sprints (8 weeks)

Technical Requirements Document

Document non-functional requirements alongside features:

Performance:
  - LCP < 2.5 seconds on 3G connection
  - API response time < 200ms (p95)
  - Support 1000 concurrent users

Security:
  - HTTPS everywhere
  - JWT with 15-minute access tokens
  - Rate limiting: 100 requests per 15 minutes
  - Input sanitization against XSS and SQL injection

Reliability:
  - 99.9% uptime target
  - Automated backups daily
  - Graceful degradation when external services fail

System Architecture

System Architecture

System architecture defines how components communicate, where data lives, and how the system scales. Designing this on paper first saves weeks of refactoring later.

High-Level Architecture

┌─────────────────────────────────────────────────────┐
│                    CLIENTS                           │
│  Browser (React SPA)    Mobile (future)             │
└──────────────┬──────────────────┬───────────────────┘
               │                  │
               ▼                  ▼
┌─────────────────────────────────────────────────────┐
│              CDN (Cloudflare)                        │
│  Static assets, caching, DDoS protection            │
└──────────────┬──────────────────────────────────────┘
               │
               ▼
┌─────────────────────────────────────────────────────┐
│         LOAD BALANCER (Cloudflare / AWS ALB)         │
└──────────────┬──────────────────────────────────────┘
               │
               ▼
┌─────────────────────────────────────────────────────┐
│              API SERVER (Express + Node.js)          │
│  Auth middleware, rate limiting, CORS, logging       │
│  Business logic in service layer                     │
│  Validation with Zod schemas                         │
└──────┬──────────────┬──────────────┬────────────────┘
       │              │              │
       ▼              ▼              ▼
┌────────────┐ ┌────────────┐ ┌──────────────┐
│ PostgreSQL │ │   Redis    │ │  S3 Bucket   │
│ Primary DB │ │   Cache    │ │ File uploads │
│ + replicas │ │ Sessions   │ │ Avatars      │
└────────────┘ └────────────┘ └──────────────┘

Database Schema with Migrations

Use Prisma for type-safe database access and automatic migrations:

// prisma/schema.prisma

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id            String    @id @default(uuid())
  email         String    @unique
  name          String
  passwordHash  String    @map("password_hash")
  role          Role      @default(MEMBER)
  avatarUrl     String?   @map("avatar_url")
  createdAt     DateTime  @default(now()) @map("created_at")
  updatedAt     DateTime  @updatedAt @map("updated_at")

  ownedProjects Project[]
  memberships   Membership[]
  assignedTasks Task[]    @relation("TaskAssignee")
  comments      Comment[]

  @@map("users")
}

model Project {
  id          String   @id @default(uuid())
  name        String
  description String?
  ownerId     String   @map("owner_id")
  createdAt   DateTime @default(now()) @map("created_at")
  updatedAt   DateTime @updatedAt @map("updated_at")

  owner       User     @relation(fields: [ownerId], references: [id], onDelete: Cascade)
  memberships Membership[]
  tasks       Task[]

  @@index([ownerId])
  @@map("projects")
}

model Task {
  id          String     @id @default(uuid())
  projectId   String     @map("project_id")
  title       String
  description String?
  status      TaskStatus @default(TODO)
  priority    Priority   @default(MEDIUM)
  assigneeId  String?    @map("assignee_id")
  dueDate     DateTime?  @map("due_date")
  createdAt   DateTime   @default(now()) @map("created_at")
  updatedAt   DateTime   @updatedAt @map("updated_at")

  project     Project    @relation(fields: [projectId], references: [id], onDelete: Cascade)
  assignee    User?      @relation("TaskAssignee", fields: [assigneeId], references: [id], onDelete: SetNull)
  comments    Comment[]

  @@index([projectId])
  @@index([assigneeId])
  @@index([status])
  @@map("tasks")
}

enum Role {
  ADMIN
  MEMBER
}

enum TaskStatus {
  TODO
  IN_PROGRESS
  IN_REVIEW
  DONE
}

enum Priority {
  LOW
  MEDIUM
  HIGH
  URGENT
}

API Route Design

Organize routes by resource. Every endpoint follows RESTful conventions:

Auth:
  POST   /api/auth/register     Create account
  POST   /api/auth/login        Get tokens
  POST   /api/auth/refresh      Refresh access token
  POST   /api/auth/logout       Revoke refresh token

Users:
  GET    /api/users/me          Get current user profile
  PATCH  /api/users/me          Update profile

Projects:
  GET    /api/projects          List user's projects (paginated)
  POST   /api/projects          Create project
  GET    /api/projects/:id      Get project details
  PATCH  /api/projects/:id      Update project
  DELETE /api/projects/:id      Delete project
  POST   /api/projects/:id/members   Add member
  DELETE /api/projects/:id/members/:userId  Remove member

Tasks:
  GET    /api/projects/:projectId/tasks      List tasks (filterable)
  POST   /api/projects/:projectId/tasks      Create task
  GET    /api/tasks/:id                      Get task
  PATCH  /api/tasks/:id                      Update task
  DELETE /api/tasks/:id                      Delete task

Comments:
  GET    /api/tasks/:taskId/comments         List comments
  POST   /api/tasks/:taskId/comments         Add comment
  DELETE /api/comments/:id                   Delete comment

Development Workflow

Development Workflow

A disciplined development workflow ensures code quality, enables parallel work, and makes deployments predictable. This section covers branching, sprint planning, and quality gates.

Git Branch Strategy

Use a simplified Git flow that keeps main always deployable:

main          ← production-ready code, protected branch
  │
  ├── develop ← integration branch, all features merge here first
  │     │
  │     ├── feature/auth          ← feature branches
  │     ├── feature/task-crud
  │     └── feature/search
  │
  ├── release/v1.0               ← release stabilization
  │
  └── hotfix/critical-bug        ← urgent production fixes

Branch naming conventions:
  feature/short-description
  bugfix/issue-number-description
  hotfix/critical-description
  release/vX.Y.Z

Sprint Planning Template

Sprint 1 (Weeks 1-2): Foundation
  ─ Project scaffolding (Vite + React, Express + TypeScript)
  ─ Database setup with Prisma, initial migration
  ─ Auth system: register, login, JWT middleware
  ─ User profile page
  ─ Shared UI shell (layout, nav, routing)
  Definition of Done: auth flow works end-to-end, CI pipeline green

Sprint 2 (Weeks 3-4): Core Features
  ─ Project CRUD with ownership validation
  ─ Task CRUD with status transitions
  ─ Dashboard showing project list and task counts
  ─ Form validation on all inputs (Zod schemas)
  Definition of Done: all CRUD operations tested, no console errors

Sprint 3 (Weeks 5-6): Collaboration
  ─ Team member invitations with email
  ─ Role-based access control (admin vs member)
  ─ Comments on tasks
  ─ Search and filter for tasks
  Definition of Done: RBAC enforced, search returns relevant results

Sprint 4 (Weeks 7-8): Polish & Deploy
  ─ Unit tests (80%+ coverage on services)
  ─ Integration tests for API endpoints
  ─ Performance audit (Lighthouse, API benchmarks)
  ─ Security review (OWASP checklist)
  ─ Production deployment with health checks
  ─ README and API documentation
  Definition of Done: deployed to production, all tests passing

Definition of Done Checklist

Every story must meet ALL of these before it is considered complete:

Code Quality:
  ☐ Code reviewed and approved by at least one team member
  ☐ No TypeScript errors or ESLint warnings
  ☐ Follows project naming conventions and code style
  ☐ No hardcoded values—all config from environment variables

Testing:
  ☐ Unit tests written for business logic (services, utils)
  ☐ Integration tests for API endpoints
  ☐ Test coverage ≥ 80% on changed files
  ☐ Manual testing on Chrome, Firefox, and mobile viewport

Security:
  ☐ Input validated on both client and server
  ☐ Authentication required on protected routes
  ☐ Authorization checked (user can only access own resources)
  ☐ No secrets committed (checked with git-secrets or similar)

Documentation:
  ☐ API endpoints documented (request/response examples)
  ☐ Complex algorithms or business rules commented
  ☐ README updated if setup steps changed

Deployment:
  ☐ Feature works on staging environment
  ☐ Database migration is reversible
  ☐ No breaking changes to existing API contracts

Tech Stack Decision Framework

Tech Stack Decision Framework

Choosing a tech stack is one of the most consequential decisions in a project. Use a structured decision matrix instead of picking based on hype or personal preference.

Frontend Decision Matrix

Criteria          React    Vue     Svelte   Angular
──────────────────────────────────────────────────
Ecosystem          5        4       3        4
Learning curve     3        4       4        2
Performance        3        3       5        3
Job market         5        3       2        4
TypeScript support 4        4       4        5
Community size     5        4       3        4
──────────────────────────────────────────────────
Total              25       22      21       22

Winner: React (largest ecosystem, best job market, proven at scale)

Backend Decision Matrix

Criteria          Express   Fastify   NestJS    Hono
──────────────────────────────────────────────────
Performance        3         5         3         5
Learning curve     5         4         2         4
Ecosystem          5         3         4         2
Flexibility        5         4         2         4
TypeScript native  3         5         5         5
──────────────────────────────────────────────────
Total              21        21        16        20

Winner: Express for learning and flexibility, Fastify for performance

Database Decision Matrix

Criteria          PostgreSQL   MongoDB   SQLite
───────────────────────────────────────────────
ACID compliance    5            3         5
Scalability        5            5         2
Query power        5            3         4
Schema evolution   4            5         2
Hosting options    5            4         3
───────────────────────────────────────────────
Total              24           20       16

Winner: PostgreSQL for structured data, add Redis for caching

Recommended Stack and Rationale

// The stack for a production-ready full stack app:

const recommendedStack = {
  frontend: {
    framework: 'React 18 + TypeScript',
    buildTool: 'Vite',
    styling: 'Tailwind CSS',
    stateManagement: 'React Query (server state) + Zustand (client state)',
    forms: 'React Hook Form + Zod validation',
    reason: 'React has the largest ecosystem. Vite is fastest build tool. Tailwind eliminates CSS boilerplate.',
  },
  backend: {
    runtime: 'Node.js 20 LTS',
    framework: 'Express + TypeScript',
    validation: 'Zod (shared schemas with frontend)',
    auth: 'JWT (access + refresh tokens)',
    logging: 'Pino (structured JSON logs)',
    reason: 'Express is battle-tested. Zod gives end-to-end type safety. Pino is fastest Node logger.',
  },
  database: {
    primary: 'PostgreSQL 16',
    orm: 'Prisma (type-safe queries, auto migrations)',
    cache: 'Redis (session store, query cache, rate limiting)',
    search: 'PostgreSQL full-text search (pg_trgm + tsvector)',
    reason: 'PostgreSQL handles relational data, full-text search, and JSON. Redis adds caching layer.',
  },
  infrastructure: {
    hosting: 'Vercel (frontend) + Railway (backend + DB)',
    cdn: 'Cloudflare (static assets, DDoS protection)',
    storage: 'AWS S3 (file uploads)',
    monitoring: 'Sentry (errors) + Pino logs',
    ci: 'GitHub Actions (test, lint, deploy)',
    reason: 'Vercel and Railway have generous free tiers and simple deployment. Cloudflare for edge caching.',
  },
};

When to Deviate from the Stack

Scenario                              Alternative                Why
──────────────────────────────────────────────────────────────────────
Need real-time features               Add Socket.IO             Native WebSocket support
High-write data (logs, events)        Switch to MongoDB          Schema flexibility, write perf
Need full-text search at scale        Add Elasticsearch          Better search features
Team knows Vue better than React      Use Vue                    Developer velocity matters most
Simple CRUD with no complexity        Use SQLite                 Zero infrastructure overhead
Need server-side rendering            Use Next.js instead        Built-in SSR and SSG

Quiz

1. In MoSCoW prioritization, what distinguishes a 'Must Have' from a 'Should Have' feature?

Question 1 options

2. Why should you use story points instead of hour-based estimates for sprint planning?

Question 2 options

3. What is the primary purpose of a Definition of Done checklist in sprint planning?

Question 3 options

Flashcards

Question

What is the difference between MoSCoW Must Haves and Could Haves?

Answer

Must Haves are mandatory for the MVP—the product is broken or non-compliant without them. Could Haves are desirable enhancements that add polish but are the first to be cut when deadlines approach. Must Haves define the minimum shippable product; Could Haves define the ideal product.

Question

What are the three layers of a typical full stack architecture and what does each handle?

Answer

1) Presentation layer (React SPA) handles UI rendering, user interaction, and client-side routing. 2) Application layer (Express API) handles business logic, authentication, validation, and data transformation. 3) Data layer (PostgreSQL + Redis) handles persistent storage, caching, and session management. Each layer communicates through well-defined interfaces (HTTP routes, ORM queries).

Question

Why should database indexes be defined during schema design rather than added reactively?

Answer

Indexes defined during schema design are based on known query patterns from the API route design. Adding them later means you are optimizing reactively after performance problems appear. Designing indexes upfront (e.g., indexing foreign keys, status columns, and frequently filtered fields) prevents slow queries in production. It also forces you to think about query patterns before building the data access layer.

Revision Notes

Key Takeaways

  • 1. Define scope with MoSCoW before coding—Must Haves form the MVP, everything else is negotiable
  • 2. Write user stories with acceptance criteria to turn vague ideas into testable requirements
  • 3. Design system architecture and API contracts on paper first to avoid costly refactors
  • 4. Use story points for estimation and track velocity across sprints for accurate forecasting
  • 5. A strong Definition of Done prevents technical debt by making quality standards explicit and enforceable
  • 6. Choose tech stack based on a structured decision matrix, not hype or personal preference

Interview Tips

  • Walk through how you would scope a project from a one-paragraph product brief—interviewers want to see structured thinking
  • Explain MoSCoW prioritization with a concrete example and justify why certain features are Must Haves vs Should Haves
  • Describe your system architecture from memory—draw the diagram showing client, API, database, and cache layers
  • Discuss tradeoffs when choosing between PostgreSQL and MongoDB for a given use case—show you understand both
  • Explain why you would use story points instead of hour estimates and how velocity tracking works over multiple sprints
  • Be ready to describe your Definition of Done and why each item on the checklist matters for production readiness

Cheat Sheet

Scope: MoSCoW → Must (MVP), Should (important), Could (nice), Won't (out). User stories: As a [role], I want [feature], so that [benefit]. Architecture: Client (React) → API (Express) → DB (PostgreSQL) + Cache (Redis). Schema: design indexes during migration, not after slow queries. Estimation: Fibonacci story points (1,2,3,5,8,13), track velocity over sprints. Sprint plan: Foundation → Core → Collaboration → Polish. Definition of Done: code reviewed, tests passing, no TS errors, docs updated, deployed to staging. Tech stack decision: score each option 1-5 on ecosystem, performance, learning curve, team familiarity—highest total wins.