Skip to content
8 min read

How to measure maintenance cost by product area

Learn how to measure maintenance cost by product area by assigning support, incidents, cloud spend, and engineering time to each module.

How to measure maintenance cost by product area
Table of Contents

Most founders can tell you their total engineering payroll and last month's cloud bill. Far fewer can answer a more useful question: which part of the product is consuming the cash?

That gap creates bad decisions. A founder may keep funding a feature because revenue is attached to it, while its support load, incident history, and specialized infrastructure quietly consume the margin. Another team may cut the platform work that protects three profitable modules because the platform appears only as overhead. Neither decision survives contact with a proper maintenance ledger.

Measure maintenance cost by product area as an operating model, not as an accounting exercise. Assign the work and spend required to keep each module usable today. Then put the result beside revenue, active customers, strategic commitments, and growth. You will not get perfect precision. You can get numbers clear enough to stop subsidizing expensive product decisions by accident.

A product area needs an owner and a boundary

A product area is a customer-facing capability or an internal capability that materially affects a customer-facing capability. It is not automatically a microservice, repository, team, or page in the product. Those structures change too often and produce reports nobody can compare over time.

A useful module has four traits. Customers can recognize the outcome it provides. Someone can make tradeoffs for it. Work can be assigned to it most of the time. You can state which systems and dependencies it includes.

For a B2B SaaS product, a starting map might look like this:

  • Identity and access: sign-in, roles, invitations, audit access.
  • Core workflow: the primary work customers pay to perform.
  • Reporting and exports: dashboards, scheduled reports, data exports.
  • Integrations: connectors, webhooks, import and synchronization jobs.
  • Platform: shared runtime, observability, deployment, developer tooling, and common data services.

Keep the list small. Ten modules are easier to operate than forty. If a module has no owner, call that out. An unowned area is already a cost finding, because every ticket and outage in that area will pull in several people before anyone decides what to do.

Do not force a false boundary just because your code has one. A billing microservice may support every paid module, while a single monolith may contain three distinct customer outcomes. Cost follows the product decision you need to make, not the folder structure your engineers inherited.

Write the boundary in a short registry. Each module should have an ID, name, accountable owner, lifecycle status, primary customer metric, and included components. Add a rule for ambiguous work. For example: "If an issue affects a customer workflow and the underlying platform, assign the repair to the workflow module and record a platform dependency." This prevents platform costs from disappearing and prevents every hard issue from becoming "shared."

The FinOps Framework makes the same point in cloud language: allocation needs clear groupings, a tagging and hierarchy strategy, and a documented method for shared costs. The useful part is not the tag itself. The useful part is agreeing on what the tag means before the bill arrives.

Direct costs should be assigned before shared costs

Direct allocation is simple: a cost belongs to the module that caused it or consumed it. Shared allocation is a judgment: a cost benefits more than one module, so you distribute it using a stated rule.

Teams often start with shared costs because they look sophisticated. That is backward. First identify what can be assigned directly. A database dedicated to reporting belongs to reporting. A support ticket about an import failure belongs to integrations. An incident caused by a role-permission regression belongs to identity and access. An engineer repairing a scheduled export belongs to reporting.

Direct assignment produces an honest record quickly. It also reduces arguments. If you classify only 60 percent of spend directly in month one, that is still far better than charging every product area a random fraction of a single company total.

Use one allocation record for every cost event. This is a compact schema that works in a spreadsheet first and in a warehouse later:

period           2026-06
source_type      support_ticket
source_id        SUP-1842
module_id        integrations
cost_class        support
amount_usd        96.00
allocation_method direct
allocation_driver null
confidence        high
notes             CSV import fails on duplicate external IDs

The fields do more work than they appear to. source_type lets you trace a total back to raw evidence. cost_class keeps cloud spend separate from people time. allocation_method tells a reader whether they are looking at a fact or a rule. confidence lets the team expose uncertain assignments instead of hiding them inside a neat-looking report.

Do not use "miscellaneous" as a module. Put uncertain items in unassigned with an explanation. If unassigned costs remain high after two reporting cycles, fix the event data, the module registry, or both. A tidy misc bucket is how recurring waste earns tenure.

Support tickets reveal where the product taxes customers

Assign support tickets to the module that the customer experienced, not merely the component an engineer changed. A user who cannot complete a workflow because an authorization check fails experienced a workflow problem and an identity dependency. The primary module should be the workflow if that is where the customer noticed the failure. Record the dependency separately so the identity team does not escape the pattern.

This distinction matters because support cost is not a measure of code complexity. It measures the burden a part of the product puts on customers and on the people who serve them. A technically elegant integration can still be a costly business if every customer needs manual setup and repeated troubleshooting.

Capture at least these fields when a ticket closes:

  • Customer-facing module and any contributing module.
  • Request type: defect, how-to, configuration, data correction, outage follow-up, or access issue.
  • Minutes spent by support, engineering, and customer success.
  • Whether the issue had a known root cause or a workaround.
  • Whether the ticket created a product, documentation, or automation action.

Do not count ticket volume alone. Ten five-minute password resets are different from one integration failure that ties up support, an engineer, and an account manager for two days. Convert actual effort to cost using fully loaded hourly rates that finance and leadership agree on. If you cannot get an exact loaded rate at first, use a standard internal rate by role and apply it consistently.

A simple calculation is enough:

support_cost_for_module =
  support_hours × support_rate
  + engineering_hours × engineering_rate
  + customer_success_hours × customer_success_rate

The popular but wrong recommendation is to charge all support to a central customer team. It is popular because it keeps product reports clean and makes engineering feel removed from customer friction. It is wrong because it protects expensive modules from the consequences of their own design. Central support should own service quality. Product areas should carry the maintenance cost their behavior creates.

Watch for repeated "how do I" tickets. They can mean weak documentation, but they can also mean the product has made a basic task too difficult. Classifying them as documentation work forever becomes an excuse to avoid a design fix.

Incident accounting must include repair and prevention

Charge incident response to the module whose failure harmed users. Charge prevention work to the module or dependency where the corrective change belongs. Those are often different, and collapsing them hides the repair pattern.

Imagine an outage in the reporting module. A reporting query exhausts a shared database connection pool. The immediate incident cost belongs to reporting because reporting triggered the customer impact. The permanent fix may require a platform connection pool limit and a reporting query redesign. Record both prevention tasks with their own module assignments, and link them to the incident ID.

Atlassian's incident handbook is worth reading for its ownership rule, not because you need its exact process. It assigns the delivery team for the faulty service responsibility for the postmortem and tracks remediation work in the responsible team's backlog. That is the behavior to copy. A postmortem without assigned follow-up work is a document. It does not reduce maintenance cost.

Use an incident ledger with four buckets:

  1. Detection and triage time.
  2. Mitigation and restoration time.
  3. Customer communication and manual recovery.
  4. Follow-up engineering that prevents a repeat.

Keep commercial impact separate unless you can calculate it cleanly. Customer credits, churn risk, and lost expansion matter, but they are not the same as maintenance spend. If you mix estimates of lost revenue into every incident total, the table becomes a negotiation rather than a record. Put those estimates in a separate impact column with the method used.

Google's SRE guidance defines toil as repetitive, predictable operational work required to maintain a service. That definition gives founders a practical cut line. An engineer investigating a novel architectural failure may create durable knowledge. An engineer restarting the same stuck job every morning is paying a recurring maintenance bill. The first may deserve a planned reliability investment. The second should trigger automation, product change, or retirement.

Track repeat incidents by module as both count and repair cost. One severe event can justify a redesign. A series of small events often exposes the more dangerous pattern because the team normalizes it and carries the work quietly.

Engineering time needs categories, not surveillance

Find payroll trapped in rework
In five business days, find $50,000+ in annual savings or the audit is free.

Engineering time is usually the largest missing line item. Cloud bills are visible because vendors invoice you. Maintenance labor hides inside pull requests, chat threads, incident calls, rushed roadmap changes, and the senior engineer who knows where every fragile job lives.

Do not fix this by asking engineers to fill out a seven-category timesheet each day. That produces fiction after the first busy week. Use the work trail you already have, then ask for small corrections at a predictable cadence.

Create a maintenance work type in your issue tracker and require a module field. Let engineers use four maintenance reasons: support defect, operational work, reliability improvement, and dependency upkeep. New capability work stays out of this ledger unless it replaces an existing maintenance burden, in which case record that expected reduction in the proposal.

For work that arrives without a ticket, use a weekly allocation note. Each engineer records rough hours by module only when the time is material. A reasonable threshold is work that took more than an hour or displaced planned work. An experienced CTO does not need a stopwatch to know when a week disappeared into a fragile integration, release recovery, or customer data correction.

The calculation should separate time from money:

maintenance_hours[module, month] =
  linked_issue_hours
  + incident_hours
  + approved_weekly_unplanned_hours

engineering_maintenance_cost[module, month] =
  maintenance_hours[module, month] × blended_engineering_rate

Use a blended rate for the initial view. It avoids turning the report into an argument about which individual costs more. If a module needs specialist work, show the specialist hours as a note. Only introduce role-specific rates when that difference changes a decision, such as deciding whether to keep a legacy technology that only one contractor can repair.

Do not count all refactoring as maintenance. Refactoring can improve future economics, but its immediate purpose may be feature delivery, a migration, or developer convenience. Classify it based on the reason it was approved. If the reason is "we keep paying for failures in this module," it belongs in maintenance. If the reason is "we prefer this framework," it does not.

Cloud allocation needs resource tags and defensible drivers

A monthly cloud invoice is too blunt to tell you what product areas cost. Assign resources directly with tags, labels, account structure, project structure, or resource naming. Then allocate the shared parts with a driver that matches usage.

Start with direct cloud spend: dedicated databases, queues, storage buckets, worker pools, external APIs, and environments. Tag each resource with module_id, environment, and owner. Validate the tags automatically where your cloud provider permits it. A cost center tag is not enough if it identifies only the company or engineering department.

Shared resources need a written driver. Use the least complicated driver that is causally connected to consumption.

  • Shared API gateway cost can follow request count by module.
  • Shared object storage can follow bytes stored and bytes retrieved.
  • A shared data warehouse can follow query compute or scanned data.
  • An observability bill can follow emitted logs, traces, or monitored hosts.
  • A shared Kubernetes cluster can follow requested CPU and memory, not application revenue.

Do not allocate shared cost by revenue unless the purpose is an internal chargeback exercise and you say so plainly. Revenue is a business outcome, not a technical consumption driver. A high-revenue module may be cheap to operate, while a low-revenue module may generate most of the requests, data retention, and alert traffic.

FinOps calls this cost-weighted allocation when you split an amount using a weighted metric. The name is less important than the audit trail. Every shared amount needs a source total, a driver total, each module's driver value, and the formula. A founder should be able to ask why integrations received 38 percent of a shared bill and get an answer in minutes.

select
  module_id,
  round(shared_cost_usd * module_requests / total_requests, 2) as allocated_usd
from monthly_module_usage
where period = date '2026-06';

That query has an obvious failure mode: a module with missing request telemetry gets charged zero. Build a control that reports missing drivers before the cost report closes. If the driver is missing, use a documented temporary proxy and mark the allocation confidence as medium or low. Zero is not a neutral default. It quietly rewards broken measurement.

Shared platform work should not disappear into overhead

End allocation debates with decisions
A fractional CTO connects maintenance cost, engineering capacity, and product tradeoffs.

Platform cost is real product cost, but it is not always fair to force it into a single feature module. Founders need to see it in two forms: the cost of the shared platform itself and each module's share of that cost.

Keep a platform module for work that benefits the product broadly: build and deployment systems, monitoring, backups, security controls, shared runtime, common libraries, and baseline infrastructure. Directly assign work that exists because a specific module demands it. If the integrations area needs a specialized queue, charge that queue and the work maintaining it to integrations even if the platform team built it.

Then distribute genuinely shared platform cost using a small number of drivers. Requests may work for runtime. Active modules may work for a baseline deployment pipeline. Engineering headcount may work for developer tooling. There is no universal driver, which is why copying a generic finance template often produces nonsense.

Avoid two failures. The first is treating platform as free, which makes feature modules look artificially profitable. The second is charging every shared cost equally, which punishes small, quiet modules and hides the areas creating the load.

Report both numbers. For example, show "reporting direct maintenance cost" and "reporting fully allocated maintenance cost." The first helps an owner fix local problems. The second helps a founder decide whether the product area pays for the capacity it consumes.

The maintenance report must support a product decision

Start with a five-day audit
For $5,000 fixed, the Team & AI Audit identifies $50,000+ yearly savings or is free.

A maintenance report is useful when it changes a decision about repair, pricing, investment, simplification, or retirement. A colorful dashboard that ends in "interesting" wastes everyone's time.

Create one monthly table with a row per module and columns for direct support cost, incident cost, engineering maintenance cost, direct cloud cost, allocated shared cost, total maintenance cost, active customers, module revenue where meaningful, and confidence. Add two ratios: maintenance cost per active customer and maintenance cost as a share of module revenue.

Use the ratios carefully. A module with a small customer base may look terrible while it is still early or strategically required. A large module may look healthy because revenue is high while its absolute maintenance cost is absorbing your best engineers. Show the dollar amount and the ratio together.

Here is the review sequence I use with founders:

  1. Sort modules by total maintenance cost. Ask what creates the top three costs.
  2. Sort by maintenance cost per active customer. Check whether a niche module needs a price change, redesign, or sunset plan.
  3. Sort by unplanned engineering hours. This finds capacity loss before finance sees it.
  4. Read every low-confidence allocation and every unassigned cost. Those items often reveal the missing ownership decision.
  5. Choose one action with an owner, expected cost reduction, and review date.

A worked example makes the point. Suppose integrations generates $18,000 of monthly revenue. It carries $2,400 in support labor, $1,600 in incident repair and prevention, $3,000 in engineering maintenance, $1,200 in direct cloud use, and $1,800 in shared allocation. Its monthly maintenance total is $10,000 before sales, general administration, or new feature work.

That does not mean "kill integrations." It means stop discussing integrations as a single line in the roadmap. You now have decisions to make. Raise the price for the customers using expensive connectors. Remove a connector with one demanding customer. Replace manual onboarding with productized setup. Put a hard limit on data synchronization. Fund a redesign that removes recurring recovery work. Or accept the cost because the module prevents churn in the core workflow. The report gives those choices a cost.

Accuracy improves through review, not a giant rollout

Do not delay the first report until every resource is tagged and every engineer has perfect data. Build a baseline from the last full month. Assign the obvious items. Mark uncertain records. Publish the gaps beside the totals.

For the first two months, hold a 30-minute allocation review with engineering, support, and finance. The point is not to debate pennies. The point is to correct category errors, settle ownership rules, and identify missing data sources. After that, the review becomes shorter because the rules are already written.

Set three controls that prevent the ledger from decaying:

  • New modules cannot ship without an owner, ID, and cost allocation plan.
  • New cloud resources need a module tag or an explicit shared classification.
  • Incident and support templates require a primary module before closure.

AI can reduce the clerical part of the process. It can suggest a module from ticket text, match an incident to services, summarize unplanned work from issue history, and flag cost records without tags. Keep a human responsible for the final assignment. Models are good at sorting ambiguous evidence; they are not accountable for deciding which product investment you should stop funding.

If your engineering organization cannot produce a first allocation table in a month, the problem is rarely reporting software. It is usually that nobody has agreed what the product areas are, who owns them, or where work lands. A Team & AI Audit can expose those ownership gaps alongside the engineering spend they create, but the operating discipline still has to live inside the company.

Start with the month that just closed. Give every ticket, incident, cloud charge, and material maintenance task a home. The report will annoy someone. Good. It means costs that were previously hidden inside "engineering" now belong to a decision someone must make.

Frequently Asked Questions

What counts as a product area for maintenance accounting?

Start with the areas that own a customer outcome, have a clear team or accountable lead, and can receive work independently. A module does not need its own repository or deployment to be measurable. It does need a stable name, an owner, and rules for assigning work.

Can we allocate costs when tickets involve more than one module?

Yes, if you record uncertainty instead of pretending it does not exist. Give every record a primary module, an allocation method, and a confidence field. Review low-confidence assignments monthly, because repeated ambiguity usually exposes a boundary problem worth fixing.

How should shared cloud costs be split across modules?

Avoid a simple equal split unless the modules truly consume equal resources. Use a driver that reflects consumption, such as requests, active tenants, data volume, support contacts, or engineering effort. Document the rule and keep it stable long enough to compare periods.

Do engineers need to track every hour they spend?

Do not demand minute-by-minute timesheets from engineers. Capture maintenance work at the ticket, incident, pull request, or weekly allocation level, then use a small number of categories. The aim is a decision-grade view, not payroll-grade surveillance.

What support work belongs in maintenance cost?

Count the support work required to keep the module useful for current customers: investigation, reproduction, customer fixes, configuration help, and recurring defect repair. Do not bury new feature delivery in support simply because a customer asked for it.

Should incident costs include lost revenue and customer credits?

Charge the immediate response and repair to the affected module, then record prevention work against the module or dependency that caused the failure. Keep customer credits and lost revenue separate unless you can calculate them reliably. Otherwise they turn a useful operating metric into a debate about assumptions.

What if a low-margin module is strategically important?

A narrow margin is a warning, not an automatic shutdown order. Compare the cost with retention, expansion, contractual commitments, strategic dependencies, and the cost of replacement. The number should force an explicit decision about repair, redesign, price, or retirement.

How often should founders review module maintenance costs?

Monthly reporting is frequent enough for founders to see a trend without turning the exercise into reporting theater. Review the raw assignments every week during the first two months, because the model will need corrections before the numbers become trusted.

Are DORA metrics enough to measure maintenance cost?

No. DORA measures delivery and operational performance, while maintenance accounting measures the cash and engineering capacity consumed by a product area. Read them together: a module with fast delivery but rising incident repair cost needs a different conversation than one with slow delivery and stable operating cost.

What is the fastest way to start measuring maintenance cost?

Begin with your last completed month, not a giant data warehouse project. Assign every support ticket, incident, cloud line item, and maintenance work item to a module, publish the first table, and mark unknown allocations openly. The first report will be imperfect, but it will reveal where your product taxonomy and ownership are weak.

Related Posts