Monolith to Microservices — The first peel

Jon Menzies-Smith
Salesforce Engineering
8 min readJul 5, 2016
Image : StockSnap

Yeah, we have a monolith here. No, it’s not that awful. In-fact, it’s why we’re successful. Heck, it’s even pretty exciting.

The dreaded monolith that causes so many developers to shudder when they hear the word, simply refers to a codebase comprised of one giant piece of software that does pretty much everything. It’s nothing to be ashamed of and is usually part of the journey most software companies have to go through to realise their success. Only when the time is right and the need is greatest should we start to worry about scaling and maintaining a large codebase. And when that time comes, then microservices may help us.

In this article I’m going to talk through my recent experience working on the Advertising Studio Campaigns platform and the considerations we made when peeling off our first microservice — and I’ll throw in some tenuous Disney references for good measure.

When I See an Elephant Fly

“The very things that held you down are gonna carry you up and up and up.” ― Timothy Mouse (Dumbo)

As a developer it’s far too easy to criticise what’s in front of you. It’s easy to read about how other companies have it so great because they have built the shiniest microservice architecture, to compare that with your own company’s codebase and to question ‘what the hell were these guys thinking?’. I think we’ve all done this and I’m as guilty of doing this as the next person, but as I always say to my colleagues — this monolith is not the problem — it’s the reason we’re all here. We should admire it.

Only a few years ago we were a fledgling startup, the founders were funding the local Starbucks while they worked 20 hours a day to build out the successful company that now employs us. They weren’t focussed on how shiny they could make the system — they needed to make it work, make it sell and do it fast. If they had spent time thinking about splitting the codebase into microservices before it was even built — then that startup probably would have failed.

Knowing when to transition from a monolith to microservices is arguably more important than knowing how to do it and there’s plenty of reasons why people like Martin Fowler advocate building a monolith first. Not only does building a monolith allow you a faster time to market, it also means you can focus on building functionality, quickly iterating on features (and deprecating them). Monoliths allow you to defer the difficult decisions of what to seperate and how to draw those bounding lines until the time that you have actually understood the problem domain and the types of interactions required. By this time you have gained the experience of designing and delivering a validated and working solution and the hidden requirements that could affect boundaries have emerged. Early microservice separation could result in wrongly drawn boundaries, more complex interactions and more infrastructure to maintain. Unless there is a genuine requirement to start with microservices from the start, or your team is experienced in developing microservices, then it may well just be a form of premature optimisation.

A Whole New World

“Do not be followed by its commonplace appearance. Like so many things, it is not what is outside, but what is inside that counts.” — Merchant (Aladdin)

OK, so we’re now working on a successful piece of software, it’s become big, it’s getting harder to maintain, developers complain about it, product owners get frustrated as we slow down and spend more and more time building features and resolving issues. Guess what? It’s probably time to start thinking about separating code. And furthermore, it’s a great time to be working on this codebase. Ambitious developers want to make a difference, they want to improve on what others have built and they want to learn as much as they can along the way. The transition to microservices is just one of those opportunities and it’s a time to get excited.

Be excited, but don’t get carried away. The transition from monolith to microservices is something that requires plenty of careful consideration, agreement from the business, planning and understanding of how things are going to change. Team structures will start to form around one or several services, infrastructure teams will need to be able to deploy and support your code in a whole new way. You’ll need the courage to take the first step but you should do so with the reassurance that you have a well thought out and realistic plan and a team around you that agrees.

Bare Necessities

“Trust in me, just in me. Shut your eyes and trust in me. You can sleep safe and sound knowing I am around“— Kaa (The Jungle Book)

It’s important to recognise that, while separating code into separate services can bring with it many benefits, it also brings new challenges and the safest way to make the transition is by taking small steps. While the thoughts of moving away from a monolith can be daunting, it’s unrealistic to expect a full rewrite of an entire codebase in a single transition. It’s perfectly ok to pick a single candidate for a microservice, identify the boundaries, separate it out from the monolith and then reflect on the experience.

The work doesn’t simply stop at separating code, there’s so much more to consider. Do you need logging and monitoring? Are you storing your code in a new repository? How will your build test and deploy process work? How do services interact? What needs to be standardised across all of your services and what can be bespoke? Do we share code? Who can contribute to this service? What are the security considerations? The more you think about the impact of moving out of the monolith the longer this list of questions will become (and the higher your chance of a brain hemorrhage). If you’re not careful what seemed like a simple task quickly becomes months of work. You need a plan in place to avoid getting distracted from your ambitions.

Our first microservice was not an all singing and dancing microservice, it was not the perfect example of how a microservice should be — but it did get released, it is being used and it has paved the way to allow us to build more. It’s also an important part of our future feature development and therefore allows us to justify further improvements. So what were the considerations and decisions we had to make?

First off, we picked something we had prioritised in our product road-map. An area of code that was notoriously painful to work with, had known performance issues and yet held significant importance to the future of our product. We picked an area ripe for change and one important enough to let us justify the extra overhead of separation. We could have peeled off something which had not changed for years, like an account service, but that wouldn’t really help us moving forward, it wouldn’t get the attention it deserved and it would probably get forgotten about. It certainly wouldn’t help us make the case to the business to invest in microservices.

We were building a service to provide data querying functionality which was to be consumed by another development team. So we took the decision to build the core functionality first, without any of the other supporting infrastructure. First and foremost, we had to get the service up and running and behaving correctly so that the other development team could work against it. There was significant rebuilding required so we had to prove that the service itself worked and we had to make it testable. We could have started off with building a logging and monitoring framework, but we would have held up the other dev teams and we would have been guessing at what we actually needed.

The first release of our microservice went out with only rudimentary logging, monitoring and alerting. There was no service discovery, each server had the service installed locally. We even released manually. But this is nothing to be ashamed of, we identified the minimum requirements and proved it works and now we had justified our decision to move out of the monolith. We had established an important piece of software that needed an improved infrastructure, and now that we had built some confidence in the idea of separating our codebase it was far easier to justify investing the time required to implement all the other bits we needed to create a well formed microservice. (Something we prioritised soon after the first release)

We also released the new microservice alongside the old functionality to allow us to gain confidence without risking what we already have and allow us to control the transition from the monolith through an ‘expand contract’ process. Our monitoring and logging, while only simple to start with, gave us some degree of trust in the system and provided a platform for us to gain more insight into the services behavior and the confidence to make the switch over.

Circle of life

“Oh yes, the past can hurt. But the way I see it, you can either run from it or learn from it.” — Rafiki (The Lion King)

Now that we have our first microservice in production, our job is not done. We’re at the beginning of a long road of separation and improvement. We need to be wary to make sure we make the right decisions along the way and regularly reassess what we have done to ensure that the next set of services are successful and that we can easily manage the growing number of services in production. We may need to change how we log or monitor once we have a few more services in place to ensure consistency, but it’s best to recognise the changes sooner rather than later and the earlier we can identify a future proofed solution the less time we need to spend refactoring.

We might not have started out with the perfect implementation, but we have started our journey and we’re in a position to continuously improve what we have. We are already reaping the benefits of moving out of the monolith and the autonomy that we have earned.

We have solved the challenge of defining a shared contract to allow interactions with our service and with this separation we’re in a position to change anything we want without worrying about the impact on the monolith. We’re also separated from the monolith’s release cycle, allowing us to move faster and move towards continuous deployment.

We have been through the process of truly understanding the requirements of a feature within the monolith and we have produced documentation describing how our service behaves. We have built tests along the way and produced a clean and well structured codebase that’s easier to locate and understand.

We’ve learned plenty through this process, not just about how our system works, but why, and we’ve had the opportunity to re-assess the reasons as we go. We’ve become more aware of devops and monitoring requirements and taken more responsibility for the reliability of our code. We have defined clear areas of ownership and our code is protected from external modification and isolated from undesired side effects.

We’ve established a platform to migrate away from the monolith piece by piece and we’ve laid the foundations for change by proving that microservices can be successful without significant risk or investment. We still have plenty to improve on and many more problems to solve but we have taken the first step and we’re already seeing the huge benefits of code separation.

Peeling away the first microservice from the monolith is a great challenge for a developer to have and a real opportunity to learn along the way. It’s a challenge that could seem daunting and requires agreement and coordination from the business, but it’s a task that is well worth the effort and can deliver plenty of rewards.

“All our dreams can come true if we have the courage to pursue them.” — Walt Disney

Jon Menzies-Smith is a member of the Advertising Studio Campaigns development team in Marketing Cloud.

--

--