The fastest way to waste a quarter is to start a decision project by training a model. You’ll build something accurate, then discover the business can’t use it: the actions it implies are not allowed, the objective it optimizes is not the one the business cares about, or the outcome it predicts arrives too late to steer anything.
So the first artifact in a decision project isn’t a notebook. It’s a decision specification: a short document that pins down what is being decided, by whom, how often, with which options, scored how, under which rules. It takes an afternoon. It saves months.
Is this a decision worth modeling?
Not every decision deserves a model. The tools in this course pay off when the decision is:
- Repeated. Made thousands or millions of times: every transaction, every customer each month, every SKU each week. One-off strategic choices (open a new office?) deserve analysis, not a decision system.
- Consequential in aggregate. Each individual decision might be worth cents, but the sum is worth millions. A 2% improvement on 5 million pricing decisions is real money.
- Observable. You can eventually see what happened after each decision. Without outcomes, you can’t learn or evaluate.
- Choosable. There are genuine alternatives, and someone has the authority to change which one is taken.
If a candidate fails “choosable”, stop. A perfect model of something nobody can change is a dashboard.
The decision specification
Here is the template we’ll use throughout the course, filled in for the running example: a subscription business deciding monthly retention offers.
decision: monthly_retention_offer
owner: VP Customer (accountable), Retention PM (responsible)
cadence: batch, 1st of each month; plus real-time on cancel-page visit
unit: one active subscriber
actions:
- none
- discount_10pct_3mo
- discount_25pct_3mo
- free_upgrade_1mo
- account_manager_call # only for annual plans > $2k
objective:
maximize: expected 12-month margin
# margin = retained revenue - offer cost - contact cost
tradeoffs:
customer_annoyance_per_contact: 0.80 # $ equivalent, set by CX lead
constraints:
hard:
- monthly_offer_budget <= 120000
- account_manager_calls <= 600 per month
- max one discount per customer per rolling 12 months
- no offers to accounts in collections or legal hold
soft:
- keep share of 25% offers under 15% (brand perception)
feedback:
primary_outcome: active at day 90 after decision
delay: 90 days
leading_proxies: [logins_next_14d, cancel_page_visits_next_30d]
current_baseline: 20% discount to churn_prob > 0.6, ~40k sends/month
Every field is there for a reason. Let’s go through the hard ones.
Actions: enumerate, then prune
List every action the decision maker could take, including doing nothing. “None” is always an action, and it’s the most important one, because it’s the baseline every other action has to beat.
Then prune with eligibility rules. The account-manager call only exists for large annual plans. Customers in collections get nothing. This produces an eligible action set per unit, which can differ from customer to customer. Representing actions this way (a global catalogue plus per-unit eligibility) keeps the decision logic clean as offers are added and retired.
Watch for actions that are really parameterized families. “Discount” with a percentage from 5 to 50 and a duration from 1 to 12 months is 100+ actions. Either discretize to a handful that operations can actually run, or treat the parameters as continuous variables in an optimization (Module 3).
Objective: one number, explicit trade-offs
An optimizer needs to compare actions, which means it needs one number per outcome. Businesses usually have several goals: margin, customer satisfaction, risk, fairness. You have two honest options:
- Convert to a common unit. Ask the CX lead “how many dollars of margin would you give up to avoid one unwanted contact?”. In the spec above, the answer was $0.80. It’s a judgment call, and it should be: the point is that the judgment is now written down, versioned and debatable instead of buried in a threshold.
- Optimize one, constrain the rest. Maximize margin subject to complaint rate below 0.3%. This is often easier for stakeholders to agree to, because a constraint reads like a policy.
What you must not do is optimize a proxy because it’s convenient. “Maximize predicted retention” is not the objective; it ignores offer cost. “Minimize churn rate” happily gives everyone the biggest discount. The objective should be the quantity the business would actually be happy to see go up.
Time horizon matters too. A discount that saves a customer for three months and then loses them is worth much less than one that retains them for two years. Decide the horizon (here, 12-month margin) and make sure the outcome you can measure is a reasonable stand-in for it.
Constraints: hard vs soft
Hard constraints must never be violated: legal requirements, contractual limits, physical capacity, budget caps that finance enforces. They define the feasible region. In code they become eligibility filters (per unit) or constraints in an optimization model (across units, like a total budget).
Soft constraints are preferences you’d rather keep but would trade off for enough value: “prefer not to send more than 15% of offers at 25%”. These usually belong in the objective as a penalty, with the penalty weight agreed by the business.
The classic mistake is treating a hard constraint as soft. If a regulator says no automated declines without a human review for a certain product, no expected gain justifies skipping review. Encode it as a rule that removes the action, not as a cost.
The other classic mistake is the opposite: treating a preference as a hard rule, which silently costs money. A good spec review asks of every constraint: “what would happen if we broke this once, for a very large gain?”. If the answer is “we’d be sued”, it’s hard. If the answer is “someone would be mildly annoyed”, it’s soft, and it deserves a price.
Cadence, latency and the unit of decision
When is the decision made, and how fast?
- Batch decisions (monthly offers, weekly replenishment) can use heavy models and global optimization across all units at once. A budget constraint across all customers is natural here.
- Real-time decisions (fraud checks, a cancel-page offer) have latency budgets of tens of milliseconds and see one unit at a time. Global constraints have to be approximated, for example with a per-decision shadow price that a batch job recomputes (Lesson 7 covers this).
Many systems have both, as in the spec above. Design for both from the start: the same eligibility rules, the same objective, and the same logging, with different execution paths.
Feedback: what tells you it worked?
Write down the outcome you’ll measure, how long it takes to arrive, and what leading signals appear earlier. Retention at day 90 takes 90 days. Logins in the next 14 days are noisy but quick. Credit default can take 18 months; early delinquency arrives in three.
Feedback delay sets the speed limit on learning. It determines how long experiments take (Lesson 20), how quickly a bandit can adapt (Module 5), and how long a bad policy can run before anyone notices (Lesson 21). Teams that skip this field promise improvement curves they can’t possibly measure.
Finally, write down the current baseline. Every decision system is replacing something: a rule, a person, a threshold. That baseline is the policy you’ll compare against, and logging its decisions today is the cheapest data investment you’ll ever make.
Next lesson: with the decision specified, how do you choose between rules, predictive models, optimization and LLMs to implement it?