Core Entities
Board: (board_id, workspace_id, name, visibility=PUBLIC|PRIVATE, owner_id, created_at). A board represents a project. Column: (column_id, board_id, name, position, wip_limit). Columns represent workflow stages: Backlog, In Progress, Review, Done. WIP limit caps the number of tasks allowed in a column (Kanban principle). Task: (task_id, board_id, column_id, title, description, assignee_id, reporter_id, priority=LOW|MEDIUM|HIGH|CRITICAL, due_date, status, position, created_at). TaskLabel: many-to-many between tasks and labels. TaskComment: (comment_id, task_id, author_id, body, created_at). TaskAttachment: (attachment_id, task_id, file_url, filename, size_bytes, uploaded_by).
Task Workflow and State Machine
Task status follows the column: moving a task to a column changes its status. State transitions are column-based (not a fixed state machine) — columns are configurable. Enforce WIP limits on transition: before moving a task to a column: COUNT tasks in that column WHERE status != DONE. If count >= column.wip_limit: reject the move (or alert). Subtasks: a task can have sub-tasks (parent_task_id foreign key). Completion of a parent requires all subtasks to be complete. Task dependencies: (blocking_task_id, blocked_task_id) — a task cannot move to “In Progress” if a blocking task is not done. Blocked tasks are highlighted in the UI.
Assignment and Notifications
When a task is assigned to a user: create a notification for the assignee (type=TASK_ASSIGNED). When a task is mentioned (@user in a comment): create a notification for the mentioned user. When a task due date is within 24 hours: a scheduled job sends due date reminder notifications. Notification channels: in-app (notification bell, real-time via WebSocket), email digest (batched every hour for low-priority notifications), push notification (for mobile app). Subscribe/unsubscribe: users can watch any task to receive notifications for all activity on it. TaskWatcher table: (task_id, user_id) — all watchers receive notifications.
Activity Log
Record every action on a task: TaskActivity (activity_id, task_id, actor_id, action_type, old_value, new_value, created_at). Action types: CREATED, STATUS_CHANGED (old: In Progress, new: Review), ASSIGNED (new assignee), COMMENT_ADDED, DUE_DATE_CHANGED, PRIORITY_CHANGED. The activity log provides the full history of a task (visible as a timeline in the UI). Use it for: audit trails, debugging (“why did this task move back?”), and computing time-in-status metrics (how long tasks spend in each column).
Search and Filtering
Within a board: filter by assignee, label, priority, due date range. These are simple SQL queries on the tasks table. Full-text search on task title and description: use Elasticsearch for text search across all boards in a workspace. Index: task_id, board_id, workspace_id, title, description, assignee_id, labels. Query: text search + filters (board_id, assignee). Sorting: by due_date, priority, created_at, or manual drag-and-drop position. Position column: Decimal or float (LeetCode-style rebalancing — assign middle values between existing positions to avoid reindexing). When positions get too dense (float precision exhausted): rebalance all positions in the column.
Board Templates and Workflows
Templates: a Board can be created from a template (predefined set of Columns and task Labels). Store templates as a BoardTemplate entity with associated ColumnTemplate rows. On board creation from template: copy columns and labels. Custom fields: teams need custom data on tasks (story points, sprint, business value). CustomField (field_id, board_id, field_name, field_type=TEXT|NUMBER|DATE|SELECT). CustomFieldValue (task_id, field_id, value). This is a key-value EAV model — flexible but harder to query. For reporting, pre-aggregate custom field values into a separate analytics table.
Interview Tips
- The position field for drag-and-drop ordering is a common interview follow-up. Use lexicographic strings or floats to represent order without updating all rows. When gaps are exhausted, rebalance.
- Notifications are a separate concern — fan out to a notification service via Kafka events. Do not couple notification sending to the task update transaction.
- WIP limits and task dependencies are the “interesting” design details that separate a basic task tracker from a real project management tool. Mention them proactively.
Asked at: Atlassian Interview Guide
Asked at: LinkedIn Interview Guide
Asked at: Snap Interview Guide
Asked at: Twitter/X Interview Guide