Showback vs Chargeback
A showback report tells a team what they spent. A chargeback system makes them pay for it. The difference sounds administrative. It is not — it determines whether engineers treat cost as their problem or as someone else's. Most organizations implement neither model correctly, then wonder why their FinOps program produces dashboards but not decisions.
The Accountability Spectrum
Most organizations live in zone A or B. Zone C — soft chargeback with OKR integration — is where cost behavior actually changes. Zone D works only when budget ownership is genuinely decentralized to team level.
Showback: The Minimum Viable Model
Showback requires three things: allocation, a report, and a recurring review cadence.
-- Weekly showback report — cost per team, variance vs prior week
WITH current_week AS (
SELECT
resource_tags_user_team AS team,
resource_tags_user_product AS product,
resource_tags_user_env AS env,
ROUND(SUM(line_item_unblended_cost), 2) AS cost_usd
FROM cur_db.cur_table
WHERE line_item_usage_start_date >= CURRENT_DATE - INTERVAL '7' DAY
AND line_item_line_item_type NOT IN ('Credit', 'Tax', 'Refund')
AND resource_tags_user_team IS NOT NULL
GROUP BY 1, 2, 3
),
prior_week AS (
SELECT
resource_tags_user_team AS team,
ROUND(SUM(line_item_unblended_cost), 2) AS cost_usd
FROM cur_db.cur_table
WHERE line_item_usage_start_date >= CURRENT_DATE - INTERVAL '14' DAY
AND line_item_usage_start_date < CURRENT_DATE - INTERVAL '7' DAY
AND line_item_line_item_type NOT IN ('Credit', 'Tax', 'Refund')
AND resource_tags_user_team IS NOT NULL
GROUP BY 1
)
SELECT
c.team,
c.product,
c.env,
c.cost_usd AS this_week,
COALESCE(p.cost_usd, 0) AS last_week,
ROUND(c.cost_usd - COALESCE(p.cost_usd, 0), 2) AS delta_usd,
ROUND(
100.0 * (c.cost_usd - COALESCE(p.cost_usd, 0))
/ NULLIF(p.cost_usd, 0)
, 1) AS delta_pct
FROM current_week c
LEFT JOIN prior_week p ON c.team = p.team
ORDER BY this_week DESC;Automate delivery. A Slack message with this table, sent every Monday morning, does more for cost culture than a quarterly review.
Shared Resource Allocation
Shared costs (platform, networking, observability) cannot be tagged to a single team. Use a split model:
def allocate_shared_costs(
shared_cost: float,
team_costs: dict[str, float],
method: str = "proportional" # or "equal" or "fixed"
) -> dict[str, float]:
"""
Distribute shared infrastructure cost across product teams.
proportional: allocate based on each team's direct cloud spend ratio
equal: split evenly across all teams
fixed: use pre-defined allocation percentages
"""
if method == "proportional":
total_direct = sum(team_costs.values())
return {
team: round(shared_cost * (cost / total_direct), 2)
for team, cost in team_costs.items()
}
elif method == "equal":
per_team = round(shared_cost / len(team_costs), 2)
return {team: per_team for team in team_costs}
elif method == "fixed":
# Define in config — sum must equal 1.0
fixed_pct = {
"payments": 0.40,
"analytics": 0.30,
"growth": 0.20,
"platform": 0.10,
}
return {
team: round(shared_cost * pct, 2)
for team, pct in fixed_pct.items()
}
raise ValueError(f"Unknown method: {method}")
# Example
team_direct = {"payments": 45000, "analytics": 30000, "growth": 15000}
shared = 10000
allocations = allocate_shared_costs(shared, team_direct, method="proportional")
for team, amount in allocations.items():
print(f" {team:12s} shared allocation: ${amount:,.2f}")Document the allocation method and publish it. Hidden allocation formulas destroy trust faster than any overcharge.
The Soft Chargeback Model
Soft chargeback ties cost efficiency to engineering OKRs without moving actual budget dollars. It is the most practical step between showback and full chargeback for teams that do not control their own P&L.
Structure:
Suggested OKR structure for engineering teams:
objective: "Operate efficiently at scale"
key_results:
- kr: "Keep compute cost per request below $0.0008"
measurement: "Monthly p95 from CUR / total API requests from CloudWatch"
target_q1: 0.0008
target_q2: 0.00075 # improve 6% QoQ
- kr: "Maintain tag coverage above 95%"
measurement: "Weekly CUR tag coverage query"
target: 95
- kr: "Zero unreviewed Cost Anomaly Detection alerts"
measurement: "Weekly anomaly alert backlog count"
target: 0Building the Weekly Digest Automation
import boto3
import json
from datetime import datetime, timedelta
def get_weekly_cost_by_team(ce_client):
end = datetime.utcnow().date()
start = end - timedelta(days=7)
resp = ce_client.get_cost_and_usage(
TimePeriod={"Start": str(start), "End": str(end)},
Granularity="MONTHLY",
Filter={"Not": {"Dimensions": {
"Key": "RECORD_TYPE",
"Values": ["Credit", "Refund", "Tax"]
}}},
GroupBy=[{"Type": "TAG", "Key": "team"}],
Metrics=["UnblendedCost"],
)
results = []
for group in resp["ResultsByTime"][0]["Groups"]:
team = group["Keys"][0].replace("team$", "") or "untagged"
cost = float(group["Metrics"]["UnblendedCost"]["Amount"])
results.append({"team": team, "cost": round(cost, 2)})
return sorted(results, key=lambda x: x["cost"], reverse=True)
def format_slack_message(costs):
lines = ["*Weekly Cloud Cost by Team*", "```"]
for row in costs:
lines.append(f"{row['team']:20s} ${row['cost']:>10,.2f}")
lines.append("```")
return "\n".join(lines)
def send_to_slack(webhook_url: str, message: str):
import urllib.request
payload = json.dumps({"text": message}).encode()
req = urllib.request.Request(webhook_url, data=payload,
headers={"Content-Type": "application/json"})
urllib.request.urlopen(req)
# Lambda handler
def lambda_handler(event, context):
ce = boto3.client("ce", region_name="us-east-1")
costs = get_weekly_cost_by_team(ce)
msg = format_slack_message(costs)
webhook = boto3.client("ssm").get_parameter(
Name="/finops/slack-webhook", WithDecryption=True
)["Parameter"]["Value"]
send_to_slack(webhook, msg)
return {"status": "sent", "teams": len(costs)}Schedule this Lambda weekly with EventBridge. The Slack message creates a shared ritual — everyone sees the number at the same time.
Key Takeaways
- Showback changes awareness; soft chargeback changes behavior; hard chargeback changes architecture — choose the model that matches your organization's budget ownership structure.
- Shared cost allocation must be documented and published; any formula applied silently will be disputed when bills rise, destroying the trust that makes cost culture work.
- Proportional allocation of shared costs (based on direct spend ratios) is the fairest default — it naturally incentivizes efficient direct spend to reduce the shared overhead allocated to a team.
- The weekly Slack digest is the highest-ROI FinOps ritual; it creates a shared cadence without requiring anyone to pull a dashboard.
- Tying cost efficiency to OKRs is the bridge between showback and chargeback — teams optimize because it affects their engineering KRs, not because finance is watching.
- Cost-per-unit metrics (cost per request, cost per active user) are more actionable than raw dollar amounts; they decouple cost from growth and measure efficiency independently.