. Design Jira-like Project Management Tool Frontend System Design Interview Guide

Medium

Design a production-ready project management tool like Jira with Kanban boards, sprints, epics, real-time collaboration, and complex workflow automation.

Backend as Black Box: Assume APIs exist for issues, boards, workflows, and WebSocket events. Focus on frontend architecture, state modeling, performance, and synchronization.

Key Challenges

  • Workflow correctness with guarded transitions
  • 10,000+ issues without UI jank
  • Real-time multi-user updates
  • 60fps drag & drop
  • JQL-like filtering at scale
Quick Links:

When teams manage projects, they expect smooth drag-and-drop, real-time collaboration, and efficient handling of 10,000+ issues—even with complex workflows. This solution designs a Jira-like project management tool that handles Kanban boards, workflow automation, real-time collaboration, and complex state management while maintaining 60fps drag interactions. The key insight: a good project management tool normalizes state for scale, models workflows explicitly, and handles conflicts gracefully.

HLD interview focus: Requirements, architecture, tradeoffs, data flow, and scaling decisions. Any implementation snippets shown are optional unless explicitly asked.

I'll start by defining what makes a great project management experience—what questions does a user need answered as they manage issues? Then I'll design the architecture that handles Kanban boards, workflow automation, real-time collaboration, and complex state management. Finally, I'll design clear boundaries between server state and UI state.

Why this approach?

Most candidates build a "board with cards." Strong candidates build a "project management system"—a system that handles workflow correctness, scales to 10,000+ issues, and maintains 60fps drag interactions. The difference is thinking about state normalization, workflow modeling, and conflict resolution, not just drag-and-drop.

Think of this like building Jira or Linear. You don't just show cards—you handle workflow transitions, real-time collaboration, complex filtering, and scale. Same principles apply here.

Before designing anything, let's define what success looks like. When teams manage projects, they need smooth drag-and-drop, real-time collaboration, and efficient issue management.

Requirements Exploration Questions

Discovery

Ask your interviewer these questions to refine requirements.

What workflow model is required?
  • Kanban only or Kanban + Scrum boards?
  • Project-level workflow customization?
  • Required transition guards (assignee, checklist, approvals)?
What scale should we design for?
  • Typical and peak issue counts per board
  • Concurrent editors per board/project
  • Number of boards a user actively monitors
What collaboration behavior matters most?
  • Live card moves and field edits
  • Conflict handling for simultaneous edits
  • Notification expectations for watched issues
What access model applies?
  • Project roles and field-level permissions
  • External guests vs internal users
  • Audit/history requirements

Functional Requirements

Must Have

MVP (Core Features - What I'd Design First):

  • Kanban board with drag-and-drop between workflow columns
  • Issue CRUD with assignee, priority, labels, due date
  • Project-scoped filters and basic search
  • Real-time board updates for active collaborators
  • Keyboard support for issue navigation and quick actions
  • Clear loading/empty/error states

Advanced Features (Add If Time Permits):

  • Sprint planning + backlog grooming workflows
  • Timeline/Gantt + dependency visualization
  • Automation rules (status transitions, notifications)
  • Offline queue with conflict-safe reconciliation

Non-Functional Requirements

Quality Bar

Performance Targets:

  • Drag interaction at 60fps for typical board sizes
  • Local card move feedback < 100ms
  • Board refresh/sync visibility < 500ms
  • Initial board load < 2s for common projects

Scalability Goals:

  • Handle 10K+ issues per project with virtualization
  • Support 100+ active users on the same board
  • Sustain high update throughput during planning sessions

Reliability & Consistency:

  • Idempotent write operations and retry-safe mutations
  • Deterministic ordering/reconciliation after reconnect
  • Conflict-safe merge strategy for concurrent edits

Accessibility Requirements:

  • WCAG 2.1 AA keyboard and screen-reader support
  • Keyboard-only drag/drop fallback interactions
  • Clear focus states and ARIA announcements for moves

Security & Compliance:

  • Role-based authz on all write operations
  • Input validation + XSS/CSRF protections
  • Audit-friendly change history for issue transitions

Observability:

  • Track p95 board load, mutation latency, and error rate
  • Monitor queue retry depth and sync conflict frequency
  • Alert on sustained drag-drop failure or websocket disconnect spikes

High-Level Architecture

┌───────────────────────────────┐
UI LAYER                │ Board / Issue Panel / Filters │
                └───────────────┬───────────────┘
                ┌───────────────────────────────┐
STATE LAYER                │ Normalized Store              │
                │ Workflow Engine               │
                │ Derived Selectors             │
                └───────────────┬───────────────┘
                ┌───────────────────────────────┐
SYNC LAYER                │ WebSocket + Offline Queue     │
                │ Conflict Resolver             │
                └───────────────┬───────────────┘
                ┌───────────────────────────────┐
SERVER (Black Box)REST + WebSocket APIs         │
                └───────────────────────────────┘

Design Principles

  • UI reads from state only
  • State is normalized
  • Server is authoritative
  • Optimistic updates for responsiveness

Tradeoffs & Comparisons

Entity and interface contract shape with cache/reconciliation model for a frontend system design interview (backend treated as a black box).

1) Component prop interfaces (boundaries)

Separate board rendering, issue details, and workflow actions via explicit contracts.

  • BoardShellProps: project context + board-level controls
  • BoardColumnProps: ordered issue ids + drag/drop callbacks
  • IssueCardProps: summary payload + quick actions
  • IssueDetailPanelProps: editable issue model + save/transition handlers

2) Hook interfaces (consumption contracts)

Hooks expose board data and mutation contracts while hiding transport details.

Normalized State Model

Instead of nested objects:

Column → Issues → Issue Details

Use lookup tables:

issues: { issueId → Issue }
columns: { columnId → Column }
issuesByColumn: { columnId → issueId[] }
columnOrder: [columnId]

Benefits:

  • O(1) lookups
  • Stable ordering
  • Minimal rerenders
  • Easy offline serialization

Workflow as State Machine

To Do ──Start──▶ In Progress ──Review──▶ Done
   ▲                                   │
   └────────────── Reopen ◀─────────────┘

Transition Flow:

  1. User action
  2. Client validation
  3. Guard execution
  4. Optimistic update
  5. Server confirm or rollback

Client cache shape (recommended)

  • entitiesById: Record<ID, Entity>
  • orderedIds: ID[] for rendering order
  • pageInfo/cursor metadata for pagination or range loading

Deep dive: Data Normalization

Consistency & reconciliation rules

  • Make writes idempotent where retries are possible.
  • Apply realtime updates with version/event ordering checks.
  • Prefer server-authoritative reconciliation after optimistic mutations.

Tradeoffs & Comparisons

Component Boundaries

Structured
BoardShell

Owns filters, swimlane mode, and board-level orchestration.

BoardColumn

Renders ordered issue cards and emits drag/drop move intents.

IssueCard

Displays summary metadata and lightweight action callbacks.

IssueDetailPanel

Handles issue editing, transitions, and comment interactions.

jira-component-interfaces.ts
export interface BoardShellProps {
  projectId: string;
  activeSprintId?: string;
  onOpenIssue: (issueId: string) => void;
}

export interface BoardColumnProps {
  columnId: string;
  issueIds: string[];
  onMoveIssue: (input: { issueId: string; toColumnId: string; index: number }) => void;
}

export interface IssueCardProps {
  issue: IssueSummary;
  onOpen: () => void;
  onAssign: (assigneeId: string | null) => void;
}

export interface IssueDetailPanelProps {
  issueId: string;
  onSave: (patch: Partial<IssueDetail>) => Promise<void>;
  onTransition: (toStateId: string) => Promise<void>;
}
jira-hook-contracts.ts
export interface UseBoardDataResult {
  columns: BoardColumnModel[];
  issuesById: Record<string, IssueSummary>;
  isLoading: boolean;
}

export interface UseIssueMutationsResult {
  moveIssue: (input: { issueId: string; toColumnId: string; index: number }) => Promise<void>;
  updateIssue: (issueId: string, patch: Partial<IssueDetail>) => Promise<void>;
  pendingCount: number;
}

export function useBoardData(_projectId: string): UseBoardDataResult {
  throw new Error('Contract-only snippet');
}

export function useIssueMutations(_projectId: string): UseIssueMutationsResult {
  throw new Error('Contract-only snippet');
}

React interfaces & integration patterns (props, hooks, callbacks).

This section covers API contracts and React consumption patterns.

API contracts (Backend as black box)

1) Board + issue list reads

GET /api/boards/:boardId
=> {
  board: { id: string; name: string; columnOrder: string[] },
  columns: Array<{ id: string; name: string; wipLimit?: number }>
}

GET /api/boards/:boardId/issues?cursor=<opaque|null>&limit=50&sort=priority|updatedAt&assignee=&label=&q=
=> {
  items: IssueSummary[],
  pageInfo: { nextCursor: string | null, hasMore: boolean }
}

2) Issue CRUD

POST /api/issues
body: { boardId: string, title: string, description?: string, columnId: string, idempotencyKey: string }
=> { issue: IssueDetail }

PATCH /api/issues/:id
body: { patch: Partial<IssueDetail>, version: number, idempotencyKey: string }
=> { issue: IssueDetail, version: number }

DELETE /api/issues/:id
body: { version: number, idempotencyKey: string }
=> { ok: true }

3) Workflow transition + drag/drop move

POST /api/issues/:id/transition
body: { toStateId: string, version: number, idempotencyKey: string }
=> { issue: IssueDetail, version: number }

PUT /api/issues/:id/move
body: { targetColumnId: string, position: number, version: number, idempotencyKey: string }
=> { issueId: string, columnId: string, position: number, version: number }

4) Comments + activity

GET /api/issues/:id/comments?cursor=<opaque|null>&limit=20
=> { items: Comment[], pageInfo: { nextCursor: string | null, hasMore: boolean } }

POST /api/issues/:id/comments
body: { content: string, idempotencyKey: string }
=> { comment: Comment }

5) Search / filters / saved views

GET /api/search/issues?boardId=&q=&assignee=&label=&state=&cursor=<opaque|null>&limit=50
=> { items: IssueSummary[], pageInfo: { nextCursor: string | null, hasMore: boolean } }

POST /api/views
body: { boardId: string, name: string, filters: IssueFilter }
=> { view: SavedView }

6) Realtime channel (board scoped)

WS /realtime/boards/:boardId

// examples
{ type: 'ISSUE_CREATED', eventId: string, issue: IssueSummary, version: number }
{ type: 'ISSUE_UPDATED', eventId: string, issueId: string, patch: Partial<IssueDetail>, version: number }
{ type: 'ISSUE_MOVED', eventId: string, issueId: string, from: string, to: string, position: number, version: number }
{ type: 'ISSUE_DELETED', eventId: string, issueId: string, version: number }
{ type: 'COMMENT_ADDED', eventId: string, issueId: string, comment: Comment, version: number }
{ type: 'PRESENCE_UPDATED', eventId: string, userId: string, boardId: string, state: 'active' | 'idle' }

Type definitions used in contracts

interface IssueSummary {
  id: string;
  boardId: string;
  columnId: string;
  title: string;
  assigneeId?: string | null;
  priority: 'low' | 'medium' | 'high' | 'critical';
  labels: string[];
  updatedAt: string;
  version: number;
}

interface IssueDetail extends IssueSummary {
  description?: string;
  stateId: string;
  reporterId: string;
  dueDate?: string | null;
  estimatePoints?: number | null;
  createdAt: string;
}

interface Comment {
  id: string;
  issueId: string;
  authorId: string;
  content: string;
  createdAt: string;
  updatedAt?: string;
}

interface IssueFilter {
  q?: string;
  assigneeIds?: string[];
  labels?: string[];
  stateIds?: string[];
  priorities?: Array<'low' | 'medium' | 'high' | 'critical'>;
}

interface SavedView {
  id: string;
  boardId: string;
  name: string;
  filters: IssueFilter;
}

Consistency rules

  • All write APIs carry idempotencyKey and version.
  • Server is authoritative on conflict (409 returns latest entity/version).
  • Client ignores stale realtime events older than cached version.

3) Integration patterns (React wiring)

  • Optimistic DnD: move card locally first, then reconcile with authoritative state.
  • Patch-by-id updates: mutate only touched issue entities to avoid full-board rerenders.
  • Permission-aware actions: disable restricted transitions based on workflow role rules.
  • Reconnect recovery: replay queued issue mutations with idempotency keys.

Integration Patterns

Structured
Optimistic DnD

Apply local card move immediately, rollback or patch after server ack.

Entity-level patching

Update only affected issue entities and column order arrays.

Workflow guards

Enforce allowed transitions at interaction boundaries.

Offline-safe queue

Queue mutations and replay deterministically on reconnect.

Virtualization Strategy

Above viewport (not rendered)
Buffer
VISIBLE viewport
Buffer
Below viewport (not rendered)

Effect: DOM size decoupled from dataset size

Optimistic Sync Flow

User Action → Optimistic Update → Server → Confirm or Rollback

Indexed Filtering

byAssignee: { userId → Set(issueIds) }
byStatus: { status → Set(issueIds) }

Intersect indexed sets, then apply remaining filters.

Performance Targets

MetricTarget
Drag latency<16ms
Filter response<100ms
Sync latency<500ms
Memory (10K issues)<100MB
Why This Works
  • Workflow modeled as state machine for correctness
  • Normalized state ensures O(1) access
  • Virtualization keeps UI fast at scale
  • Optimistic updates preserve responsiveness
  • Server authority ensures consistency
Key Takeaways
  1. Model workflows as state machines
  2. Normalize state
  3. Virtualize large lists
  4. Use optimistic updates with rollback
  5. Index frequently filtered fields

Key Takeaways

  • State machines enforce workflow correctness
  • Normalized state enables O(1) access patterns
  • Virtualization maintains performance at 10K+ issues
  • Optimistic updates improve perceived responsiveness
  • Indexed filtering ensures scalable query performance