Can Clinical Guidelines Be Converted Into Code? The Challenges of Algorithmic Medicine
Medicine is full of algorithms. Software is built on them. But translating a clinical recommendation into executable code reveals something fundamental about both disciplines: knowing what a rule says is not the same as knowing when that rule applies.
A 54-year-old patient arrives for a routine primary care appointment. His LDL cholesterol is 168 mg/dL. He has hypertension, no known atherosclerotic cardiovascular disease (ASCVD), and a family history of premature myocardial infarction.
Should he receive a statin?
A clinician might consider his age, calculated cardiovascular risk, family history, blood pressure, laboratory results, preferences, and several other variables before discussing treatment.
A programmer might initially see a simpler problem:
if (patient.ldl >= 160) {
recommend("Start a statin");
}
Four lines of code. One condition. One recommendation.
And potentially a serious misrepresentation of clinical guidance.
The problem is not that the code is syntactically incorrect. It does exactly what it was designed to do.
The problem is that the clinical reasoning has been reduced to something much narrower than the evidence actually supports.
This is the central challenge of algorithmic medicine: a clinical recommendation is not automatically an if/else statement.
As healthcare increasingly incorporates electronic health records, clinical decision-support systems, automated risk calculators, and artificial intelligence, translating medical knowledge into executable logic has become an important engineering problem.
The 2026 American College of Cardiology (ACC) and American Heart Association (AHA) multisociety guideline on the management of dyslipidemia provides a useful case study. It contains risk categories, laboratory thresholds, treatment recommendations, clinical exceptions, and decision pathways that appear ideal for computational implementation.
Yet the closer we examine those recommendations, the more complicated the translation becomes.
The challenge is not simply writing code that follows a guideline.
It is writing code that understands the boundaries of the guideline.
1. Clinical Guidelines and Computer Algorithms Are Different Kinds of Knowledge
A clinical practice guideline is an evidence-based document intended to inform professional judgment.
It synthesizes research, evaluates evidence quality, identifies populations likely to benefit from interventions, and communicates recommendations for clinical practice.
A software algorithm operates differently.
A deterministic program evaluates explicitly defined inputs and follows rules established by its developer. For the same inputs and program version, it should produce the same outputs.
That difference matters.
Consider three possible statements:
- Treatment is recommended.
- Treatment is reasonable.
- Treatment may be considered.
These statements are not interchangeable.
In cardiovascular guidelines, recommendations are often assigned a Class of Recommendation (COR) and Level of Evidence (LOE). The class communicates the strength or nature of the recommendation, while the evidence level describes the supporting scientific evidence.
For example, Class I generally identifies an intervention that is recommended or indicated. Class IIa identifies an intervention that is reasonable, while Class IIb describes one that may be considered. Class III recommendations can identify interventions that provide no benefit or cause harm.
Now consider this implementation:
type Recommendation = "treat" | "do-not-treat";
This representation has already discarded critical information.
It cannot distinguish a strongly recommended intervention from one that is reasonable after individualized discussion. It has no way to capture uncertainty, contraindications, incomplete evidence, or the need for shared decision-making.
A better model must preserve the distinctions:
type RecommendationClass =
| "I"
| "IIa"
| "IIb"
| "III_NO_BENEFIT"
| "III_HARM";
type EvidenceLevel =
| "A"
| "B-R"
| "B-NR"
| "C-LD"
| "C-EO";
type ClinicalRecommendation = {
recommendationClass: RecommendationClass;
evidenceLevel: EvidenceLevel;
intervention: string;
rationale: string;
sourceReference: string;
};
This is still a simplified model, but it represents a crucial shift.
Instead of asking only, "What should the computer tell the clinician to do?" we begin asking, "What exactly does the evidence support, and how should that support be represented?"
Clinical guidelines contain not only instructions, but also degrees of certainty and authority.
An accurate implementation must preserve both.
2. The First Trap: Converting a Threshold Into a Universal Rule
The 2026 dyslipidemia guideline uses the AHA's PREVENT-ASCVD risk equations to help guide primary-prevention decisions in appropriate adults.
For eligible patients, estimated 10-year ASCVD risk is categorized as:
Scroll horizontally to read all columns.
| Risk category | 10-year PREVENT-ASCVD risk |
|---|---|
| Low | Less than 3% |
| Borderline | 3% to less than 5% |
| Intermediate | 5% to less than 10% |
| High | 10% or greater |
These thresholds look like an ideal programming exercise.
function classifyRisk(risk: number): string {
if (risk < 3) return "Low";
if (risk < 5) return "Borderline";
if (risk < 10) return "Intermediate";
return "High";
}
Within its assumed input domain, this function correctly categorizes the numerical risk estimates.
But even this elementary example conceals several assumptions.
What if the risk is negative?
What if the value is NaN?
What if the patient is outside the validated population?
What if the number represents 30-year rather than 10-year risk?
What if the patient already has ASCVD, making a primary-prevention risk classification the wrong decision pathway?
The function cannot answer any of those questions.
A more defensible implementation begins with validation.
type RiskCategory =
| "low"
| "borderline"
| "intermediate"
| "high";
type RiskResult =
| {
status: "valid";
category: RiskCategory;
riskPercent: number;
}
| {
status: "invalid";
reason: string;
};
function classifyRisk(
riskPercent: number
): RiskResult {
if (
!Number.isFinite(riskPercent) ||
riskPercent < 0 ||
riskPercent > 100
) {
return {
status: "invalid",
reason: "Risk must be between 0 and 100%."
};
}
const category: RiskCategory =
riskPercent < 3
? "low"
: riskPercent < 5
? "borderline"
: riskPercent < 10
? "intermediate"
: "high";
return {
status: "valid",
category,
riskPercent
};
}
This version is safer, but it introduces another important distinction.
A mathematically valid number is not necessarily a clinically valid risk estimate.
Before interpreting PREVENT-ASCVD, the application must verify that the correct equation, prediction horizon, patient population, and input measurements were used.
A valid 6.2% estimate obtained from the wrong model is not clinically interchangeable with a properly calculated 6.2% PREVENT-ASCVD risk.
The official PREVENT equations also contain coefficients and model-specific requirements. They cannot be faithfully implemented by inventing a simplified formula that happens to return a plausible percentage.
In clinical software, provenance is part of correctness.
3. The Second Trap: Choosing the Wrong Decision Pathway
Suppose our application accepts these patient characteristics:
const patient = {
age: 58,
ldl: 142,
hasASCVD: true,
hasDiabetes: true,
hasCKD: false,
preventRisk: 8.4
};
An inexperienced implementation might immediately classify the patient as having intermediate 10-year cardiovascular risk.
const category = classifyRisk(
patient.preventRisk
);
But this patient already has established ASCVD.
The primary-prevention risk classification is not the appropriate basis for selecting the secondary-prevention treatment pathway.
The error is more serious than using an imperfect risk estimate. The application has selected the wrong clinical question.
This suggests that clinical decision support needs an explicit routing stage before applying individual recommendations.
type ClinicalPathway =
| "secondary-prevention"
| "severe-hypercholesterolemia"
| "primary-prevention"
| "requires-review";
type Patient = {
hasASCVD: boolean | null;
ldl: number | null;
hasFamilialHypercholesterolemia: boolean | null;
};
function selectPathway(
patient: Patient
): ClinicalPathway {
if (patient.hasASCVD === true) {
return "secondary-prevention";
}
if (patient.hasASCVD === null) {
return "requires-review";
}
if (patient.ldl === null) {
return "requires-review";
}
if (patient.ldl >= 190) {
return "severe-hypercholesterolemia";
}
if (patient.hasFamilialHypercholesterolemia === true) {
return "requires-review";
}
return "primary-prevention";
}
This example demonstrates pathway selection, not complete guideline implementation. A real system would also require age eligibility, special-population pathways, laboratory validation, and more detailed clinical criteria.
There is also a subtle architectural problem: patients do not always belong to only one clinically meaningful category.
A patient can simultaneously have established ASCVD, diabetes, chronic kidney disease, elevated lipoprotein(a), and statin intolerance.
Assigning a single label may obscure important overlapping recommendations.
Instead of treating pathway selection as the entire assessment, a more capable system evaluates multiple applicable rules.
type ClinicalIndication =
| "established-ascvd"
| "diabetes"
| "chronic-kidney-disease"
| "severe-hypercholesterolemia"
| "elevated-lpa"
| "statin-intolerance";
function identifyIndications(
patient: {
hasASCVD: boolean;
hasDiabetes: boolean;
hasCKD: boolean;
ldl: number;
elevatedLpa: boolean;
statinIntolerant: boolean;
}
): ClinicalIndication[] {
const indications: ClinicalIndication[] = [];
if (patient.hasASCVD) {
indications.push("established-ascvd");
}
if (patient.hasDiabetes) {
indications.push("diabetes");
}
if (patient.hasCKD) {
indications.push("chronic-kidney-disease");
}
if (patient.ldl >= 190) {
indications.push("severe-hypercholesterolemia");
}
if (patient.elevatedLpa) {
indications.push("elevated-lpa");
}
if (patient.statinIntolerant) {
indications.push("statin-intolerance");
}
return indications;
}
The output might be:
{
"indications": [
"established-ascvd",
"diabetes",
"chronic-kidney-disease",
"elevated-lpa"
]
}
The system now recognizes overlapping clinical features instead of forcing them into a single category.
Importantly, this list is not itself a list of treatment orders. Each indication still requires its own eligibility criteria and interpretation.
The next challenge is determining which recommendations apply together, which modify one another, and which need additional clinical review.
4. The Third Trap: Confusing Missing Information With a Negative Finding
One of the most dangerous assumptions in software is that missing information can be treated as false.
Consider:
const patient = {
hasASCVD: false,
hasDiabetes: false,
isPregnant: false
};
Where did these values come from?
Were the conditions explicitly assessed and ruled out?
Or did the developer initialize every field as false because the application needed default values?
Those are entirely different situations.
In an electronic health record, the absence of a diagnosis code does not reliably establish the absence of a disease. Likewise, an unrecorded pregnancy status is not equivalent to confirmation that pregnancy is impossible or absent.
Clinical logic often requires at least three states:
type ClinicalBoolean =
| true
| false
| null;
Here, null means unknown or not yet established.
Consider the difference:
const patientA = {
hasASCVD: false
};
const patientB = {
hasASCVD: null
};
Patient A has explicitly documented absence of ASCVD.
Patient B has insufficient information to establish ASCVD status.
A robust clinical engine should not treat both patients identically.
type Assessment =
| {
status: "ready";
}
| {
status: "missing-data";
missingFields: string[];
};
function validateAssessment(
patient: {
age: number | null;
hasASCVD: ClinicalBoolean;
ldl: number | null;
}
): Assessment {
const missingFields: string[] = [];
if (patient.age === null) {
missingFields.push("age");
}
if (patient.hasASCVD === null) {
missingFields.push("ASCVD history");
}
if (patient.ldl === null) {
missingFields.push("LDL cholesterol");
}
if (missingFields.length > 0) {
return {
status: "missing-data",
missingFields
};
}
return {
status: "ready"
};
}
This may seem like ordinary input validation, but it reflects a deeper principle.
In clinical software, uncertainty must be represented as data rather than silently eliminated.
An application that cannot complete an assessment should say so.
For example:
Unable to determine the applicable lipid-management pathway because ASCVD history has not been established. Confirm clinical history before interpreting primary-prevention risk.
That is a more trustworthy result than confidently providing a recommendation based on an assumption the user never authorized.
5. The Fourth Trap: Multiple Guidelines Can Apply Simultaneously
Clinical recommendations rarely exist in isolation.
Consider a hypothetical 63-year-old patient with type 2 diabetes, stage 3 chronic kidney disease, LDL-C of 132 mg/dL, and no documented ASCVD.
The 2026 dyslipidemia guideline provides specific recommendations for several higher-risk primary-prevention populations, including appropriate adults with diabetes or chronic kidney disease.
A naive decision tree might evaluate only the first matching condition:
if (patient.hasDiabetes) {
return diabetesRecommendation;
} else if (patient.hasCKD) {
return ckdRecommendation;
} else {
return generalPreventionRecommendation;
}
This is the wrong structure if the purpose is to identify every clinically relevant indication.
The else if statement makes the branches mutually exclusive.
The patient has diabetes, so the CKD branch never executes.
The developer has accidentally encoded the assumption that diabetes and CKD cannot matter simultaneously.
A rule engine is better suited to independent evaluations.
type RuleMatch = {
ruleId: string;
reason: string;
};
type Rule = {
id: string;
applies: (patient: ClinicalProfile) => boolean;
reason: string;
};
type ClinicalProfile = {
hasDiabetes: boolean;
hasCKD: boolean;
hasASCVD: boolean;
};
const rules: Rule[] = [
{
id: "ASCVD_PATHWAY",
applies: p => p.hasASCVD,
reason: "Established ASCVD is documented."
},
{
id: "DIABETES_PATHWAY",
applies: p => p.hasDiabetes,
reason: "Diabetes is documented."
},
{
id: "CKD_PATHWAY",
applies: p => p.hasCKD,
reason: "Chronic kidney disease is documented."
}
];
function evaluateRules(
patient: ClinicalProfile
): RuleMatch[] {
return rules
.filter(rule => rule.applies(patient))
.map(rule => ({
ruleId: rule.id,
reason: rule.reason
}));
}
For the hypothetical patient, the engine identifies both diabetes and CKD as relevant considerations.
What happens next is the important part.
The system should not simply collect all matching treatment suggestions and present them as equally applicable instructions.
It must evaluate the eligibility boundaries of each recommendation, determine whether one pathway modifies another, and preserve any unresolved questions.
Sometimes two rules are compatible.
Sometimes one is more specific.
Sometimes an exception modifies an otherwise applicable recommendation.
And sometimes the clinical guideline does not supply enough information for software to automatically resolve the situation.
A safe engine needs a way to represent that last possibility.
type DecisionStatus =
| "supported"
| "conditional"
| "needs-review"
| "insufficient-data"
| "not-applicable";
type Decision = {
status: DecisionStatus;
applicableRules: string[];
explanation: string;
};
A system that always returns a definitive answer is not necessarily more intelligent.
It may simply be incapable of acknowledging uncertainty.
6. The Fifth Trap: Clinical Exceptions Are Not Edge Cases
Software engineers often think of edge cases as unusual inputs that require special handling.
In medicine, exceptions are frequently central to safe decision-making.
Consider a hypothetical patient whose lipid profile and risk factors otherwise support consideration of cholesterol-lowering medication.
The patient is also pregnant, planning pregnancy, or breastfeeding.
Now the application must incorporate medication-specific reproductive safety, treatment timing, relevant exceptions, and individualized clinical review.
The general lipid-management pathway cannot operate as though these facts do not exist.
An unsafe implementation might look like this:
if (patient.ldl >= treatmentThreshold) {
recommend("Initiate therapy");
}
A safer architecture introduces a separate safety evaluation.
type SafetyFlag = {
code: string;
severity: "review" | "critical";
message: string;
};
type ReproductiveStatus =
| "pregnant"
| "planning-pregnancy"
| "lactating"
| "none"
| "unknown"
| "not-applicable";
function evaluateSafety(
status: ReproductiveStatus
): SafetyFlag[] {
if (
status === "pregnant" ||
status === "planning-pregnancy" ||
status === "lactating"
) {
return [{
code: "REPRODUCTIVE_REVIEW",
severity: "review",
message:
"Medication-specific reproductive safety review required."
}];
}
if (status === "unknown") {
return [{
code: "REPRODUCTIVE_STATUS_UNKNOWN",
severity: "review",
message:
"Clarify relevant reproductive status before medication guidance."
}];
}
return [];
}
This does not itself determine which treatments are appropriate. It identifies circumstances requiring an additional layer of clinical evaluation.
That distinction matters.
A safety flag should not be mistaken for a clinical contraindication unless the supporting evidence specifically establishes one.
Likewise, the absence of a safety flag does not prove that treatment is safe.
This is why production clinical systems need structured contraindication data, medication-specific rules, current prescribing information, and clinician oversight rather than a handful of universal exceptions.
The larger lesson is that exceptional circumstances must be considered during system design, not appended after the primary algorithm is finished.
7. Shared Decision-Making Cannot Be Reduced to a Boolean
One of the most interesting challenges arises when clinical recommendations explicitly depend on patient preferences.
Imagine a patient with a 10-year PREVENT-ASCVD risk of 4.1%.
Under the 2026 dyslipidemia guideline, this falls within the borderline-risk category. After consideration of relevant clinical factors, statin therapy may be reasonable within the applicable recommendation pathway.
The guideline does not imply that every patient with a 4.1% estimate must automatically receive an identical prescription.
A patient may place substantial value on reducing lifetime cardiovascular risk.
Another may be particularly concerned about adverse effects, treatment burden, or medication costs.
A third may want additional risk clarification before deciding.
All three may make different decisions after an informed clinical discussion.
Software can identify the applicable recommendation. It cannot legitimately manufacture the patient's preferences.
Consider this simplistic approach:
if (risk >= 3) {
recommendStatin = true;
}
This converts an area of clinical judgment into a universal command.
A better model separates evidence-based eligibility from patient decisions.
type PatientPreference =
| "favors-treatment"
| "favors-lifestyle-first"
| "requests-more-information"
| "undecided"
| "not-discussed";
type SharedDecision = {
clinicalOption: string;
guidelineSupport: string;
patientPreference: PatientPreference;
discussionCompleted: boolean;
};
The application could then generate an educational summary such as:
{
"riskCategory": "borderline",
"clinicalOption": "Discuss moderate-intensity statin therapy",
"guidelineSupport": "Conditional recommendation",
"patientPreference": "requests-more-information",
"discussionCompleted": false
}
Notice what this system does not do.
It does not interpret an undecided patient as having declined therapy.
It does not interpret a patient's willingness to take medication as proof that the medication is indicated.
And it does not treat the guideline's evidence assessment as a replacement for the patient's informed preferences.
Shared decision-making is not a checkbox that magically converts uncertainty into certainty.
It is a clinical process involving evidence, values, context, and communication.
The appropriate role of software is to support that process, not impersonate it.
8. Risk Calculators Are Models, Not Oracles
The 2026 guideline's adoption of PREVENT-ASCVD provides another important lesson for software development.
Risk calculators produce estimates.
They do not observe the patient's future.
The distinction becomes especially important when a numerical prediction is presented with apparent precision.
Imagine an application displaying:
10-year ASCVD risk: 7.4%
The number may be calculated correctly, but the user still needs to know what it represents.
Which outcome is being predicted?
Which time horizon is being used?
Was the calculation performed using the appropriate PREVENT equation?
Were all inputs validated?
Does the patient fall within the model's intended population?
What clinically relevant risk factors are not captured by that model?
A robust application should represent these properties explicitly.
type RiskEstimate = {
model: "AHA_PREVENT";
modelVersion: string;
outcome: "ASCVD";
horizonYears: 10 | 30;
estimatedRiskPercent: number;
calculatedAt: string;
inputValidationPassed: boolean;
};
Even this structure should only be populated after an actual validated calculation.
A developer should never substitute another equation or invent coefficients merely because a complete implementation is difficult.
When a validated calculation is unavailable, the application can return:
type RiskCalculationResult =
| {
status: "calculated";
estimate: RiskEstimate;
}
| {
status: "unavailable";
reason: string;
};
The system may direct the user to the official AHA PREVENT calculator rather than manufacture a result.
The same reasoning applies to optional testing.
Coronary artery calcium (CAC), lipoprotein(a), and apolipoprotein B (ApoB) can contribute additional information in relevant clinical situations.
But the availability of additional data does not mean every value should automatically trigger a new treatment command.
Risk-enhancing findings may refine a decision, alter the interpretation of estimated risk, or prompt discussion.
A useful distinction for clinical software is:
type ClinicalFindingRole =
| "establishes-indication"
| "modifies-risk-assessment"
| "informs-treatment-goal"
| "requires-follow-up"
| "context-only";
A patient's elevated lipoprotein(a), for example, should not be treated as though it were simply another numerical input that can be substituted for LDL-C.
Different biomarkers measure different clinical properties. Their units, thresholds, and interpretations are not universally interchangeable.
The software must respect those differences.
9. Clinical Logic Should Be Explainable and Traceable
Traditional software tests often ask one question:
Did the function return the expected value?
For clinical decision support, that is necessary but insufficient.
We also need to know why the result was returned.
Suppose an application identifies an LDL-C goal for a patient in a particular secondary-prevention category.
The result should explain which clinical pathway was selected, which patient characteristics made that pathway applicable, which guideline recommendation supports the goal, and whether any information remains unresolved.
A useful design separates the output from the reasoning that produced it.
type RuleTrace = {
ruleId: string;
matched: boolean;
explanation: string;
sourceReference: string;
};
type ClinicalResult = {
guidelineVersion: string;
pathway: string;
status: DecisionStatus;
recommendations: ClinicalRecommendation[];
missingInformation: string[];
safetyFlags: SafetyFlag[];
reasoning: RuleTrace[];
};
A hypothetical result might resemble:
{
"guidelineVersion": "ACC/AHA Dyslipidemia 2026",
"pathway": "secondary-prevention",
"status": "needs-review",
"recommendations": [],
"missingInformation": [
"Very-high-risk ASCVD classification",
"Current lipid-lowering therapy",
"Medication tolerance"
],
"safetyFlags": [],
"reasoning": [
{
"ruleId": "ASCVD_HISTORY",
"matched": true,
"explanation": "Established ASCVD is documented.",
"sourceReference": "2026 dyslipidemia guideline"
}
]
}
The example deliberately avoids issuing a final treatment recommendation.
The correct secondary-prevention target depends on additional risk classification and clinical information.
In the 2026 guideline, very-high-risk secondary-prevention patients have an LDL-C goal below 55 mg/dL and non-HDL-C goal below 85 mg/dL, while other secondary-prevention patients may have different applicable targets.
The software should establish the appropriate category before presenting a target as patient-specific.
A transparent system makes this visible.
It should be possible to trace the result backward:
Output → matched rule → qualifying patient information → guideline recommendation → supporting evidence.
This traceability makes the software easier to audit, test, update, and challenge.
More importantly, it gives clinicians a reason to trust the result without requiring them to trust the program blindly.
10. A Better Architecture for Guideline-Based Software
As the examples become more complicated, an architectural pattern begins to emerge.
Clinical logic should not be scattered throughout interface components.
For example, this is difficult to maintain:
function ResultsPage({ patient }) {
return (
<div>
{patient.ldl >= 190
? "Recommend treatment"
: "Evaluate risk"}
</div>
);
}
Not only is the clinical logic embedded directly in the presentation layer, but the message oversimplifies what should happen next.
A more maintainable approach separates the system into distinct responsibilities.
Clinical Decision-Support Application
│
├── Input Layer
│ ├── Patient Characteristics
│ ├── Laboratory Measurements
│ ├── Existing Treatment
│ └── Patient Preferences
│
├── Validation Layer
│ ├── Missing Data
│ ├── Unit Validation
│ ├── Physiologic Plausibility
│ └── Model Eligibility
│
├── Clinical Logic Layer
│ ├── Pathway Selection
│ ├── Risk Assessment
│ ├── Rule Evaluation
│ ├── Exception Handling
│ └── Conflict Detection
│
├── Evidence Layer
│ ├── Guideline Version
│ ├── Recommendation Metadata
│ ├── Source References
│ └── Rule Traceability
│
└── Presentation Layer
├── Clinical Summary
├── Applicable Recommendations
├── Unresolved Questions
├── Shared Decision Support
└── Evidence Explanation
The user interface becomes responsible for collecting and displaying information.
The clinical engine becomes responsible for structured rule evaluation.
The evidence registry becomes responsible for documenting where each implemented recommendation came from.
This separation makes testing substantially easier.
It also limits the risk that a harmless interface change will accidentally modify a clinical threshold.
A clinical rule registry
Instead of hardcoding every recommendation inside one enormous function, each rule can be expressed as a structured object.
type GuidelineRule = {
id: string;
version: string;
source: {
guideline: string;
section: string;
recommendationClass?: RecommendationClass;
evidenceLevel?: EvidenceLevel;
url: string;
};
requiredInputs: string[];
evaluate: (
patient: ClinicalProfile
) => {
applies: boolean;
explanation: string;
};
};
Here is an illustrative example that identifies the presence of established ASCVD.
const ascThereIsDiseaseRule: GuidelineRule = {
id: "ASCVD_HISTORY_CHECK",
version: "1.0.0",
source: {
guideline: "ACC/AHA Dyslipidemia 2026",
section: "Secondary prevention",
url: "https://doi.org/10.1161/CIR.0000000000001423"
},
requiredInputs: ["hasASCVD"],
evaluate: patient => ({
applies: patient.hasASCVD === true,
explanation:
patient.hasASCVD === true
? "Established ASCVD requires secondary-prevention assessment."
: "This rule did not establish secondary-prevention eligibility."
})
};
This rule is a pathway-identification example, not an implementation of a specific graded treatment recommendation. A real guideline registry would map treatment rules to their exact recommendation identifiers, evidence grades, eligibility criteria, and relevant exceptions.
The rule engine can then be versioned and tested independently of the React interface.
That matters because clinical guidelines change.
A future recommendation may modify a threshold, eligibility criterion, treatment goal, or drug-specific consideration.
When clinical logic is scattered across dozens of interface components, updating it becomes difficult and potentially unsafe.
When rules are structured, independently tested, and linked to evidence, changes can be reviewed systematically.
11. Testing Clinical Software Requires More Than Unit Tests
Suppose our risk-classification function contains this statement:
if (risk < 5) return "borderline";
A minor change accidentally introduces an incorrect boundary:
if (risk <= 5) return "borderline";
The program still compiles.
Most inputs still produce exactly the same answer.
But a risk estimate of precisely 5% now falls into the wrong category.
Boundary conditions matter in medicine because guideline thresholds often determine which recommendation becomes applicable.
Automated testing should explicitly target these boundaries.
import { describe, expect, it } from "vitest";
describe("PREVENT risk classification", () => {
it.each([
[2.9, "low"],
[3.0, "borderline"],
[4.9, "borderline"],
[5.0, "intermediate"],
[9.9, "intermediate"],
[10.0, "high"]
])(
"classifies %s%% as %s",
(risk, expected) => {
const result = classifyRisk(risk);
expect(result.status).toBe("valid");
if (result.status === "valid") {
expect(result.category).toBe(expected);
}
}
);
});
But threshold tests are only the beginning.
A guideline-derived decision engine also needs scenarios designed to reveal clinically meaningful failures.
Consider these examples:
Scroll horizontally to read all columns.
| Clinical scenario | Expected system behavior |
|---|---|
| Established ASCVD | Select secondary-prevention assessment |
| Missing ASCVD history | Request clarification rather than assume absence |
| Diabetes and CKD coexist | Identify both applicable considerations |
| Risk exactly 5% | Classify as intermediate, when PREVENT classification is appropriate |
| Pregnancy-related concern | Flag medication-specific safety review |
| Unknown baseline LDL-C | Do not invent percentage LDL-C reduction |
| Patient outside model eligibility | Do not generate an unsupported PREVENT estimate |
| Conflicting or unresolved guidance | Return a transparent review status |
These tests should be derived from validated clinical requirements, not from whatever the current implementation happens to produce.
There is an important difference between testing that code behaves consistently and validating that code implements medicine correctly.
A broken algorithm can pass every unit test if the developer wrote the tests around an incorrect interpretation of the guideline.
For that reason, the process must include clinical review, independent reference scenarios, source verification, and regression testing after guideline updates.
Testing percentage LDL-C reduction
Even apparently simple calculations require explicit data rules.
function calculateLDLReduction(
baseline: number | null,
current: number | null
): number | null {
if (
baseline === null ||
current === null ||
!Number.isFinite(baseline) ||
!Number.isFinite(current) ||
baseline <= 0 ||
current < 0
) {
return null;
}
return (
((baseline - current) / baseline) * 100
);
}
This function does not invent a baseline measurement.
It also does not automatically interpret the result as treatment success. Clinical interpretation still depends on the reliability and timing of measurements, treatment context, and applicable goals.
This is an example of a broader engineering principle:
Every clinical calculation should define what it knows, what it assumes, and when it must decline to calculate.
12. Where Does Artificial Intelligence Fit?
So far, the examples have used deterministic rules rather than machine learning or large language models.
There is a reason for that.
Many important clinical tasks require reproducibility and exact application of defined criteria.
A validated risk equation should not change its output because a language model happens to phrase its reasoning differently.
A laboratory threshold should not be approximated by generated prose.
A contraindication should not disappear because a probabilistic model overlooked it.
Large language models can be useful in clinical knowledge systems, but their role must be carefully defined.
For example, an AI component might assist with:
- Explaining guideline terminology in plain language.
- Summarizing the clinical factors identified by a validated rule engine.
- Helping users navigate long documents and discover relevant sections.
- Drafting educational explanations from verified structured results.
It should not be trusted to invent missing clinical values, calculate an undocumented version of PREVENT, or independently decide that contradictory recommendations have been resolved.
One possible architecture is:
Patient Information
│
▼
Validated Input Schema
│
▼
Deterministic Clinical Engine
│
▼
Structured Clinical Result
│
├── Applicable Rules
├── Missing Information
├── Safety Flags
├── Evidence References
└── Unresolved Decisions
│
▼
Optional AI Explanation Layer
│
▼
Clinician-Reviewable Summary
The AI layer explains validated findings rather than originating unsupported medical conclusions.
Even then, generated explanations require safeguards. They should be checked against the structured output, preserve uncertainty, and avoid adding new treatment recommendations.
This hybrid architecture combines the strengths of different technologies.
Deterministic software handles calculations and explicit conditions.
Structured evidence provides traceability.
AI can help communicate complicated information.
Clinicians retain responsibility for interpreting the results in the patient's actual clinical context.
The goal is not to make AI sound like an authoritative physician.
The goal is to make evidence-based information more accessible without concealing how conclusions were reached.
13. The Most Important Output Might Be "I Don't Know"
Most consumer applications are designed to produce an answer.
Clinical software sometimes needs to do the opposite.
It should be able to determine that available information does not support an answer.
Consider this result:
type ClinicalEngineOutput =
| {
status: "completed";
result: ClinicalResult;
}
| {
status: "needs-more-information";
missingFields: string[];
}
| {
status: "requires-clinical-review";
issues: string[];
}
| {
status: "unsupported";
reason: string;
};
This type forces developers to account for situations where the engine cannot responsibly provide a definitive recommendation.
A result might say:
The available information supports consideration of lipid-lowering therapy, but the appropriate treatment pathway cannot be finalized because additional ASCVD risk classification and medication-tolerance information are required.
Or:
The patient falls outside the validated population for this risk model. No risk estimate was generated.
Or:
Multiple guideline pathways may apply. Further clinical review is required to determine which recommendation governs the treatment decision.
These are not software failures.
They are examples of a system operating within its defined limits.
In clinical decision support, declining to make an unsupported inference is a feature.
It demonstrates that the software recognizes the difference between an answer it can calculate and a decision it cannot legitimately make.
14. Turning Clinical Guidelines Into Code Is a Knowledge-Engineering Problem
The deeper challenge of algorithmic medicine is not programming syntax.
It is knowledge representation.
To transform a clinical guideline into executable software, a developer must translate prose into explicit structures while preserving its meaning.
A statement that appears straightforward on paper may depend on:
- The population for whom the evidence applies.
- The definitions of the clinical conditions involved.
- The strength and certainty of the recommendation.
- Other conditions that modify its applicability.
- Data that may be missing, uncertain, or unreliable.
- Decisions that depend on patient values.
- Exceptions and contraindications.
- The version of the guideline being implemented.
A simple algorithm might represent medicine as:
const recommendation =
condition ? treatmentA : treatmentB;
A more faithful representation looks something like this:
function evaluateClinicalGuidance(
patient: PatientProfile
): ClinicalEngineOutput {
const validation = validatePatient(patient);
if (!validation.valid) {
return {
status: "needs-more-information",
missingFields: validation.missingFields
};
}
const safety = evaluateSafetyFactors(patient);
if (safety.requiresReview) {
return {
status: "requires-clinical-review",
issues: safety.issues
};
}
const pathways = identifyApplicablePathways(patient);
const rules = evaluateApplicableRules(
patient,
pathways
);
if (rules.hasUnresolvedConflict) {
return {
status: "requires-clinical-review",
issues: rules.conflicts
};
}
if (!rules.hasSufficientEvidence) {
return {
status: "unsupported",
reason:
"No validated recommendation can be established."
};
}
return {
status: "completed",
result: buildClinicalResult(rules)
};
}
This is an architectural sketch, not a validated clinical implementation. The helper functions would require independent definitions, clinical review, evidence mapping, and testing before real-world use.
But the structure demonstrates the essential design philosophy.
The engine validates before interpreting.
It evaluates safety and applicability before presenting recommendations.
It permits multiple relevant rules.
It represents unresolved conflicts.
And it can decline to provide an answer.
These characteristics are more important than the number of conditions the software can evaluate.
15. Building LipidPath: An Experiment in Executable Clinical Knowledge
These questions are particularly relevant to a project I am developing for Simpyll: LipidPath, an interactive, educational clinical decision-support application based on the 2026 ACC/AHA multisociety dyslipidemia guideline.
The concept is straightforward: allow users to enter relevant clinical characteristics, follow applicable dyslipidemia management pathways, and examine the evidence supporting the results.
The engineering challenge is considerably more interesting.
LipidPath needs to distinguish primary from secondary prevention, evaluate overlapping indications, apply risk models only to eligible patients, interpret laboratory measurements appropriately, and communicate the limits of available information.
It also needs to distinguish strong recommendations from conditional ones.
A guideline explorer can display a recommendation and its supporting evidence.
An executable decision engine must first establish whether that recommendation actually applies.
Those are different responsibilities.
The project therefore provides an opportunity to explore clinical informatics and software engineering together: translating evidence into structured rules, building transparent decision traces, validating boundary conditions, and designing an interface that makes complex medical information understandable.
The intended result is not an autonomous prescribing system.
It is an educational tool demonstrating how guideline-based clinical decision support can be designed, tested, and explained.
More broadly, LipidPath is an experiment in answering a difficult question:
Can we make clinical guidelines computationally useful without stripping away the context that makes them clinically meaningful?
I believe the answer is yes, but only if we treat the translation as an exercise in preserving knowledge rather than merely automating decisions.
Conclusion: Executable Medicine Needs More Than Executable Rules
A clinical guideline is not a computer program waiting to be typed into an editor.
It is a structured interpretation of scientific evidence intended to guide decisions in circumstances that are often incomplete, uncertain, and individualized.
Converting that knowledge into software requires much more than implementing thresholds and conditional branches.
We must preserve recommendation strength, evidence quality, patient eligibility, exceptions, overlapping indications, and the uncertainty inherent in clinical practice.
We must distinguish a calculated risk from a clinical decision.
We must design systems that can explain their outputs and identify their limitations.
And we must acknowledge that some decisions cannot be reduced to code without losing something essential.
The most valuable clinical software is not necessarily the software that makes the most decisions.
It is the software that helps clinicians and patients make better-informed decisions, using transparent, validated, and appropriately limited computational tools.
A good clinical algorithm should know when a rule applies.
A trustworthy clinical algorithm should also know when it doesn't.
References
American Heart Association. (2026). The American Heart Association PREVENT online calculator. https://professional.heart.org/en/guidelines-and-statements/prevent-calculator
American Heart Association. (2026, March 13). Top things to know: 2026 guideline on the management of dyslipidemia. https://professional.heart.org/en/science-news/2026-guideline-on-the-management-of-dyslipidemia/top-things-to-know
Blumenthal, R. S., Morris, P. B., Gaudino, M., et al. (2026). 2026 ACC/AHA/AACVPR/ABC/ACPM/ADA/AGS/APhA/ASPC/NLA/PCNA guideline on the management of dyslipidemia: A report of the American College of Cardiology/American Heart Association Joint Committee on Clinical Practice Guidelines. Circulation, 153(17), e1154–e1276. https://doi.org/10.1161/CIR.0000000000001423
Clinical and engineering note: The TypeScript examples in this article are simplified illustrations of software design principles, not validated implementations of the ACC/AHA guideline. They are not intended for clinical diagnosis, treatment selection, or prescribing. Any production clinical decision-support application requires complete source mapping, independent clinical validation, appropriate safety review, and ongoing maintenance. This article and the LipidPath project are independent educational work and are not endorsed or certified by the ACC or AHA.