Recent travel to Rwanda has brought me to build a POC (Proof-of-Concept) with a familiar stack, only in a very different structure.

To better understand why the POC was built that way, I should give you the backstory.

The Backstory

In 2016, I was invited to present about Drupal & Elm in DrupalCamp Tokyo. I always like to bring this fact up in any kind of conversation - but this time there’s even a reason beyond my usual bragging rights: The flight is terribly long from Israel to Tokyo. Twenty-four hours door-to-door kind of long.

As it so happened, a short time before my flight, Adam, Gizra US Director had virtually dropped a PDF on my table. I was preparing myself for yet another long RFP (Request for proposal) with an impossible spec, and an even less possible timeline. I was surprised to see that was not the case. That PDF was forty-something pages, with a wireframe per page and some notes, showing the flow of a rather interesting app.

Wireframe from the spec

Three years later I still refer to those pages as the best spec we’ve ever received. The people behind those wireframes were Dr. Wendy Leonard and her Ihangane team. They were planning an electronic medical record for an HIV prevention program in Rwanda.

I was really impressed. Sure, the wireframes were rougher than usual, but they did exactly what they were supposed to. The team was smart enough to not rush into development and in fact, they even printed out the spec pages, went to the field, sat with nurses, and let them click on the screens. The printed screens. They clicked on paper!

Anyway, did I ever mention I was invited to Tokyo in 2016?

That long 24 hours flight. I’ve finished my book (“Ancillary Justice”), watched a movie (“Wreck-It Ralph”, as for some reason I love watching cartoons on planes), and there were still many hours before my arrival. So I took my laptop out, spun up a Drupal backend and an Elm frontend - and the first POC for Ihangane’s app called “E-Heza” was born in the sky.

The Good Ol’ Days

And so began the development. A decoupled architecture, where we had a RESTful server in the backend and Elm in the front. Like any project we had: different challenges, deadlines, deadlines we missed, scope creep, managing the project, managing the client, managing expectations, delivering, hot fixing, being proud of what we’ve accomplished, having a technical debt. Rinse and repeat.

While we were developing, the Ihangane team was hard at work implementing our work in the field. In fact, they were able to get it going in a dozen health centers in Rwanda. And while it worked for the most part, there was one challenge we knew was lurking, and waiting - Offline.

Have you ever worked with non profits, or NGOs, and they tell you they have a need for a site, and some of its users are in developing countries where infrastructure isn’t always good, internet is slow, and so we must take care of a light theme and shave every single byte possible, but then in the design they insist on hero banners with huge images, and every possible JS transition?

Well, we have. But that was not such a case. This time it was for real - all health centers had internet, but it was not rare for them to have the connections dropped for days on end. And so, we decided it was time to implement a proper offline support. The reason I say “proper” Is that we actually had offline support from the get go, but it was a very crude one. Only after the nurses checked all the patients, and a “session” was done, did the nurses manually start a sync process. But that wasn’t very nice, and we decided to go with “opportunistic syncing” - where data was pushed and pulled whenever an internet connection was available.

Offline & Service Workers

I’m going to side step, and go technical here: Getting offline support was admittedly a bit easier than I had feared. It is surely the most complicated part of the system, but we were able to keep it confined to the edges of the front-end application.

Elm, in all its greatness, already taught us to think about interacting with JS the same way we would with a remote server. So, instead of having Elm contact directly the remote server, we’ve placed a service worker as a middleware.

That meant that Elm keeps using HTTP requests, the same way it always did, but now we intercept those requests, and the service worker decides - are we online, then call the server and sync the data; Or are we offline, and we should use the data from IndexDB. To make the wiring of the offline part to Elm even less disruptive, we’ve structured our responses from the service worker to be similar to the ones we used to get from the server. In other words, the change was minimal, and it looks beautiful.

But still, as one can imagine, adding this layer brings a new set of challenges. Our app isn’t a news reader, where the worst case scenario is you cannot read your news. No, our app is all around creating new content, and the worst-case scenario for us is losing patient data.

The Rwanda Visit

Back to the main narrative. I was invited to attend a workshop that Ihangane had organized in Rwanda (you know, like that one time I was invited to Tokyo in 2016). It was a two-day workshop where different stakeholders came, and had discussions not only around Ihangane’s solutions but also in general - how to improve the health care in Rwanda.

Some parts were boring - the usual amount of suits talking about things they don’t necessarily know much about. But there was one topic that I found especially interesting: The Government of Rwanda is spearheading a “Health Cloud” effort. This project is a huge undertaking. To give just one example of the many challenges involved - currently in Rwanda when a baby is born, they don’t always get a national ID. In other words, there’s no unique identifier for every person - so the challenges are somewhat larger than just which best methods to use to create this platform.

But what really caught my attention, is how dedicated they were to make sure no vendor locking will happen under their supervising eye. I really appreciated it. I can only imagine how many private, for-profit organizations are trying to get their foot in the door in those emerging markets. I’m not saying all for-profit organizations are evil, I’m just not naive.

Rural Rwanda

I love saying “Rural Rwanda.” With my Israeli accent, it’s a real tongue twister for me. I also loved having a one day visit in the outskirts of Kigali, visiting nurses in Muhondo health center and see our system in action.

Make sure your speakers are on. That’s what I call true “stress testing” - when you have a room with about a hundred mothers and their (cute, yet screaming) babies, every extra second the app takes, is another second people are waiting.

What a great day it was for us - watching with our own eyes, seeing the good parts of the app, finding a couple of bugs, but mainly spotting workflows that could be improved, and benefit the team operating it, as well as the patients. When you see these women that have walked for hours with their babies on their backs to reach the health center, “try to refresh the page” or “let’s wait a couple minutes for the system to fully sync” is becoming less acceptable. I was very proud to see how we were able to be part of a thing that really does good to much less fortunate people.

Letting it All Sink In

  • We have mothers with HIV or high risk for HIV.
  • We have their babies, that need to be examined, and be checked for different signs of malnutrition.
  • We have no internet connection in some areas.
  • We need to support Android devices, as the measurements taken in the field are being registered in those devices.
  • We have this “Health Cloud” idea, that we want to help think about. We don’t want to follow a typical enterprise kind of thinking, but rather use our own more radical one.

At the same time of my visit to Rwanda I have started my affair with Plain text accounting with hledger. I think this quote sums it up nicely:

Accounting data is valuable; we want to know that it will be accessible for ever - even without software. We want to know when it changes, and revision-control it. We want to search and manipulate it efficiently.

It took my brain about a month to switch the word “Accounting” with “Medical”, as in:

Medical data is valuable; we want to know that it will be accessible for ever …

Then another thing happened. Did I ever tell you about my invitation to DrupalCamp Tokyo in 2016? Well, while I was in Rwanda I was invited to DrupalCamp Tokyo 2019.

Once again I was in a plane, finished my book (“The Sudden Appearance of Hope”), watched a movie (“Aladdin”, because cartoons are for kids!), and still had many hours ahead of me. Up in the sky, and for the sake of a good story I’d like to believe it was in the exact same spot the first POC was conceived, a new one started to form.

Same Ingredients, Different Mix

What if we didn’t have a DB, and stored medical records in plain text, the same as we’ve been doing with our accounting data?

What if Git, faithful and efficient Git, was taking care of syncing and working offline?

What if we’ve used Termux - to have a terminal on Android devices, and thus open the door to running a server locally on each device?

I felt those questions required me to answer them. The result is in this repo and the most important part about it, is the data. All the information we have is stored in YAML files. For example, here’s how we define the information of a mother, and the relation to her children:

id: 000f8e43-d638-49fa-8c9a-3429bb819f21
first_name: Aurelia
last_name: Sherlyn
children:
    - ad1bce42-69c7-4d55-82e1-129fb4b91e87
    - b16df0be-ad66-4901-b7a1-2aa720c9968e
    - 0c9a59e4-dd51-40d6-8978-157fc9b65909

That’s it really. Thank you for reading, bye bye.

No, seriously, there’s some more on how we have some software to help view and manipulate the data (which is what you are probably interested in), but I think that having the data in a human readable format (as opposed to be inside DB tables) is the biggest thing in this POC.

Think about how you build a feature in a traditional website:

  1. The client has a feature idea.
  2. You sketch out some wireframes, the client approves them, and they will be out of the loop until section (6).
  3. You do a technical design.
  4. You add a table in your DB.
  5. You write some front end code.
  6. You deploy to TEST - and there the client sees the outcome for the first time.

But with plain text medical records, the “client” can write for themselves how the result of measurements should look. Any kind of conversation would be around that piece of data. Even if there’s no software on top of it, you know what the measurements are just by reading it.

version: 1
group_meeting: 2020-01-30-morning
# We have a single config file in the root, that has the measurements' units.
height: 125
weight: 12
# Mid-Upper Arm Circumference, known as MUAC.
muac: 30

For me, that’s the biggest win. In fact, maybe that’s the “no vendor lock-in” solution for Health Cloud, we can start building on top!

The Disposable Software

I wanted to build the local server in Haskell, but it seemed that to get it running on Termux it was more complicated than a simple pkg install php. So I decided to go with Symfony, and it was indeed a good decision. Setting everything up worked nicely, with a lot of copy & paste from the docs that are great. I’d say that only the Normalizer package (the equivalent of encoders/ decoders in Elm and Haskell) wasn’t fun to work with. Maybe because I’m so used to the guarantees of Elm and Haskell, working with a non-typed language felt less reliable.

Since PHP has its own web server, all we have to do is spin up the web server, and then use the browser to connect to that local server. The server is not meant for production, but in our case we have only a single client, so it actually works great. We can view data, we can add or change data, and we can even upload images directly from the device.

Adding measurements via Browser

Looking at the Git diff, we’d see the commit that resulted with submitting the form.

version: 1
group_meeting: 2020-01-30-morning
- height: 125
+ height: 130
weight: 12

I also wanted to show how the fact that we have the app in PHP doesn’t mean we are tightly coupled with it. We only care about the data, and that one is language agnostic. So I came up with a tiny Haskell utility, that you can give it a path to the data in another folder, and it will create 100 fake mothers and children, so we could test the app with more data.

Furthermore, since we’ve changed the structure of the stack, and we no longer need to think about a server as an entry point to get or manipulate data, the next thing I did was to create a console command to show a listing of mothers participating in a single group meeting. Below we can see the fake date we’ve created with the Haskell utility.

A list of mothers shown in the terminal

Final Thoughts

I think the biggest accomplishment here is stepping outside of the usual stack structure, and recognizing the unique situation that we are facing. This structure isn’t going to be right for a majority of the cases, but I’d argue that there are a good portion of problems that could be solved with such a stack. Obviously it’s not a panacea, and it brings different problems; on the other hand, I find it solves some existing ones quite nicely.

In terms of syncing and access control - that’s baked deep inside Git, not to mention that having it as distributed, version controlled, and (sorry for the buzzword) “blockchain”-ed has many merits. In terms of security of running a local PHP server - since it’s running locally, and it’s not open to the outside world, it’s nothing more than a nicer interface to editing files. But obviously, by using Symfony and applying best practices - we make sure we write secure code.

I don’t know what’s next for this POC, as it’s not only for me to decide. But I do hope we’d get a chance to move forward some more with it, to reach a point where we can test it out in the field. If one day a nurse in a tiny room with many crying babies will tell me it improves her day to day work - that would be well worth it.

amitaibu's profile

Amitai Burstein

@amitaibu