Quick Summary
My organization's engineering group is spread across ~44 team spaces plus one main space to hold our roadmap. Our hierarchy (which has been in use, with all the accumulated cruft that entails, for 5+ years), has been:
- Initiative (main project)
- Roadmap Item (main project)
- Epic (team projects)
- Story, Bug, etc. (team projects)
This list is a bit deceiving - our "initiatives" aren't really part of the hierarchy (they functionally represent products), and for a while it has felt like we needed another layer to give our Product folks a way to organize their work. Many of our biggest projects were too hard to track easily and we had lots of zombie roadmap items which never seemed to die because it was easier to increase scope than cut more tickets.
When shopping this idea around internally I wanted to give people something tangible to look at, so I made the changes in our sandbox environment by moving the issuetypes themselves up a layer. This proved to be pretty destructive and I will admit I cheated a little by simply telling Codex "It's late and this is test data, so I don't care what breaks. Make it work." and going to bed. I woke up to the hierarchy I wanted and a few confused messages from other admins.
We finally got alignment with everyone to insert another issuetype between Roadmap Item (now renamed "Project") and Epic which we would call "Release". So…"make it work" and go to bed, right? I wish.
I ended up having to remote control some stuff the next day too. Ugh.
Prep Work
Not too long ago, I worked with Codex to set it up with up a "context library" of md files which I use for longer term memory (think "Obsidian for dummies"). There's a skill to use it and a skill to ingest information into it, and among other things it contains accumulated knowledge about:
- How to use Jira's API and where to figure out anything it doesn't already know.
- What Jira spaces my engineering programs use, how they are configured, and how people use them.
I built this over time by finishing a task and then doing things like "/ingest context library how we did this" or "update my context library using /ingest context library with the current Jira spaces in the categories…" after making a bunch of changes.
A Note about the Atlassian Connector
I do have the most recent Atlassian connector installed, but I did not use it for any part of this process except some small one-off reads for two reasons:
- I have found that in almost every situation, having Codex use the API's directly more accurately delivers the outcomes I intended in much less time (the quickie analysis I did on this landed on something like 3x faster).
- I have not had the time necessary to confirm that this new connector is Rovo-free, and in my experience everything Rovo touches turns to suck.
Planning the Plan
After re-confirming that my context library was up-to-date, I had Codex look at the scope of issues I knew would be the most challenging based on what I'd hand-waved the most in sandbox:
- The older "roadmap item" layer involved making a lot of decisions about how to lay out work that was done, old, and\or otherwise not really worth my weekend time but also not something I could avoid given the laws of physics in Jira.
- There are a few spaces outside of my engineering programs using the "roadmap" and "initiative" layers; if someone broke my production data the way I did on sandbox without a long conversation first, I'd be extremely unkind to them in response.
In the end, I decided to make two new issuetypes (Project (New) and Initiative (New)) and migrate only the active work I cared about into the new ticket types as part of the first pass. The way it would work is…
- I would generate a full audit ledger of all of the tickets in scope for the change all the way through from "initiative" to "epic". This served both as a source of truth outside Jira as the changes were implemented, and so I have a solid foundation to restore things to the way they were if I need to pull an emergency revert. This actually ended up being hugely beneficial for another reason I will get to later.
- I would disconnect the original "initiative" layer from it's children and use "move" to change the type to the new ticket type one layer up the hierarchy.
- Next I would do functionally the same operation one step lower down at the "Project" level - disconnecting them from their children and using "move" to change their type - finishing with reconnecting all of them back to their old parents.
- Then I would use the API to insert functional duplicates of the "Epic" issues as "Releases", living in our main Engineering space and linked up to bridge between "Project" and "Epic" tickets.
- Finally, I would add the "(Old)" suffix onto the old issuetypes and remove the "(New)" one from the new and swap the icons, so that the old legacy types got the suffixes and funny-looking icons.
I chose this path mainly because it allowed me to punt on fixing the initial two problems until later, but once I saw how long generating and checking the audit ledger would take, I was very glad I did. Ultimately this path led to just over 20k less issues I'd need to modify (or about 79.4%).
Executing the Plan, Part 1 - During Work Hours
I started by having Codex generate a spreadsheet containing the current state of every "in-scope" ticket, initiative to epic. This worked out to be just under 3,000 issues (mostly in the epic layer) but took almost 3 1/2 hours to create because it needed to traverse the hierarchy, check fields for problematic values, and otherwise do quite a bit of stitching.
In parallel, I did as much of the "behind the scenes" work as I could: setting up the new ticket types, putting them on the exact same configurations as their counterparts, and making new icons (close enough but not identical, so that it would be intuitive what it was supposed to be and whether it was the old or the new type; I ended up using the same icons in different colors). I had Codex do this as well saying something like "Go create the ticket types and set them with the configurations we need them to have to make the migration simple as we've designed it."
Executing the Plan, Part 2 - After Work Hours
Once people rolled off for the day, I could get to work with the rough stuff. I started by doing two checks on the ledger:
- A high-level pass to find any new tickets that had been created inside the scope of parent initiatives I was concerned about.
- A differential look at the ledger vs. Jira production to ensure that it was still accurate.
Both caught changes that needed to be reflected in the ledger. Then, taking a deep breath and holding tightly to my all of the butts, I gave it the go-ahead.
The Outcome
One thing that didn't really occur to me until this point was that, as part of creating the ledger, I had been pre-resolving most of the problems that might interrupt the ultimate processing work. For example, as it compiled the ledger it identified disables values still present in the source tickets and asked me how to handle them; when it came time to actually "do the stuff", it already had answers for everything.
So when I told it "start the migration by moving the initiatives, and then the projects", the questions or permission requests I assumed would slow this down…never came. It just kept chugging and chugging. Eventually, I got bored and went to bed. When I woke up the next morning, these two layers were completely done.
I kicked off the fourth step and once again Codex just hummed along, carving out and executing batches of changes and updating the ledger accordingly. After about an hour and a half, it finished inserting the "Release" ticket layer. I happened to be away from my desk, and so rather than trying to audit the results in Jira on my phone, I just had another agent review the plan I put together, the conceptual goal I wanted to achieve, and tell me where the execution had missed the mark.
It found a few Epic fields I had forgotten to include in the list to port to the new "Releases" and a handful of tickets in our "Non-Initiative Work" initiative which needed some specialized attention, but other than that a clean bill of health. When I got home I did several manual passes, but aside from some bad hygiene what I found was a job pretty well done.
Conclusions
- Validated the value of my context library. In the past, I have had to laboriously explain to Codex exactly what certain concepts meant or how I wanted it to accomplish specific tasks in Jira (usually after realizing what I just did was trash because I assumed it knew something it didn't). Being able to leverage all of that prep work sliced a tremendous amount of lift off of this effort.
- Some of my old assumptions need adjusting. Completing tasks similar to this without AI has informed a lot of my "how to Jira" thinking to this point: design considerations and best practices like "keep teams on a single shared set of configurations" and "try to limit your custom field usage by generic (and therefore re-usable) field names" were meant in many ways to manage administrative overhead. With AI, that work simply is not the time-sink it used to be.
- I do not want to administer Jira without Codex. Without AI, I would have spent this weekend chained to my desk and straining to remember whether I was processing batch 200 or 201 out of 764. Instead, I was able to have AI handle the manual execution work based on a plan we came up with together, seeded with my initial thinking and then augmented by a robot's attention to detail and exactitude…and I was able to keep coaching little league. Win-win!
I wanted to publish this both for anyone who was wondering how to solve this exact problem in a way that doesn't lead to desolation, but also as an example of how AI can help make Jira more manageable for most of us in the trenches. AMA.