Skip to content
About
Main content

Test the commitment-sizing rule

Simulate a modelNeeds Python with NumPy

Problem

A commitment (reserved capacity, a savings plan) buys a lower unit price in exchange for paying whether you use it or not. Committing too little leaves demand on the expensive price; committing too much pays for idle units. The two errors are not symmetric: an unused committed unit wastes its whole price, while an uncovered unit costs only the difference between on-demand and committed. So the best commitment sits below the mean of demand, at the level that demand exceeds often enough to justify paying for it every day.

Generate 12 months of daily demand with a mean of 60 units and a peak of 100 (any skewed distribution). For commitment levels from 20 to 100, compute total cost, where committed units cost 0.6 each whether used or not, and the rest cost 1.0. Plot cost against commitment level. Try a different distribution and a 20% yearly price fall in years 2 and 3, and see how the best level moves.

The script tries a right-skewed (gamma) and a left-skewed (beta) demand shape.

The question. At which commitment level is the year cheapest, where does that sit in the distribution of daily demand, and how does a falling on-demand price move it?


Predict

Under right-skewed demand with a mean of 60, at which percentile of daily demand does the cheapest one-year commitment sit?

Run it

  1. 1Install uv once. It fetches Python and the lab's packages for you.

    Terminal
    curl -LsSf https://astral.sh/uv/install.sh | sh
  2. 2Get the script. It is the whole lab: read it, then download it.

    test-the-commitment-sizing-rule.py55 linesDownload
    View the code
    test-the-commitment-sizing-rule.py
    # /// script
    # requires-python = ">=3.10"
    # dependencies = ["numpy"]
    # ///
    # Copyright (c) 2026 Aayush Sharma · SPDX-License-Identifier: MIT
    # https://thedataengineering.com/lab-code/LICENSE.txt
    """Test the commitment sizing rule.
    
    A year of skewed daily demand (mean 60, peak 100). Committed units cost 0.6
    each whether used or not; the rest cost 1.0 on demand. Total cost for every
    commitment level, against the demand distribution's percentiles; then a
    different shape, and a 20% yearly price fall over a 3-year commitment.
    """
    import argparse, json
    import numpy as np
    
    p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
    p.add_argument("--mean", type=float, default=60, help="Mean daily demand.")
    p.add_argument("--peak", type=float, default=100, help="Peak daily demand.")
    p.add_argument("--committed-price", type=float, default=0.6, help="Price of a committed unit, used or not.")
    p.add_argument("--price-fall", type=float, default=0.2, help="Yearly fall of the on-demand price, for the 3-year case.")
    p.add_argument("--seed", type=int, default=0, help="Random seed.")
    p.add_argument("--json", help="Also write the results to this JSON file.")
    args = p.parse_args()
    rng = np.random.default_rng(args.seed)
    
    def shaped(kind, days=365):
        x = rng.gamma(3, 1, days) if kind == "right-skewed (gamma)" else rng.beta(5, 2, days)
        x = (x - x.min()) / (x.max() - x.min())                  # scale to [0, 1]
        lo = (args.mean - args.peak * x.mean()) / (1 - x.mean())  # then to [lo, peak] with the requested mean
        return lo + (args.peak - lo) * x
    
    def cost(demand, c, years=1):
        total = 0.0
        for y in range(years):
            on_demand = (1 - args.price_fall) ** y if years > 1 else 1.0
            total += c * args.committed_price * len(demand) + np.maximum(demand - c, 0).sum() * on_demand
        return total
    
    rows, best = [], []
    levels = list(range(20, 101, 2))
    for kind in ("right-skewed (gamma)", "left-skewed (beta)"):
        d = shaped(kind)
        for years, label in ((1, "1 year"), (3, f"3 years, on-demand price -{args.price_fall:.0%}/yr")):
            costs = [cost(d, c, years) for c in levels]
            for c, v in zip(levels, costs): rows.append({"demand": kind, "term": label, "commit": c, "cost": v / years})
            bc = levels[int(np.argmin(costs))]
            pct = float(np.mean(d < bc) * 100)
            best.append({"demand": kind, "term": label, "best_commit": bc, "percentile": pct, "mean": float(d.mean())})
            print(f"{kind:<22} {label:<32} best commitment {bc:>3} = P{pct:.0f} of demand (mean {d.mean():.0f})")
    
    if args.json:
        with open(args.json, "w") as f:
            json.dump({"tables": {"costs": rows, "best": best}}, f)
    
  3. 3Run it in the folder you saved it to, with the defaults first. Then change a knob from the table below and run it again.

    Terminal
    uv run test-the-commitment-sizing-rule.py
    uv run test-the-commitment-sizing-rule.py --help     # every knob
Knobs
Knob · defaultWhat it changes
--mean60 Mean daily demand.
--peak100 Peak daily demand.
--committed-price0.6 Price of a committed unit, used or not.
--price-fall0.2 Yearly fall of the on-demand price, for the 3-year case.
--seed0 Random seed.

Our run

Yearly cost by commitment levelApple M2 · 8 cores · 8 GB
1 year3 years, on-demand price -20%/yr
Demand shape
Data table
Yearly cost by commitment level
Committed unitsseriesCost per year
201 year19K
221 year18.7K
241 year18.4K
261 year18.1K
281 year17.8K
301 year17.5K
321 year17.2K
341 year16.9K
361 year16.6K
381 year16.4K
401 year16.1K
421 year15.8K
441 year15.5K
461 year15.2K
481 year14.9K
501 year14.7K
521 year14.5K
541 year14.3K
561 year14.3K
581 year14.3K
601 year14.4K
621 year14.5K
641 year14.8K
661 year15K
681 year15.3K
701 year15.7K
721 year16K
741 year16.4K
761 year16.8K
781 year17.2K
801 year17.6K
821 year18K
841 year18.4K
861 year18.9K
881 year19.3K
901 year19.7K
921 year20.2K
941 year20.6K
961 year21K
981 year21.5K
1001 year21.9K
203 years, on-demand price -20%/yr16.3K
223 years, on-demand price -20%/yr16.1K
243 years, on-demand price -20%/yr15.9K
263 years, on-demand price -20%/yr15.8K
283 years, on-demand price -20%/yr15.6K
303 years, on-demand price -20%/yr15.5K
323 years, on-demand price -20%/yr15.3K
343 years, on-demand price -20%/yr15.2K
363 years, on-demand price -20%/yr15K
383 years, on-demand price -20%/yr14.9K
403 years, on-demand price -20%/yr14.7K
423 years, on-demand price -20%/yr14.5K
443 years, on-demand price -20%/yr14.4K
463 years, on-demand price -20%/yr14.2K
483 years, on-demand price -20%/yr14.1K
503 years, on-demand price -20%/yr14K
523 years, on-demand price -20%/yr13.9K
543 years, on-demand price -20%/yr13.9K
563 years, on-demand price -20%/yr13.9K
583 years, on-demand price -20%/yr14K
603 years, on-demand price -20%/yr14.1K
623 years, on-demand price -20%/yr14.4K
643 years, on-demand price -20%/yr14.6K
663 years, on-demand price -20%/yr14.9K
683 years, on-demand price -20%/yr15.2K
703 years, on-demand price -20%/yr15.6K
723 years, on-demand price -20%/yr16K
743 years, on-demand price -20%/yr16.3K
763 years, on-demand price -20%/yr16.7K
783 years, on-demand price -20%/yr17.2K
803 years, on-demand price -20%/yr17.6K
823 years, on-demand price -20%/yr18K
843 years, on-demand price -20%/yr18.4K
863 years, on-demand price -20%/yr18.9K
883 years, on-demand price -20%/yr19.3K
903 years, on-demand price -20%/yr19.7K
923 years, on-demand price -20%/yr20.2K
943 years, on-demand price -20%/yr20.6K
963 years, on-demand price -20%/yr21K
983 years, on-demand price -20%/yr21.5K
1003 years, on-demand price -20%/yr21.9K
macOS 26.6.2 · Python 3.13.3 · numpy 2.5.4 · run 2026-10-20

The cheapest commitment

The cheapest commitment
DemandTermBest commitmentPercentile of demandMean demand
right-skewed (gamma)1 year5639.560
right-skewed (gamma)3 years, on-demand price -20%/yr5428.260
left-skewed (beta)1 year584060
left-skewed (beta)3 years, on-demand price -20%/yr4625.860
Output · uv run test-the-commitment-sizing-rule.py
right-skewed (gamma)   1 year                           best commitment  56 = P39 of demand (mean 60)
right-skewed (gamma)   3 years, on-demand price -20%/yr best commitment  54 = P28 of demand (mean 60)
left-skewed (beta)     1 year                           best commitment  58 = P40 of demand (mean 60)
left-skewed (beta)     3 years, on-demand price -20%/yr best commitment  46 = P26 of demand (mean 60)

What we found

For one year the cheapest commitment was 56 units under right-skewed demand, at P39.5 of daily demand, and 58 units (P40) under left-skewed demand: below the mean of 60 in both, at the expected P40. That is the newsvendor answer for these prices: a committed unit pays for itself only on days when demand exceeds it, and at 0.6 against 1.0 it needs to be used more than 60% of the time, which puts the optimum at the 40th percentile.

A falling on-demand price lowered the best commitment further, to 46 units (P25.8) under left-skewed demand over three years: every year the discount is worth less, so less of the demand is worth locking in.


Read it

The commitment level is a percentile, not a forecast: the price ratio sets it. With committed price c and on-demand price 1, commit up to the level that demand exceeds a share c of the time. Most teams commit at the mean or at last quarter's peak, which pays for idle units on most days.

A commitment also inverts the marginal incentive inside its period: usage below the cap is free at the margin, so optimizing below the cap is worth nothing and pulling work into the period is worth a lot. Same engineer, same workload, opposite advice, decided only by a contract.