Configuring Jira for Project Profitability (Without Changing Your Team's Workflow)
A practical setup guide for agencies that want to read margin out of Jira without forcing engineers to change how they work. Sub-project structure with components, role tagging, three custom fields, and the JQL hooks that make it all queryable.
Most agencies on Jira have it configured for delivery, which is correct, and not configured for finance, which is also correct. The temptation when finance asks for project profitability data is to either bolt on a heavy plugin (which compromises engineering UX) or replace Jira with a platform that has finance baked in (which compromises everything else).
There is a third option that almost every agency we work with ends up at: a small set of structural conventions on top of stock Jira that make profitability queryable without changing how engineers actually log work. Sub-project structure expressed through components, role tagging through one custom field per ticket, and three light-weight conventions on epics and labels.
The setup takes about a half day per project type to install. After that, the same Jira your engineers were already using produces clean role-rate vs employee-cost variance, sub-project margin, and unbilled-scope reports. None of it requires anyone on the engineering side to change their daily workflow.
This is the setup we use ourselves and the one we recommend before any agency installs a finance tool on top of Jira.
The mistake we see most often when agencies decide to "do project profitability properly" is that they commit to a tool change before they've made the structural changes Jira itself needs. They install Tempo Cost Tracker, or move to Productive, or commission a custom build, and discover three months in that the underlying problem wasn't the tool — it was that Jira had been set up for delivery without the few small conventions that make finance queryable.
This article is the structural fix. It's the setup we use on our own Jira instance, with notes on what to do at each layer, and the JQL hooks that follow naturally once the structure is in place. It is independent of whether you eventually layer Saldo or Tempo or anything else on top — it's the foundation those tools rely on.
The mental model: Jira is the deliverables system, finance is a layer
The mistake we should not make: trying to push finance concepts (margin, cost, P&L) into Jira's domain model. Jira's domain is what is being built. Finance's domain is what it costs and what it earns. These two domains share data (worklogs, assignees, estimates) but they are not the same.
The structural fix is to make Jira a clean source of the data that finance needs, without making Jira itself produce financial outputs. The financial outputs live in a layer above Jira that reads its data and joins it with cost-side data (hourly costs, role rates, overhead) that doesn't belong in Jira at all.
What Jira needs to provide, structurally:
- Worklogs that are attributable to a named person, on a known sub-project type, against an expected role.
- Sub-project boundaries that are queryable, so the same Jira project can carry Development, Support and Internal work without commingling them.
- Estimate and role data on each ticket that's required to land before work starts.
Three small structural conventions cover all three. Once they're in place, the rest is JQL.
Convention 1: sub-project structure through components
Most agencies model a client engagement as a Jira project (e.g., the project key CLIENT for the work for one client account). Inside that project, the agency runs Development work (the build), Support work (post-launch and ongoing fixes) and Internal work (account management, programme overhead). These three categories have different economics — different pricing models, different cost lines, different margin reports — but they often live as undifferentiated tickets in the same Jira project.
The fix is to use Jira's component field to mark sub-project membership. Three components per Jira project:
Development— fixed-scope build work, usually with an estimate and a hard end date.Support— post-launch and ongoing work, usually under a monthly retainer with a fixed budget of hours.Internal— account management, programme overhead, internal meetings, anything not directly billable to a deliverable.
Every ticket in the project must be tagged with exactly one of these three components. The validation lives in the project's create-issue workflow (Jira allows required-component validation natively).
Once components are clean, JQL queries can scope cleanly:
project = CLIENT AND component = "Development"
project = CLIENT AND component = "Support" AND created > startOfMonth()
project = CLIENT AND component = "Internal" AND timespent > 0
The reporting layer then computes margin separately for each sub-project type. Development gets a fixed-scope margin. Support gets a budget-vs-spend report. Internal gets pushed into overhead, where it belongs, rather than confusing the build's margin line.
The cost of installing this convention is about an hour per existing Jira project (re-tagging existing tickets, setting up the workflow validation). The benefit is that financial reporting can stop guessing about which work belongs to which engagement.
Convention 2: role tagging through one custom field
The second convention is a single custom field on each ticket: Estimated Role. It's an enum: Lead, Senior, Mid, Junior, QA, DevOps, Designer, PM, Director.
The field is required on tickets where the estimate exceeds a small threshold — we use 8 hours. Tickets under 8 hours default to Mid and don't enforce. This avoids interrupting engineers on small bug fixes while making sure substantial tickets carry the role information.
The field's purpose: it answers the question "who was this estimated for". Combined with the worklog data (who actually did it) and the cost data (what each person actually costs the agency), it allows the role-rate vs employee-cost variance calculation that's at the centre of project margin.
This field is the single most useful piece of structure you can add to Jira for finance reporting. It is also the easiest to install — one custom field, one validation rule, one default value. We've seen agencies install it across their entire Jira instance in an afternoon.
The reporting hook, in JQL form:
project = CLIENT AND component = "Development"
AND "Estimated Role" is not EMPTY
AND timespent > 0
For every result, the reporting layer compares worklogAuthor's actual role (from the cost system) against the ticket's Estimated Role. The variance flows directly into the role-drift query we wrote about in the four queries article.
Convention 3: estimate-and-role gates before "In Progress"
The third convention is a workflow gate. A ticket cannot transition from any "Open" or "Backlog" state to "In Progress" unless it has both:
- An estimate (in hours or story points, your choice)
- A role tag (the
Estimated Rolefield above)
This is enforced through Jira's transition validation. The cost is a small bit of friction at ticket startup — about 30 seconds per ticket. The benefit is that worklogs cannot accumulate against tickets that have no estimate or role context.
Without this gate, the role-drift query above is impossible to run reliably, because some percentage of tickets won't have the data. With the gate, the data is consistent: every ticket that has a worklog has an estimate and a role.
A common worry: this slows down emergency work. The fix: a "no-estimate" pre-tagged value (-1 or 0) that can be entered for emergency-fix tickets to bypass the gate, with the convention that any ticket using this value has to be re-estimated within 24 hours of starting. About 5% of our tickets use this in practice; almost all are bug-fix work that's correctly time-boxed.
Three more small conventions worth installing
Beyond the three core conventions, three smaller ones add useful structure for financial reporting without affecting daily workflow:
Epic = milestone, with budget-tag labels
Use Jira epics to model project milestones, not just feature groupings. Add a label of the form budget:NNNN on each epic, where NNNN is the hours budgeted for the milestone. This makes "are we on budget for milestone X" a single JQL plus a worklog sum, which finance can run weekly.
project = CLIENT AND issuetype = Epic AND labels = "budget:200"
Client-side stakeholder tagging on tickets that touch the client
A custom field Client Stakeholder (single-select) on tickets that involve client interaction. This makes "how much time are we spending with each client stakeholder" a queryable number, which is useful for noticing the client side has a power user who's quietly running up your hours.
Worklog descriptions follow a light template
Not enforced, but a culture norm: worklog descriptions follow a simple {verb} {object} template (fixed checkout flow bug, wrote integration tests for auth). This makes worklog reports readable to finance and useful in client status updates without requiring engineers to write essays.
What the data looks like, once installed
Once the three core conventions and three smaller ones are in place, the data Jira produces is enough to compute:
- Project margin per sub-project, separately for Development / Support / Internal, against role-rate revenue and employee-cost actuals.
- Role drift at the ticket level, computed by joining
worklogAuthoragainst the cost system's role mapping vs the ticket'sEstimated Role. - Sub-project drift (work in the wrong sub-project), via the component query and ticket age check.
- Milestone burn against epic budget labels.
- Client-side load by stakeholder.
All of these can be computed by any reporting layer that can read the Jira API. Saldo does this; Tempo does this; a custom build does this; a Google Sheet pulling the Jira REST API does this. The structural work in Jira is the part that doesn't depend on the choice of reporting layer.
What changes for engineers
We are careful about this because the temptation is always to push finance work onto engineers. The conventions above were chosen specifically to minimise engineering-side work:
- The
Estimated Rolefield is set by whoever creates the ticket (usually the project lead, not the engineer). Engineers see it but don't have to fill it in. - The component is also set by ticket creation, usually by the project lead. Engineers do not have to choose between Development / Support / Internal — the project lead has decided.
- The estimate-and-role gate happens at ticket creation, not at engineering pickup. Engineers see tickets that already meet the gate.
- Worklog templating is a soft convention, not a hard rule. It does not block work.
In practice, the engineering-side change from installing all of this is "one more field to glance at when picking up a ticket". Nothing else changes about how worklogs are recorded.
What this enables, business-side
Once the structure is in place, three things become possible business-side:
- Mid-flight margin reads. Finance can see, on Monday, which projects are tracking against their estimate at the role level. Conversations with project leads happen the same week.
- Sub-project pricing reviews. The Support component on each project produces a clean budget-vs-spend report, monthly. Retainers that have grown beyond their pricing get repriced before they've eaten the build's margin.
- Honest closing packs. The closing pack stops being a reconciliation exercise and becomes a roll-up of numbers that already exist on the dashboard. Finance close time drops from days to hours.
The setup work is a half day per project type plus an hour per existing Jira project to retag. The benefit, on our own data, was a 22-percentage-point reduction in mid-flight margin variance within the first quarter.
If you want all of this running on your real Jira instance, costed against named-individual hourly rates with overhead allocation already wired in — that's what Saldo is. The 15-minute demo runs against your real Jira data; we don't ask for card details up front, and we'll only follow up if it landed.
Going deeper: Saldo vs Tempo and Productive — where it lives, why it doesn’t replace Jira
Continue inside Saldo