Breaking apart your Monolith: Free your Front-end
Breaking apart a monolith is something that takes a while… a long while. There are a few things that while not reducing code, can have a pretty large impact on the overall agility of your product development. Separating out your Front-end code from your monolith’s repository/project/deployment life-cycle is one of those methods. It’s often stateless when embedded into a monolith and one of the safer option to begin your journey. Here’s why.
The efficiency of your development is increased significantly
Starting with deployments: When your front-end code is separated from your monolith it doesn’t need to follow the same deployment schedule anymore. I’ve written before about the issues with deploying on a schedule and some of the benefits associated with continuous deployment.
If approaching this style of deployment is new to your business and you aren’t completely confident with the prospect of abandoning your schedule, front-end deployments are a great starting point. They can require relatively low configuration, almost no infrastructure and are generally very fast to compile and publish. (compared to complex, bulky legacy monoliths.)
By this point it should be made clear that there should be a clear separation between any business logic and your front-end, with most actions being sent to a post page or a controller/API endpoint. There can be workflows and fancy interactions facilitated by your font-end code, but at the end of the day the output to the listeners is what’s important.
Bugs and unforeseen behaviour will no longer have a multi-day life expectancy: Deploy your date-picker component and it bootstraps incorrectly in IE? Well fix it in your new, fully tested front-end project and have it deployed within 15 minutes! Changing how the drop-down works in your Text editor? It doesn’t matter what it looks like as long as the resulting text value is the same. A library the front-end uses has a vulnerability found? Just update the package and deploy.
Testing your front-end becomes more specific
Because you now have some very specific front-end code, your tests can be written to reflect that. Have a date-picker component you need tested? Add a myriad of unpublished test pages with a setup for exactly what you want to test. Run your tests locally, or even have your CI/DC tool run them for you. Enjoy writing functional tests with helpers such as Protractor to navigate through your web pages with a fully mockable backend stack. No more worrying about setting up entire vertical stacks to test your front-end behaviours.
Front-end code doesn’t need to be deployed into the same infrastructure as your monolith.
For the most part, front-end files shouldn’t be dynamic in their file generation, once the code is built and compiled it shouldn’t need to change until the next deployment. One of the benefits of most modern front-end technologies is the fact that they only need to have their location referenced on your pages to be able to be accessed by your site. This means that while your monolith infrastructure may be complex and difficult to amend a secondary deployment into, it can almost be completely ignored. Your front-end can be deployed into almost any static file hosted location (s3 for example) and then simply referenced by your monolith at the outside location, instead of a local, relative path.
Your new front-end process doesn’t need to be its own monolith
Just because you have all your front-end code already in a monolith doesn’t mean it all has to live together outside of your monolith. Feel free to separate your domains and components out into their own repositories. Hopefully there isn’t too much crossover with dependencies, even then they can be independently deployed and referenced across each other! (Although git submodules can be a giant pain in the neck).
You can experiment with new technologies
Now you have your front-end separated out, don’t shy away from branching out. You don’t have to worry about deep linking your new framework all the way up from your base pages. Try creating one of your module in React or instead of building out an entire MVC workflow try building it in Angular with your monolith providing the begin and end point. It’s time to move away from having to commit to ingraining these new technologies all the way through your product.
This process doesn’t need to be done all at once
Start small. You don’t need to start with a code extraction. Start by creating some smaller more modular components, or replacing some of your less complex components. A pattern that we have been developing over the last year or so is the replacement of our form components, namely our Rich Text Editor and our Date-Picker. The process is fairly straightforward at this point:
- Create a new repository for the component
- Develop the component locally
- Build and compile the component to to a bucket in S3
- Replace all instances of the old component in our monolith with a reference to our new components’ script in S3 and make sure the form values our post pages and controllers are looking for are still present. Configuration that used to be populated server side can now be passed to the client side and handled there. No more baking out magic strings of HTML.
By doing this we are able to make very wide and shallow changes to our monolith. It’s also worth mentioning that last step also includes the replacement of 3 tech stacks worth of components, classic ASP, webforms and .Net MVC.
After we’ve completed this process, we now have a single version of our component spread across all of our tech stacks with almost all bugs solve-able with a 15 minute front-end deployment.
These changes can easily be reverted
Worried about committing to the process too hard? Fear not! While I doubt you will want to after taking your first big step into breaking down your monolith: Going from a distributed code base back down to a singular one again is super easy. Just move your code right back into your old projects, reference local paths and you’re back to where you started. After all, you’ve been doing this for years to build a monolith anyway right?
This all sounds great, but what are the downsides?
Getting your first deployed front-end workflow is going to be a learning experience. There are plenty of CI/CD tools out there and each one has its nuances. But there are plenty of examples and tutorials all over the place now that devops as a culture has become a more prominent figure in the tech community.
Versioning your front-end can become a thing. In most cases for myself I’ve found that adding functionality or fixing bugs can be free enough to not have to worry too much about what the back-end will be expecting or what it can support just yet. But sometimes you are put in a situation where you may need to have two or more possible paths throughout your code as you change the behaviour of the front or back-end to be expecting things to happen a certain way. Usually this lasts until both of the tech stacks are in sync with each other and the old redundant code can be removed.
Versioning your API can also become a new practice, while traditionally your monolith would always be deploying a new version of its front-end code alongside the back-end code, it’s now asynchronous. Because of this you may need to ensure your API’s have an appropriate versioning system implemented for your consumer projects. This is also good practice for getting ready to publicly expose your API to clients in the future.
Repository rot can become a bit of a problem: keeping front-end packages up to date can be a bit of a pain, especially if you are spread across multiple repositories with multiple build processes. Almost every project I’ve worked on that’s been created in this way has it’s own packages to keep up to date and depending on how frequent you need to change this front-end code, you may find your packages wildly out of date almost every time you (infrequently) pick up the code. I don’t think this is unique to this method of development and I’ve seen this in projects of all sizes. Because you will find that your front-end repositories are relatively self contained with (hopefully) a myriad of quality tests, updating packages usually isn’t that big of a deal. At least compared to updating packages across a monolith with 100s of projects included.
Never look back
Over the last few years of following this practice I’m a big fan. To be honest when some of the components were proposed to be developed this way it was hard for me to see the benefit, after all why go through all that effort when I can just add a small JavaScript file or two into the monolith? But after being able to almost instantly deploy fixes to bugs, develop outside the monolith cycle, write thorough tests and choose my own technologies, I could never look back.