Inside HEDIS: A Claims-Based Implementation from Scratch

What does it actually take to implement HEDIS quality measures from raw Medicare claims data? We built it to find out.

Built with Claude · GitHub

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:

TableRowsContents
beneficiary8,246Enrollment, demographics, death dates
inpatient58,066Hospital admissions and discharges
outpatient575,092Facility outpatient and ED claims
carrier1,121,004Physician and professional services
dme103,828Durable medical equipment
hha6,215Home health
hospice12,107Hospice
snf12,548Skilled nursing facility
pde515,520Part 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 beneficiary file choice matters more than it looks. We initially downloaded the 2025 file, then switched to the 2021 file. The 2025 file records deaths through 2025 — anyone who died between 2022 and 2025 would be incorrectly excluded from a 2021 denominator. The 2021 file reflects who was actually enrolled that year.

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:

MeasureNameDenominatorRate
PCRPlan All-Cause Readmissions 3,049 admissions 31.9%
FUHFollow-Up After MH Hospitalization 19 discharges 68.4% / 84.2%
COLColorectal Cancer Screening 4,860 eligible 26.6%
IETInitiation & Engagement of SUD Treatment 168 episodes 6.5% engagement
AABAvoidance of Antibiotics for Bronchitis 825 episodes 98.3% †
AMRAsthma Medication Ratio 59 members 78.0% †
FUMFollow-Up After ED Visit for MH 0 N/A ‡
ABAAdult 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;
DenominatorNumeratorRate
3,049 index admissions972 readmitted within 30 days31.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).

WindowDenominatorNumeratorRate
7-day19 discharges1368.4%
30-day19 discharges1684.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 NDC gap is a real production constraint too — health plans that implement HEDIS have to maintain licensed NDC reference tables and update them annually. It's one of the reasons pharmacy-intensive measures are often phased in after claims-based measures are stable.

The Measures

The full implementation — all 8 measures, denominator and numerator queries, rate calculations, member-level results, and conclusions — is in the notebook below.

View the measures notebook →