Engineering cost attribution
One invoice, six meters, and no owner on any of them.
Short answer
GitHub engineering cost attribution is the work of splitting one platform invoice across the teams that generated it. Each meter needs a different key: Actions attributes by repository, Codespaces and storage by owner, Copilot by assigned user. Some of it can be attributed exactly, some only modeled — and the difference should always be visible.
The unit of attribution is a team, not a person
Per-developer cost is a tempting output and a bad one to publish. It invites comparisons that the underlying data cannot support, it makes people optimise their own visible number rather than the actual cost, and for most meters the per-person split is modeled anyway.
Attribute to teams. A team is the smallest unit that owns a budget, owns repositories, and can actually change what it spends. Per-developer figures belong in diagnostics — finding the seat nobody has opened — not on a leaderboard.
Six meters, six different attribution keys
What arrives as one GitHub line item is metered six ways, and each needs its own mapping:
- Copilot seats
- Attributed by assigned user → team. Clean, because assignment is explicit. This is the easiest meter to allocate correctly.
- AI credits
- Attributed by consuming user → team. The daily per-user signal makes this possible; the model breakdown behind it does not always survive.
- Actions minutes
- Attributed by repository → owning team, priced per job before aggregation because runner rates differ.
- Codespaces
- Attributed by the user who created the codespace. Idle and oversized instances are the recurring waste here.
- Storage & packages
- Attributed by repository or package owner. Grows monotonically unless someone sets retention, so it drifts toward whoever moved fastest two years ago.
- Self-hosted runners
- Not on the GitHub bill at all — it lands on your cloud invoice. Attributable only by modeling from your own rates, so it is always an estimate.
Attributed, modeled, and unallocated
A credible attribution model has three buckets and shows all three. Attributed is cost with a defensible key — a Copilot seat assigned to a named person on a named team. Modeled is cost derived by applying a rate or a split to observed usage; per-repository Copilot cost and self-hosted runner cost both live here, and both should carry an as-of timestamp. Unallocated is everything you could not honestly place: orphaned repositories, shared pipelines, the monorepo six teams use.
Presenting the modeled bucket as though it were attributed is the mistake that destroys trust in the whole model, usually in the first meeting where the number costs a team something. GitHub gives you two different kinds of number and they are not interchangeable. Usage signals — a seat's last-activity timestamp, minutes consumed, a daily per-user credit hint — arrive fast and describe behaviour. Billed amounts arrive from the billing API, lag the signals by a few days by design, and are what finance will actually be invoiced. Merge the two and you get a dashboard that is confidently wrong. Keep them apart, anchor the signal to the billed total, and show the gap.
From attribution to accountability
Attribution on its own changes nothing. It becomes useful at the moment a team sees its own number regularly, next to a number it can compare against — last month, or the median team.
The sequence that works: publish per-team cost as showback, let teams dispute it and fix the ownership map, then set budgets per team, then alert on variance rather than on totals. Chargeback, if you do it at all, comes after teams have stopped arguing about whether the numbers are right.
FAQ
Frequently asked questions
How do you attribute GitHub costs to teams?
Meter by meter, with a different key for each. Copilot seats attribute by assigned user, AI credits by consuming user, Actions by repository ownership, Codespaces by creator, storage by repository or package owner. Self-hosted runner cost is not on the GitHub bill at all and can only be modeled from your own infrastructure rates.
Can GitHub attribute Copilot cost per repository?
No. Copilot is billed per assigned user with no repository dimension, so any per-repo Copilot figure is modeled from observed activity rather than read from the invoice. It is a legitimate allocation input, but it is an estimate and should be labelled as one with the date it was computed.
Should we show cost per developer?
Only as a diagnostic, not as a published metric. Per-developer figures invite comparisons the underlying data cannot support, and for most meters the per-person split is modeled anyway. Attribute to teams — the smallest unit that owns a budget, owns repositories, and can actually change what it spends.
What do we do with costs we can't attribute?
Show them as an explicitly unallocated bucket rather than splitting them with a formula nobody agreed to. An unallocated share that shrinks quarter over quarter is a credible signal of a maturing model. A complete allocation resting on an invented split rule collapses the first time it costs a team real money.
Try it
See these numbers for your own org
Connect your organization read-only and FinOpsAid meters it nightly — seats, credits, minutes, Codespaces and storage, attributed to teams and reconciled against your billing API. Every modeled figure is labelled an estimate, with the date it was computed.