spaghetti-code-killer

Seven Dead

Chapter 1 of 14

The Farber case has been on my desk for eleven days -- long enough to stop seeing code as code and start seeing it as behavior. The financial transaction logic is pinned to the corkboard in four printed sections, each one worked over with red pen -- arrows, circles, the variable name `accountHolder_v3_REAL` underlined twice because it surfaces twice in the codebase, once in the reporting structure an auditor would see and once in the live transaction handler where the actual money moves. Two values. One account. That's how you skim $2.3 million from a municipal pension fund and stay invisible for nineteen months: you hide the discrepancy inside the architecture instead of inside the books, and you count on the auditors looking at the reports instead of the code.

The server racks behind the glass wall hum at a frequency I stopped hearing in year two. The fluorescents overhead buzz at a slightly different pitch, and I stopped hearing that in year one. You learn to work inside the ambient noise of systems that don't stop; it's the silence that means something's wrong.

Ray Okonkwo is across the room at his desk, which looks like it belongs to someone with a plan. His second monitor is running a search query he set up before lunch. His coffee is at a temperature he would describe as correct. He has a framed photo of his three kids facing the wall during work hours -- he told me this with a completely straight face when I asked, six months ago, and I believed him because it is exactly the kind of thing Ray would actually do. Mine went cold an hour ago. I haven't moved the mug because moving it means acknowledging I'm not going to drink it, and I'm not ready to commit.

I draw another arrow on the printout. Red pen, paper, marked by hand -- old habit from homicide. You see different things on paper than on a screen. Scrolling flattens the relationships between code sections; a physical page spreads them out, lets you see the shape. I've explained this to four colleagues over three years. None of them changed their methods. I haven't changed mine either. The corkboard behind me is proof of that, and the Farber case is going to close by end of week.

My father was a machinist. He taught me that understanding how something works is a form of respect. I took the lesson all the way: first into people, then into the machines people build to do their work for them. The machines are easier. They don't lie on purpose.

Captain Reyes calls me up at 3:47 PM. Her office is at the end of the precinct hallway, glass-walled, always the same temperature. One plant on the windowsill, somehow thriving. She has the kind of authority that lives in stillness -- doesn't move around a lot, doesn't make gestures to fill space. When she hands you something, she waits. She hands me a file folder and waits.

"Sunny Meadows Nursing Facility," she says. "Eleven miles north. Seven patients died over four months. All medication overdoses. All managed by an automated dispensing system -- AI-assisted, scheduled dosing, no nurse override required." She pauses a beat. "Vendor is MediCore Solutions. MediCore is calling it a software bug."

I open the folder. The first page is a summary sheet. Seven names, seven dates of death, seven ages. The oldest is 91. The youngest is 76. All the deaths happened between 1 and 3 AM. Anderson, Brooks, Chen, Davis, Evans, Foster, Goldberg.

"The families," Reyes says, "are calling it murder."

"Dr. Patricia Vance filed the initial complaint," Reyes says. "Medical director at the facility. She noticed the pattern and went directly to police, which means something."

"What does it mean?"

"That she didn't trust her own administration to investigate." She watches me read. "She didn't trust MediCore to investigate MediCore, either."

I can't argue with the logic. The case file is thicker than the summary page -- incident reports, autopsy summaries, a preliminary forensic imaging request for the vendor's codebase. "You want the code," I say.

"I want to know if seven people died because of a programming error or because of a programmer." She says it the way she says everything: as a fact not yet verified. "Those are different investigations with different endings."

I tuck the folder under my arm. Reyes looks at me with an expression I've never successfully parsed -- it could be trust, it could be calculation, it's probably both operating at the same time. She was a homicide detective for twelve years before she ran this unit. She sees the whole board. Whatever's behind that look is more than one thing.

"Talk to Ray," she says. "Forensic image by end of day."

I unpin the Farber sections from the corkboard -- the case is closing, they can go in the file -- and turn to the whiteboard. I write the names in marker, left to right across the full length:

ANDERSON — BROOKS — CHEN — DAVIS — EVANS — FOSTER — GOLDBERG

Under each name, the date of death. Four months between first and last. Eleven days from Anderson to Brooks. The gaps vary after that.

Ray stands behind me with his arms crossed, reading. He works the way he's always worked: patient, building the picture one data point at a time, asking questions that sound simple and aren't. "All opioids," he says.

"All opioids. Different drugs -- morphine, oxycodone, fentanyl -- but the mechanism is the same. Respiratory depression." I cap the marker. "All deaths logged as within normal parameters by the dispensing system."

"What does autopsy say?"

"Elevated drug levels consistent with overdose. Also consistent with their prescriptions at the high end of the dosage range." I turn to face him. "Every one of them was already on pain management. The therapeutic window narrows as you age -- the margin between a high therapeutic dose and a lethal one compresses. Each death was individually explainable. Elderly patient, complex medication regimen, the system gave a little too much."

"Until you line up the names," Ray says.

"Until Goldberg. Dr. Vance counted the names in order of death and saw A through G." I add the ages under each name: 84, 79, 88, 76, 91, 82, 85. "Seven deaths. Seven consecutive letters. Four months. All in the middle of the night."

"That's not a bug," Ray says.

"That's what I'm going to prove," I tell him. I step back. The names run the length of the board, black on white, eleven feet of people who are dead and a pattern that doesn't happen by accident. "A bug produces random errors or systematic ones. Systematic errors follow the logic of the code -- they hit every patient, or every patient with a certain drug interaction profile, or every patient in a specific dose range. They don't alphabetize." I pick up my coffee. Cold. I drink it anyway. "If the code is doing what Dr. Vance thinks it's doing, someone built a kill list and put it inside the dosing logic. Which means somewhere in MediCore's codebase there's a conditional check that knows what letter a person's last name starts with."

Ray looks at Anderson through Goldberg. "Who would H have been?" I don't answer that, because the answer is: someone at Sunny Meadows with a last name starting with H who is currently alive because a doctor counted to seven. "Pull the MediCore corporate structure," I tell him. "Who built the system. Who maintains it. What their legal exposure looks like." He turns from the board. "You'll take the code." "I'll take the code."

The forensic image arrives at 6:14 PM. I load it onto the evidence workstation -- a laptop we never connect to the internet, the digital forensics unit's version of gloving up -- and open the MediCore CareFlow codebase for the first time.

It's a JavaScript application, three years old based on the oldest commit dates. I start with the main module. This is the hallway -- the entry point, the code that runs first and calls everything else. The outer layer looks competent: function names are clear, the architecture makes sense, someone at some point knew what they were doing. You could debug the top layer in an afternoon. Then I start following the calls.

`calculatePatientDosage` calls `getDosageParameters`. `getDosageParameters` calls `applyPatientProfile`. `applyPatientProfile` calls `checkContraindications`. `checkContraindications` calls `loadMedicationMatrix`. `loadMedicationMatrix` calls `resolvePatientHistory`. `resolvePatientHistory` calls `getBaselineDosage`. I keep going.

`getBaselineDosage` calls `fetchAdjustmentFactors`. `fetchAdjustmentFactors` calls `computeSafetyLimits`. `computeSafetyLimits` calls `validateLimitParameters`. I keep going.

At callback layer twelve I find `validateLimitParameters` calling something named `applyPatientClassification`. At callback layer thirteen, `applyPatientClassification` reaches into a dependency imported from a library with a last-updated timestamp of March 2018. I look it up on my phone. The npm package page has a red banner: DEPRECATED -- This package is no longer maintained.

The medication safety calculations for a deployed nursing home AI are running through a library that nobody has touched since 2018. The library doesn't get security patches. It doesn't get bug fixes. It exists in the codebase because removing it would require rewriting the sections that depend on it, and nobody at MediCore ever wanted to pay that bill. I scroll back to the outer layer and find the variable handling the dosage multiplier:

```javascript var temp_fix_FINAL_v2_USE_THIS_ONE = patientBaseDose * adjustmentFactor; ```

The comment block above it:

```javascript // TODO: replace with proper calculation module // last updated: see git blame (multiple people) // DO NOT DELETE -- removing this breaks the dispenser integration // ... yes the name is a mess, it's load-bearing, don't ask ```

Seven people are dead because their medication dosing ran through a variable named by someone in the middle of what sounds like a genuinely terrible week, running on a library nobody's looked at in six years, nested inside seventeen callbacks that apparently no one ever traced all the way down. The variable is neither temporary nor final. No one should use it. Seven people did. That last part is what MediCore is calling a bug.

I send the print job to the office laser printer and stand up for the first time in two hours. My back registers its objections. I stretch and refill my mug -- the machine makes coffee that Ray calls adequate and I call functional -- and wait by the printer while it runs. It hums differently than the servers: warmer, a mechanical patience that's closer to thinking than processing. The pages come out in order, the first two hundred lines of the main module, warm from the machine. I collect the stack and carry it back to my desk, lay the pages out flat, uncap the red pen.

That's the thing about fresh printouts I've never successfully explained to anyone who doesn't already do this. The brief temperature of paper just off the printer makes the code feel like evidence instead of output. It's irrational. It's also true.

Old habit, from before. In homicide you printed the incident report, the witness statements, the autopsy summary, and you read them with a pen in your hand because the marks were permanent, which kept you honest. You couldn't undo a circle or un-draw an arrow, so you only marked what you were certain about. My first partner, Carusso, used blue ink. I've always used red. I don't remember anymore whether I chose it deliberately or just grabbed what was in my bag. It's been mine long enough that it doesn't matter.

Someone built CareFlow to keep elderly patients safe. That's what it was for: precision medication management, fewer human errors, the right dose at the right time without a nurse having to verify at 2 AM on her third consecutive shift. Machines don't make tired mistakes. They don't misread labels or confuse patients or rush because there are eight other rooms to check. They do what they're told. The whole deal depends on someone building them carefully enough.

Seven people with last names from A to G are dead. Somewhere in the seventeen layers of callbacks between the outer function and the deprecated library at the bottom, the careful thing broke or was broken. I'm going to find where. The first page is in front of me -- the function definitions, the import statements, the beginning.

I start reading.

ContentsNext →