Cost Optimization and Technology Financial Management Questions
Analyzing and reducing technology and enterprise costs and applying financial discipline to technology spend. Covers total cost of ownership modelling (cost lines, discounting and NPV, sensitivity analysis, depreciation, sunk and stranded costs, decommissioning), fixed and variable cost structure and unit cost, spend analysis and cost leakage, make-versus-buy and capex-versus-opex decisions, and the ROI and payback case for technology investments. Includes cost allocation, showback and chargeback of shared platforms, software licensing and contract economics (perpetual, subscription, per-seat, consumption, renewals, escalators and commitments), and the supplier terms that change what a purchase really costs. Emphasizes designing and governing cost reduction programs, prioritizing initiatives, verifying that savings are real and not double-counted, protecting quality and reliability while cutting, and surfacing hidden or downstream costs. Cloud-bill engineering, sourcing and supplier negotiation, and budgeting-process mechanics are covered elsewhere.
Write a function that takes, for two options, the upfront capital, annual run cost, an annual cost growth rate, a discount rate and a horizon in years, and returns each option's discounted TCO. Extend it to run several scenarios and report which option is cheaper in each.
Sample Answer
Direct answer
Discounted TCO (total cost of ownership in today's money) is the upfront capital plus each year's run cost, grown at the cost growth rate and divided by (1 + discount rate) raised to the year. I write one pure function (it uses only its inputs and returns a result, with no hidden state or side effects) that prices a single option, and a second function that runs it for both options across named scenarios and reports the cheaper option in each, so the scenario table is the product and the first function is the engine.
Approach
- Convention (stated so a reader can reproduce it): capital is paid at time 0; the run cost in year t is annual_run x (1 + growth)^(t - 1), paid at the end of year t and discounted by (1 + rate)^t.
- A scenario is a (discount rate, horizon in years) pair. The scenario function loops over scenarios, calls the TCO function for both options and compares.
Code
def discounted_tco(capex, annual_run, growth, rate, years):
"""Upfront cost at time 0 plus run costs for years 1..years.
Run cost in year t is annual_run * (1 + growth) ** (t - 1),
paid at the END of year t, discounted by (1 + rate) ** t."""
if years < 0 or rate <= -1:
raise ValueError("years must be >= 0 and rate > -1")
total = capex
for t in range(1, years + 1):
total += annual_run * (1 + growth) ** (t - 1) / (1 + rate) ** t
return total
def compare_scenarios(option_a, option_b, scenarios):
"""option_* = (capex, annual_run, growth). scenarios = {name: (rate, years)}.
Returns a list of (scenario, tco_a, tco_b, cheaper)."""
rows = []
for name, (rate, years) in scenarios.items():
a = discounted_tco(*option_a, rate, years)
b = discounted_tco(*option_b, rate, years)
cheaper = "A" if a < b else "B" if b < a else "tie"
rows.append((name, a, b, cheaper))
return rows
if __name__ == "__main__":
on_prem = (500_000, 120_000, 0.05) # capex, year-1 run cost, run-cost growth
cloud = (0, 260_000, 0.02)
scenarios = {
"base (8%, 5y)": (0.08, 5),
"high rate (14%, 5y)": (0.14, 5),
"long horizon (8%, 10y)": (0.08, 10),
"short horizon (8%, 3y)": (0.08, 3),
"zero rate (0%, 5y)": (0.0, 5),
}
print(f"{'scenario':<24}{'A on-prem':>12}{'B cloud':>12} cheaper")
for name, a, b, cheaper in compare_scenarios(on_prem, cloud, scenarios):
print(f"{name:<24}{a:>12,.0f}{b:>12,.0f} {cheaper}")
print(discounted_tco(100, 0, 0, 0.1, 5))
print(discounted_tco(0, 100, 0, 0.0, 3))
Output
scenario A on-prem B cloud cheaper
base (8%, 5y) 1,025,537 1,077,185 A
high rate (14%, 5y) 949,519 924,246 B
long horizon (8%, 10y) 1,482,026 1,886,602 A
short horizon (8%, 3y) 824,160 682,842 B
zero rate (0%, 5y) 1,163,076 1,353,050 A
100.0
300.0
The last two lines are hand-checkable: 100 of capital with no run cost is 100.0 at any rate, and 100 a year for 3 years at a 0% rate is 300.0.
One real case by hand, A (on-premises) in the base scenario at 8% over 5 years. Year t run cost is 120,000 x 1.05^(t - 1), discounted by 1.08^t. Year 1: 120,000 / 1.08 = 111,111. Year 2: 126,000 / 1.08^2 = 108,025. Year 3: 132,300 / 1.08^3 = 105,024. Year 4: 138,915 / 1.08^4 = 102,107. Year 5: 145,861 / 1.08^5 = 99,270. These sum to 525,537; adding the 500,000 of capital paid at time 0 (not discounted) gives 1,025,537, the first row of the output.
Reading the result
Option A (on-premises: $500,000 upfront, $120,000 a year growing 5%) wins at the base case, over a long horizon and at a 0% rate, because its capital is spread over many years of cheaper running. Option B (cloud: no capital, $260,000 a year growing 2%) wins when the discount rate is high (future costs shrink, so avoiding the upfront payment matters more: going from 8% to 14% cuts B's total from 1,077,185 to 924,246, a drop of 152,939, but A's only from 1,025,537 to 949,519, a drop of 76,018, because A's 500,000 is paid at time 0 and is never discounted) and when the horizon is short (A's capital is not amortised). The conclusion therefore flips on two assumptions, rate and horizon, which is what the scenario function exists to expose.
Key points
- The function is pure (no globals, no input or output), so it is testable; the two printed sanity checks are the start of a test suite.
- Growth is passed per option: on-premises costs often escalate (maintenance, staffing) while cloud prices may be flat or falling.
- A tie is reported as "tie"; with floating-point inputs compare with a tolerance (Python's math.isclose, which treats two decimals as equal when they differ by a tiny amount) if exact equality matters.
Complexity
Each TCO call is O(years) time and O(1) memory (work grows in step with the number of years; memory stays constant); the scenario function is O(S x years) for S scenarios (work grows with scenarios times years), since it calls the TCO function twice per scenario.
Edge cases
- years = 0 returns the capital alone.
- rate = 0 is valid (an undiscounted sum); rate <= -1 would divide by zero or flip signs, so the function raises ValueError. Negative growth (falling run costs) is allowed.
- Not modelled: tax, resale value, mid-year payment timing, staffing curves, uncertainty. For uncertainty, replace the fixed scenarios with sampled inputs (random draws from assumed ranges) and report the share of runs in which each option is cheaper.
You have launched dozens of cost initiatives across several teams. How do you track forecast against realised savings and make sure they actually reach the bottom line?
Sample Answer
Direct answer
Treat savings like any benefit that must be proven: keep one initiative register (owner, baseline, forecast by month, go-live date), compute realised savings from the spend ledger (the finance system's record of actual spend) as baseline minus actual, and publish a monthly variance report of forecast vs realised per initiative. Savings only "reach the bottom line" when the owner's budget is reduced by the realised amount, so the tracking process ends in a budget change, not a slide.
Data flow
flowchart LR
A[Initiative register: owner, baseline, forecast] --> C[Realised = baseline minus actual]
B[Spend ledger / billing export by tag] --> C
C --> D[Variance report: forecast vs realised]
D --> E[Monthly review and dispute path]
E --> F[Budget and reforecast updated]
Read the diagram left to right: the register supplies each initiative's baseline and forecast, the ledger supplies actual spend, the difference is the realised saving, and the variance report compares it with the forecast.
Roles and mechanics
- Owner (the budget holder) commits to the forecast. Analyst (business intelligence or finance) computes realised savings from the ledger, not from the owner's own spreadsheet. Program office runs the review.
- Baseline must be fixed at registration, adjusted for volume (cost per unit of usage) or the saving will be confounded (mixed up with another cause) by growth. Example: the cluster's baseline is $50,000 a month at registration, but by May it serves 20% more queries. If its cost scales with queries, the volume-adjusted baseline is $50,000 x 1.2 = $60,000, and May's realised saving against actual spend of $35,000 is $25,000, not the $15,000 a flat baseline shows. Without the adjustment, growth would hide $10,000 of real saving.
- Tagging: every initiative needs a tag (a label on each cloud resource or ledger line naming the initiative, which also appears in the billing export, the provider's itemised bill) or a cost-centre mapping (a link to the accounting unit that pays) in the ledger so its spend is attributable.
- Variance report: forecast, realised, variance and a reason code (a short label chosen from a fixed list: late go-live, adoption lower than planned, baseline wrong, offsetting cost). Reason codes make the pattern visible across dozens of initiatives.
- Contested amounts: a team that disputes a figure brings evidence to the review; the analyst's ledger-based figure stands unless the baseline is shown to be wrong. Escalate unresolved cases to the finance arbiter.
- Tooling: start with a spreadsheet or a warehouse table plus a dashboard; adopt a dedicated tracking tool only when the number of initiatives makes manual reconciliation the bottleneck.
Worked example (runnable)
CREATE TABLE initiative (id INTEGER PRIMARY KEY, name TEXT, owner TEXT, baseline_monthly REAL, forecast_monthly_saving REAL, go_live TEXT);
CREATE TABLE ledger (initiative_id INTEGER, month TEXT, actual_spend REAL);
INSERT INTO initiative VALUES
(1,'Rightsize analytics cluster','Platform',50000,15000,'2026-03'),
(2,'Consolidate monitoring tools','SRE',30000,9000,'2026-03');
INSERT INTO ledger VALUES
(1,'2026-03',44000),(1,'2026-04',36000),(1,'2026-05',35000),
(2,'2026-03',30000),(2,'2026-04',26000),(2,'2026-05',21500);
SELECT i.name,
SUM(i.baseline_monthly - l.actual_spend) AS realised,
SUM(i.forecast_monthly_saving) AS forecast,
SUM(i.baseline_monthly - l.actual_spend) - SUM(i.forecast_monthly_saving) AS variance
FROM initiative i JOIN ledger l ON l.initiative_id = i.id AND l.month >= i.go_live
GROUP BY i.id ORDER BY i.id;
Run with sqlite3 -header -column :memory: < file.sql. The query joins each initiative to its ledger rows from the go-live month onward (months are text like 2026-03, which sort correctly as text), computes baseline minus actual for every such row, and SUM adds those rows up per initiative; the forecast column sums the monthly forecast once per ledger row, so three months give 3 x $15,000 = $45,000. This example uses a flat baseline, so it does not make the volume adjustment described above. Output:
name realised forecast variance
---------------------------- -------- -------- --------
Rightsize analytics cluster 35000.0 45000.0 -10000.0
Consolidate monitoring tools 12500.0 27000.0 -14500.0
Over March to May, the two initiatives forecast $72,000 and realised $47,500, a shortfall of $24,500. Both are ramping (the cluster saved $6,000, $14,000 and $15,000 in successive months; the monitoring consolidation saved $0, $4,000 and $8,500), which is the typical pattern: adoption lags the plan. The report's job is to distinguish a ramp (the monthly figure is still rising toward the forecast) from a shortfall (it has plateaued below the forecast), because the actions differ.
Making it reach the bottom line
When an initiative's run-rate is confirmed, the owner's budget is cut by that amount in the next reforecast. If the budget is not reduced, the saving has been absorbed by other spend and is not a saving in any sense finance can use. Report realised savings by category (run-rate, one-time, avoidance) and keep the cumulative forecast-to-realised ratio visible by team.
Pitfalls
Using forecast as if it were realised; baselines that ignore volume growth; savings netted against spending in a different account so nobody can reconcile; no reason codes, so every variance is a debate.
You oversee a business unit with five SaaS subscriptions. Subscription A costs $5,000 a month with 10 active users at about 20% utilisation, B costs $2,000 with 50 users at 90%, C costs $150 with 1 user at 10%, D costs $100 with no users, and E costs $50 with 20 users at 25%. Which actions would you take first, and what monthly and annualised savings would they deliver?
Sample Answer
Direct answer
First cancel D ($100 a month, no users) and C ($150, one user at 10% utilisation), and right-size A (match the number of paid seats to the number actually needed), which is the big one (shelfware, meaning paid-for software that sits unused, is the target throughout). Taking A as 10 active users on a roughly 50-seat licence ($5,000 a month, about $100 per seat) and cutting it to 12 seats saves $3,800 a month. The three actions together save $4,050 a month, $48,600 a year, which is about 55% of the $7,300 a month the unit spends (annualised, meaning multiplied by 12 to show a full year). B ($2,000, 90% used) stays, and E ($50) is not worth special effort.
The reading of "utilisation" for every line
The question gives a count of users and a utilisation percentage, but no seat total. I read every line the same way: the user count is active users, and utilisation is active users divided by licensed seats (seats are the paid licences). For A, 10 active users at about 20% only makes sense if about 50 seats are licensed (10 / 50 = 20%). That is the assumption the answer rests on: $5,000 / 50 seats = $100 per seat. The same reading gives about 56 seats for B (50 / 0.90 = 55.6, about $36 a seat), about 10 seats for C (1 / 0.10, $15 a seat) and about 80 seats for E (20 / 0.25, $0.63 a seat). If A is actually a flat-price plan, with no per-seat pricing, the lever is different (negotiate or move to a lower tier) and the saving would have to be quoted by the vendor instead of calculated.
The table
| Subscription | Monthly cost | Usage | Action | Monthly saving | Annualised |
|---|---|---|---|---|---|
| A | $5,000 | 10 active, about 20% of ~50 seats | Cut to 12 seats (10 active plus a small buffer): $1,200 a month | $3,800 | $45,600 |
| C | $150 | 1 active user, 10% of ~10 seats | Cancel, move the user to a tool they already have | $150 | $1,800 |
| D | $100 | no users | Cancel | $100 | $1,200 |
| B | $2,000 | 50 active, 90% of ~56 seats | Keep, check headroom | $0 | $0 |
| E | $50 | 20 active, 25% of ~80 seats | Trim to 24 seats at renewal: $15 a month | $35 | $420 |
A, D, C, E = 5000, 100, 150, 50
seat_a = A / 50
keep_a = 12
save_a = A - seat_a * keep_a
total = save_a + C + D
print("A new cost:", seat_a * keep_a, "A saving:", save_a)
print("monthly:", total, "annual:", total * 12)
print("A annual:", save_a * 12, "C annual:", C * 12, "D annual:", D * 12)
seat_e = E / 80
print("E per seat:", seat_e, "keep 24 seats, saving:", E - 24 * seat_e)
A new cost: 1200.0 A saving: 3800.0
monthly: 4050.0 annual: 48600.0
A annual: 45600.0 C annual: 1800 D annual: 1200
E per seat: 0.625 keep 24 seats, saving: 35.0
A alone is 94% of the saving ($3,800 of $4,050), so it gets the effort. D and C are quick and low-risk, and do them in the same week. E trimmed to 24 seats (20 active plus a small buffer) saves $35 a month ($420 a year), which I would fold into the renewal rather than chase; it is left out of the $4,050 total.
Order of action and why
- A: right-size. Largest saving by far. Confirm who the 10 active users are, check the contract (minimum seats, the fewest seats it lets you pay for; annual commitment; notice period, how far ahead you must tell the vendor of a cut) and ask the vendor for seat reduction at the next renewal (the date the contract rolls over for another term) if mid-term cuts are not allowed.
- D: cancel. Nobody uses it. First check that nothing depends on it (integrations, data retention, single sign-on).
- C: cancel or consolidate. One user at 10%. Ask them what they use it for; a feature in an existing tool usually covers it.
- B: leave alone. It is 90% used and that is healthy. Check headroom: if the team is growing, you may need seats rather than a cut.
- E: low priority. Handle at renewal.
Caveats on the savings
- Savings arrive when contracts allow. An annual commitment means the saving starts at renewal, so the first-year cash saving can be lower than $48,600.
- Annualising $4,050 x 12 assumes the removals are permanent. Add a quarterly seat review so shelfware does not creep back.
- Utilisation can mislead. A 20%-used tool might be used heavily by a small critical team, so ask before cutting.
What is sensitivity analysis in a TCO model, and how would you run one? Walk through a concrete example.
Sample Answer
Direct answer
Sensitivity analysis in a TCO (total cost of ownership) model means changing one input at a time across a plausible range and recording how far the answer moves, so you learn which assumptions the conclusion actually depends on. You run it by choosing the output (here, which option is cheaper and by how much), setting a plausible low and high for each input, recomputing the output at each end, ranking inputs by the size of the swing, and then spending your effort firming up the top few. For example, one supplier is cheaper per unit but fails more often, so the cheaper unit price may or may not survive the cost of the failures. For a critical component, the two variables to test first are the defect or failure rate of the cheaper supplier and the cost each defect causes, because both are uncertain and both multiply every unit.
The method
- Fix a base case and the output metric. Here: the three-year cost advantage of supplier A over supplier B, where a positive number means A is cheaper.
- List the inputs and give each a plausible range from evidence (quotes, history, pilot data), not a blanket plus or minus 10%. A firm quoted price has a narrow range; a new supplier's defect rate does not.
- Vary one input at a time, others at base, and record the output at the low and high end.
- Sort by swing (a tornado chart is this table drawn as bars, longest at the top).
- Find break-even values: the input value at which the decision flips, so the two options cost exactly the same.
- Repeat for the top inputs together (two-way table), because inputs interact.
Worked example (illustrative inputs)
Two suppliers of a critical component, 50,000 units a year for three years. A sells at $42 with a 2.0% defect rate; B sells at $46 with a 0.5% defect rate. Each defect costs $200 (rework, expedited replacement, warranty). Expected cost per unit: A is $42 plus 2.0% of $200 ($4), so $46; B is $46 plus 0.5% of $200 ($1), so $47. A is cheaper by $1 a unit, which is $150,000 over three years.
base = dict(volume=50_000, price_a=42.0, price_b=46.0, defect_a=0.020, defect_b=0.005, defect_cost=200.0)
YEARS = 3
def advantage_of_a(p):
"""3-year cost advantage of supplier A over supplier B, in $ (positive means A is cheaper)."""
per_unit_a = p["price_a"] + p["defect_a"] * p["defect_cost"]
per_unit_b = p["price_b"] + p["defect_b"] * p["defect_cost"]
return (per_unit_b - per_unit_a) * p["volume"] * YEARS
ranges = { # (low, high) plausible range for each input
"price_a": (41.0, 43.0),
"price_b": (45.0, 47.0),
"defect_a": (0.010, 0.040),
"defect_b": (0.003, 0.010),
"defect_cost": (100.0, 400.0),
"volume": (30_000, 70_000),
}
print(f"base advantage of A: {advantage_of_a(base):,.0f}")
rows = []
for name, (lo, hi) in ranges.items():
at_lo = advantage_of_a(dict(base, **{name: lo}))
at_hi = advantage_of_a(dict(base, **{name: hi}))
rows.append((abs(at_hi - at_lo), name, lo, hi, at_lo, at_hi))
for swing, name, lo, hi, a, b in sorted(rows, reverse=True):
print(f"{name:<12} {lo:>9} to {hi:<9} advantage {a:>10,.0f} to {b:>10,.0f} swing {swing:>9,.0f}")
# Break-even defect rate for A, holding everything else at base
d = (base["price_b"] + base["defect_b"] * base["defect_cost"] - base["price_a"]) / base["defect_cost"]
print(f"A stops being cheaper when its defect rate passes {d:.2%}")
base advantage of A: 150,000
defect_a 0.01 to 0.04 advantage 450,000 to -450,000 swing 900,000
defect_cost 100.0 to 400.0 advantage 375,000 to -300,000 swing 675,000
price_b 45.0 to 47.0 advantage 0 to 300,000 swing 300,000
price_a 41.0 to 43.0 advantage 300,000 to 0 swing 300,000
defect_b 0.003 to 0.01 advantage 90,000 to 300,000 swing 210,000
volume 30000 to 70000 advantage 90,000 to 210,000 swing 120,000
A stops being cheaper when its defect rate passes 2.50%
Reading it:
- The top two are A's defect rate (a swing of $900,000) and the cost of a defect ($675,000). Both are large and both are poorly known for a supplier with no history. The quoted prices swing $300,000 each, but a signed quote can be firmed up, so they rank lower in effort.
- Volume swings the least ($120,000). The per-unit gap is only $1, so volume scales a small difference. Volume would rank first if you were asking about total spend instead of the difference between options.
- Break-even. A stops being cheaper when its defect rate passes 2.50%, only half a percentage point above the base of 2.0%. That is a thin cushion, and the decision should wait for evidence: a pilot lot or a quality audit.
Choosing which two to test first
Rank variables by uncertainty times leverage: how wrong could we be, and how much does the output move per unit of error. A high-leverage variable with a firm value (a contracted price) does not need testing; a high-leverage uncertain one (defect rate, lead-time reliability for a critical part) does.
Related tools and pitfalls
- A scenario analysis moves several inputs together into coherent stories (supplier fails audit; demand doubles). Sensitivity moves them singly. Use both.
- One-at-a-time hides interactions: a high defect rate and a high defect cost together cost more than the two swings added up, which is why the top two get a two-way table. Here is that table, A's three-year advantage in $ for A's defect rate against the cost of a defect:
| A's defect rate | $100 a defect | $200 a defect (base) | $400 a defect |
|---|---|---|---|
| 1.0% | 525,000 | 450,000 | 300,000 |
| 2.0% (base) | 375,000 | 150,000 | -300,000 |
| 4.0% | 75,000 | -450,000 | -1,500,000 |
Moving only the defect rate to 4.0% gives -$450,000 and moving only the defect cost to $400 gives -$300,000; moving both gives -$1,500,000, far worse than either alone, because the two multiply. Each cell is (4 + defect cost x (0.5% minus A's defect rate)) x 150,000.
- Ranges taken from a single optimistic source make the tornado look short and the conclusion look safe.
- Report the break-even, not only the ranking, because a decision-maker can judge whether 2.5% is plausible.
You find a discretionary spend category of $3.2M spread over 450 line items. How would you work out which few items drive most of the spend, surface savings and turn that into a recommendation the budget owners will accept?
Sample Answer
Direct answer
Aggregate the 450 line items to the level people can act on (vendor, category, budget owner), rank them by spend and plot cumulative share (a Pareto or ABC analysis, where the few big items form the "A" group), then apply a different lever to each group: negotiate or consolidate (move spend from several vendors to fewer) the big ones, apply simple policy to the tail, and stop what has no owner. Turn it into a recommendation by giving each budget owner a short list of options sized in dollars with a confidence level, agreed with them in advance rather than announced.
Steps
- Clean and aggregate. Group duplicates and vendor name variants, then roll the 450 lines up by vendor and category. Line items hide concentration: ten lines from one vendor can be the largest relationship.
- Rank and cumulate. Sort descending, compute cumulative share. See where the curve bends.
- Segment by nature. The whole $3.2M is discretionary in the sense that it is not mandatory, but within it some lines are locked and some are not: contractual (locked until renewal, for example a three-year SaaS agreement), changeable now (can be reduced or stopped this quarter, for example conference sponsorships or contractor hours), and one-off (does not recur, for example a one-time migration project). Each has a different lever and a different date.
- Match levers to items. For the big items: renegotiate price, reduce volume or scope, consolidate vendors, move to a cheaper alternative. For the tail: a catalogue (a list of pre-approved products and prices to buy from), a card policy, an approval threshold (a spend level above which a manager must sign off). For items with no clear owner or business reason: stop.
- Size savings as ranges. Low, expected and high, with the evidence for each, and what has to be true.
- Test with owners before presenting. Walk each owner through their own items first so that nothing in the room is a surprise.
Worked example
Illustrative data: 450 amounts drawn from a skewed (lognormal) distribution, meaning most values are small and a few are very large, as in real spend. A fixed seed (a starting number for the random generator) makes the data identical on every run, and the amounts are scaled to total $3.2M.
import random
rng = random.Random(7)
raw = [rng.lognormvariate(0, 1.3) for _ in range(450)]
scale = 3_200_000 / sum(raw)
items = sorted((round(x * scale) for x in raw), reverse=True)
total = sum(items)
cum = 0
marks = {50: None, 80: None}
for i, v in enumerate(items, 1):
cum += v
for m in marks:
if marks[m] is None and cum >= total * m / 100:
marks[m] = (i, cum)
print("total", total, "items", len(items))
for m, (n, c) in marks.items():
print(f"{m}% of spend reached by top {n} items ({n/450:.1%} of lines), ${c:,}")
print("tail: bottom 250 items = $%s" % f"{sum(items[200:]):,}")
print("top item $%s, 20th item $%s" % (f"{items[0]:,}", f"{items[19]:,}"))
top = sum(items[:40])
print("top 40 = $%s (%.1f%%)" % (f"{top:,}", 100*top/total))
for pct in (5, 10, 15):
print(f"{pct}% saved on top 40 = ${top*pct/100:,.0f}")
total 3199996 items 450
50% of spend reached by top 37 items (8.2% of lines), $1,607,601
80% of spend reached by top 136 items (30.2% of lines), $2,560,513
tail: bottom 250 items = $365,054
top item $146,280, 20th item $33,102
top 40 = $1,657,919 (51.8%)
5% saved on top 40 = $82,896
10% saved on top 40 = $165,792
15% saved on top 40 = $248,688
The total is $3,199,996 because each item is rounded to a whole dollar. In this data half the money sits in 37 items (8.2% of lines), and 80% needs 136 items (30.2%), so the "80/20 rule" is a pattern to test, not a law: here it is closer to 80/30. The bottom 250 lines are only $365,054 (11.4%), so item-by-item review of the tail would burn analyst time for little. A 10% saving on the top 40 items is worth $165,792, or 5.2% of the category, and that is the sort of figure to take to the budget owners as the expected case, with 5% ($82,896) as the cautious one. Your real data will have a different shape, so run it on the actual ledger.
Turning the analysis into something owners accept
Here is what one owner's page might look like (illustrative vendors and figures; the savings are a percentage of the line, so 5% of $146,000 is $7,300 and 10% is $14,600):
| Line item | Annual spend | Nature | Option | Saving range | Confidence | Decision by |
|---|---|---|---|---|---|---|
| Contractor agency | $146,000 | Contractual until renewal in 8 months | Renegotiate rates 5% to 10% | $7,300 to $14,600 | 70% | Before the notice date |
| Conference sponsorships | $33,000 | Changeable now | Halve scope | up to $16,500 | 60% | This quarter |
| Migration project | $20,000 | One-off | Keep, no recurring saving | $0 | n/a | Complete |
The owner sees their own vendors and dollar values, picks keep, renegotiate, reduce or stop for each, and the confidence column shows what has to be true for the saving. Principles:
- Show them their own numbers first, in a form they recognise (their cost centre, their vendors), and let them correct it. Disputes about data kill recommendations faster than disputes about savings.
- Offer options, not a cut. For each big item: keep, renegotiate, reduce scope, or stop, with the dollar value and the risk. Owners choose; you track.
- Protect what is revenue- or compliance-critical, and say so explicitly. It signals you are not simply cutting.
- Separate recurring from one-off savings and give dates, since a saving that arrives at renewal in eight months is mostly outside this budget year: the $7,300 to $14,600 in the table is an annual run-rate, and if the budget year is the next twelve months only about four months of it (roughly $2,400 to $4,900) lands in-year.
- Agree ownership and a tracking cadence, so the saving is measured against a baseline and not assumed.
Pitfalls
- Using one rule for everything. Cutting 10% across the board punishes efficient owners and misses the real concentration.
- Judging savings before the data is clean. A duplicate vendor name inflates a different vendor's rank.
- Presenting a single number. Owners trust ranges with stated conditions more than a confident total.
- Ignoring the tail. Not item-level work, but a policy (an approved catalogue, a spend threshold) can cap its growth cheaply.
Unlock Full Question Bank
Get access to all 25 Cost Optimization and Technology Financial Management interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.