Behavioural Economics Frameworks: The Science of How Customers Actually Decide
Classical economics assumes rational actors who carefully weigh costs and benefits before making optimal decisions. This is nonsense. Humans are not rational. We are predictably irrational. We make decisions with tired brains, limited attention, emotional states, and cognitive shortcuts that evolution gave us for survival, not for comparing subscription plans. Behavioural economics studies how people actually decide, not how they should decide. This matters enormously for anyone building products or designing experiences. If you design for the mythical rational consumer, you will be confused when your carefully reasoned value proposition fails to convert. If you design for actual human psychology, you can build experiences that work with the brain rather than against it. This post will give you the core frameworks of behavioural economics, the biases that drive purchase decisions, and the practical tools to apply this knowledge to your products and marketing.
Classical economics assumes rational actors who carefully weigh costs and benefits before making optimal decisions. This is nonsense. Humans are not rational. We are predictably irrational. We make decisions with tired brains, limited attention, emotional states, and cognitive shortcuts that evolution gave us for survival, not for comparing subscription plans. Behavioural economics studies how people actually decide, not how they should decide. This matters enormously for anyone building products or designing experiences. If you design for the mythical rational consumer, you will be confused when your carefully reasoned value proposition fails to convert. If you design for actual human psychology, you can build experiences that work with the brain rather than against it. This post will give you the core frameworks of behavioural economics, the biases that drive purchase decisions, and the practical tools to apply this knowledge to your products and marketing.
Dual Process Theory: Two Systems, One Brain
Daniel Kahneman's Nobel Prize winning research identified two distinct modes of thinking, popularised in his book "Thinking, Fast and Slow."
System 1: Fast, Intuitive, Emotional
System 1 operates automatically and quickly, with little or no effort and no sense of voluntary control. It is:
Effortless. You do not choose to recognise a face or understand simple sentences. It just happens.
Associative. It works by pattern matching, connecting the current situation to past experiences.
Emotional. Feelings are System 1 outputs. You feel fear before you consciously identify the threat.
Overconfident. System 1 generates intuitive answers and is bad at knowing what it does not know.
Always on. You cannot turn it off. It runs constantly, generating impressions, intuitions, and impulses.
Examples of System 1 thinking:
Recognising that an object is closer than another.
Detecting hostility in a voice.
Answering 2 + 2 = ?
Reading text on a billboard.
Driving a car on an empty road.
Understanding simple sentences.
System 2: Slow, Deliberate, Analytical
System 2 allocates attention to effortful mental activities. It is:
Effortful. It requires concentration and depletes cognitive resources.
Logical. It can follow rules, compare objects, and make deliberate choices.
Lazy. It does not like to work and will delegate to System 1 whenever possible.
Limited capacity. You can only focus on one demanding task at a time.
Easily disrupted. Distraction, time pressure, or cognitive load shut it down.
Examples of System 2 thinking:
Focusing on one voice in a crowded room.
Looking for a specific person in a crowd.
Multiplying 17 × 24.
Comparing two products on multiple attributes.
Filling out a tax form.
Parking in a narrow space.
Why This Matters for Commerce
Here is the critical insight: most consumer decisions are System 1 decisions.
We like to think we carefully evaluate options, weigh pros and cons, and make rational choices. In reality, most purchases are intuitive, emotional, and automatic. System 2 only kicks in under specific conditions:
High stakes. Major purchases where the cost of error is significant.
Something feels off. When intuition hits a snag or something seems wrong.
Explicit prompting. When someone or something forces deliberation.
Unfamiliarity. When the situation does not match any pattern System 1 recognises.
Designing for System 1
If most decisions are System 1, your design should work with System 1:
Make it easy. Reduce cognitive load. Clear layouts, simple language, obvious next steps.
Use visuals. System 1 processes images faster than text.
Create good feelings. Aesthetics, warmth, familiarity all matter to System 1.
Provide social proof. System 1 loves to follow the crowd.
Use anchoring. The first number System 1 sees becomes the reference point.
Leverage defaults. System 1 goes with the flow. The default is the path of least resistance.
When System 2 Engages
Knowing when System 2 engages helps you design for different contexts:
import pandas as pd
# System engagement by purchase context
contexts = [
{
'context': 'Grocery shopping (familiar brands)',
'system_1_dominant': True,
'system_2_triggers': 'Price increase, out of stock, new product',
'design_implication': 'Optimise for recognition, shelf placement, packaging'
},
{
'context': 'Impulse e-commerce purchase',
'system_1_dominant': True,
'system_2_triggers': 'Complex checkout, unexpected costs, trust concerns',
'design_implication': 'One click purchase, no surprises, strong trust signals'
},
{
'context': 'SaaS subscription signup',
'system_1_dominant': True,
'system_2_triggers': 'Pricing complexity, long forms, unclear value',
'design_implication': 'Clear pricing, minimal friction, obvious benefit'
},
{
'context': 'Enterprise software evaluation',
'system_1_dominant': False,
'system_2_triggers': 'Default state (high stakes, multiple stakeholders)',
'design_implication': 'Detailed documentation, ROI calculators, case studies'
},
{
'context': 'Insurance purchase',
'system_1_dominant': False,
'system_2_triggers': 'Default state (complex, high stakes, unfamiliar)',
'design_implication': 'Simplification, comparison tools, trust building'
},
{
'context': 'Luxury goods',
'system_1_dominant': True,
'system_2_triggers': 'Price justification demands, social pressure',
'design_implication': 'Emotional appeal first, rational justification available'
},
]
print("System 1 vs System 2 by Purchase Context")
print("=" * 100)
print(f"{'Context':40} | {'Dominant':12} | {'Design Implication':45}")
print("-" * 100)
for ctx in contexts:
dominant = 'System 1' if ctx['system_1_dominant'] else 'System 2'
print(f"{ctx['context']:40} | {dominant:12} | {ctx['design_implication'][:45]}")
print("\nKey Insight:")
print("Even high consideration purchases often START with System 1 (first impression)")
print("System 2 kicks in later but the System 1 impression shapes what gets considered")
Cognitive Biases That Drive Purchase Decisions
Cognitive biases are systematic patterns of deviation from rationality. They are not bugs. They are features. Evolution selected for fast, good enough decisions over slow, optimal ones. Understanding these biases lets you design experiences that work with human cognition.
Confirmation Bias
We seek, interpret, and remember information that confirms our existing beliefs.
In commerce: Customers who already like your brand will interpret ambiguous information favourably. Customers who distrust you will find reasons to confirm that distrust.
Application: Reinforce existing positive associations. Do not try to argue against negative beliefs. Provide easy wins that confirm "I made the right choice" after purchase.
Availability Heuristic
We judge probability by how easily examples come to mind.
In commerce: Recent news, vivid stories, and memorable examples disproportionately influence perception. A single viral complaint can outweigh thousands of positive experiences.
Application: Make positive experiences memorable and shareable. Address negative stories quickly before they become the available example. Use case studies and testimonials to make success stories easy to recall.
Recency Bias
We overweight recent events relative to older ones.
In commerce: The last interaction shapes overall impression. A great checkout experience overrides earlier frustrations. A bad support call taints the entire relationship.
Application: Optimise the end of experiences. Peak end rule: people remember peaks and endings. Make the last touch point excellent.
Sunk Cost Fallacy
We continue investing in something because of what we have already invested, not because of future returns.
In commerce: Users who have invested time, data, or customisation are reluctant to switch, even to superior alternatives. This is why onboarding that requires effort increases retention.
Application: Create investment early. Progress bars, customisation, data accumulation all increase switching costs. But be ethical. Genuine value should underpin the investment.
Status Quo Bias
We prefer the current state of affairs. Change is costly, even when the change would be beneficial.
In commerce: Existing customers stay until something triggers reconsideration. New customer acquisition requires overcoming the prospect's status quo bias.
Application: For retention, prevent the trigger to reconsider. For acquisition, create that trigger for prospects using competitors.
Default Bias
We tend to accept pre selected options. The default wins unless there is active reason to change it.
In commerce: The pricing tier you pre select becomes the most chosen tier. The email subscription checkbox state determines most subscription rates. The pre filled form values become the submitted values.
Application: Defaults are the most underused growth lever in the industry. Every default in your product is a design decision. Make it intentionally.
Measuring Bias Impact
import numpy as np
import pandas as pd
from scipy import stats
# A/B test: impact of default selection on pricing tier choice
np.random.seed(42)
def simulate_tier_selection(n_users, default_tier, tiers=['basic', 'pro', 'enterprise']):
"""Simulate tier selection with different defaults."""
# Base preferences (if no default existed)
base_prefs = {'basic': 0.35, 'pro': 0.45, 'enterprise': 0.20}
# Default bias: 30-40% boost to default option
default_boost = 0.35
selections = []
for _ in range(n_users):
probs = base_prefs.copy()
# Apply default boost
probs[default_tier] *= (1 + default_boost)
# Renormalise
total = sum(probs.values())
probs = {k: v/total for k, v in probs.items()}
# Select based on adjusted probabilities
selection = np.random.choice(tiers, p=[probs[t] for t in tiers])
selections.append(selection)
return pd.Series(selections).value_counts(normalize=True)
print("Default Bias Impact on Pricing Tier Selection")
print("=" * 70)
n = 5000
for default in ['basic', 'pro', 'enterprise']:
dist = simulate_tier_selection(n, default)
print(f"\nDefault set to: {default.upper()}")
for tier in ['basic', 'pro', 'enterprise']:
pct = dist.get(tier, 0) * 100
indicator = ' ← DEFAULT' if tier == default else ''
print(f" {tier.title():12}: {pct:5.1f}%{indicator}")
# Revenue impact
prices = {'basic': 29, 'pro': 79, 'enterprise': 199}
print("\nRevenue Impact of Default Selection (5,000 users):")
print("-" * 70)
for default in ['basic', 'pro', 'enterprise']:
dist = simulate_tier_selection(n, default)
revenue = sum(prices[tier] * dist.get(tier, 0) * n for tier in prices)
print(f" Default {default.title():12}: £{revenue:,.0f} monthly revenue")
print("\nKey Insight:")
print("Simply changing the default can shift revenue by 20-40%")
print("This requires zero product changes, just a different pre-selection")
The Complete Bias Toolkit
import pandas as pd
bias_toolkit = [
{
'bias': 'Confirmation bias',
'mechanism': 'Seek info that confirms existing beliefs',
'leverage': 'Reinforce positive associations, provide post-purchase validation',
'avoid': 'Arguing against established negative beliefs',
'test': 'Segment by prior brand sentiment, measure message effectiveness'
},
{
'bias': 'Availability heuristic',
'mechanism': 'Judge by ease of recalling examples',
'leverage': 'Make success stories vivid and memorable',
'avoid': 'Letting negative stories dominate recall',
'test': 'Track share of voice, measure brand association strength'
},
{
'bias': 'Recency bias',
'mechanism': 'Overweight recent experiences',
'leverage': 'Optimise final touchpoints (checkout, offboarding)',
'avoid': 'Neglecting the end of user journeys',
'test': 'Correlate last-touch quality with NPS and repeat purchase'
},
{
'bias': 'Sunk cost fallacy',
'mechanism': 'Continue investing due to past investment',
'leverage': 'Create early investment (data, customisation, progress)',
'avoid': 'Empty switching costs without genuine value',
'test': 'Track onboarding depth vs retention curves'
},
{
'bias': 'Status quo bias',
'mechanism': 'Prefer current state over change',
'leverage': 'For retention: prevent reconsideration triggers',
'avoid': 'For acquisition: failing to create trigger',
'test': 'Identify and track trigger events in churn analysis'
},
{
'bias': 'Default bias',
'mechanism': 'Accept pre-selected options',
'leverage': 'Set defaults to optimal paths for user and business',
'avoid': 'Leaving defaults to chance or developer preference',
'test': 'A/B test default variations, measure selection rates'
},
{
'bias': 'Anchoring',
'mechanism': 'First number becomes reference point',
'leverage': 'Show higher anchor before actual price',
'avoid': 'Low anchors that make actual price feel expensive',
'test': 'Test anchor values, measure price sensitivity'
},
{
'bias': 'Social proof',
'mechanism': 'Follow what others do',
'leverage': 'Show popularity, reviews, testimonials',
'avoid': 'Showing low numbers that signal unpopularity',
'test': 'A/B test social proof elements, measure conversion lift'
},
]
print("Cognitive Bias Toolkit for Commerce")
print("=" * 100)
for bias in bias_toolkit:
print(f"\n{bias['bias'].upper()}")
print(f" Mechanism: {bias['mechanism']}")
print(f" Leverage: {bias['leverage']}")
print(f" Avoid: {bias['avoid']}")
print(f" Test: {bias['test']}")
Choice Architecture: Designing Decisions
Richard Thaler and Cass Sunstein's book "Nudge" introduced choice architecture: the practice of influencing decisions by organising the context in which people make them.
The core insight: there is no neutral presentation. Every choice has architecture. The order of options, the default, the framing, the friction, all influence the outcome. You are a choice architect whether you intend to be or not.
The Elements of Choice Architecture
Defaults. The pre selected option. As shown above, defaults have enormous power.
Order. Position affects selection. First and last positions get more attention (primacy and recency effects). Middle options seem like compromises.
Framing. The same information presented differently changes decisions. "90% fat free" versus "10% fat." "Save £200" versus "Avoid losing £200."
Friction. Every additional step, field, or click reduces completion. Adding friction to undesired actions is as powerful as removing it from desired ones.
Feedback. Immediate feedback guides behaviour. Progress indicators, confirmation messages, error prevention all shape choices.
Expect error. Design for mistakes. Auto save, undo, confirmation dialogs all help.
Defaults: The Most Powerful Lever
Defaults deserve special attention because they are so powerful and so often ignored.
Organ donation. Countries with opt out policies have near universal donation rates. Opt in countries have rates of 15 to 20%. Same population, same procedure, different default.
Retirement savings. Auto enrollment in pension plans dramatically increases participation. People who would never actively enrol stay in plans they were defaulted into.
Software settings. Most users never change default settings. The defaults you ship are the settings most users will use.
Pricing page. The tier you pre select or highlight becomes the most chosen tier.
import numpy as np
import pandas as pd
# The power of defaults across domains
default_examples = [
{
'domain': 'Organ donation (Austria vs Germany)',
'opt_in_rate': 0.12,
'opt_out_rate': 0.99,
'default_lift': 8.25
},
{
'domain': 'Retirement savings enrollment',
'opt_in_rate': 0.35,
'opt_out_rate': 0.85,
'default_lift': 2.43
},
{
'domain': 'Email newsletter subscription',
'opt_in_rate': 0.08,
'opt_out_rate': 0.75,
'default_lift': 9.38
},
{
'domain': 'Privacy permissive settings',
'opt_in_rate': 0.15,
'opt_out_rate': 0.90,
'default_lift': 6.00
},
{
'domain': 'Recurring donation (monthly vs one-time)',
'opt_in_rate': 0.18,
'opt_out_rate': 0.62,
'default_lift': 3.44
},
{
'domain': 'Extended warranty purchase',
'opt_in_rate': 0.12,
'opt_out_rate': 0.45,
'default_lift': 3.75
},
]
print("The Power of Defaults Across Domains")
print("=" * 80)
print(f"{'Domain':45} | {'Opt-In':10} | {'Opt-Out':10} | {'Lift':8}")
print("-" * 80)
for ex in default_examples:
print(f"{ex['domain']:45} | {ex['opt_in_rate']*100:8.1f}% | {ex['opt_out_rate']*100:8.1f}% | {ex['default_lift']:6.2f}x")
print("\nKey Insight:")
print("Defaults routinely deliver 2-10x lift over opt-in alternatives")
print("This is the most underused lever in product design")
Friction as Choice Architecture
Friction cuts both ways. Reducing friction on desired paths increases completion. Adding friction on undesired paths decreases them.
Friction to add (slow down undesired actions):
Cancellation flows with confirmation steps.
Account deletion requiring email verification.
Large purchases requiring review screens.
Unsubscribe flows with re engagement offers.
Friction to remove (speed up desired actions):
One click checkout.
Pre filled forms.
Social login options.
Guest checkout.
Saved payment methods.
import numpy as np
import pandas as pd
# Modelling friction impact on conversion
np.random.seed(42)
def model_friction_impact(base_conversion, steps, friction_per_step=0.15):
"""Model how each step reduces conversion."""
return base_conversion * ((1 - friction_per_step) ** steps)
# Checkout flow comparison
checkout_flows = [
{'flow': 'One-click (Amazon style)', 'steps': 1, 'base_conversion': 0.85},
{'flow': 'Standard checkout (3 steps)', 'steps': 3, 'base_conversion': 0.85},
{'flow': 'Account required checkout', 'steps': 5, 'base_conversion': 0.85},
{'flow': 'Enterprise form (8 steps)', 'steps': 8, 'base_conversion': 0.85},
]
print("Friction Impact on Checkout Conversion")
print("=" * 70)
print(f"{'Flow':35} | {'Steps':7} | {'Completion':12}")
print("-" * 70)
for flow in checkout_flows:
completion = model_friction_impact(flow['base_conversion'], flow['steps'])
print(f"{flow['flow']:35} | {flow['steps']:5} | {completion*100:10.1f}%")
# Cancellation flow (friction works in reverse)
print("\nFriction in Cancellation Flows:")
print("-" * 70)
cancel_flows = [
{'flow': 'One-click cancel', 'steps': 1, 'churn_completion': 0.95},
{'flow': 'Survey + confirm', 'steps': 2, 'churn_completion': 0.80},
{'flow': 'Survey + offer + confirm', 'steps': 3, 'churn_completion': 0.65},
{'flow': 'Call to cancel', 'steps': 5, 'churn_completion': 0.40},
]
for flow in cancel_flows:
saved = (1 - flow['churn_completion']) * 100
print(f"{flow['flow']:35} | {flow['steps']:5} steps | {flow['churn_completion']*100:5.1f}% complete cancel | {saved:5.1f}% saved")
print("\nKey Insight:")
print("Each friction point costs ~15% conversion in either direction")
print("Use friction strategically: remove from desired paths, add to undesired")
Order Effects
The order in which options appear influences selection:
Primacy effect. First options get more attention and selection, especially when evaluation effort is high.
Recency effect. Last options are remembered better, especially in sequential presentation.
Compromise effect. Middle options seem safer and get selected more often when positioned between extremes.
Decoy effect. An inferior option can make an adjacent option more attractive by comparison.
import numpy as np
import pandas as pd
# Order effects in pricing page layout
np.random.seed(42)
def simulate_order_effect(layout, n_users=3000):
"""Simulate selection based on tier order."""
# Base attractiveness
base_attract = {'basic': 0.30, 'pro': 0.50, 'enterprise': 0.20}
# Position effects
position_boost = {0: 0.10, 1: 0.15, 2: 0.05} # First, middle (compromise), last
adjusted = {}
for pos, tier in enumerate(layout):
adjusted[tier] = base_attract[tier] + position_boost[pos]
# Normalise
total = sum(adjusted.values())
adjusted = {k: v/total for k, v in adjusted.items()}
return adjusted
layouts = [
['basic', 'pro', 'enterprise'], # Standard: cheap to expensive
['enterprise', 'pro', 'basic'], # Reverse: expensive to cheap
['basic', 'enterprise', 'pro'], # Pro in last (recency)
]
print("Order Effects on Pricing Tier Selection")
print("=" * 70)
for layout in layouts:
dist = simulate_order_effect(layout)
layout_str = ' → '.join([t.title() for t in layout])
print(f"\nLayout: {layout_str}")
prices = {'basic': 29, 'pro': 79, 'enterprise': 199}
expected_revenue = sum(prices[t] * dist[t] for t in prices)
for tier in layout:
print(f" {tier.title():12}: {dist[tier]*100:5.1f}%")
print(f" Expected ARPU: £{expected_revenue:.2f}")
print("\nKey Insight:")
print("The middle position benefits from compromise effect")
print("Put your preferred tier in the middle position")
print("Expensive-first ordering can increase overall ARPU through anchoring")
Hyperbolic Discounting: The Lure of Now
Humans do not discount future rewards rationally. We use hyperbolic discounting, which means we wildly overvalue immediate rewards relative to future ones.
The standard economic model assumes exponential discounting: a fixed percentage discount per time period. If you discount 5% per month, £100 next month is worth £95 now, and £100 in 12 months is worth £54 now.
Hyperbolic discounting does not work like this. Instead:
£100 now versus £100 tomorrow: strong preference for now.
£100 in 365 days versus £100 in 366 days: almost no preference.
The same one day delay matters enormously in the near future and barely at all in the distant future. This is why we procrastinate on long term projects but rush for immediate rewards.
The Commercial Implications
A bird in the hand is worth roughly 4 in the bush at 12 months. People will choose £25 now over £100 in a year, even though that implies a discount rate that would bankrupt any rational investor.
"Instant access" beats "lifetime value" in copy. Promising future benefits triggers hyperbolic discounting. Promising immediate benefits does not.
One free month works better than larger annual discounts. A free month is immediate and concrete. 20% off annual billing is abstract and distant.
Designing for Hyperbolic Discounting
import numpy as np
import pandas as pd
def hyperbolic_discount(value, delay_days, k=0.05):
"""Hyperbolic discount function: V = V0 / (1 + k*D)"""
return value / (1 + k * delay_days)
def exponential_discount(value, delay_days, daily_rate=0.0003):
"""Exponential discount function: V = V0 * e^(-r*D)"""
return value * np.exp(-daily_rate * delay_days)
# Compare offers with different time structures
offers = [
{'offer': 'Get £10 off now', 'immediate_value': 10, 'future_value': 0, 'delay': 0},
{'offer': 'Get £15 off next month', 'immediate_value': 0, 'future_value': 15, 'delay': 30},
{'offer': 'Get £25 off in 3 months', 'immediate_value': 0, 'future_value': 25, 'delay': 90},
{'offer': 'Get £50 off in 6 months', 'immediate_value': 0, 'future_value': 50, 'delay': 180},
{'offer': 'Get £100 off in 12 months', 'immediate_value': 0, 'future_value': 100, 'delay': 365},
]
print("Hyperbolic vs Exponential Discounting of Offers")
print("=" * 90)
print(f"{'Offer':30} | {'Nominal':10} | {'Hyperbolic':12} | {'Exponential':12}")
print("-" * 90)
for offer in offers:
total_nominal = offer['immediate_value'] + offer['future_value']
if offer['delay'] == 0:
hyp_value = total_nominal
exp_value = total_nominal
else:
hyp_value = offer['immediate_value'] + hyperbolic_discount(offer['future_value'], offer['delay'])
exp_value = offer['immediate_value'] + exponential_discount(offer['future_value'], offer['delay'])
print(f"{offer['offer']:30} | £{total_nominal:8.2f} | £{hyp_value:10.2f} | £{exp_value:10.2f}")
print("\nKey Insight:")
print("Under hyperbolic discounting, £10 now is worth more than £100 in 12 months")
print("This is why 'first month free' beats 'save £200 over the year'")
# Pricing framing comparison
print("\n" + "=" * 90)
print("Annual vs Monthly Pricing Framing")
print("-" * 90)
framings = [
{'frame': '£99/year (save £89 vs monthly)', 'annual_cost': 99, 'monthly_cost': 15.67, 'savings': 89, 'delay_to_savings': 365},
{'frame': '£99/year = £8.25/month', 'annual_cost': 99, 'monthly_cost': 8.25, 'savings': 0, 'delay_to_savings': 0},
{'frame': 'First month free, then £12/month', 'annual_cost': 132, 'monthly_cost': 0, 'savings': 12, 'delay_to_savings': 0},
]
print(f"{'Frame':45} | {'Effective Cost':15} | {'Immediate Benefit'}")
print("-" * 90)
for f in framings:
immediate = '£0 first month' if f['monthly_cost'] == 0 else f"£{f['monthly_cost']:.2f}/month"
print(f"{f['frame']:45} | £{f['annual_cost']}/year | {immediate}")
print("\nThe 'First month free' frame typically converts best despite higher total cost")
print("Immediate gratification beats larger but delayed savings")
Practical Applications
Free trials over discounts. "Try free for 30 days" is immediate. "50% off your first year" is delayed gratification.
Instant rewards over points. "Get 10% cash back now" beats "Earn points toward future rewards."
Same day delivery. People will pay disproportionate premiums for immediate delivery.
Immediate progress indicators. Show users they are making progress now, not just building toward future value.
Upfront onboarding investment. Get users invested immediately so they have something to lose.
Mental Accounting: Money in Buckets
Mental accounting, another Thaler concept, describes how people categorise and treat money differently depending on its source, intended use, or storage location.
Rationally, money is fungible. £100 is £100 whether you earned it, found it, won it, or received it as a gift. But psychologically, these are different £100s in different mental buckets.
How Mental Accounting Works
Source affects spending. "Found" money is spent more freely than "earned" money. A £50 tax refund goes to entertainment. A £50 bonus might go to savings.
Buckets create constraints. Money in the "holiday fund" feels unavailable for car repairs, even when both are equally important.
Windfall spending. Unexpected gains are spent differently than expected income. Lottery winnings, inheritance, and gambling wins are often spent frivolously.
Pain of payment varies. Cash feels more painful to spend than credit cards. Credit cards feel more painful than subscriptions. Each has a different mental account.
Commercial Applications
Gift cards. A £50 gift card is spent more freely than £50 cash. It is already in the "spending" bucket. It does not feel like "real" money.
Rewards points. Points exist in a separate mental account from money. People will "spend" 50,000 points on something they would never pay £500 for.
Store credit. Returns that become store credit stay in the store's ecosystem. Cash returns leave.
Bonus budgets. Corporate procurement often has separate budgets for different categories. A tool might be impossible to buy with the "software" budget but easy with the "training" budget.
import numpy as np
import pandas as pd
# Mental accounting impact on spending behaviour
spending_scenarios = [
{
'scenario': 'Earned income (salary)',
'amount': 100,
'discretionary_spend_rate': 0.20,
'category': 'Essential spending first'
},
{
'scenario': 'Tax refund',
'amount': 100,
'discretionary_spend_rate': 0.55,
'category': 'Windfall / found money'
},
{
'scenario': 'Birthday gift (cash)',
'amount': 100,
'discretionary_spend_rate': 0.70,
'category': 'Gift / treat yourself'
},
{
'scenario': 'Casino winnings',
'amount': 100,
'discretionary_spend_rate': 0.85,
'category': 'House money'
},
{
'scenario': 'Gift card to specific store',
'amount': 100,
'discretionary_spend_rate': 0.95,
'category': 'Pre-committed spending'
},
{
'scenario': 'Loyalty points (perceived £100 value)',
'amount': 100,
'discretionary_spend_rate': 0.98,
'category': 'Not real money'
},
]
print("Mental Accounting: Same Amount, Different Spending")
print("=" * 80)
print(f"{'Scenario':35} | {'Amount':8} | {'Discretionary':14} | {'Category'}")
print("-" * 80)
for s in spending_scenarios:
disc = s['discretionary_spend_rate'] * s['amount']
print(f"{s['scenario']:35} | £{s['amount']:6} | £{disc:12.2f} | {s['category']}")
print("\nKey Insight:")
print("The same £100 has very different 'spend-ability' based on source and framing")
print("Gift cards and points are spent more freely because they are in a 'fun money' bucket")
# Revenue implication
print("\n" + "=" * 80)
print("Revenue Strategy: Gift Cards vs Cash Refunds")
print("-" * 80)
returns_per_month = 1000
avg_return_value = 75
cash_refund_retained = 0.05 # 5% comes back as a new purchase
store_credit_retained = 0.85 # 85% spent in store
cash_revenue = returns_per_month * avg_return_value * cash_refund_retained
credit_revenue = returns_per_month * avg_return_value * store_credit_retained
print(f"Monthly returns: {returns_per_month:,} at avg £{avg_return_value}")
print(f"Cash refund retained revenue: £{cash_revenue:,.0f}")
print(f"Store credit retained revenue: £{credit_revenue:,.0f}")
print(f"Revenue difference: £{credit_revenue - cash_revenue:,.0f}/month")
Choice Overload: Less Is More
Sheena Iyengar's famous jam study demonstrated choice overload in action. A supermarket display offering 24 varieties of jam attracted more attention but converted at only 3%. A display with 6 varieties converted at 30%. Ten times more purchases with fewer options.
Why More Choice Hurts
Decision paralysis. Too many options makes choosing harder. Rather than choose badly, people choose not to choose.
Evaluation difficulty. Comparing 3 options is easy. Comparing 24 options is cognitively exhausting.
Regret anticipation. More options means more paths not taken. The anticipated regret of missing the "best" option causes avoidance.
Satisfaction reduction. Even when people do choose, more options reduce satisfaction. They keep wondering if another option was better.
The Complexity Threshold
Choice overload kicks in above a complexity threshold. For simple decisions with clear criteria, more options can help. For complex decisions with unclear trade offs, fewer options convert better.
Simple decisions (clear criteria): More options may help until logistics become overwhelming.
Complex decisions (multiple trade offs): Fewer options almost always convert better.
Most purchase decisions are complex. Customers do not have clear criteria. They are comparing apples to oranges. This is why reduction almost always helps.
Reducing Effective Choice
import numpy as np
import pandas as pd
from scipy import stats
def simulate_choice_overload(n_options, base_conversion=0.15, complexity='high'):
"""Simulate conversion rate based on number of options."""
if complexity == 'high':
# High complexity: sharp decline after ~6 options (Iyengar pattern)
if n_options <= 6:
return base_conversion * (1 - 0.02 * n_options)
else:
return base_conversion * 0.88 * (0.85 ** (n_options - 6))
else:
# Low complexity: gradual decline
return base_conversion * (0.95 ** (n_options - 1))
print("Choice Overload: Conversion by Number of Options")
print("=" * 70)
print(f"{'Options':10} | {'High Complexity':18} | {'Low Complexity':18}")
print("-" * 70)
for n in [2, 4, 6, 8, 12, 16, 24]:
high_conv = simulate_choice_overload(n, complexity='high')
low_conv = simulate_choice_overload(n, complexity='low')
print(f"{n:8} | {high_conv*100:16.1f}% | {low_conv*100:16.1f}%")
print("\nKey Insight: The Iyengar Jam Study Pattern")
print("6 jams: 30% conversion")
print("24 jams: 3% conversion")
print("10x more purchases with 1/4 the options")
# Practical application: pricing tiers
print("\n" + "=" * 70)
print("Pricing Tier Reduction Example")
print("-" * 70)
tier_scenarios = [
{'tiers': 2, 'description': 'Basic / Pro', 'decision_ease': 'High'},
{'tiers': 3, 'description': 'Basic / Pro / Enterprise', 'decision_ease': 'Good'},
{'tiers': 4, 'description': 'Free / Basic / Pro / Enterprise', 'decision_ease': 'Medium'},
{'tiers': 5, 'description': '5 tiers with feature variations', 'decision_ease': 'Low'},
{'tiers': 7, 'description': 'Complex tier matrix', 'decision_ease': 'Very Low'},
]
for s in tier_scenarios:
conv = simulate_choice_overload(s['tiers'], base_conversion=0.08, complexity='high')
print(f"{s['tiers']} tiers: {s['description']:35} | Conv: {conv*100:5.2f}% | Ease: {s['decision_ease']}")
print("\nRecommendation: 3 tiers is the sweet spot for most products")
print("Offers real choice without overwhelming")
print("Enables compromise effect on middle tier")
Strategies for Reducing Effective Choice
Reduce actual options. Cut SKUs, collapse tiers, simplify plans.
Progressive disclosure. Show core options first, reveal advanced options on request.
Smart defaults. Pre select the most appropriate option for each customer segment.
Guided selling. Help customers narrow options through questions or configurators.
Recommendations. "Most popular" or "Recommended for you" reduces the consideration set.
Comparison tools. Make it easier to evaluate the options that remain.
Status Quo Bias: The Power of Inertia
Status quo bias is the preference for the current state of affairs. People tend to stick with their current situation even when objectively better alternatives exist.
Why Status Quo Wins
Loss aversion. Changing risks losing what you have. Staying risks only missing potential gains. Losses hurt more.
Cognitive ease. The current situation is known. Alternatives require evaluation effort.
Regret avoidance. If you change and it goes wrong, you will regret the choice. If you stay and miss out, you can blame circumstances.
Ownership. You already "own" your current situation. Endowment effect applies.
Implications for Retention
Status quo bias explains why churn prevention is mostly about avoiding triggers that prompt reconsideration, not about features or pricing.
Most customers are not actively evaluating alternatives. They are on autopilot. They will stay unless something triggers reconsideration.
Triggers to reconsider include: Price increases, service failures, life changes, competitor marketing reaching them at vulnerable moments, contract renewal prompts.
Once triggered, they evaluate rationally. This is when features and pricing matter. But if you prevent the trigger, the evaluation never happens.
import numpy as np
import pandas as pd
# Model status quo bias in subscription retention
np.random.seed(42)
def model_churn_with_triggers(months, base_monthly_churn=0.03, trigger_events=None):
"""Model how trigger events affect churn."""
if trigger_events is None:
trigger_events = {}
remaining = 1.0
monthly_data = []
for month in range(1, months + 1):
# Check for trigger events
trigger_multiplier = trigger_events.get(month, 1.0)
# Status quo bias means churn is low unless triggered
month_churn = base_monthly_churn * trigger_multiplier
churned = remaining * month_churn
remaining -= churned
monthly_data.append({
'month': month,
'remaining': remaining,
'churned': churned,
'trigger': trigger_multiplier > 1
})
return pd.DataFrame(monthly_data)
# Scenario 1: No trigger events
no_triggers = model_churn_with_triggers(24)
# Scenario 2: Price increase at month 12
price_increase = model_churn_with_triggers(24, trigger_events={12: 3.5, 13: 2.0})
# Scenario 3: Annual renewal prompt at month 12
renewal_prompt = model_churn_with_triggers(24, trigger_events={12: 2.5})
print("Status Quo Bias and Churn Triggers")
print("=" * 70)
print(f"{'Scenario':30} | {'12 Month Retention':20} | {'24 Month Retention':20}")
print("-" * 70)
for name, df in [('No triggers', no_triggers),
('Price increase M12', price_increase),
('Renewal prompt M12', renewal_prompt)]:
m12 = df[df['month'] == 12]['remaining'].values[0]
m24 = df[df['month'] == 24]['remaining'].values[0]
print(f"{name:30} | {m12*100:18.1f}% | {m24*100:18.1f}%")
print("\nKey Insight:")
print("Without triggers, status quo bias keeps customers subscribed")
print("Triggers (price changes, renewal prompts) spike churn 2-4x")
print("Churn prevention = trigger avoidance more than feature building")
# Tactics to avoid triggers
print("\n" + "=" * 70)
print("Trigger Avoidance Tactics")
print("-" * 70)
tactics = [
{'trigger': 'Price increase', 'tactic': 'Grandfathering, gradual increases, value addition'},
{'trigger': 'Annual renewal prompt', 'tactic': 'Auto-renewal, rolling contracts, remove friction'},
{'trigger': 'Service failure', 'tactic': 'Proactive communication, rapid recovery'},
{'trigger': 'Competitor outreach', 'tactic': 'Engagement, community, switching cost building'},
{'trigger': 'Life change', 'tactic': 'Flexible pause options, plan adaptation'},
{'trigger': 'Billing issue', 'tactic': 'Dunning sequences, payment method updating'},
]
for t in tactics:
print(f" {t['trigger']:25}: {t['tactic']}")
Implications for Acquisition
The flip side: status quo bias makes new customer acquisition harder. Prospects are biased toward their current solution.
Create trigger events. Help prospects recognise problems with their status quo. Competitor comparison tools, ROI calculators, industry benchmarks.
Reduce switching costs. Free migration, data import tools, onboarding assistance.
Offer trials. Let them experience your product before committing to change.
Target moments of natural change. Job changes, company growth, contract expirations.
Putting It Together: A Behavioural Audit
import pandas as pd
def behavioural_audit(product_context):
"""Generate behavioural economics recommendations."""
audit = {
'dual_process': [],
'biases': [],
'choice_architecture': [],
'time_preference': [],
'mental_accounting': [],
'choice_overload': [],
'status_quo': []
}
# Dual process
if product_context.get('low_consideration', False):
audit['dual_process'].append('Design for System 1: visuals, emotion, simplicity')
else:
audit['dual_process'].append('Support System 2: documentation, comparison tools')
audit['dual_process'].append('But optimise first impression for System 1')
# Choice architecture
if product_context.get('has_tiers', False):
audit['choice_architecture'].append('Set default to preferred tier')
audit['choice_architecture'].append('Use compromise effect: put target tier in middle')
audit['choice_architecture'].append('Audit all defaults: are they intentional?')
audit['choice_architecture'].append('Add friction to cancellation, remove from conversion')
# Time preference
if product_context.get('recurring_revenue', False):
audit['time_preference'].append('Lead with immediate benefit, not annual savings')
audit['time_preference'].append('Consider free trial over discount')
# Mental accounting
if product_context.get('has_returns', False):
audit['mental_accounting'].append('Offer store credit over cash refunds')
if product_context.get('rewards_programme', False):
audit['mental_accounting'].append('Points feel different from money: leverage this')
# Choice overload
if product_context.get('num_options', 0) > 6:
audit['choice_overload'].append('Consider reducing options or progressive disclosure')
audit['choice_overload'].append('Add recommendations to reduce effective choice')
# Status quo
if product_context.get('subscription', False):
audit['status_quo'].append('Map and prevent churn trigger events')
audit['status_quo'].append('Build switching costs through investment')
if product_context.get('acquisition_focus', False):
audit['status_quo'].append('Help prospects see problems with status quo')
audit['status_quo'].append('Reduce switching costs with migration tools')
return audit
# Example: SaaS product audit
saas_context = {
'low_consideration': False,
'has_tiers': True,
'recurring_revenue': True,
'has_returns': False,
'rewards_programme': False,
'num_options': 4,
'subscription': True,
'acquisition_focus': True
}
print("Behavioural Economics Audit: SaaS Product")
print("=" * 70)
audit = behavioural_audit(saas_context)
for category, recommendations in audit.items():
if recommendations:
print(f"\n{category.upper().replace('_', ' ')}:")
for rec in recommendations:
print(f" • {rec}")
print("\n" + "=" * 70)
print("Priority Actions:")
print(" 1. Audit all defaults (highest leverage, lowest effort)")
print(" 2. Map churn triggers and prevention tactics")
print(" 3. Reframe pricing around immediate benefit")
print(" 4. Consider tier reduction or guided selling")
Conclusion: Design for Humans, Not Economists
Behavioural economics is not a collection of tricks to manipulate customers. It is a lens for understanding how humans actually make decisions. Armed with this understanding, you can design experiences that work with human psychology rather than against it.
The core insights:
System 1 dominates. Most decisions are fast, intuitive, emotional. Design for this reality.
Defaults are destiny. The options you pre select become the choices most users make. Use this power responsibly.
Friction shapes behaviour. Every click matters. Add friction where you want to slow things down. Remove it where you want speed.
Now beats later. Immediate rewards trump future benefits, even when the math does not support it.
Money has buckets. The same amount is spent differently depending on its source and framing.
Less is more. Above a complexity threshold, fewer options convert better.
Inertia is powerful. People stick with what they have unless something triggers reconsideration.
Every design decision is a choice architecture decision. The question is not whether to influence behaviour. You already are. The question is whether to do so intentionally and in ways that serve both your users and your business.
I have been applying behavioural economics to product design and marketing for over a decade. The frameworks in this post come from academic research but have been tested and refined in commercial contexts across e-commerce, SaaS, and B2B. The code examples are production ready.
Need help auditing your product through a behavioural economics lens? Or designing experiments to test these frameworks in your specific context? I can help you identify the highest leverage interventions and build measurement systems that show what actually works. Let us chat.