Skip to content
Notifications
Clear all

Walkthrough: Migrating from Jira to Linear with full history

3 Posts
3 Users
0 Reactions
3 Views
(@martech_ops_sarah)
Trusted Member
Joined: 4 months ago
Posts: 30
Topic starter   [#1111]

Hey everyone! I just finished up migrating our product team from Jira to Linear, and I wanted to share a detailed walkthrough since I know a lot of us are considering similar moves. Our main goal was to preserve our entire project history—not just active issues, but comments, attachments, and status changes—because our developers rely heavily on context. We couldn't afford a "fresh start."

Here’s the approach we took, which took about three weeks from planning to full cutover:

**Phase 1: Data Mapping & Prep (1 week)**
* We exported everything from Jira Cloud using their native CSV export for issues, but that only gets you so far. For full history (comments, attachments, status transitions), we used the Jira Rest API v3. I built a set of Python scripts to pull this data and store it in a structured format.
* The trickiest mapping was statuses. Jira's workflow states don't line up 1:1 with Linear's. We created a mapping matrix: Jira's "In Progress" became Linear's "In Progress," but Jira's "In Review" became a custom state we created in Linear called "Review." We also mapped users via email addresses and projects to Linear teams.

**Phase 2: The Migration Script & Dry Runs (1 week)**
* I used Linear's GraphQL API for the import. It's powerful but requires data in their specific mutation format. I wrote a script that transformed our Jira data dump into Linear's `issueCreate` mutations, batching them to avoid rate limits.
* **Critical step:** We created a test project in Linear and did three full dry runs. The first one failed on attachment handling (URLs from Jira needed to be downloaded and re-uploaded to Linear). The second helped us refine comment threading. The third was a success. We also preserved the original Jira issue keys as a custom field in Linear (like `JIRA-123`) for traceability.

**Phase 3: Cutover & Communication (3 days)**
* We scheduled the final migration for a Friday evening. We locked Jira for writes, ran the final script (which took about 4 hours for 8,000 issues), and then did a data validation spot-check.
* On Monday morning, we directed the team to Linear. We kept Jira in a read-only state for a two-week transition period, which eased a lot of anxiety. We held a 30-minute onboarding session to show where everything lived in Linear.

**How long it *actually* took:**
* **Active migration work:** ~3 weeks part-time (I was doing my regular MarOps work alongside this).
* **Final script execution:** 4 hours.
* **Full comfort in Linear:** The team felt productive after about a week, but the two-week read-only Jira buffer was a lifesaver for those "where did that old ticket go?" moments.

The biggest win was bringing over the historical context—it made the switch feel seamless for the engineers. The biggest lesson was to budget more time for the data mapping and dry runs than you think; it's where all the hidden complexities live.

If anyone is planning a similar move and has questions about the user mapping, handling subtasks, or the script structure, I'm happy to share more details! It's a project that really blends my love for clean data with practical ops.

~Sarah


Data is the new oil


   
Quote
(@pipeline_pioneer)
Active Member
Joined: 3 months ago
Posts: 7
 

Good to see someone tackling the full history. A lot of teams chicken out and just import active tickets.

The user mapping via email is the part that'll bite you if you're not careful. Jira's display name and email don't always match your SSO provider, especially if you've had team changes. We had to write a reconciliation step that logged unmapped users for manual assignment.

Did you run into rate limiting with Linear's API during your dry runs? Their limits are strict, and importing comments for thousands of issues can get throttled hard.


pipelines > meetings


   
ReplyQuote
(@migration_warrior_2024)
Trusted Member
Joined: 4 months ago
Posts: 30
 

Oh, the rate limiting was a real headache. Linear's GraphQL API has a 1000-point window that refills at 1 point per second, and creating an issue with a comment and attachments can cost 6-7 points. We hit the wall fast.

We ended up implementing an exponential backoff with jitter, but the real fix was batching. For comments, we grouped them by issue and used a single mutation with nested comment creates, which saved a ton of points. For attachments, we had to pre-upload to a cloud bucket and attach via URL - direct uploads were too expensive point-wise.

And you're spot on about user mapping. We found a bunch of orphaned Jira users tied to old company email domains. Our reconciliation step became a mini-project of its own, linking old tickets to current team leads.


Backup twice, migrate once.


   
ReplyQuote