A common tension in empirical work involves covariates that are
affected by the treatment (aka “bad controls”), where there is an
argument for (at least in some sense) trying to control for them, but
there are also issues that arise when controlling for them directly. In
Caetano et al. (2026), in the context of difference-in-differences
identification strategies, we provide two approaches to dealing with bad
controls that respect the bad control being affected by the treatment
while playing a genuine role as a covariate too. The
badcontrols package implements these approaches.
Our paper considers two alternative approaches to identifying the untreated potential version of the bad control:
Condition on the pre-treatment value of the bad control. This approach leads to a version of Callaway and Sant’Anna (2021) that involves conditioning on the pre-treatment value of the bad control. It is also simple and can be implemented with standard tools.
Assume covariate unconfoundedness for the bad control. This generalizes the first approach by modeling the bad control’s untreated evolution using additional covariates. This approach leads to a more complicated estimator, but the assumptions may be more credible in some applications.
For both approaches, badcontrols provides imputation,
parametric doubly robust, and machine-learning estimators. The main
function is didbc(), which follows a syntax similar to
did::att_gt() and ptetools::pte_default().
Paper: Caetano et al. (2026).
The development version can be installed from GitHub:
install.packages("remotes")
remotes::install_github("hugosantanna/badcontrols")The package includes a simulation with a treatment-affected covariate
X and exogenous covariate Z. The demo here is
based on the covariate unconfoundedness assumption (New Approach 2
above), where we assume that unconfoundedness holds for the bad control
after conditioning on the exogenous covariate Z, the
pre-treatment value of the bad control, and the lagged outcome. The data
that we generate below is a panel with 2000 units and 4 periods.
library(badcontrols)
sim <- simulate_bad_controls(n = 2000, T_max = 4)
head(sim$data) id period G D Y X Z W
1 1 1 0 0 -1.709723760 -1.05953298 -1.2070657 -1.3337493
2 1 2 0 0 -1.564713922 -1.76561193 -1.2070657 -1.3337493
3 1 3 0 0 -2.423375081 -1.63998935 -1.2070657 -1.3337493
4 1 4 0 0 -1.494701781 -1.16835526 -1.2070657 -1.3337493
5 2 1 2 0 -0.003018587 -0.04756332 0.2774292 -0.1592186
6 2 2 2 1 1.833864636 0.85624078 0.2774292 -0.1592186
res <- didbc(
yname = "Y",
gname = "G",
tname = "period",
idname = "id",
data = sim$data,
bad_control_formula = ~X,
xformula = ~Z,
bad_control_cov_formula = ~Y,
est_method = "dr_ml",
nuisance_method = "parametric",
bstrap = FALSE
)
summary(res)Overall ATT:
ATT Std. Error [ 95% Conf. Int.]
1.4559 0.0304 1.3964 1.5154 *
Dynamic Effects:
Event Time Estimate Std. Error [95% Pointwise Conf. Band]
-2 -0.0323 0.0369 -0.1047 0.0401
-1 0.0780 0.0315 0.0163 0.1397 *
0 1.0931 0.0239 1.0462 1.1400 *
1 1.7832 0.0368 1.7111 1.8553 *
2 2.4412 0.0539 2.3356 2.5468 *
---
Signif. codes: `*' confidence band does not cover 0
The same interface can be used with
est_method = "imputation" or with
nuisance_method = "ml" for cross-fitted machine-learning
nuisance estimates.
To cite the paper underlying this package:
Caetano, C., Callaway, B., Payne, S., and Sant’Anna, H. (2026). “Difference-in-Differences with Bad Controls.” arXiv preprint arXiv:2608.03881. https://arxiv.org/abs/2608.03881
@article{caetano2026badcontrols,
title = {Difference-in-Differences with Bad Controls},
author = {Caetano, Carolina and Callaway, Brantly and Payne, Stroud and Sant'Anna, Hugo},
journal = {arXiv preprint arXiv:2608.03881},
year = {2026},
url = {https://arxiv.org/abs/2608.03881}
}
To cite the badcontrols package itself:
Caetano, C., Callaway, B., Payne, S., and Sant’Anna, H. (2026). badcontrols: Difference-in-Differences with Bad Controls. R package version 1.0.0. https://github.com/hugosantanna/badcontrols
@Manual{caetano2026badcontrolspkg,
title = {{badcontrols}: Difference-in-Differences with Bad Controls},
author = {Carolina Caetano and Brantly Callaway and Stroud Payne and Hugo Sant'Anna},
year = {2026},
note = {R package version 1.0.0},
url = {https://github.com/hugosantanna/badcontrols}
}
GPL (>= 3)