Moving from Jira Data Center to Jira Cloud is rarely hard for technical reasons. The hard part is finding out what your instance actually does today, and whether the business still needs all of it.
For a customer with a large Jira Data Center instance, we built an AI-assisted pipeline that reads the instance read-only and writes business-level documentation to Confluence: issue types, fields, workflows, Behaviours, and Automation rules, across a large number of projects. This article shows what is extracted, how only changed pages are regenerated, how the AI turns scripts into plain language, and what a finished page looks like.
The problem: business logic hidden in configuration
After many years on Data Center, an instance holds an enormous number of artifacts, many of them customized. When they were designed, they mirrored the intended process. Since then, usage has moved on: fields serve other purposes, rules fire in situations nobody planned for, and parts of the configuration have drifted from the original concept. None of this is written down. It lives in places a business owner never looks at:
- Screen schemes. Which fields appear on create, view, and edit, per issue type. Screen names can be misleading: a screen called "Default" may serve all three operations, and an edit screen may have no fields at all.
- Workflow rules. Conditions, validators, and post functions, many of them ScriptRunner Groovy, with the real role logic in shared utility classes the scripts import.
- Behaviours. Field logic on the screen: hide, require, pre-fill, restrict options. Some are mapped to "All projects" and never show up when you filter by project.
- Script fragments. Buttons and web items that create one issue from another, pre-filled and connected with a predefined link type. They are part of how an issue type is operated in context, and they appear in no process document.
- Automation rules. Triggers, branches, and actions that change issues in the background, some global, some per project.
The Jira Cloud Migration Assistant moves the technical artifacts. It cannot tell you which of them still describe how the business works. And since Cloud is a different platform, every rule has to be decided on again: rebuild, simplify, or drop.
The approach: document the business first, then transform
Instead of a plain lift-and-shift, a migration can start from a documented target that the business has reviewed, with the Cloud design built on top of it. That requires documentation business owners can read and push back on: every page describes an issue type in business terms — which fields it has, who may do what in which status, which rules check or change data, and how often each field is actually used.
Process owners review these pages in Confluence, where they already comment and approve. Every rule gets a verdict: keep the process, simplify it, or drop it. The Cloud implementation then starts from that reviewed target, not from a copy of the old configuration.
The pipeline is not tied to a migration. It only regenerates what changed, so it can document a Data Center instance continuously, and with other extractors a Cloud site as well.
The pipeline at a glance
Five stages; one of them uses AI.
The five stages of the pipeline: Jira Data Center, extraction, fingerprint check, AI interpretation or deterministic rendering, review gate, Confluence
A script extracts the configuration and compares it with the previous run. The AI is called only when data or the prompt changes. Diagrams, mockups, and tables are rendered by code, and a person approves each page before it goes live.
Step 1: ScriptRunner REST endpoints for what the standard API doesn't expose
The standard Jira REST API has no endpoints for workflow validators with their script code, for Behaviours or script fragments, or for the Groovy files on the server. Five small ScriptRunner custom REST endpoints close those gaps.
Endpoint | Returns |
|---|
workflowRules
| Conditions, validators, and post functions per transition of a workflow |
workflowScript
| The source of a Groovy file from the server's script roots |
behavioursConfig
| All Behaviours with mappings, field rules, and initializers |
scriptFragments
| Script fragments such as buttons and web items, with their conditions and the scripts behind them |
linkMatrix
| How issue types are linked to each other across projects |
The skeleton of such an endpoint is short:
@BaseScript CustomEndpointDelegate delegate
workflowScript(httpMethod: "GET", groups: ["jira-administrators"]) {
MultivaluedMap query, String body ->
def path = query.getFirst("path")
// only *.groovy, no "..", must resolve inside a script root
// read the file, cap its size, return it as JSON
Response.ok(new JsonBuilder([path: path, script: text]).toString()).build()
}
Four rules keep the endpoints safe on a production instance:
- GET only. Nothing writes to Jira.
- Admin group only. The endpoints use an administrator's normal personal access token.
- Path guard. The script endpoint only serves
.groovy files inside the configured script roots. - Compact JSON. ScriptRunner objects are projected to plain maps with depth and size caps, otherwise a single enum can drag half of Jira into the response.
Automation for Jira needs no custom endpoint. Its own REST API in Data Center returns every rule with its complete trigger and component tree.
Step 2: Extract everything, and double-check what the API tells you
For each project and issue type, the extractor builds one JSON payload: statuses, fields, screens and tabs, the screen per operation, workflows with rules and scripts, roles, field options, Behaviours, script fragments, and Automation rules.
Most of it comes from standard REST calls. The catch is that several of them return incomplete or misleading data without any error, so the extractor cross-checks each one against a second source. Three examples: screen names say nothing about the operation, so the screen scheme is read from the project settings instead; createmeta only knows create fields, so a field catalog is loaded once per run to type the fields on view, edit, and transition screens; and role logic often sits in shared Groovy classes, so the extractor follows import statements and fetches those too.
Two more things matter at scale: field usage (JQL counts per field over the last 6 and 12 months, kept out of the AI input because they change daily) and rate limits (Data Center answers with HTTP 429 after thousands of calls; the extractor backs off, retries, and skips a unit rather than publishing a half-empty page).
Step 3: Fingerprints, so only what changed is rebuilt
Generating one large issue-type page takes the AI several minutes and tens of thousands of output tokens. Rebuilding every page on every run would be slow and expensive, and it would flood Confluence with meaningless page versions.
So every run stores a state file per project and issue type: the complete Jira payload plus a set of fingerprints. Jira returns byte-identical data on repeated calls, with no timestamps or counters, enabling change detection.
Fingerprint | Hashes | If it changed |
|---|
Jira data | The canonical JSON payload | Ask the AI again |
Prompt | Model, system prompt, page outline, limits | Ask the AI again |
Diagram | Source code of the diagram renderer | Rebuild the page from stored text, no AI |
Page assembly | Source code of every layout function | Rebuild the page from stored text, no AI |
The decision per unit reads like this:
if state is None or state.prompt_fp != prompt_fingerprint():
return "generate" # ask the AI
if canonical(state.payload) != canonical(jira_payload):
return "generate" # Jira configuration changed
if state.svg_fp != svg_fingerprint() or state.page_fp != page_fingerprint():
return "page_only" # new layout, same content: no AI call
if not page_exists:
return "page_only" # restore a deleted page
return "skip"
The fingerprints hash source code, not version numbers. Somebody always forgets to bump a version number; nobody can forget to change the code they have just changed. Any change that shapes the output needs a fingerprint, or the next run silently fails to deliver it.
The review gate uses the same state: a rejected page is stored with a rejected flag. If the layout code has changed since then, the next run repairs the page without an AI call; only if nothing changed is the AI called again.
Step 4: Ask the AI what the script means
This is where AI is actually useful. A validator called CheckApprover.groovy tells a process owner nothing. The AI reads the script and the utility classes it imports, then states the business rule it implements.
We use Claude with a fixed model version, one call per issue type. The prompt has three parts:
- System prompt. The role (technical writer for Jira configurations) and the hard rules: use tables, invent nothing, never name classes, files, or field IDs, never mention where data came from, leave out a chapter without data.
- Page outline. The exact chapters and table columns, so all pages look alike. One instruction does most of the work: when a condition or validator references a script, read the code and state the effective business rule it implements.
- Payload. The extracted JSON for this issue type, including the script sources.
For Behaviours, prose is too vague for field logic, so the AI writes indented pseudocode with a small fixed vocabulary:
// Behaviour script (simplified)
if (getActionName() == "Submit for Approval") {
getFieldByName("Budget").setRequired(true)
}
if (getFieldByName("Category").getValue() == "Maintenance") {
getFieldByName("Business Case").setHidden(true)
}
ON TRANSITION "Submit for Approval"
REQUIRE "Budget"
WHEN "Category" = "Maintenance"
HIDE "Business Case"
A process owner reads the second block and says "Yes, still true" or "We dropped that rule years ago". That is the review a migration decision needs.
Three design decisions keep this reliable and affordable:
- A stable prompt prefix and a variable tail. About 89% of the prompt is identical across the issue types of a project. With prompt caching on the API, that cuts input costs by roughly two-thirds.
- A pinned model. An alias like "latest" changes the output without warning, and the fingerprints would not notice.
- An empty working directory. The model gets the payload and nothing else, so it cannot wander off into unrelated files.
Step 5: Diagrams and mockups come from code, not from the AI
The AI writes text and tables. Everything that must be exact is drawn by code from the same payload: same data, same picture, no AI cost, no invented arrows.
Visual | What it shows |
|---|
Workflow diagram | Statuses ranked by process order, forward transitions solid, backward ones dashed, self-transitions as a counter badge |
Field model | One card per screen tab, with check marks for create, view, edit, and transition screens |
Screen mockups | Each screen tab redrawn as Jira renders it, with the right widget per field type |
Usage badges | Share of issues with this field filled in the last 6 and 12 months, red at 5% or less |
Automation flow | Each rule as a when / if / then chain, with IDs resolved to names |
Relationship map | How issue types link to each other across projects |
Workflow diagram of an anonymized example workflow, drawn by the deterministic renderer
A create-screen mockup next to its field table, as it appears on the Confluence page
A fixed assembly step converts the Markdown into Confluence storage format: mockups next to their tables, status and type labels, uniform column widths, numbered headings, each transition linked to its screen section. Every page is checked as strict XHTML before upload.
What a typical page looks like
The page tree is organized the way the business thinks about its processes, not the way Jira stores them:
The Confluence page tree: landing page with relationship map, process groups, project pages, one page per issue type, Behaviours page, and one page per Automation rule
An issue-type page always has the same chapters, numbered and with a table of contents:
- Screens and fields overview. Every field and which screens show it.
- Create screen. One section per tab with a mockup and a field table: field, ID, type, required, values, usage, and a business comment.
- View screen and Edit screen. Same structure. An empty edit screen is documented as empty, not replaced by another screen.
- Workflow. The diagram, then one row per transition: from, transition, to, screen, roles, preconditions, validations, and post functions, in business language.
- Transition screens. The screens that open during a transition, linked from the workflow table.
A complete issue-type page for an anonymized example, from the table of contents down to the transition screens
The Behaviours page lists every Behaviour of the project in one table: issue type, screens, target fields, and the pseudocode from Step 4.
The Behaviours page of a project: one row per Behaviour with issue type, screens, target fields, and the generated pseudocode
Each Automation rule gets its own page: trigger, scope, business logic, a flow diagram, and an implementation table that keeps every JQL query and smart value raw and copyable for the rebuild in Cloud. Webhook credentials are always redacted.
An Automation rule page with its when/if/then flow diagram and the implementation table
Beyond the migration
Once the documentation is in place, running it again is inexpensive, and the same pipeline serves three additional purposes.
A regression test for the configuration. Every run stores a complete snapshot. When a fingerprint changes, the pipeline can notify the admins and point to the exact difference: which transition, which validator, which field. A workflow changed by mistake can be compared with its previous state, and because Confluence keeps every page version, the before-and-after is also readable in business language.
Business documentation as a deliverable. Organizations are increasingly asked for business-level documentation of their Jira processes, for audits, onboarding, or process ownership. Written by hand it takes weeks and is outdated at the next change; the pipeline keeps it current.
Hygiene and quality assurance. The extracted data shows where an instance can be simplified:
- Links that are rarely used
- Custom fields that exist several times under similar names
- Fields that are almost never filled, or that sit on no screen at all
- Behaviours and Automation rules that act on fields no screen shows
- Projects with an unusually high number of Behaviours or rules
All of this carries over to Jira Cloud: after a move, the extraction layer points at the Cloud site, and the reviewed target is continuously compared with the live configuration.
From documentation to Cloud: where our apps come in
The move to Cloud is not a platform change to get over with. It is the one moment when every rule in your Jira gets a conscious decision — keep, simplify, or drop — and the configuration becomes a business decision again. And for teams that rebuild the reviewed rules with our apps, this documentation is the enabler.
The reviewed documentation doubles as a build list for Jira Cloud, and a migration is the moment to move these rules into no-code configuration. Three kinds of findings map directly to our apps:
Found in Data Center | What the documentation gives you | Rebuild in Cloud with | Why switch now |
|---|
Issue picker fields and the links they create | Every picker field per issue type, its usage, and the link types between issue types | VIP.LEAN Issue Pickers | Once the link types are reviewed and consolidated, the picker fields are populated from the migrated issue links. Existing data carries over; new links are created and kept in sync in both directions |
Behaviours | Each Behaviour as reviewed business pseudocode: when, which field, which rule | VIP.LEAN Behaviours Builder | Every rule is reviewed anyway. Configure it without code, in the same trigger, condition, and action shape the business approved, and let admins maintain it |
Script fragments that create linked issues | Every create-and-link button: where it appears, which issue type it creates, which fields it pre-fills, and which link it sets | VIP.LEAN Create and Link | Buttons are configured graphically: template, placement, and link in one place, with Behaviours on the new issue as an optional extra |
If you'd like to see this documentation generated for your instance, or if you're planning to use our apps in a migration, get in touch, and we'll set it up with you.