HEDIS is the quality measurement framework used by nearly every U.S. health plan — roughly 90 measures across preventive care, chronic disease management, mental health, and utilization. The spec is published. The measures are well-documented. What's harder to find is a clear picture of what the implementation actually looks like: what data you need, how you structure it, and where a claims-only approach runs out of road.
This project is a learning exercise with two goals: get hands-on with HEDIS development at a technical level, and learn how to work effectively with AI to do it. Both matter. In 2026, knowing how to direct AI toward a domain-specific problem — and knowing when to push back on what it produces — is becoming as valuable as the domain knowledge itself.
The Data
The source is the CMS Synthetic Medicare Enrollment, FFS Claims, and PDE dataset — generated using Synthea, structured identically to real Medicare claims. Same column names, same file formats, same quirks. It's the closest thing to real CMS data you can work with without a data use agreement.
The full download is nine files, about 940 MB unzipped:
| Table | Rows | Contents |
|---|---|---|
| beneficiary | 8,246 | Enrollment, demographics, death dates |
| inpatient | 58,066 | Hospital admissions and discharges |
| outpatient | 575,092 | Facility outpatient and ED claims |
| carrier | 1,121,004 | Physician and professional services |
| dme | 103,828 | Durable medical equipment |
| hha | 6,215 | Home health |
| hospice | 12,107 | Hospice |
| snf | 12,548 | Skilled nursing facility |
| pde | 515,520 | Part D prescription drug events |
Claim dates span April 2015 through October 2022. The most recent fully covered calendar year is 2021 — that's the measurement year for everything that follows.
The Schema
Nine tables in SQL Server 2022 Developer Edition running on Linux (Pop!_OS), one per source file. SQL Server has supported Linux since 2017 — Developer Edition is free and carries all Enterprise features, licensed for development and testing. Every column name matches the CMS source exactly — no renaming, no snake_case translation. This is a deliberate choice: it keeps the schema recognizable to anyone who has worked with CMS data, and it eliminates a class of bugs that comes from maintaining a column name map between the source and the database.
-- dates arrive as 'DD-Mon-YYYY' strings, e.g. '14-Jun-2021'
-- stored as VARCHAR(15), converted at query time
CREATE TABLE inpatient (
BENE_ID VARCHAR(15) NOT NULL,
CLM_FROM_DT VARCHAR(15), -- admission date
CLM_THRU_DT VARCHAR(15), -- discharge date
CLM_LINE_NUM INT, -- 1 = header record
PRNCPAL_DGNS_CD VARCHAR(7), -- principal ICD-10 diagnosis
ICD_DGNS_CD1 VARCHAR(7), -- secondary diagnoses CD1-CD25
-- ...
HCPCS_CD VARCHAR(15),
REV_CNTR VARCHAR(4)
);
The ETL is Python with pyodbc and pandas, loading each CSV in
5,000-row chunks with fast_executemany. The loader does one thing: move data
from files into tables. No business logic, no transformations, no measure code.
Which Measures Survived Contact with the Data
HEDIS 2024 has 90+ measures. About half can be implemented from claims data alone — the rest require EHR data, lab results, pharmacy NDC value sets, or CAHPS survey responses. Before writing a single measure query, we ran coverage checks against the actual data: eligible population size, diagnosis code presence, procedure code presence.
Eight measures were evaluated. Here's what we found:
| Measure | Name | Denominator | Rate |
|---|---|---|---|
| PCR | Plan All-Cause Readmissions | 3,049 admissions | 31.9% |
| FUH | Follow-Up After MH Hospitalization | 19 discharges | 68.4% / 84.2% |
| COL | Colorectal Cancer Screening | 4,860 eligible | 26.6% |
| IET | Initiation & Engagement of SUD Treatment | 168 episodes | 6.5% engagement |
| AAB | Avoidance of Antibiotics for Bronchitis | 825 episodes | 98.3% † |
| AMR | Asthma Medication Ratio | 59 members | 78.0% † |
| FUM | Follow-Up After ED Visit for MH | 0 | N/A ‡ |
| ABA | Adult BMI Assessment | 341 eligible | Dropped § |
† NDC drug classification uses placeholder labeler prefixes — the NCQA value sets are not bundled with the synthetic data.
‡ ED revenue center codes (0450–0459) are not populated on outpatient claims in this dataset.
§ Z68.x BMI documentation codes are absent from Synthea-generated data.
The dropped measures and the caveated ones are as informative as the clean implementations. They map the boundary between what claims-based HEDIS can do and what requires additional data sources — which is exactly the question a real health plan analytics team asks when scoping a new HEDIS program.
PCR — Plan All-Cause Readmissions
The cleanest measure in the set. Index admissions are inpatient discharges in 2021;
the numerator is any subsequent inpatient stay within 30 days. The key detail is
CLM_LINE_NUM = 1 — inpatient claims have one row per revenue line, so
the header record has to be selected explicitly to avoid counting individual service
lines as separate admissions.
WITH index_admissions AS (
SELECT
BENE_ID,
CONVERT(date, CLM_FROM_DT, 106) AS admit_dt,
CONVERT(date, CLM_THRU_DT, 106) AS discharge_dt
FROM inpatient
WHERE CLM_LINE_NUM = 1
AND CONVERT(date, CLM_FROM_DT, 106) BETWEEN '2021-01-01' AND '2021-12-31'
),
readmissions AS (
SELECT DISTINCT i.BENE_ID, i.admit_dt
FROM index_admissions i
JOIN inpatient r
ON i.BENE_ID = r.BENE_ID
AND r.CLM_LINE_NUM = 1
AND CONVERT(date, r.CLM_FROM_DT, 106) > i.discharge_dt
AND CONVERT(date, r.CLM_FROM_DT, 106) <= DATEADD(day, 30, i.discharge_dt)
)
SELECT
i.BENE_ID, i.admit_dt, i.discharge_dt,
CASE WHEN r.BENE_ID IS NOT NULL THEN 1 ELSE 0 END AS readmitted_30_day
FROM index_admissions i
LEFT JOIN readmissions r
ON i.BENE_ID = r.BENE_ID
AND i.admit_dt = r.admit_dt;
| Denominator | Numerator | Rate |
|---|---|---|
| 3,049 index admissions | 972 readmitted within 30 days | 31.9% |
The 31.9% rate is roughly double the real-world Medicare average (~15%). Synthea doesn't model realistic care-transition behavior, so readmissions are overrepresented in the synthetic data. The logic is correct — the same query against real claims would produce a meaningful rate.
FUH — Follow-Up After Hospitalization for Mental Illness
For members discharged from a psychiatric inpatient stay, did they receive an outpatient follow-up within 7 or 30 days? Three tables involved: inpatient identifies the index discharge, carrier and outpatient together form the follow-up visit pool.
The mental illness diagnosis filter is PRNCPAL_DGNS_CD LIKE 'F%' AND SUBSTRING(..., 2, 2) BETWEEN '20' AND '99' — ICD-10 F20 through F99, which covers schizophrenia, mood disorders, anxiety, personality disorders, and more, while excluding F00-F19 (substance use disorders, which have their own measure: IET).
| Window | Denominator | Numerator | Rate |
|---|---|---|---|
| 7-day | 19 discharges | 13 | 68.4% |
| 30-day | 19 discharges | 16 | 84.2% |
Small denominator — 8,246 synthetic members don't produce many psychiatric hospitalizations. The rates are higher than real-world HEDIS averages (35–40% at 7 days, 50–55% at 30 days), which makes sense: Synthea schedules follow-up visits regularly without modeling the care-coordination gaps that drive low real-world rates. The measure structure is clean and has no data caveats.
Where Claims Run Out
Three failure modes appeared, each worth understanding:
Missing procedure codes (ABA, BCS)
Adult BMI Assessment requires Z68.x BMI documentation codes. Breast Cancer Screening requires mammography HCPCS codes (77065–77067). Both are absent from Synthea-generated data. The denominators built correctly — 341 members with qualifying office visits for ABA, 2,071 eligible women for BCS — but the numerators returned zero. Synthea generates diagnosis codes reliably but is sparse on documentation and screening procedure codes.
Missing claim types (FUM)
Follow-Up After ED Visit for Mental Illness requires outpatient claims with ED revenue center codes (0450–0459) and a mental illness principal diagnosis. The revenue center field is not populated on outpatient claims in this dataset. Zero qualifying ED visits found. The query structure is correct — it would work against real Medicare outpatient data.
Missing reference data (AAB, AMR)
Antibiotic Avoidance and Asthma Medication Ratio both require NDC drug classification — identifying which prescriptions are antibiotics (AAB) or controller vs. reliever asthma medications (AMR). The NCQA publishes these value sets as part of the HEDIS specification, but they're licensed content not bundled with the CMS synthetic data. Both measures are implemented with placeholder NDC labeler-prefix lists. The rates they produce (98.3% and 78.0%) are not meaningful without the full value sets.
The Measures
The full implementation — all 8 measures, denominator and numerator queries, rate calculations, member-level results, and conclusions — is in the notebook below.