Clojure in London: HealthUnlocked
A health network powered by Clojure
As you write more, you like it more. And it's becoming better and better over time.
HealthUnlocked is a social network for people with healthcare conditions. It is a network built on communities, operated by partner groups such as charities and patient groups; HealthUnlocked provides the platform and technology for these organisations to manage their communities.
Originally HealthUnlocked was built on top of .NET, but over the last few years they have migrated to a Microservice based architecture featuring Clojure.
I caught up with Head of Technology Alex Trofymenko and Head of Engineering Russell Dunphy to learn more.
Jon: How did HealthUnlocked get started?
Russell About seven years ago our Chief Medical Officer saw that there was a gap in provision and information for patients. As a doctor he was finding that patients would leave a consultation with little to no information. There was no place to go to ask questions or to find other people who were having a similar experience.
Around this time social networks were getting popular, but there was nothing health focused; 70% per cent of people have never met someone with the same condition as themselves before joining HealthUnlocked. We have been able to bridge that gap, providing advice on coping and navigating your own health conditions.
Jon: What's an example of the functionality provided by HealthUnlocked to community managers?
Russell: It's not about prescriptions or diagnostics, rather it's about providing coping advice, helping individuals to understand what's happening. This includes management of posts and messages, topics and users' communities.
Alex: It's a peer support network, allowing for patients to share experiences with each other, to ask each other questions. It means that in addition to medical professionals, peers can also help each other.
Russell: We also use machine learning to understand what our users write. We can tailor the information and content we provide to people, making it relevant and life changing, to give people the information they need to better manage their own health.
Jon: What's the tech history of HealthUnlocked?
Alex: The company was originally built on Bizspark using .NET. About four years ago we started rebuilding from the big monolith into a Microservice architecture, which allowed us to easily migrate from one language to another. The different parts of our applications talked to each other via REST or message queues.
Russell: I wonder if it's fair to use the term Microservices; sometimes when people use Microservices they take it to ridiculous extremes. Our services are 'reasonably sized services'.
Alex: Yes, the big difference is we went from about 1 big application to 20 services. We looked at it recently, and we went from about 275K lines of C# code to about 70K lines of Clojure code for one project. The reduction of code was quite remarkable.
Jon: So where did Clojure come in?
Alex: It started off as a sort of an experiment. As we were rewriting the app, we wanted an MVC API. We tried using Node, but it didn't really work for us, so we chose Clojure. After building this - it took two days - we went from on there. It was a very incremental process of trying out different languages and seeing what we liked.
Jon: It just stuck?
Alex: As you write more, you like it more. And it's becoming better and better over time.
Russell: I still get blown away by how powerful it is. Just the other day I wrote a tool to parse an entire export of a colleague's emails - pull out the stats and to build a historical record. It was only 30 lines to work with a complex file type I hadn't seen before; it's just really exciting to feel this kind of power.
Jon: What's the team size?
Russell: We have eight full stack developers and two data scientists.
Alex: All the developers are familiar with Clojure. We use pull requests to help get functionality into production.
Jon: What's your tech stack?
Russell: We are just introducing some ClojureScript and Reframe.
Alex: We also use RabbitMQ for messaging, our websites use NGINX. We're then heavily reliant on AWS.
Rusell: We've got some lambdas.
Alex: Yeah we're experimenting with serverless architectures.
Jon: It sounds like you've got a good experimental culture?
Alex: Yes, whilst we need to focus on speed of development as we're constantly under pressure to deliver new things, we do experiment, as learning is a primary driver for developers to feel more engaged and more interested. We don't compromise on operational stability.
Russell: At the same time, it is really useful to have a smallish set of technologies in the stack. I'm not going to purposefully choose radically new technologies... we're lucky that Clojure is such a good language.
Jon: Have you got any learnings about using Clojure at the architectural level?
Russell: So, controversially, I've come to prefer bigger applications with are composed of independent vertical slices of functionality. We enforce 'in application' segregation using different package structures as boundaries of the various sub-domains. This gives us the benefit of isolation without the pain of setting up the boilerplate of Microservices.
Alex: We're moving towards having a single GIT repo owning access to a data store with multiple apps inside of it. We will have a shared directory for schemas that cross application boundaries.
Russell I think it's important to explicitly define and test interactions between applications using shared schemas, testing both the consumers and producers against these schemas, to make guarantees.
Jon: Have you look at Clojure.Spec?
Russell We've got so much Plumatic Schema. We're being a bit cautious about migrating.
Alex: One step at a time.
Russell: Spec has the really powerful idea that you don't have specs for Maps, but you have specs for the keys. In other words the data types of the fields inside the map are defined as individual specs at the key level, which makes the specs more reusable. This is similar to how the Datomic Schema is defined using schema attributes.
Jon: Not sure I'll be able to write that up!
Russell We've also started gently introducing generative testing for our integration tests that hit our databases. We've found a halfway house where effectively we're writing example-based tests; all of the fields that are not specified are generated using test.check generators and schema generators.
Jon: Don't you get random test failures that are a pain to find?
Russell: Yes, that's the power of it! It forces you to think through all your different assumptions.
Alex: It would be impossible to write a test case for every scenario that you would need to find every bug generative. With generative testing you will eventually get around to finding a bug, and when you find them, you fix them. They application then becomes more robust.
Jon: I suppose that test reporting is key?
Alex: Yes, test reports that show the expected output and what was generated.
Jon: What's your hiring story?
Russell: It's tough hiring, tough finding people.
Alex: Clojure is still relatively niche. We are looking more for passionate, engaged developers; we try to get them to write tests in Clojure to see how they'll pick it up. It's about hiring smart people more than finding developers that have coded Clojure before.
Russell: It's super important to find someone who is going to be a positive addition to the team. When I first joined I had assumed hiring Clojure developers would be easy, but there are some challenges.
A lot of Clojure developers are very interested in the technical side of development, but to work in HealthUnlocked you need an holistic viewpoint - to understand the whole aspect of the company. We need people who want to deliver good, working software rather than using the latest cool Clojure libraries.
Alex: Because it's LISP based, it's easy to get someone that's very academic but not a practical developer. This is probably true to an extent with all functional languages.
Russell: In Ruby I got so into hyper magical object oriented code, that I cringe now at this kind of complexity. Now I value simplicity.
Alex: Our culture here is very important, we do social activities as well as write code. Experience in Clojure is very useful, but if you don't have it then you can pick it up quickly.
Russell: We're good at hiring for diversity also. We strive for diversity and we see dividends from it. Diversity makes our company vastly better.
Alex: Because of the technologies used and our approach, we aim for a smaller number of people but of higher quality.
Russell: Clojure is so simple that people will pick it up quickly.
Jon: What editors do you use?
RusselL I converted one person to Vim.
Alex: Yes, he's been coding very slowly ever since!
State of Clojure?
Jon: What do you think of the current state of Clojure in London?
Russell: There are lots of meetups and lots to do socially. ClojureBridge is great.
We need more Clojure developers as the bigger companies have sucked up so many.
Jon: What do you think of Clojure's future?
Alex: It will be interesting to see how Clojure and data science come together. Clojure doesn't seem to be made for matrix machine learning and Python has a clear lead. I will be interested to see how this develops.
Russell: Same for NLP and sparse matrices.
Alex: We're always hiring.