Skip to main content

Getting Ready: Task Management System

Getting Ready: Task Management System

Why This Problem Matters

Task management systems are the backbone of modern software development and business operations. Every technology company runs on one — Jira powers Agile teams at companies like Spotify and Netflix, Trello organizes workflows at millions of small businesses, Asana coordinates cross-functional projects at enterprises like Deloitte and NASA, and Linear has become the tool of choice for fast-moving startups. The project management software market exceeds $6 billion annually, and virtually every developer interacts with a task manager daily.

From an engineering perspective, a task management system is deceptively rich. On the surface it looks like simple CRUD — create, read, update, delete tasks. But beneath that surface lie several compelling design challenges:

  • State management — A task moves through a lifecycle (TODO → IN_PROGRESS → DONE), and not every transition is valid. You can't move a task directly from DONE back to TODO in most workflows.
  • Notification fan-out — When a task is updated, multiple people may need to know: the assignee, the reporter, watchers, and team leads. This is a classic Observer pattern scenario.
  • Undo/Redo — Premium task managers let users reverse accidental changes. Implementing undo/redo cleanly requires the Command pattern — encapsulating each mutation as an object that knows how to execute and reverse itself.
  • Filtering and search — Users need to find tasks by status, priority, assignee, due date, or tags. The filtering logic must be extensible without modifying existing code.

This is why the Task Management System appears in LLD interviews at companies like Atlassian, Microsoft, Amazon, and Uber. Interviewers use it to test whether you can model a real CRUD-heavy domain with clean OOP, apply behavioral patterns like Observer and Command, and handle the subtleties of task state transitions.

What You'll Learn

By the end of this design problem, you will:

  • Model a CRUD-heavy domain with proper encapsulation — tasks, users, task lists, and their interactions
  • Apply the Observer Pattern to implement notification fan-out when tasks are created, updated, or reassigned (Fundamentals S12)
  • Apply the Command Pattern to build a full undo/redo system where every task mutation is reversible (Fundamentals S12)
  • Design a task state machine — model valid transitions between TODO, IN_PROGRESS, and DONE states
  • Draw a UML Class Diagram deriving each class, attribute, and relationship directly from requirements
  • Implement the complete system in Python and Java with clean separation between domain logic, notification, and command infrastructure
  • Test edge cases — undo when history is empty, assigning a task to a non-existent user, duplicate task creation, priority conflicts

Prerequisites

Before starting this problem, make sure you're comfortable with:

  • OOP Fundamentals — classes, objects, encapsulation, and polymorphism (Fundamentals S2)
  • OOP Relationships — especially Composition and Association; the TaskManager composes TaskLists, while Tasks associate with Users (Fundamentals S4)
  • Observer Pattern — how a subject notifies multiple observers when its state changes. This drives our notification system. If Observer feels unfamiliar, review Fundamentals S12 first.
  • Command Pattern — how to encapsulate operations as objects with execute() and undo() methods. This powers our undo/redo. If Command feels unfamiliar, review Fundamentals S12 first.
  • UML Class Diagrams — how to read classes, attributes, methods, and relationship arrows (Fundamentals S5)
  • Enums — we'll use enums for TaskStatus and Priority, providing type safety over raw strings (Fundamentals S2)

You should also have completed at least one earlier design problem (e.g., DP1 Parking Lot or DP5 Logging Framework) so you're familiar with the requirements → UML → code → patterns workflow.

Problem Scope

We will design: The core domain logic of a task management system — creating and managing tasks with titles, descriptions, due dates, priorities, and statuses; organizing tasks into task lists; assigning tasks to users; notifying observers when tasks change; and supporting undo/redo for task mutations.

We will NOT design: Database persistence, REST APIs, a web or mobile UI, real-time collaboration (WebSockets), file attachments, recurring tasks, Gantt charts, or multi-tenant architecture. This is a Low Level Design (object-oriented) exercise, not a System Design (infrastructure) problem.

Difficulty: Easy — This problem is an excellent bridge from pure data-modeling problems (like Parking Lot) to behavior-rich systems. The class count is manageable (7-10 classes), but the design depth comes from correctly wiring Observer for notifications and Command for undo/redo. Don't underestimate the subtlety of making every mutation reversible.

How a Real Task Manager Works

Before designing the software, let's understand the real-world system we're modeling. If you've used Jira, Trello, or Asana, these flows will be familiar:

  1. Task creation — A user creates a task with a title, optional description, priority level (LOW, MEDIUM, HIGH, URGENT), and an optional due date. The task starts in TODO status.
  2. Task assignment — The creator or a team lead assigns the task to a specific user. The assignee receives a notification.
  3. Status transitions — As work progresses, the assignee moves the task through states: TODO → IN_PROGRESS → DONE. Some systems also support CANCELLED. Each transition may trigger notifications to watchers.
  4. Task lists (boards/projects) — Tasks are organized into task lists or boards. A task belongs to exactly one list. Lists help teams group related work — "Sprint 23 Backend", "Bug Fixes", "Feature Requests".
  5. Watching and notifications — Users can "watch" a task to receive updates. When the task's status, assignee, priority, or due date changes, all watchers are notified.
  6. Editing — Users can update a task's title, description, priority, due date, or status at any time (subject to permission).
  7. Undo/Redo — If a user accidentally changes a task's priority from HIGH to LOW, they can undo the change. The system maintains a history of commands that can be reversed.
  8. Deletion — Tasks can be deleted from a list. Some systems soft-delete (mark as archived) rather than hard-delete.

Notice how these operations naturally decompose into commands (create, update, delete, assign, change status) that can each be reversed — which is exactly the foundation for our Command pattern implementation.