Compile Ready
All low level design problems
Low Level Design/Expert/Expert Systems

Design Jira (LLD)

Issues, workflows/state machines, sprints, and a flexible custom-field model with audit history.

Expert 65m interview 15m read Medium frequency Popularity 80
State Observer Strategy Command Atlassian Amazon Oracle

Problem Statement

Design the low-level object model for Jira, an issue tracker used by software teams to plan work, track execution, and enforce a configurable delivery workflow.

The core system should model projects, issues of different types, assignees, a backlog, sprints, allowed workflow transitions, and notifications when an issue changes state. The interview focus is the domain model and extension seams, not a full REST API, search engine, or database schema.

Business context

Jira is an expert-level LLD because it combines several interview themes in one domain: a typed issue model, workflow state machine, sprint planning, assignment, notification fan-out, and policy customization per project.

A strong design keeps workflow rules configurable instead of hard-coding if current == Open and next == InProgress across services. It also leaves room for new issue types such as Epic or Spike without editing every creation path.

Functional Requirements

  • Create projects that own a backlog, sprints, and a shared issue collection.

  • Create Story, Task, and Bug issues through an extensible issue-type factory.

  • Assign or reassign issues to users by user id.

  • Keep issues in a backlog until they are moved into a sprint.

  • Configure workflow states and allowed transitions, such as Open to InProgress to InReview to Done.

  • Transition an issue only when the current workflow state allows the requested target state.

  • Notify subscribed observers whenever a transition succeeds.

  • Expose a ranked backlog view so planning can use a replaceable ordering policy.

Non-Functional Requirements

Workflow correctness

Invalid transitions must fail atomically and leave the issue in its original state. The allowed-transition rule belongs to the current state object.

Configurability

A project should be able to assemble states and transitions from configuration. The service should not need a switch statement for every workflow.

Extensibility

Adding an issue type, ranking policy, or notification channel should be additive and local, not a cascade of edits through the service layer.

Auditability

Every transition should produce a transition record with from state, to state, actor, note, and timestamp so production systems can build an audit log.

Low latency for planning

Core operations are in-memory map lookups and list updates. Backlog ranking should be fast enough for interactive sprint planning.

Requirement Clarification

QDo projects have independent workflows?

For the base design, the service receives one workflow configuration per project. The same model supports many projects by constructing one IssueService per project or storing a workflow map per project id.

QWhich issue types are required?

Support Story, Task, and Bug. The implementation uses IssueType.create as the factory method so a new type can define defaults without changing IssueService.

QIs backlog ordering fixed?

No. Use a Strategy for backlog ranking. The sample ranks by priority and last update, but the service accepts any comparator-producing policy.

QDo notifications need email, Slack, and webhooks?

The model only needs an observer seam. The sample observer prints to the console, while production observers can send email, Slack messages, webhooks, or mobile pushes.

QDo we need custom fields and permissions?

Mention them as production extensions. The core interview path should stay focused on issues, workflow transitions, sprints, assignees, and notification events.

UML Class Diagram

Rendering diagram…
The service orchestrates creation, assignment, planning, and transitions. The current **WorkflowState** owns the transition rule, observers receive transition events, and issue creation is delegated to **IssueType**.

Sequence Diagram

Rendering diagram…
The transition path is deliberately small: find issue, ask the current state to approve, mutate the issue, then notify observers. No workflow rule is hard-coded in the service.

Entity Identification

Project

Owns the planning surface for one team: backlog, sprint registry, and movement of issues from backlog into a sprint.

keynamebacklogsprints

Issue

Represents one unit of work with type, title, reporter, assignee, priority, state, optional sprint, comments, and update timestamp.

idtypeassigneeprioritystatesprint

IssueType

Factory enum for issue creation. Story, Task, and Bug choose their default priority while the service stays unaware of type-specific construction.

STORYTASKBUGcreate

WorkflowState

State interface for configurable workflow steps. Each state knows the names of states it may transition to and approves or rejects a requested transition.

nameallowedTargetsapproveTransition

Transition

Immutable audit event produced when a workflow change is approved. Carries from state, to state, actor, note, and occurrence time.

fromStatetoStateactornoteoccurredAt

Sprint

Time-boxed planning container. It owns a list of committed issues and updates each issue's sprint pointer when membership changes.

idnamestartDateendDateissues

IssueService

Application facade for the domain. It indexes issues, delegates creation to the factory, delegates transition legality to states, ranks backlog through a strategy, and notifies observers.

projectstatesissuesrankingStrategyobservers

IssueObserver

Observer seam for transition notifications. Production adapters can send email, Slack, webhooks, or analytics events from the same callback.

onTransition

IssueRankingStrategy

Backlog ordering policy. The sample uses priority then update time; teams can inject WSJF, due-date, severity, or manual-rank strategies.

comparator

Design Patterns Used

State

WorkflowState encapsulates the allowed outgoing transitions for each workflow node. IssueService asks the current state to approve a target instead of owning a brittle matrix of conditional checks.

Strategy

IssueRankingStrategy makes backlog ordering replaceable. Priority-first planning, manual rank, severity, due date, or WSJF can be injected without changing issue creation or transitions.

Observer

IssueObserver decouples workflow transitions from notification delivery. The service emits one domain event and observers decide how to notify users or external systems.

Factory Method

IssueType.create centralizes Story, Task, and Bug construction. Type-specific defaults live with the type, while IssueService simply asks the selected type to create an issue.

Step-by-Step Design

  1. 1Make the workflow a set of state objects

    Represent Open, InProgress, InReview, and Done as WorkflowState instances. Each state carries the names it can move to, making the workflow configurable from data.

    Map<String, WorkflowState> states = new LinkedHashMap<>();
    states.put("Open", WorkflowState.named("Open", "InProgress"));
    states.put("InProgress", WorkflowState.named("InProgress", "InReview", "Open"));
    states.put("InReview", WorkflowState.named("InReview", "Done", "InProgress"));
    states.put("Done", WorkflowState.named("Done"));
  2. 2Keep issue creation behind the type factory

    IssueService should not switch on Story, Task, and Bug. The selected IssueType creates the issue and applies its default priority.

    public Issue createIssue(IssueType type, String id, String title, String reporter) {
        WorkflowState open = states.get("Open");
        Issue issue = type.create(id, title, reporter, open);
        issues.put(issue.id(), issue);
        project.addToBacklog(issue);
        return issue;
    }
  3. 3Let the current state approve transitions

    The service fetches the target state, then asks the current state to approve. If the edge is missing, no mutation happens and no notification is sent.

    WorkflowState target = requireState(targetState);
    Transition transition = issue.state().approveTransition(issue, target, actor, note);
    issue.transitionTo(target);
    notifyObservers(issue, transition);
  4. 4Model backlog and sprint membership explicitly

    Project owns the backlog and the sprint registry. Moving an issue to a sprint removes it from backlog and lets the sprint own the committed issue list.

  5. 5Use observers for transition notifications

    Observers receive the issue plus transition record after a successful state change. This keeps notification channels out of the workflow engine.

  6. 6Make planning policy injectable

    Backlog ranking is a strategy because teams disagree on ordering. The sample strategy sorts by priority then most recently updated.

Complete Java Implementation

Loading…

Explanation of Every Class

Issue

The domain object for a unit of work. It stores immutable identity and type data, mutable assignment/state/sprint pointers, comments, and an update timestamp. State mutation is intentionally narrow through transitionTo.

IssueType

Factory enum for Story, Task, and Bug. It owns display names and default priorities, and its create method hides construction details from IssueService.

WorkflowState

State interface plus configurable implementation. Each state advertises allowed target state names and approves a transition only when the target is present in that set.

Transition

Immutable transition audit record. It captures from state, to state, actor, optional note, and timestamp for notifications and future audit history.

Sprint

Time-boxed container for committed issues. It validates dates, prevents duplicate issue membership, and updates the issue's sprint pointer.

Project

Planning aggregate that owns backlog and sprint registry. It knows how to add an issue back to backlog, create sprints, and move an issue into a sprint.

IssueService

Facade over the domain. It stores the issue index, creates issues via IssueType, validates workflow state names, delegates transition approval to WorkflowState, ranks backlog via strategy, and notifies observers.

Main

Runnable demonstration that builds the Open to InProgress to InReview to Done workflow, creates a Bug, assigns it, moves it into a sprint, and performs valid transitions with notifications.

Dry Run

Sample input

Workflow: Open -> InProgress -> InReview -> Done. Create Bug EI-7, assign it to maya, move it into Sprint 1, then transition through the workflow.

StepActionCurrent stateSprint or backlogNotificationResult
1create BUG EI-7OpenBacklogNoneIssue created with priority P1
2assign to mayaOpenBacklogNoneAssignee becomes maya
3move to Sprint 1OpenSprint 1NoneIssue leaves backlog
4transition to InProgressInProgressSprint 1Observer notifiedAllowed from Open
5transition to InReviewInReviewSprint 1Observer notifiedAllowed from InProgress
6transition to DoneDoneSprint 1Observer notifiedAllowed from InReview

The dry run shows the state machine doing the guard work. If step 4 tried Open -> Done, approveTransition would throw before the issue changed state or observers were called.

Complexity Analysis

OperationTimeSpaceNote
createIssueO(1)O(1)Map insert plus backlog append; issue construction is constant time.
transitionIssueO(O)O(1)O is the number of observers. State lookup and allowed-target check are constant for small configured sets.
rankedBacklogO(B log B)O(B)Copies and sorts B backlog issues using the injected strategy comparator.
moveToSprintO(B + S)O(1)Removes from a backlog list and checks duplicate membership in the sprint issue list.
notify observersO(O)O(1)Each observer is invoked once after a successful transition.

The sample keeps lists simple for interview clarity. A production Jira would index backlog order, sprint membership, labels, assignees, and state with repositories and query indexes.

Extensibility

New issue type

Add a new IssueType constant such as Epic or Spike with its default priority and creation behavior. IssueService.createIssue does not change.

New workflow

Load a different state map, for example Open to Triage to InProgress to Blocked to Done. The same WorkflowState interface validates edges.

New notification channel

Implement IssueObserver for email, Slack, webhook, analytics, or audit persistence and register it with the service.

New planning order

Inject another IssueRankingStrategy, such as manual rank, due date, severity, or weighted shortest job first.

Custom fields

Attach a typed custom-field map to Issue or introduce FieldDefinition per project. Keep validation outside the transition engine.

Alternative Designs

Central transition matrix

Store allowed transitions in a Map from source state to target states and let the service check the matrix directly.

Tradeoffs

Simple to load from configuration, but it weakens the State pattern because state-specific hooks and behavior are pushed back into the service.

Command objects for workflow actions

Represent transitions as command objects such as StartProgress, SubmitReview, and ResolveIssue. Each command validates permissions and applies changes.

Tradeoffs

Useful when transitions have rich side effects, but heavier than a configurable state graph for the base interview design.

Separate repository layer from day one

Introduce IssueRepository, ProjectRepository, and SprintRepository interfaces and keep service methods transactional.

Tradeoffs

More realistic for production but distracts from the core LLD question if introduced before the domain model is clear.

Common Mistakes

  • ×

    Hard-coding workflow transitions inside IssueService with nested conditionals instead of using state objects.

  • ×

    Letting invalid transitions partially mutate the issue before throwing.

  • ×

    Sending notifications before the transition succeeds, creating false alerts.

  • ×

    Modelling Story, Task, and Bug as only strings, then scattering type-specific defaults through the code.

  • ×

    Mixing sprint planning logic with workflow validation, which makes backlog and state changes hard to reason about.

  • ×

    Returning mutable backlog or sprint lists and allowing callers to corrupt project state.

  • ×

    Forgetting the audit event, which makes transition notifications and history impossible to reconcile.

Follow-up Interview Questions

QHow would you support project-specific workflows?

Store a workflow definition per project and build a state map for each project. IssueService can be scoped per project, or it can resolve states by project key before validating a transition.

QHow do permissions fit into this model?

Add a policy check before approveTransition, for example PermissionPolicy.canTransition(actor, issue, target). Keep authorization separate from workflow graph validity.

QHow would you add transition audit history?

Persist every Transition through an observer or repository after the issue state changes. The observer can append to an audit log in the same transaction in production.

QHow would you prevent concurrent transitions from racing?

Use a transactional repository with optimistic versioning on Issue. The update checks the issue version and current state, so only one transition wins.

QHow would you support custom fields per issue type?

Define project-level field definitions and validate an issue's custom-field map against its IssueType. Avoid hard-coding fields on the base Issue class.

Production Considerations

Persistence and transactions

Use repositories and database transactions so issue state, sprint membership, and transition audit records commit together.

Concurrency control

Add optimistic locking with an issue version column. Transition updates should include current state and version in the write condition.

Notification delivery

Observers should enqueue durable events rather than sending synchronously on the user request path. Workers can retry email, Slack, or webhook delivery.

Search and reporting

Index issues by project, assignee, state, sprint, labels, and text fields in a search store. Keep search projections derived from the source of truth.

Workflow migration

Changing a workflow in production needs migration rules for issues currently in states that may be removed, renamed, or split.

What Interviewers Look For

  • Did you make the workflow configurable instead of hard-coded in service conditionals?

  • Can you explain why WorkflowState is the right owner for allowed transitions?

  • Is issue-type creation extensible through a factory method rather than scattered defaults?

  • Do notifications happen only after a successful transition?

  • Did you separate backlog or sprint planning policy from workflow validation?

  • Can your design evolve toward repositories, audit history, permissions, and custom fields without rewriting the core model?

Quiz

0/5 answered

  1. 1.Why should **IssueService** ask **WorkflowState** to approve a transition?

  2. 2.Which seam makes backlog ordering replaceable?

  3. 3.What should happen if a user tries **Open -> Done** in the base workflow?

  4. 4.Why is **IssueType.create** useful?

  5. 5.When should observers be notified?

Practice Variants

Add blocked and reopened states

Intermediate

Extend the workflow to allow InProgress to Blocked, Blocked to InProgress, and Done to Reopened. Verify invalid edges still fail.

Add role-based transition permissions

Advanced

Introduce a permission policy so only assignees can move to InReview and only project leads can move to Done.

Add durable audit history

Intermediate

Create an observer that appends Transition records to an audit repository and supports fetching history for an issue.

Add custom issue fields

Expert

Define project-level field definitions and validate issue-specific field values based on IssueType.

Flashcards

Cheat Sheet

Entities: Project, Issue, IssueType, WorkflowState, Transition, Sprint, IssueService, IssueObserver, IssueRankingStrategy.

Patterns: State for workflow transitions, Factory Method for issue creation, Observer for notifications, Strategy for backlog ranking.

Flow: create issue in Open backlog → assign user → move to sprint → transition through allowed states → emit notification after success.

Invariant: invalid workflow edges do not mutate state and do not notify observers.

Default workflow: Open → InProgress → InReview → Done.

Complexity: createIssue O(1), transitionIssue O(observers), rankedBacklog O(B log B), moveToSprint O(B + S).

Extend: add issue types in IssueType, add workflows by loading different states, add channels through IssueObserver, add planning policies through IssueRankingStrategy.

References