Provider Behavior
The physician as imperfect agent
In most markets, buyers evaluate products themselves. Healthcare is different: patients lack the expertise to assess what care they need. They rely on physicians to recommend treatments — but the physician is also the seller. This dual role creates a principal-agent problem: the patient (principal) delegates decision-making to the physician (agent), whose interests may not align perfectly with the patient’s.
Payment systems and incentives
How we pay physicians shapes what they do:
| Payment model | How it works | Incentive |
|---|---|---|
| Fee-for-service (FFS) | Paid per service rendered | Do more |
| Capitation | Paid a fixed amount per patient per period | Do less |
| Salary | Paid a fixed wage regardless of volume | Do neither more nor less |
Under FFS, the physician’s income rises with the number of services provided. The marginal revenue of an additional test, visit, or procedure is positive. This creates an incentive to over-provide care — ordering tests of marginal value, scheduling unnecessary follow-ups, or recommending procedures when watchful waiting would suffice.
Under capitation, the physician receives a fixed payment per enrolled patient. Every service provided is a pure cost. This creates an incentive to under-provide — see patients briefly, avoid expensive tests, and refer complex cases elsewhere.
Under salary, the physician’s income is independent of volume. In theory, this eliminates volume incentives. In practice, salaried physicians may underwork (no reward for effort) or may respond to non-monetary incentives like professional norms.
Supply-induced demand
The most provocative claim in provider economics is supply-induced demand (SID): physicians can shift the demand curve itself because patients follow their recommendations. If a region gets more surgeons, there will be more surgeries — not because the population got sicker, but because physicians recommend surgery more liberally when competition heats up.
The classic evidence comes from the Dartmouth Atlas of Health Care, which documents enormous geographic variation in care intensity. Medicare spending per beneficiary varies by a factor of 2–3 across US regions, with little corresponding variation in health outcomes. Some of this is SID; some is practice style variation; untangling the two remains an active research area.
Physician behavior under different payment models
#| standalone: true
#| viewerHeight: 650
library(shiny)
ui <- fluidPage(
tags$head(tags$style(HTML("
.stats-box {
background: #f0f4f8; border-radius: 6px; padding: 14px;
margin-top: 12px; font-size: 14px; line-height: 1.9;
}
.stats-box b { color: #2c3e50; }
.good { color: #27ae60; font-weight: bold; }
.bad { color: #e74c3c; font-weight: bold; }
.info-box {
background: #ebf5fb; border-radius: 6px; padding: 12px;
margin-top: 10px; font-size: 13px;
}
"))),
sidebarLayout(
sidebarPanel(
width = 3,
selectInput("payment", "Payment model:",
choices = c("Fee-for-service" = "ffs",
"Capitation" = "cap",
"Salary" = "sal"),
selected = "ffs"),
sliderInput("ffs_rate", "FFS rate ($/service):",
min = 50, max = 500, value = 200, step = 25),
sliderInput("agency", "Physician concern for patient (0-1):",
min = 0, max = 1, value = 0.5, step = 0.05),
tags$div(class = "info-box",
HTML("<b>0</b> = pure profit maximizer<br>
<b>1</b> = perfect patient agent")
),
tags$hr(),
uiOutput("info_box")
),
mainPanel(
width = 9,
plotOutput("provider_plot", height = "500px")
)
)
)
server <- function(input, output, session) {
vals <- reactive({
payment <- input$payment
ffs_rate <- input$ffs_rate
agency <- input$agency
# Patient-optimal quantity (where marginal benefit = marginal cost to patient)
# Assume diminishing returns: MB(Q) = 100 - 5*Q
# Social marginal cost = $100 per service
MC <- 100
# Patient optimal: MB = MC => Q* = (100 - MC) / 5 = 0... let's rescale
# MB(Q) = 500 - 20*Q, MC = 100 => Q* = 20
MB <- function(Q) 500 - 20 * Q
Q_optimal <- (500 - MC) / 20 # = 20
# Physician's choice depends on payment model
if (payment == "ffs") {
# Under FFS, marginal revenue = ffs_rate
# Physician weighs: agency * MB(Q) + (1 - agency) * ffs_rate vs MC_effort
# Physician provides until: agency * MB(Q) + (1-agency) * ffs_rate = MC_effort
# Simplify: MC_effort for physician is low (say 50)
MC_phys <- 50
# Q_chosen: agency * (500 - 20*Q) + (1-agency) * ffs_rate = MC_phys
Q_chosen <- (agency * 500 + (1 - agency) * ffs_rate - MC_phys) / (agency * 20)
Q_chosen <- max(1, min(Q_chosen, 30))
} else if (payment == "cap") {
# Under capitation, MR = 0 (fixed payment regardless)
# Physician provides until: agency * MB(Q) = MC_effort
MC_phys <- 50
Q_chosen <- (agency * 500 - MC_phys) / (agency * 20)
Q_chosen <- max(1, min(Q_chosen, 30))
} else {
# Under salary, MR = 0 but effort is less costly (no production pressure)
MC_phys <- 50
# Similar to capitation but with slightly more provision (professional norms)
norm_boost <- 0.2 # salary physicians provide a bit more due to norms
Q_chosen <- (agency * 500 - MC_phys) / (agency * 20) + norm_boost * Q_optimal
Q_chosen <- max(1, min(Q_chosen, 30))
}
# Health outcome (concave in Q, with diminishing returns)
health <- function(Q) 100 * (1 - exp(-0.15 * Q))
H_optimal <- health(Q_optimal)
H_chosen <- health(Q_chosen)
# Physician income
if (payment == "ffs") {
phys_income <- Q_chosen * ffs_rate
} else if (payment == "cap") {
phys_income <- 5000 # fixed capitation
} else {
phys_income <- 8000 # fixed salary
}
# Social welfare = patient benefit - social cost
# Patient benefit at Q: integral of MB from 0 to Q = 500*Q - 10*Q^2
benefit <- function(Q) 500 * Q - 10 * Q^2
SW_optimal <- benefit(Q_optimal) - MC * Q_optimal
SW_chosen <- benefit(Q_chosen) - MC * Q_chosen
# Gap
gap <- Q_chosen - Q_optimal
list(Q_optimal = Q_optimal, Q_chosen = Q_chosen,
H_optimal = H_optimal, H_chosen = H_chosen,
phys_income = phys_income, SW_optimal = SW_optimal,
SW_chosen = SW_chosen, gap = gap,
payment = payment, agency = agency,
MC = MC, ffs_rate = ffs_rate)
})
output$provider_plot <- renderPlot({
v <- vals()
par(mfrow = c(1, 2), mar = c(5, 5, 3, 2))
# Panel 1: Quantity comparison
Q_seq <- seq(0, 30, length.out = 200)
MB_seq <- 500 - 20 * Q_seq
plot(Q_seq, MB_seq, type = "l", lwd = 3, col = "#2c3e50",
xlab = "Quantity of care", ylab = "$ per unit",
main = "Marginal Benefit vs Cost",
ylim = c(0, 550), xlim = c(0, 30),
cex.lab = 1.1)
abline(h = v$MC, lwd = 2, lty = 2, col = "#7f8c8d")
text(28, v$MC + 20, "MC (social)", col = "#7f8c8d", cex = 0.85)
# Mark optimal Q
abline(v = v$Q_optimal, lty = 3, col = "#27ae60", lwd = 2)
text(v$Q_optimal + 0.5, 500, expression(Q * "*" ~ "(optimal)"),
col = "#27ae60", cex = 0.85, adj = 0)
# Mark chosen Q
abline(v = v$Q_chosen, lty = 3, col = "#e74c3c", lwd = 2)
text(v$Q_chosen + 0.5, 450,
paste0("Q chosen (", round(v$Q_chosen, 1), ")"),
col = "#e74c3c", cex = 0.85, adj = 0)
# Shade the gap
if (abs(v$gap) > 0.5) {
q_lo <- min(v$Q_optimal, v$Q_chosen)
q_hi <- max(v$Q_optimal, v$Q_chosen)
q_shade <- seq(q_lo, q_hi, length.out = 100)
if (v$gap > 0) {
# Over-provision: DWL between optimal and chosen
polygon(c(q_shade, rev(q_shade)),
c(500 - 20 * q_shade, rep(v$MC, length(q_shade))),
col = adjustcolor("#e74c3c", 0.2), border = NA)
text((q_lo + q_hi) / 2, (500 - 20 * (q_lo + q_hi) / 2 + v$MC) / 2,
"Over-\nprovision", col = "#e74c3c", cex = 0.8, font = 2)
} else {
polygon(c(q_shade, rev(q_shade)),
c(500 - 20 * q_shade, rep(v$MC, length(q_shade))),
col = adjustcolor("#3498db", 0.2), border = NA)
text((q_lo + q_hi) / 2, (500 - 20 * (q_lo + q_hi) / 2 + v$MC) / 2,
"Under-\nprovision", col = "#3498db", cex = 0.8, font = 2)
}
}
if (v$payment == "ffs") {
abline(h = v$ffs_rate, lwd = 1.5, lty = 4, col = "#9b59b6")
text(28, v$ffs_rate + 20, "FFS rate", col = "#9b59b6", cex = 0.85)
}
# Panel 2: Outcomes comparison
labels <- c("Patient\nOptimal", "Physician\nChosen")
bar_colors <- c("#27ae60", "#e74c3c")
# Health outcomes
health_vals <- c(v$H_optimal, v$H_chosen)
bp <- barplot(health_vals, names.arg = labels,
col = bar_colors, main = "Health Outcome",
ylab = "Patient health (0-100)",
ylim = c(0, 110), cex.lab = 1.1, cex.names = 0.9)
text(bp, health_vals + 3,
paste0(round(health_vals, 1)), font = 2, cex = 0.95)
# Add welfare info at bottom
mtext(paste0("Social welfare gap: ",
ifelse(v$SW_chosen < v$SW_optimal,
paste0("-$", format(round(v$SW_optimal - v$SW_chosen), big.mark = ",")),
"$0")),
side = 1, line = 3.5, cex = 0.9,
col = ifelse(v$SW_chosen < v$SW_optimal, "#e74c3c", "#27ae60"))
})
output$info_box <- renderUI({
v <- vals()
gap_txt <- if (abs(v$gap) < 0.5) {
"<span class='good'>Approximately optimal</span>"
} else if (v$gap > 0) {
paste0("<span class='bad'>Over-provision: +", round(v$gap, 1), " units</span>")
} else {
paste0("<span class='bad'>Under-provision: ", round(v$gap, 1), " units</span>")
}
payment_label <- switch(v$payment,
ffs = "Fee-for-service",
cap = "Capitation",
sal = "Salary")
tags$div(class = "stats-box",
HTML(paste0(
"<b>Payment:</b> ", payment_label, "<br>",
"<b>Agency:</b> ", v$agency, "<br>",
"<hr style='margin:8px 0'>",
"<b>Optimal quantity:</b> ", round(v$Q_optimal, 1), "<br>",
"<b>Chosen quantity:</b> ", round(v$Q_chosen, 1), "<br>",
"<b>Gap:</b> ", gap_txt, "<br>",
"<hr style='margin:8px 0'>",
"<b>Patient health:</b> ", round(v$H_chosen, 1), " / ",
round(v$H_optimal, 1), "<br>",
"<b>Physician income:</b> $",
format(round(v$phys_income), big.mark = ","), "<br>",
"<b>Social welfare loss:</b> $",
format(round(max(0, v$SW_optimal - v$SW_chosen)), big.mark = ",")
))
)
})
}
shinyApp(ui, server)
Things to try
- Under FFS, set agency to 0 (pure profit maximizer): the physician provides far more than optimal. Health barely improves because of diminishing returns, but physician income is high.
- Under FFS, raise the FFS rate: over-provision gets worse as each additional service is more profitable.
- Switch to capitation: over-provision disappears but is replaced by under-provision. The physician provides less than optimal to save costs.
- Set agency to 1 under any payment model: the physician provides the optimal quantity regardless of payment — a perfect agent is not distorted by financial incentives.
- Compare physician income across models: FFS income depends on volume, capitation and salary are fixed.
The target income hypothesis
One additional theory: physicians may have a target income in mind. If fees are cut (say, by Medicare), FFS physicians may increase volume to maintain their income — the opposite of what standard supply-and-demand predicts. This is the target income hypothesis, and evidence for it is mixed but persistent.
The Dartmouth Atlas findings are consistent with this: regions with more physicians per capita have higher utilization rates, not lower prices. More supply does not reduce the price of care — it increases the quantity, suggesting that supply creates its own demand.
Did you know?
McGuire and Pauly (1991) developed the foundational model of physician agency, showing that the direction of the distortion depends entirely on the payment mechanism. Under FFS, physicians over-provide; under capitation, they under-provide. The optimal payment system is a blend — part FFS, part capitation — that makes the physician’s marginal revenue equal to the marginal social benefit of care.
The ACA (2010) and subsequent legislation have pushed US healthcare toward value-based payment, which ties reimbursement to outcomes rather than volume. Accountable Care Organizations (ACOs), bundled payments, and pay-for-performance programs all attempt to align physician incentives with patient welfare. Results have been mixed — savings exist but are modest.
The Dartmouth Atlas of Health Care has documented since the 1990s that Medicare spending per beneficiary varies by a factor of 2–3 across US Hospital Referral Regions. Patients in Miami receive far more care than patients in Minneapolis, with no better outcomes. Jack Wennberg, who founded the Atlas project, called this “unwarranted variation” and argued it reflects supply-induced demand and practice style differences rather than patient needs.