Replication Lab: Autor (2003) Wrongful Discharge Laws and Outsourcing
Replicate the event study analysis from Autor (2003) on the effect of wrongful discharge laws on temporary help employment. Construct event-time indicators, estimate dynamic treatment effects, test pre-trends, and apply the Sun-Abraham heterogeneity-robust estimator.
Overview
David Autor's 2003 paper "Outsourcing at Will: The Contribution of Unjust Dismissal Doctrine to the Growth of Temporary Employment" (Journal of Labor Economics, 21(1), 1–42; DOI: 10.1086/344122) examines how the adoption of wrongful discharge laws (specifically, the implied-contract exception to employment-at-will) affected the use of temporary help services across U.S. states. The paper uses the staggered adoption of these laws across states between the 1970s and 1990s as a natural experiment, estimating event study models to trace out the dynamic effects.
Key findings:
- Temporary help employment increases significantly after adoption of wrongful discharge laws
- The pre-adoption coefficients are close to zero, supporting the parallel trends assumption
- The effect builds gradually over several years post-adoption
- The magnitude implies that wrongful discharge laws account for a meaningful share of the rise in temporary employment
What you will learn:
- How to construct event-time (relative time) indicators for a staggered adoption design
- How to estimate a two-way fixed effects (TWFE) event study regression
- How to create and interpret the event study plot
- How to formally test for pre-trends
- How to implement the Sun and Abraham (2021) heterogeneity-robust estimator
- Why staggered adoption creates problems for TWFE and how to address them
Prerequisites: Difference-in-differences, fixed effects (see the DiD and FE labs).
Step 1: Generate the Simulated Panel Dataset
library(fixest)
library(modelsummary)
# Simulate state-year panel with staggered adoption
set.seed(42)
n_states <- 50
n_years <- 25
years <- 1975:1999
# Staggered adoption
adopt_year <- rep(NA, n_states)
treated_states <- sample(1:n_states, 35)
for (s in treated_states) {
adopt_year[s] <- sample(1980:1994, 1)
}
state_fe <- rnorm(n_states, 0, 0.5)
year_fe <- seq(0, 1.5, length.out = n_years)
df <- expand.grid(state = 1:n_states, year = years)
df$adopt_year <- adopt_year[df$state]
df$ever_treated <- as.integer(!is.na(df$adopt_year))
df$event_time <- ifelse(df$ever_treated == 1, df$year - df$adopt_year, NA)
df$treated_post <- as.integer(!is.na(df$adopt_year) & df$year >= df$adopt_year)
# Dynamic treatment effect
df$tau <- ifelse(df$treated_post == 1 & df$event_time >= 0,
0.05 * pmin(df$event_time, 8) + 0.01 * df$event_time, 0)
df$y <- 2.0 + state_fe[df$state] + year_fe[df$year - 1974] +
df$tau + rnorm(nrow(df), 0, 0.15)
cat("Panel:", n_states, "states x", n_years, "years =", nrow(df), "obs\n")
cat("Treated:", sum(!is.na(adopt_year)), " Never-treated:", sum(is.na(adopt_year)), "\n")Expected output:
| state | year | y | treated_post | event_time | adopt_year | ever_treated |
|---|---|---|---|---|---|---|
| 0 | 1975 | 1.87 | 0 | -12.0 | 1987 | 1 |
| 0 | 1980 | 2.15 | 0 | -7.0 | 1987 | 1 |
| 0 | 1990 | 2.68 | 1 | 3.0 | 1987 | 1 |
| 35 | 1985 | 2.51 | 0 | — | — | 0 |
| 35 | 1995 | 3.12 | 0 | — | — | 0 |
Summary statistics:
| Statistic | Value |
|---|---|
| Panel dimensions | 50 states x 25 years = 1,250 obs |
| Treated states | 35 |
| Never-treated states | 15 |
| Adoption years | 1980–1994 (staggered) |
| Mean outcome (y) | ~2.0 + state FE + time trend |
| DGP treatment effect | 0.05 * min(k, 8) + 0.01 * k |
Step 2: Construct Event-Time Dummies
# Create event-time dummies for TWFE
# Bin endpoints, reference period = -1
df$et <- ifelse(df$ever_treated == 1, df$event_time, NA)
df$et_binned <- pmax(pmin(df$et, 10), -6) # Bin at -6 and +10
# For fixest, we can use the i() function directly
# But let us also create manual dummies for transparency
for (k in -6:10) {
if (k == -1) next # reference period
varname <- paste0("et_", ifelse(k < 0, paste0("m", abs(k)), k))
if (k == -6) {
df[[varname]] <- as.integer(df$ever_treated == 1 & df$et <= k)
} else if (k == 10) {
df[[varname]] <- as.integer(df$ever_treated == 1 & df$et >= k)
} else {
df[[varname]] <- as.integer(df$ever_treated == 1 & df$et == k)
}
}
cat("Event-time dummies created. Reference period: t = -1\n")Expected output:
| Event-time indicator | Description | Observations |
|---|---|---|
| et_m6 (k <= -6) | Binned pre-period | ~210 (treated obs 6+ years before) |
| et_m5 (k = -5) | 5 years before adoption | ~35 |
| et_m4 (k = -4) | 4 years before | ~35 |
| et_m3 (k = -3) | 3 years before | ~35 |
| et_m2 (k = -2) | 2 years before | ~35 |
| k = -1 | Reference (omitted) | — |
| et_0 (k = 0) | Year of adoption | ~35 |
| et_1 to et_9 | 1–9 years after | ~35 each |
| et_10 (k >= 10) | Binned post-period | ~175 (treated obs 10+ years after) |
Step 3: Estimate the TWFE Event Study
# TWFE Event Study using fixest
# fixest handles event studies elegantly with i() syntax
es_model <- feols(y ~ i(et_binned, ever_treated, ref = -1) |
state + year, data = df,
cluster = ~state)
summary(es_model)
# The coefficients are automatically named by event timeExpected output:
| Event Time (k) | TWFE Coefficient | SE | True Effect (DGP) |
|---|---|---|---|
| -6 (binned) | ~0.00 | 0.04 | 0 |
| -5 | ~0.01 | 0.05 | 0 |
| -4 | -0.01 | 0.05 | 0 |
| -3 | 0.02 | 0.05 | 0 |
| -2 | -0.01 | 0.05 | 0 |
| -1 (ref) | 0.000 | -- | 0 |
| 0 | ~0.06 | 0.05 | 0.05 |
| 1 | ~0.11 | 0.05 | 0.11 |
| 2 | ~0.17 | 0.05 | 0.17 |
| 3 | ~0.23 | 0.05 | 0.23 |
| 4 | ~0.29 | 0.05 | 0.29 |
| 5 | ~0.35 | 0.05 | 0.35 |
| 6 | ~0.41 | 0.06 | 0.41 |
| 7 | ~0.47 | 0.06 | 0.47 |
| 8 | ~0.48 | 0.06 | 0.48 |
| 9 | ~0.49 | 0.06 | 0.49 |
| 10 (binned) | ~0.50 | 0.05 | 0.50+ |
The DGP treatment effect is tau = 0.05 * min(k, 8) + 0.01 * k. Post-treatment coefficients should rise steadily, leveling off around k = 8 where the 0.05-per-year component caps.
Step 4: Plot the Event Study Coefficients
# Event study plot using fixest
iplot(es_model,
main = "Event Study: Wrongful Discharge Laws and Temp Employment",
xlab = "Event Time (years relative to adoption)",
ylab = "Coefficient (relative to t = -1)")
abline(v = -0.5, col = "red", lty = 2)
# Alternative: manual plot
coefs <- coef(es_model)
ses <- se(es_model)
# Extract event times from coefficient names
# (fixest names them automatically)Step 5: Formal Pre-Trends Test
# Joint test of pre-trends using fixest::wald
# Test that all pre-treatment coefficients are jointly zero
pre_test <- wald(es_model, "et_binned::-[2-6]")
print(pre_test)
# Alternative: manual F-test
# Get pre-treatment coefficient names
pre_names <- grep("et_binned::-[2-6]", names(coef(es_model)), value = TRUE)
if (length(pre_names) > 0) {
linearHypothesis_result <- car::linearHypothesis(es_model, pre_names)
print(linearHypothesis_result)
}Expected output:
| Statistic | Value |
|---|---|
| H0 | All pre-treatment coefficients = 0 |
| Wald chi-squared | ~2.0–6.0 |
| Degrees of freedom | 5 (testing k = -6 through k = -2) |
| p-value | > 0.05 (typically 0.3–0.8) |
| Conclusion | PASS: Cannot reject flat pre-trends |
| Pre-trend coefficient | Estimate | t-statistic |
|---|---|---|
| et_m6 | ~0.00 | ~0.1 |
| et_m5 | ~0.01 | ~0.2 |
| et_m4 | ~-0.01 | ~-0.2 |
| et_m3 | ~0.02 | ~0.4 |
| et_m2 | ~-0.01 | ~-0.2 |
All individual pre-treatment coefficients are small and statistically insignificant (|t| < 1.96).
The joint pre-trends test fails to reject the null (p > 0.05). Does this prove that the parallel trends assumption holds?
Step 6: Sun-Abraham Heterogeneity-Robust Estimator
Recent research by Sun and Abraham (2021) shows that TWFE event study estimates can be biased under treatment effect heterogeneity across cohorts. The Sun-Abraham estimator provides a heterogeneity-robust alternative.
# Sun-Abraham estimator using fixest
# fixest implements this with sunab()
df$cohort <- ifelse(is.na(df$adopt_year), 10000, df$adopt_year) # Never-treated = Inf
sa_model <- feols(y ~ sunab(cohort, year) | state + year,
data = df[df$cohort != 10000 | df$ever_treated == 0, ],
cluster = ~state)
summary(sa_model)
# Compare TWFE and Sun-Abraham plots
par(mfrow = c(1, 2))
iplot(es_model, main = "TWFE Event Study")
iplot(sa_model, main = "Sun-Abraham Event Study")
# Aggregate Sun-Abraham estimates
summary(sa_model, agg = "att")Expected output:
| Event Time (k) | TWFE Coefficient | Sun-Abraham Coefficient |
|---|---|---|
| -6 | ~0.00 | ~0.00 |
| -4 | ~-0.01 | ~-0.01 |
| -2 | ~-0.01 | ~-0.01 |
| -1 (ref) | 0.000 | 0.000 |
| 0 | ~0.06 | ~0.06 |
| 2 | ~0.17 | ~0.17 |
| 5 | ~0.35 | ~0.36 |
| 8 | ~0.48 | ~0.49 |
| 10 | ~0.50 | ~0.51 |
With the relatively homogeneous treatment effects in this DGP, the TWFE and Sun-Abraham estimates should be very similar. With real data exhibiting treatment effect heterogeneity across cohorts, larger differences would emerge.
In a staggered adoption design, the TWFE estimator can produce biased estimates of the event study coefficients. Which of the following is the key reason?
Step 7: Compare with Published Results
Key comparisons with Autor (2003):
| Feature | Autor (2003) | Our Simulation |
|---|---|---|
| Pre-trends | Flat (insignificant) | Check your pre-trend test |
| Post-treatment effect | Positive, growing | Check your event study plot |
| Effect builds over time | Yes (gradual) | Should match our DGP |
| State + year FE | Yes | Yes |
| Clustered SE | At state level | At state level |
The central finding is that wrongful discharge laws cause an increase in temporary employment, with the effect building gradually over several years. The pre-treatment coefficients should be close to zero, supporting the parallel trends assumption.
Extension Exercises
-
Callaway-Sant'Anna estimator. Implement the Callaway and Sant'Anna (2021) estimator using the
didpackage in R orcsdidin Stata. Compare the group-time ATT estimates with the TWFE results. -
Treatment effect heterogeneity. Modify the DGP so that early adopters have larger treatment effects than late adopters. Re-estimate the TWFE event study and the Sun-Abraham estimator. How much does the bias in TWFE increase?
-
Anticipation effects. Add anticipation effects to the DGP (effects begin 1-2 years before formal adoption, as states may announce laws before enacting them). How does this affect the pre-trends test and the estimated treatment effects?
-
Bacon decomposition. Implement the Goodman-Bacon (2021) decomposition to understand the weights TWFE places on different 2x2 DiD comparisons. Which comparisons receive negative weights?
-
Wild cluster bootstrap. With only 50 clusters, asymptotic cluster-robust inference may be unreliable. Implement the wild cluster bootstrap ((Cameron et al., 2008)) and compare confidence intervals with the conventional clustered SEs.
Summary
In this replication lab you learned:
- Event study designs trace out dynamic treatment effects and provide a visual test of parallel trends
- The reference period (t = -1) is conventional; all coefficients are relative to the period just before treatment
- Flat pre-treatment coefficients are necessary (but not sufficient) for the parallel trends assumption
- Standard TWFE event studies can be biased under staggered adoption with heterogeneous treatment effects
- The Sun and Abraham (2021) estimator avoids "forbidden comparisons" by estimating cohort-specific effects and aggregating them with proper weights
- Our simulated results reproduce the key pattern from Autor (2003): flat pre-trends followed by a gradual increase in temporary employment after wrongful discharge law adoption
- It is recommended to report the event study plot, pre-trends test, and sensitivity to alternative estimators