Review instructions for the balance Python package (weighting and balancing utilities for correcting bias in tabular datasets). Prioritize correctness, statistical validity, reproducibility, and backward compatibility.
For project architecture, build/test commands, and file layout, see CLAUDE.md in the repository root (at Meta: fbcode/core_stats/balance/CLAUDE.md).
- Each PR should represent one self-contained idea that is easy to test, review, and accept.
- Flag PRs that combine multiple unrelated changes:
- mixing feature additions with refactoring
- addressing multiple independent bugs in one PR
- combining documentation updates with unrelated code changes
- bundling multiple distinct features together
- If a PR touches multiple subsystems or modules for unrelated reasons, suggest splitting into focused PRs.
- Large PRs are acceptable if all changes serve a single, cohesive goal (e.g., implementing one feature that naturally spans multiple files).
- When flagging scope issues:
- list the distinct concerns or ideas present in the PR
- suggest a logical split (e.g., "Consider separating the refactoring into its own PR")
- explain how splitting will improve reviewability and testability
- For externally-authored PRs imported from
facebookresearch/balance, do not assume open-source review covered API consistency — run §5.5 explicitly even if the GitHub PR is already approved. CI does not flag taste/consistency drift.
- Verify the implementation matches the intended method (IPW / CBPS / rake / poststratification).
- Confirm assumptions and constraints are handled explicitly (e.g., positivity, normalization, convergence criteria).
- Check output semantics: shapes, index alignment, column names, and dtype stability.
- Ensure missingness and invalid inputs have well-defined behavior (error vs. warning vs. coercion).
- When the diff touches
balance/interop/diff_diff.py, verify theweight_type="pweight"contract is still upheld — diff-diff's staggered estimators (CallawaySantAnna, StackedDiD, ImputationDiD, HeterogeneousAdoptionDiD, TwoStageDiD, WooldridgeDiD, TROP, StaggeredTripleDifference, ChaisemartinDHaultfoeuille, TripleDifference, SyntheticDiD) each inline aweight_type != "pweight"rejection in theirfit()method (the canonical example isCallawaySantAnna.fitin diff-diff'sstaggered.py). The history-column drop inbalance.interop._common.drop_history_columnsmust run before anydd.aggregate_survey()handoff to avoid leakingweight_pre_adjust/weight_adjusted_*as covariates. (Symbol-based anchors instead of line numbers — line numbers in cross-repo files rot silently the moment either repo is touched.)
- Validate early: required columns exist, dtypes are supported, and parameter ranges are enforced.
- Weights: explicitly handle/forbid zero, negative, infinite, or NaN weights (as appropriate for the API).
- Make error messages actionable:
- name the missing/invalid column
- list available columns when relevant
- state how to fix it
- Prefer existing helpers in
utils.pyinstead of duplicating validation logic.
- New or changed behavior MUST be covered by deterministic
pytesttests undertests/. - Exercise edge cases when applicable:
- missing columns / schema mismatch
- unexpected dtypes (object/category/int/float/bool)
- NaN/inf handling in inputs and outputs
- extreme/boundary weights, clipping, normalization
- empty dataframes / single-row inputs
- Keep tests stable:
- avoid order/time dependence and uncontrolled randomness
- if randomness is necessary, fix seeds and assert with tolerances
- Coverage expectation: aim for >90% coverage on new code (
pytest --cov). - Prefer using
from balance import load_datain tests when appropriate.
- The codebase is Pyre-typed (
# pyre-strict) withfrom __future__ import annotationson every file. - New/modified public APIs must have complete type hints. Avoid returning
Anyor widening types unless justified. - New/modified public functions/classes must include a docstring with at least one concrete usage example.
- MIT license header required on every source file.
- Do not silently change defaults, return shapes, column names, or CLI flags.
- If a breaking change is intentional:
- call it out clearly in the PR summary
- add migration guidance and “before → after” examples
- For deprecations:
- use proper warnings
- document timeline and replacement usage
- update changelog accordingly
For every new public default, parameter name, or string-literal option the PR introduces, do the following — and quote evidence in your review comment, not just a verdict:
A. Default-value alignment. For each new keyword default (library=, threshold=, order_by=, line=, show=, bar_width=, etc.):
- Grep the package for the same parameter name on neighbouring methods (start with
BalanceDF.plot,BalanceDFCovars.*,BalanceDFOutcomes.*,BalanceDFWeights.*, andstats_and_plots/). - Quote both defaults: "
BalanceDF.plotdefaults tolibrary='plotly', this PR introduceslibrary='seaborn'onBalanceDFCovars.love_plot." - If they differ, require either (a) the new default is changed to match, or (b) the PR summary documents why divergence is correct.
B. Parallel-parameter detection. When a PR adds a new parameter to a method that already has a similar dispatch parameter:
- Identify the existing dispatch parameter (e.g.
dist_typeonBalanceDF.plot). - Check whether the new parameter's accept-list could be expressed as additional values on the existing parameter's
Literal. - If yes, request removal of the new parameter and extension of the existing
Literalinstead. Example:plot_type="love_plot"next todist_type="love_plot"— pickdist_type, dropplot_type. One spelling per concept.
C. Option-name truthfulness. For each new string-literal accept value (e.g. order_by="max", library="balance", metric="kld"):
- Read the name literally and write down what you'd expect the implementation to do.
- Read the implementation.
- If the name's natural reading does not match the implementation, flag it — propose either a rename or a different implementation. Example:
order_by="max"implemented asdata.abs().max(axis=1)is "max of|before|and|after|" — the name doesn't disambiguate signed/absolute or pre/post; rename to something explicit ("max_abs","diff", etc.) or change the semantics.
D. Type-system reach. For each new Literal[...] introduced (e.g. LovePlotLibrary, LovePlotOrderBy):
- Verify it's exported from a stable location and reused in any sibling method that takes the same parameter.
- If a sibling method's signature uses
stror a differentLiteral, flag the divergence.
- User-visible fixes/features MUST include an entry in
CHANGELOG.md. - Breaking changes MUST be explicitly labeled and include migration notes.
- Tutorial framing for interpretive defaults. When the PR changes (or adds) a default that controls how a tutorial output is read — sort order, axis direction, scale, sign convention, baseline — verify the first tutorial cell that exercises the default has a markdown sentence motivating it, not just the option-list cell that comes later. Concrete check: open the relevant
.ipynbcell, find the markdown immediately above (or referencing) the new-default call, and confirm a reader running only that one cell can answer "what does the visual order/scale/sign mean here?". If they can't, request a one-sentence framing in that markdown.
- New dependencies should be rare, lightweight, and justified.
- If touching requirements/setup metadata:
- verify supported Python versions
- avoid unnecessary pin churn
- consider transitive impact
- Flag performance regressions using representative dataset sizes.
- Avoid unnecessary dataframe copies and large intermediate objects.
- Prefer vectorized operations (NumPy/pandas) over Python loops.
- For iterative/optimization routines: ensure stopping criteria, max iterations, and tolerances are documented and tested.
- Favor clear, pandas-friendly code.
- Avoid mutating user-provided inputs in-place unless explicitly documented.
- Preserve existing logging patterns: one logger per module via
logger = logging.getLogger(__package__). - Use
DeprecationWarningfor deprecations with clear replacement guidance and removal timeline. - Keep tests fast; prefer small fixtures and shared factories/helpers in
tests/. - Fix random seeds in tests for reproducibility; assert with tolerances.
Sampleis constructed viaSample.from_frame()factory — not__init__directly.
- Keep feedback concise and actionable.
- Point to exact lines/files and propose concrete fixes and/or specific missing tests.
- If uncertain, ask for a small reproducible example or an additional test to clarify behavior.
- When review guidelines change, update both this file and
CLAUDE.mdto keep them in sync. - This file owns the detailed review checklist.
CLAUDE.mdowns architecture, build/test, and project structure.