Saaspocalypse
The migration runbook

Migrating off Jira — phase by phase.

Jira migrations are about three things: extracting via the Cloud REST API (which paginates differently for issues than for most other resources), faithfully rebuilding the workflow + scheme spaghetti as state machines, and translating JQL searches and Marketplace-app bills into first-class features in the new product. Issue keys (PROJ-1234) are preserved everywhere so external links, commits, and PR references keep resolving.

12-16 weeks end-to-endFixed priceParallel run before cutoverYou own the code

The phases, tuned to Jira

Each phase below is the Jira-specific version of our standard playbook. Same shape every engagement, different specifics per SaaS.

  1. Workflow + scheme audit

    2-3 weeks

    Every Jira instance has accumulated a forest of workflows, screen schemes, permission schemes, notification schemes and field configurations. We inventory ruthlessly — most teams collapse 30+ workflows into 5-6 once duplicates and unused ones are removed.

    Export every workflow definition + transition rule via the REST API
    List every screen scheme, permission scheme, notification scheme, field configuration scheme
    Inventory active Marketplace apps and what each one actually does (most teams have 3-6 they pay for; 1-2 are doing real work)
    Catalogue saved JQL filters — these become saved views in the new product
    Identify the integrations (GitHub, GitLab, Slack, OpsGenie, Statuspage) that need to keep working through migration
    You walk away with

    A Workflow + Scheme Inventory + a Marketplace-app shortlist (rebuild / replace / drop).

  2. Schema + issue-key strategy

    1 week

    Issues become a properly-typed table with the issue key (PROJ-1234) preserved everywhere. Custom fields stop being string blobs. Workflows become state machines defined in TypeScript and reviewed in PRs.

    Design `issues` table with `jira_key` column (indexed) + native columns for the top 20 fields
    Custom fields get sensible types (Date, Money, Decimal, Enum) instead of cf[10042] strings
    Workflow definitions as TypeScript state machines (transitions, validators, post-functions)
    Permission scheme → 4-6 named roles in code
    Plan a JQL-equivalent query layer in Postgres (most JQL clauses are 1:1 to SQL with the right indexes)
    You walk away with

    Schema migrations + workflow state machines in TypeScript + the issue-key preservation strategy.

  3. Extract via REST API

    1-2 weeks

    Jira Cloud's REST API paginates issues via `?startAt` + `maxResults` (default 50, max 100). The search endpoint is where you'll spend most of the extract budget — chunk by project + by date range to stay under the rate limits.

    Authenticate via OAuth 2.0 (3LO) or API token with HTTP Basic
    Iterate `/rest/api/3/search` per project with JQL `project = X ORDER BY created`
    Pull each issue's comments, worklogs, changelog, attachments separately (the search response is shallow)
    Extract sprints + versions + components per project
    Pull users, groups, project roles and permissions for the RBAC mapping
    Save the raw JSON — it's useful evidence during reconciliation
    You walk away with

    A re-runnable extract pipeline + a reconciliation report comparing issue counts per project.

  4. Build + weekly demos

    5-8 weeks

    Issues, boards, JQL-equivalent search, automation rules and dashboards rendered server-side. Time-tracking and roadmaps stop being paid Marketplace apps and become first-class columns.

    Issues list + Kanban + sprint views — virtualised grid for the 50k-issue case
    JQL-equivalent search via Postgres full-text + structured filters; saved filters become URL state
    Automation rules as TypeScript functions with a real audit trail (the Jira automation rule cap stops being a thing)
    Time-tracking as a column on the issue table (no Tempo / Clockwork app needed)
    Roadmap / Gantt as a derived view over due dates + dependencies (no Advanced Roadmaps subscription)
    Dashboards rendered server-side from real SQL
    You walk away with

    A working tracker on staging that your engineering team uses for one full sprint.

  5. Parallel run + reconciliation

    2-3 weeks

    Both systems live for two-three weeks. Issues created in Jira sync nightly into the new product; webhooks from GitHub / GitLab / Slack fire into both during the ramp.

    Daily incremental sync from Jira via the `updated >= -1d` JQL filter
    Bridge integrations (GitHub commit messages, Slack notifications) to both systems
    Daily reconciliation: open vs done counts per project, comment-count drift, attachment integrity
    Train engineers in two 45-minute sessions, recorded for the rest
    Pin the issue-key strategy in commit messages — `PROJ-1234` still resolves on both sides
    You walk away with

    A reconciliation dashboard + a green sprint demo from the new product.

  6. Cutover + Jira read-only

    1 day + 90-day fallback

    Jira goes read-only on a Tuesday morning, GitHub / Slack integrations point only at the new product, Atlassian Marketplace apps get cancelled the same week.

    Cut SSO + bookmark redirects to the new tracker
    Lock Jira to read-only (revoke project-admin and edit permissions)
    Cancel Atlassian Marketplace apps in writing — the bill drops the same month
    Hold a 90-day Jira read-only window for compliance / regret / audit
    Cancel the Atlassian Cloud subscription auto-renewal
    You walk away with

    Cutover Runbook + cancelled Marketplace subscriptions + a quiet inbox where the renewal email used to land.

The Jira APIs we'd actually hit

Concrete endpoints we use to extract your data during the parallel-run phase. We keep the extract scripts — they're useful at every renewal.

Search issues (REST API v3)
GET /rest/api/3/search?jql={jql}&startAt={n}&maxResults=100
The primary extract endpoint — paginate issues per project ordered by created date
Get issue with expansions
GET /rest/api/3/issue/{issueKeyOrId}?expand=changelog,renderedFields
Pull the full issue including changelog (history of every field change) and rendered body
Get issue comments
GET /rest/api/3/issue/{issueKeyOrId}/comment
Paginate every comment per issue (search response is shallow)
Get workflows
GET /rest/api/3/workflow/search
Extract every workflow definition + transition rules for the state-machine rebuild
Get projects
GET /rest/api/3/project/search
List projects, project keys, project leads, default assignees
Get users + groups
GET /rest/api/3/users/search
Pull users + group memberships for the RBAC mapping
Get sprints (Agile API)
GET /rest/agile/1.0/board/{boardId}/sprint
Sprint history per board for the velocity + burn-down rebuild
OAuth 2.0 (3LO)
POST https://auth.atlassian.com/oauth/token
Three-legged OAuth for the extract tooling (or HTTP Basic with an API token for unattended)

The Jira-specific gotchas

Things we've learnt the hard way (or that the documentation understates). We'll surface these on the kickoff call so nobody is surprised in week 6.

What stays where it is

Migrations work because they're scoped honestly. Here's what we don't move — by design.

  • ·Atlassian Marketplace apps — replaced by first-class features or dropped
  • ·Jira Service Management — separate scoping conversation (it's a different product, not in Jira Software scope)
  • ·Bitbucket repos — keep them where they are or migrate to GitHub separately
  • ·Confluence — paired separately (it's its own SaaS to kill, see /confluence/migration)
  • ·Atlassian Intelligence / AI add-ons — replaced with Claude/GPT against your own data
  • ·Advanced Roadmaps as a separate product — folded into the new tracker as a view over due dates + dependencies

Get a Jira-shaped migration plan.

Tell us your seat count, contract end date, and the integrations you can't lose. We'll come back within a business day with a fixed-price scope and a timeline tuned to your renewal.

Scope a Jira migration

One business day. NDA + DPA on request.