Our main application is a monolithic beast, covering multiple distinct user flows, with very little UI cross over. I recently tasked one of our squads which was about to build yet another user flow into the application to do a tech spike into how it should be structured - packages, folders, microservices - what’s our way forward?
But what are the actual problems we are trying to address here? Keen engineers are very quick to suggest things like “microservices architectures” as the way forward - from my perspective here be dragons.
- By splitting into microservices are we actually addressing any issues we currently have?
- By splitting into microservices are we now creating more issues that we didn’t have? Network calls? Dependency hell?
Why do people want to jump into microservices? From my reading and experience people see it has the hot trend in tech or see it as a way to further their skills in potentially new areas like k8s to orchestrate their microservices. The other aspect could be the tendency for us engineers to say “let’s rewrite this / start from scratch / green fields. After all, in a project that’s been going on a few years there’s been lots of chefs across the codebase.
Popping back up the topic stack what problem are we addressing?
The main issues we have in my eyes:
- Release coordination across the monolith - every user flow has a different product owner who needs to sign off on the release
- Cognitive overhead - the codebase is large and sprawling, it is non trivial to find backend or front end components for specific product flows. If a stack trace is thrown - do we know what product it is for?
- Cypress testing speed - currently on commit our cypress suite runs, this takes around 20mins for the entire thing to run in parallel
From these points I can deduce a few requirements for any architectural / solution design changes:
Clear separation of concerns - areas of the code base that can have CODEOWNERS assigned, better folder structures that dictate what’s where - how to find relevant files. Decouple as much as reasonable, shared functions or components should be obviously shared - but items for specific flows should be split out.
For example product flow X should not be able to directly access product flow Ys database to compute items. That should be done via a function call that can be typed and tested. If these were microservices they would be abstracted behind a REST API and a cheeky network call - but we don’t need a micro service to maintain clear separation.
Around releases, this should only be necessary for something major, minor small features should be shippable daily/weekly if they aren’t then they should be feature toggled.. releases shouldn’t have to wait for everyone.
If we can’t do this than our feature branches are too big, or too interweaved… signs of features being difficult to maintain and sprawled across different files. We’re never going to get to a point where we ship features globally if so much overhead is required for a release or a new feature.
Cypress testing with feature switches enabled … these should be done via proper feature switches in something like launch darkly. Which can automatically switch features in an environment. That also promotes visibility of features, testing by non technical team members.
That’s where my head is currently at.