PROJECT · 2020 · AUTHOR

meinrezept.online Prescription Platform

Online prescription platform in Hamburg. Backend leads built PHP/Node.js microservices and RabbitMQ pharmacy routing under pandemic-era load in 2020.

Screenshot of meinrezept.online — the Hamburg online prescription platform

In April 2020, every healthcare startup in Germany suddenly became important.

Pharmacies were overwhelmed. Doctors were doing video consultations for the first time. Patients were mailing in paper prescriptions. The whole country woke up and noticed that the prescription pipeline — the path from a doctor writing a script to a patient getting their medication — was almost entirely analogue. In Hamburg, I joined meinrezept.online as one of the backend leads to help build the system sitting in the middle of that.

This is not a “we saved lives during a pandemic” post. It is a “here is what the engineering actually looked like” post — because the engineering was genuinely interesting and most of it is invisible from the outside.

The prescription format problem

Before you can route a prescription to a pharmacy, you have to read it. In Germany in 2020, this meant dealing with multiple formats simultaneously: the Muster-16 paper form, early digital prescription formats that were not yet standardized, fax images, and the occasional PDF that a doctor’s office had generated from whatever practice management software they were running.

Each format has a different extraction path. The paper form has a known layout but arrives as a scan. The digital formats have structured data but not all pharmacies could consume all of them. The PDF from the practice management software is the worst case — structured data locked inside a document format that was never meant to be parsed programmatically, in a format that was actively evolving as the German government pushed the Elektronisches Rezept (eRezept) rollout in real time.

The backend’s job was to normalize all of these into a single internal representation that the rest of the system — the pharmacy routing layer, the order tracking service, the patient notification service — could consume without caring about the input format. This is a harder problem than it sounds when the input formats are moving under you.

Pharmacy integration is legacy-heavy by design

Here is what the pharmacy integration space looks like from the backend: every major pharmacy chain has its own ordering system; many independent pharmacies run WINAPO, ProFit, or ADG — practice management systems that are decades old and expose ordering interfaces via protocols you have not heard of in a modern backend context. HTTP/JSON is not the dominant wire format. EDIFACT and HL7 variants show up. Some pharmacies have a web portal and an unofficial API that someone reverse-engineered. Some have nothing and you are back to sending a structured email.

This is not a complaint. It’s what healthcare integration looks like at the consumer-facing end. The hospital-scale EHR work I’d done before (AFAQ, HAKEEM) operates at a different abstraction level — dedicated integration engines, HL7 FHIR contracts, a technical counterpart at the other system. At the consumer pharmacy scale, you’re working with whatever the pharmacy actually has, which is frequently much less than you’d choose.

RabbitMQ was the message broker, which was exactly right for this scale. Not Kafka-level volume — reliable delivery between a manageable number of services with retry semantics, dead-letter handling, and the decoupling of inbound order receipt from outbound pharmacy dispatch. RabbitMQ handles this cleanly. The exchange topology used topic exchanges routing on pharmacy region, prescription type, and fulfillment path — which let us add routing logic without touching every consumer.

Microservices with three languages

The stack was PHP 7, Python, and Node.js. This is the real world. You inherit the language the first service was written in. You pick the best tool for the next problem — Python for the extraction work, Node.js for the real-time status API. You end up with a polyglot codebase held together by message contracts rather than language consistency.

The message contract discipline matters more than the language consistency, honestly. If every service agrees on what an order.received event looks like — the schema, the versioning, the required fields — then the fact that the producer is PHP and the consumer is Node.js is an implementation detail. The dangerous failure mode is letting each service evolve its understanding of the domain independently, so that order.received means something slightly different to the pharmacy dispatch service than to the patient notification service. You find this out at the worst possible time: when a patient calls support saying they got a “prescription dispatched” notification but the pharmacy has no record of the order.

ReactJS on the frontend — not I primarily responsible for — connected to the same domain model through the status API. Consistency between what the patient sees in the UI and what the backend’s event log says is an underrated integration concern that became visible when pandemic-era order volume revealed timing gaps that staging environments had not anticipated.

The pandemic timeline amplified everything

Joining in April 2020 meant joining a team where the product was being load-tested by real user demand in a way no staging environment had modeled. Healthcare anxiety is a real traffic driver. Inbound order volume spikes around any news event — a lockdown announcement, a change in pharmacy opening hours, a news story about medication shortages. You learn very quickly which parts of your service are stateful and which aren’t, because the stateful parts are the ones that fail non-gracefully under load spikes.

The extraction pipeline being decoupled from the order acceptance path was the critical architectural call. A slow pharmacy integration should not prevent a patient’s prescription from being received and acknowledged. The acknowledgment and the fulfillment are different concerns and should fail independently. This change was made under load, which is not ideal — but it’s a real engineering memory, and the reasoning is sound in retrospect and in a whiteboard session.

What the range across healthcare scales teaches you

I’ve shipped healthcare software at two very different scales: large enterprise EHR and clinical workflow systems (hospital-scale, hundreds of thousands of patient records, complex clinical decision support), and consumer-facing healthcare apps (prescription ordering, patient notifications, pharmacy routing). They share regulatory requirements — patient data handling, prescription validity, audit trails — but almost nothing else.

The consumer-facing side has faster iteration cycles, less formal integration partner relationships, and more pressure to handle the long tail of edge cases in the data. The enterprise side has structured integration, formal HL7 and FHIR contracts, and slower change cycles. Both are hard. The range is useful.

meinrezept.online is still operating. The pharmacy routing and prescription processing infrastructure from this period — built under genuinely unusual pressure, against an evolving regulatory landscape, with a team adapting to the same lockdowns the system was serving — is part of that operation. The pipes still run.