About five times per year, Ethiopia ACT mobilizes week-long medical trips to provide care for hundreds of underserved people in Addis Ababa. I’ve been on three of these teams in the last five years and have used my tech skills to help.1 On my most recent trip, in January 2025, I built a clinic data system, based on feedback and conversations with the staff since 2024.
Screenshot: Dashboard of new clinic data system
The data system helps the organization to operate better during clinics and to prepare for future clinics. For this project, I used Ruby on Rails with Avo.
Gathering Requirements (2024 trip)
Ethiopia ACT had clear goals for a clinic data system, which I documented on the 2024 trip:
- Identify common diagnoses. This would allow them to plan community health interventions, prepare providers before arriving in the country, and ensure they have appropriate medications for common diseases.
- Budget and plan for needed labs and imaging.
- Increase data accuracy and decrease staff review time.
I also captured some non-functional requirements, such as the budget available and the need for flexible reporting on the data.
I researched many existing software systems, hoping to find a suitable one. I even talked to one promising vendor but ultimately didn’t find an existing option that was satisfactory. Some systems seemed too complex; others seemed relevant but lacked features we wanted.2
Although the trip in 2024 didn’t end with a solution, I came to understand the problem really well. I ended the trip with a Google Doc of requirements from the stakeholders.
Building the App (2025 trip)
When I returned for a week in January 2025, I was determined to put some initial solution in place. The week before I left, I reviewed the requirements again and did some research.3 For rapid development, I wanted to use Rails, but I wasn’t sure what to do for the frontend. I started looking at the latest version of Twitter Bootstrap, as I’ve had good results using it before.
When I found Avo, I was intrigued. I watched a couple of videos and realized it could be a perfect fit for the project. I could use Rails and rely on Avo for the frontend, using their DSL. Maybe I wouldn’t need to write any frontend code at all, not even to use a framework like Bootstrap.4
Despite limited focused development time each day, I built a functional app within the week:
- I started by creating an empty Rails app with Avo and defined the data models. I wrote tests for the ActiveRecord models and defined validations.
- By the middle of the week, I had a basic Rails app running in production using Hatchbox and was able to focus on implementing more features.
- By the end of the week, I had my first users entering data and giving me feedback – and I had configured regular backups of the SQLite production database to a Google Cloud Storage bucket.
Using Avo
I was pleased with how fast the app came together with Avo and how well it worked on mobile, which was an important requirement.
I used the Pro version for this project. The free version is a bit limited. The features that made me want Pro included:
- Dashboards
- Granular permissions with Pundit
- Menu customization (although I am sure I could have overridden certain partials to accomplish the same goal)
I used the dashboard feature to provide stats on the clinic. Whenever I had been on medical teams in the past, we always wondered about how many patients we had seen each day and about top diagnoses; since we had all this data in the app, it was trivial to display in a dashboard and provided real-time understanding of the clinic.
Using AI
AI assistance unblocked me at multiple points, helping with research and debugging. One of my favorite small uses of AI in this project was giving Claude a screenshot of the Ethiopia ACT website and asking it to customize the Avo branding colors config for consistency. The result was beautiful!
Follow Up Work
I have continued to ship improvements to the app. One of the first improvements was adding semantic search for the medical diagnoses. The clinic uses a standard list of about 20 diagnoses. Because I don’t know the list, and doctors will write more specific diagnoses, it was a pain to find the right category.
To find results for a search query, I used both Jaro-Winkler for fuzzy matching and embeddings for semantic search.5 It was so cool to see a search like “having a baby” turn up the diagnosis for “pregnancy” using the embeddings! It makes it easier for users to find the right diagnosis category.
Conclusion
This project has made it much easier for ACT to operate and plan for clinics. Having a structured way to enter data is a big win in itself, and we will continue to evolve the reporting and analytics as we gather more data. I have also gotten good feedback from the staff on the usability of the app, and how much time it saves them versus a more manual process for creating reports.
-
I wrote about my first project here. ↩
-
Even after the 2024 trip, I continued to research solutions. The most promising software system was not an option for the organization because of data locality: For regulatory reasons, we needed to be able to host the system in Ethiopia, but the vendor could not help with this. ↩
-
Beyond wanting to go into the week with a plan, I have also learned that Internet connectivity is not as reliable in Ethiopia as at home. I figured it would also be wise to download any software that I knew I wanted in advance, while I had a fast connection. ↩
-
I wasn’t opposed to using Bootstrap, but I did feel that figuring out how to lay out the frontend application would slow me down a bit. When I tried Avo for a quick prototype, I was impressed and felt like it would reduce the number of decisions that I needed to make. ↩
-
The biggest challenge was finding a simple way to package the model with the application. Ruby’s ML ecosystem is smaller than Python’s, and I wanted to avoid a sidecar container. Eventually I found the informers gem, which was perfect! I used it to call the sentence-transformers/all-MiniLM-L6-v2 model. Once I had a way to run the model from Ruby, I was able to build the search experience. Following the model documentation, I prefixed search queries with “Represent this sentence for searching relevant passages:” before calculating embeddings. ↩