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.