Skip to main content

Design an Issue Tracker & Project Management System (Jira / Linear)

Modern issue tracking platforms like Jira and Linear manage the development lifecycles of thousands of organizations. The system must support customizable, enterprise-grade workflow state machines (e.g. Backlog β†’\to In Progress β†’\to Code Review β†’\to Done) with custom transition guards, sub-millisecond local-first or real-time Kanban board synchronization across distributed teams, and complex filtering capabilities (e.g., Jira Query Language - JQL) over hundreds of millions of tickets.


1. Understanding the Problem

Functional Requirements

  1. Issue Lifecycle Management: Create, edit, assign, prioritize, and delete issues with rich text, attachments, and hierarchical sub-tasks.
  2. Configurable Workflow State Machine: Organizations can define arbitrary states and valid directed transitions with authorization guards and automated post-functions.
  3. Real-Time Kanban & Sprint Boards: Multiple users dragging and dropping cards across board columns must see updates reflected in real time across all open client browsers.
  4. Issue Audit Trail (Activity History): Maintain an immutable chronological changelog of all field mutations, comments, and state changes.
  5. Advanced Search & Filtering (JQL): Filter issues by complex multi-clause expressions (e.g. project = INFRA AND status = 'In Review' AND assignee = currentUser() ORDER BY priority DESC).

Non-Functional Requirements

  • Sub-50ms Board Rendering & Move Latency: Dragging a card must reflect immediately on the screen and broadcast to peers in <100ms< 100\text{ms}.
  • Optimistic Concurrency Control: Prevent concurrent conflicting updates when two team members edit the same issue simultaneously.
  • Multi-Tenant Data Isolation: Strict tenant isolation across thousands of enterprise organizations.
  • Scale: Support 50 Million active users across 100,000 organizations managing 1+ Billion issues.

Capacity Estimations & Sizing (5 Years)

  • Active Tenants: 100,000 organizations.
  • Total Issues: 1 Billion issues across 5 years.
  • Issue Mutations: 20 Million issue updates, status moves, and comments per day.
  • Storage Calculations:
    • Issue metadata (title, status, assignee, priority, timestamps): ∼1Β KB\sim 1\text{ KB} per record.
    • 1 Billion issues Γ—1Β KB=1Β Terabyte\times 1\text{ KB} = \mathbf{1\text{ Terabyte}} base table storage.
    • Audit history & JSON changelogs: Average 15 updates per issue β€…β€ŠβŸΉβ€…β€Š15Β BillionΒ auditΒ entriesΓ—200Β bytesβ‰ˆ3Β Terabytes\implies 15\text{ Billion audit entries} \times 200\text{ bytes} \approx \mathbf{3\text{ Terabytes}}.
    • Attachments (images, logs, PR attachments): 50Β Petabytes50\text{ Petabytes} stored in Amazon S3 / Google Cloud Storage.
  • Throughput Sizing:
    • Read QPS (board views, searches): Average 15,000 QPS, peaking at 40,000 QPS.
    • Write QPS (card moves, comments): Average 250 QPS, peaking at 2,000 QPS.

2. The Set Up

Defining Core Entities

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ ISSUE β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ issue_id β”‚ UUID β”‚ PRIMARY KEY β”‚
β”‚ organization_id β”‚ UUID β”‚ Tenant Shard Key β”‚
β”‚ project_key β”‚ VARCHAR(10) β”‚ E.g. "ENG", "INFRA" β”‚
β”‚ issue_number β”‚ INT β”‚ Sequential per proj β”‚
β”‚ title β”‚ VARCHAR(256) β”‚ Summary Text β”‚
β”‚ current_state_id β”‚ UUID β”‚ FK to WORKFLOW_STATE β”‚
β”‚ assignee_id β”‚ UUID β”‚ User FK β”‚
β”‚ priority β”‚ ENUM β”‚ LOW, MED, HIGH, URG β”‚
β”‚ fractional_pos β”‚ DOUBLE β”‚ Board Order Rank β”‚
β”‚ version β”‚ INT β”‚ Optimistic Lock Ver β”‚
β”‚ updated_at β”‚ TIMESTAMP β”‚ Modification Time β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ WORKFLOW_TRANSITION β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ transition_id β”‚ UUID β”‚ PRIMARY KEY β”‚
β”‚ organization_id β”‚ UUID β”‚ Tenant Scope β”‚
β”‚ from_state_id β”‚ UUID β”‚ Source State β”‚
β”‚ to_state_id β”‚ UUID β”‚ Destination State β”‚
β”‚ required_role β”‚ VARCHAR(64) β”‚ Authorization Guard β”‚
β”‚ post_action_hook β”‚ VARCHAR(128) β”‚ Webhook / CI Trigger β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ ISSUE_AUDIT_LOG β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ log_id β”‚ INT64 β”‚ Auto-Increment ID β”‚
β”‚ issue_id β”‚ UUID β”‚ Target Issue β”‚
β”‚ actor_user_id β”‚ UUID β”‚ Author of Change β”‚
β”‚ field_name β”‚ VARCHAR(64) β”‚ "status", "assignee" β”‚
β”‚ old_value β”‚ TEXT β”‚ JSON snapshot β”‚
β”‚ new_value β”‚ TEXT β”‚ JSON snapshot β”‚
β”‚ created_at β”‚ TIMESTAMP β”‚ Immutable Timestamp β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The API Design

1. Transition Issue Status (Card Move)​

POST /api/v1/issues/{issue_id}/transitions
Content-Type: application/json
Authorization: Bearer <user_token>
If-Match: "version_42"

{
"to_state_id": "state_done_4910",
"board_id": "board_sprint_12",
"prev_fractional_pos": 4.0,
"next_fractional_pos": 5.0,
"comment": "Merged in PR #412"
}

Response (200 OK):

{
"issue_id": "ENG-1042",
"new_status": "DONE",
"version": 43,
"new_fractional_pos": 4.5,
"updated_at": "2026-10-01T14:22:00Z"
}

2. Advanced Search Query (JQL)​

POST /api/v1/search/jql
Content-Type: application/json

{
"jql": "project = 'ENG' AND status != 'DONE' AND priority in ('HIGH', 'URGENT') ORDER BY updated_at DESC",
"limit": 50,
"cursor": "eyJpZCI6IDEwNDJ9"
}

Response (200 OK):

{
"total_matches": 142,
"issues": [
{
"key": "ENG-1042",
"title": "Fix memory leak in Netty WebSocket gateway",
"status": "IN_REVIEW",
"priority": "URGENT",
"assignee": "[email protected]"
}
]
}

3. High-Level Design

Issue Tracker Workflow State Machine & Real-Time Sync ArchitectureInteractive Topology
Read QPS
100K/sec
Write QPS
1K/sec
Latency SLA
< 15ms
5-Yr Storage
~15 TB
Active Scenario: User submits long URL -> Token Generator (KGS) allocates Base62 ID -> Writes to DB & Warm Cache
Client / AppBrowser / MobileAPI GatewayEnvoy / NGINXURL ServiceStateless Golang/JavaRedis CacheCluster (LRU)Primary DBPostgres / DynamoDBKafka / FlinkClick Analytics
Interactive Component Inspector
Click any architecture node on the SVG canvas to view under-the-hood engine mechanics, failure gotchas, and runtime tags.

Walkthrough of Core Flows

1. The Issue State Transition & Card Move Flow​

  1. User drags card ENG-1042 from "In Review" to "Done" on a Kanban board.
  2. The client checks local optimistic state, rendering the card in "Done" immediately (0ms user lag).
  3. The client sends POST /issues/{id}/transitions with the optimistic version header If-Match: "42".
  4. The Workflow State Machine Engine:
    • Queries the tenant's workflow schema to verify that transition (IN_REVIEW -> DONE) is valid.
    • Evaluates Transition Guards (e.g., verifying that the user has the Developer role and that required fields like "Resolution" are provided).
  5. Database Transaction:
    • Updates current_state_id, fractional_pos = 4.5, increments version = 43 in PostgreSQL/Spanner.
    • Appends a new immutable row in ISSUE_AUDIT_LOG.
  6. Real-Time Fanout:
    • Publishes an IssueUpdatedEvent to Redis Pub/Sub or Apache Kafka.
    • Connected WebSocket Gateway Pods broadcast the mutation down persistent duplex sockets to all teammates currently viewing the board, smoothly animating the card into the new column.

2. The Search Ingestion & Query Flow (JQL)​

  1. Every write to the issue table triggers a Transactional Outbox / CDC (Change Data Capture) event via Debezium.
  2. The event stream updates the Elasticsearch / OpenSearch issue index within 200ms.
  3. When users execute complex JQL queries, the request is parsed by a Lexer/Parser into an AST (Abstract Syntax Tree), translated into an Elasticsearch Bool/Filter query, and executed against the search cluster in <30ms< 30\text{ms}.

4. Potential Deep Dives & Bottlenecks

Deep Dive 1: Configurable Workflow State Machine Engine

How do enterprise systems support custom customer workflows without hardcoding transitions in code?

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ WORKFLOW DIRECTED GRAPH ENGINE β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”‚
β”‚ [Backlog] ──(Start Work)──► [In Progress] β”‚
β”‚ β–² β”‚ β”‚
β”‚ β”‚ (Submit PR) β”‚
β”‚ β”‚ β–Ό β”‚
β”‚ (Reject) ◄─────────────── [Code Review] β”‚
β”‚ β”‚ β”‚
β”‚ (Merge) β”‚
β”‚ β–Ό β”‚
β”‚ [Done] β”‚
β”‚ β”‚
β”‚ State Machine Verification Pipeline: β”‚
β”‚ 1. Check: Does edge (Current_State -> Target) exist? β”‚
β”‚ 2. Evaluate Guards: User Role, Branch Rules, Approval β”‚
β”‚ 3. Execute Atomic State Mutation β”‚
β”‚ 4. Fire Post-Functions: Webhooks, Jira Automations β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  • Directed Graph Representation: The workflow is stored as an adjacency list of transitions. Transitions specify from_state, to_state, guard_expression, and post_actions.
  • Validation: Adding or editing a workflow requires running Cycle and Deadlock Detectors (Tarjan's strongly connected components algorithm) to ensure the workflow contains at least one path from Initial to Terminal state without unreachable orphan nodes.

Deep Dive 2: Real-Time Board Concurrency & Conflict Resolution

What happens when User A and User B drag the exact same card to different columns simultaneously?

  • Optimistic Concurrency Control (OCC):
    • Every issue row has an integer version column.
    • The update query executes:
      UPDATE issues
      SET current_state_id = :new_state, version = version + 1
      WHERE issue_id = :id AND version = :expected_version;
    • Outcome: User A's transaction succeeds (version becomes 43). User B's transaction matches 0 rows and fails with HTTP 412 Precondition Failed.
  • Client Reconciliation: User B's client receives the 412 error, snaps the card back to its actual position, and displays a toast notification: "Issue was moved by User A".

Deep Dive 3: Fractional Indexing for Board Column Ordering

How do we support inserting an issue between two existing cards in a 5,000-issue backlog without updating 5,000 database rows?

  • Same as Spotify playlists, issues use Floating-Point Fractional Indices:
    • Card 1: pos = 1.0
    • Card 2: pos = 2.0
    • Insert between Card 1 and 2 β€…β€ŠβŸΉβ€…β€Špos=1.5\implies \mathbf{pos = 1.5}.
  • Only a single row is updated in the database.

Deep Dive 4: JQL Lexer, Parser & Search Indexing

How is a query like project = 'ENG' AND (labels in ('infra', 'perf') OR priority = 'HIGH') evaluated?

User JQL String ──► [Lexer (Tokenize)] ──► [Recursive Descent Parser] ──► AST
β”‚
β–Ό
Elasticsearch Query
  1. Lexical Analysis (Lexer): Breaks query string into tokens (IDENTIFIER, OPERATOR, LPAREN, STRING_LITERAL).
  2. Abstract Syntax Tree (AST): A recursive-descent parser builds a binary expression tree respecting operator precedence (AND over OR).
  3. Elasticsearch Translation:
    • project = 'ENG' maps to { "term": { "project_key": "ENG" } }.
    • priority in ('HIGH', 'URGENT') maps to { "terms": { "priority": ["HIGH", "URGENT"] } }.
    • Wrapped in an outer { "bool": { "must": [...] } } query executed against index shards.

5. Architectural Trade-Off Matrix

Design AlternativeOption AOption BSelected Choice & Rationale
Board Sync ProtocolClient HTTP Polling every 5sStateful WebSockets via Redis Pub/SubWebSockets: Slashes server bandwidth and database read QPS by 90%; delivers immediate sub-100ms card drag animations.
Search EnginePostgreSQL SQL LIKE / JSONBDedicated Elasticsearch / OpenSearchElasticsearch: Relational databases fail on multi-clause dynamic faceted filtering and text tokenization across millions of issues.
Card OrderingArray Index ShiftingFractional Indexing (1.0, 1.5, 2.0)Fractional Indexing: Turns an O(N)O(N) row-shifting write cascade into a single O(1)O(1) atomic row update.
Changelog TrackingShadow Tables with Entire Row DuplicationJSON Patch (RFC 6902) Diff LogJSON Patch: Stores only the specific field mutated, cutting audit log database storage by 80%.

6. What is Expected at Each Level?

Mid-Level (L4 / IC4)

  • Designs basic schemas for issues, projects, users, and audit logs.
  • Understands status changes and proposes WebSockets or polling for board synchronization.
  • Proposes standard indexing on (project_id, status) for list queries.

Senior (L5 / IC5)

  • Designs configurable workflow state machines with validation guards and post-action hooks.
  • Implements optimistic concurrency control (version / If-Match) to resolve race conditions during concurrent moves.
  • Details fractional indexing to avoid massive row-shifting write cascades.
  • Explains asynchronous search indexing using transactional outbox and Elasticsearch.

Staff+ (L6 / Principal)

  • Evaluates Local-First architecture (e.g. Linear's sync engine using client-side SQLite/IndexedDB and CRDT reconciliation).
  • Formulates multi-tenant data partitioning (tenant-per-schema vs shared database with row-level security).
  • Designs distributed WebSocket presence systems showing avatar cursors and real-time viewing indicators across thousands of concurrent teammates.
  • Formulates JQL query cost estimation engines to abort pathological user queries that could cause search cluster denial-of-service.
πŸ“–
Track Page Progress0 / 635 Read
Knowledge Base Completion0%