Measured Behaviour
Published
Everything this series has measured so far is a value read off one record and compared against a threshold, a table or a set. ABO compatibility is not that. It is a RELATION between two records, and although the relation is symmetric in the sense that matters clinically, the nested lookup that implements it is not - which is why its two parties fail in opposite directions.
A relation is not a property
You cannot say a unit of blood is compatible. You can only say it is compatible with somebody. Compatibility is not written on the bag and it is not written on the patient's notes; it is a fact about the pair, and the software has to hold it as one.
The way every transfusion system holds it is a nested table indexed first by the recipient's group and then by the donor's. That is the obvious implementation and it is the right one. It is also, unavoidably, ASYMMETRIC: the recipient selects a ROW and the donor selects a CELL within it, and a missing row and a missing cell are two different kinds of miss.
A missing row means the library was not told who the patient is. That is a question about the order rather than about serology, and no library can refuse every order in a hospital on the strength of a field it was not given. A missing cell means the library WAS told both groups and the table pairs them nowhere, which is a serological answer: not established, therefore not permitted. Each default is the reasonable choice for the question it answers. Together they are why the two halves of one relation fail opposite ways.
What we built and what the correct run says
Thirteen units in the refrigerator - six red cell units and six plasma units across all four groups, plus one unit whose retained segment was used up in an earlier investigation. Eleven orders across five wards, including a trauma call whose group is not back from the laboratory yet and a massive haemorrhage on intensive care that needs plasma before a crossmatch is possible. Twenty proposed pairings.
The correct run issues fifteen and holds five, each for a different and correct reason: D-positive red cells for a D-negative woman of childbearing potential; group O plasma for a group A patient; group A cells offered on a trauma call before the group is known; group O plasma offered on an emergency plasma order; and a perfectly good unit with no segment retained. Every price counter reads zero - no incompatible unit reaches anybody, no millilitre of incompatible component is transfused, and nothing goes out that could not be investigated afterwards.
Two of the configuration keys in this area are pinned at the library's own default values, deliberately. The asymmetry this article is about belongs to the library, not to anything the hospital chose.
The two parties, in opposite directions
We removed the RECIPIENT'S GROUP from the order records and executed the result. The table lookup finds no row, the library takes its permissive branch, and every elective pairing on the worklist is permitted. ISSUED went from 15 to 16, HELD from 5 to 4, and the price counter from 0 to one unit and 262 mL: a group A patient received group O plasma, which carries anti-A.
We put that back and removed the DONOR'S GROUP from the unit records instead. Now the row is found and the cell is not, so the library takes its strict branch. ISSUED went from 15 to ZERO. Every unit in the building was refused. The blood bank telephone starts ringing in about ninety seconds.
Two names on two records, one relation between them, and the same transformation on each. One produced a silent transfusion of incompatible plasma; the other produced a hospital-wide stop. A reviewer asked which of these two fields is the dangerous one has no way to answer from the clinical domain, because clinically they are symmetric. The answer is in the shape of the lookup.
We also expected the union of the two to be identical to the recipient half, on the reasoning that once the row is missing the cell is never consulted. It was not: ISSUED came out at 14 rather than 16. The emergency-release path does not go through the table at all - it compares the unit's group directly against a universal donor group - so on that path the loud half survives the loss of the quiet one. The ladder holds where the lookups are nested and stops holding where a code path bypasses them.
The universal donor is a different group for plasma
This is the arm that has nothing to do with software and everything to do with what the software is about.
Group O is the universal donor of RED CELLS, because O cells carry neither the A nor the B antigen. Group AB is the universal donor of PLASMA, because AB plasma carries neither anti-A nor anti-B. Those are opposite ends of one axis, and the plasma compatibility table is the exact transpose of the red cell one. A library written for the commonest product a transfusion service issues - and red cells are that product by a wide margin - ships the red cell table as its built-in and expects you to configure the plasma one.
So we removed the COMPONENT CLASS from the unit records: one field, on the bag, saying which product this is. Every pairing now falls back to the built-in red cell table and the red-cell universal donor. ISSUED moved only from 15 to 14, which looks like nothing. The price counter went from 0 to THREE units and 807 mL.
That net figure of minus one is two things at once. Four perfectly good plasma pairings were newly refused - a group O patient denied group AB plasma, a group B patient denied group AB plasma - because under the red cell table AB is the restrictive group rather than the universal one. And three genuinely incompatible pairings were newly permitted, including group O plasma released without a crossmatch to a group A patient on the emergency path, because under the red cell rule O is exactly what a fallback names as universal.
One lost field, both directions, on the same run, decided by which product the bag happens to hold. A plausibility check cannot see this: every group in every cell of both tables is a real group, and every verdict the library printed is a verdict it genuinely reached.
The keys that fail the way you would guess
For completeness, because a matrix is only useful if the ordinary cells are in it too. Removing the enforcement flag - the key that decides whether an incompatible verdict actually stops an issue, rather than merely being printed beside it - issued all twenty pairings, put four incompatible units and 1,099 mL into patients, and released the unsegmented unit as well. That is the largest single figure in the area and it is the least interesting, because it is what everybody expects renaming a boolean to do.
Removing the Rh policy, the recipient's D type, or the unit's D type all produced the same result: sixteen issued, one incompatible unit, 283 mL, and a D-positive red cell unit transfused into a D-negative woman of childbearing potential. Three different names, one destination - which is worth knowing when you are deciding how narrow a pattern needs to be.
Removing the universal-donor map took ISSUED to 17 and the price counter to two units and 554 mL, because emergency release stopped being restricted to universal units at all. And removing the segment reference took ISSUED to zero, which is the same loud stop as the donor group.
What this means if you protect a transfusion system
The practical advice is the same as everywhere else in this series and the reason for it is sharper here. Protect the bundle; do not let the rename pattern reach the names an installed library reads or the fields on records it is handed.
What this area adds - and the exclusion list is where you act on it - is that you cannot rank those fields by importance. `recipientGroup` and `donorGroup` are the same field twice over as far as a clinician is concerned, and they produced a silent incompatible transfusion and a hospital-wide refusal respectively. `componentClass` looks like metadata and is the field that inverts the meaning of the whole table.
If you are designing rather than protecting, there is one change worth making regardless: do not let a fallback be shared across products whose safe answer differs. A universal-donor setting that has to be per-component is a setting that will eventually be read for the wrong component, whether by a rename, a configuration edit, a library upgrade, or a serialisation hop that drops a key.
And measure with a worklist that has questionable pairings on it. Our negative control - the same wards on a day when the laboratory proposed only group-identical, D-matched, segmented pairings - is the fixture almost everybody builds, because it is the day you would show an inspector. It is blind by construction to every arm above that loosens a check.
Frequently asked questions
Can obfuscation change a blood compatibility verdict?
It can change which table the verdict is read out of and which of the two parties the library can still read, if a renaming pattern reaches the field names on the order or unit records. In our measurements one field produced a silent issue of incompatible plasma and another produced a refusal of every unit in the building.
Why do the recipient's group and the donor's group fail differently?
Because a compatibility table is a nested lookup. The recipient selects a row and the donor selects a cell within it, and a missing row means the library was not told who the patient is while a missing cell is a serological answer. The permissive default on the first and the strict default on the second are each reasonable, and together they point opposite ways.
What is the risk with plasma specifically?
Plasma compatibility is the exact transpose of red cell compatibility - group O is the universal donor of red cells and group AB is the universal donor of plasma. A library that falls back to its built-in red cell table applies the reversed rule to plasma. In our fixture that permitted three incompatible pairings and refused four good ones on the same run.
Does the run fail loudly when this happens?
Sometimes, and the loud cases are the safe ones. Losing the donor group or the segment reference refused all twenty pairings, which somebody notices immediately. Losing the recipient group or the component class left the run reporting a full worklist with a consistency check that passed.
Which configuration keys mattered most in the measurement?
The enforcement flag produced the biggest number - all twenty pairings issued and 1,099 mL of incompatible component - but it is the least surprising. The interesting ones were the component class on the unit record and the universal-donor map, because both left the report looking normal.
Do the Rh fields behave the same way?
In our fixture the Rh policy key, the recipient's D type and the unit's D type all produced the same outcome: sixteen issued, one incompatible unit and 283 mL, with D-positive red cells going to a D-negative recipient of childbearing potential. Three different names reaching one destination.
What should we test before shipping a protected build?
Run the issue engine against a worklist that contains pairings that ought to be held, on both red cells and plasma, and compare the decisions rather than checking that the run completed. A worklist of group-identical, D-matched pairings cannot detect any arm that loosens a check, because there is nothing on it to loosen.
Related reading