Header background

10 tips for migrating from monolith to microservices

Transforming an application from monolith to microservices-based architecture can be daunting, and knowing where to start can be difficult. Because monolithic applications combine database, client-side interfaces, and server-side application elements in a single executable, they’re difficult to understand, even for their own administrators.

Today’s customer expectations can’t tolerate tightly coupled dependencies, difficulties deploying changes, or long release cycles. Unsurprisingly, organizations are breaking away from monolithic architectures and moving toward event-driven microservices. However, the move to microservices comes with its own challenges and complexities.

Limits of a lift-and-shift approach

A traditional lift-and-shift approach, where teams migrate a monolithic application directly onto hardware hosted in the cloud, may seem like the logical first step toward application transformation. However, it’s common that teams need to refactor an application to help with performance after the lift-and-shift operation. It is better to consider refactoring as part of the application transformation process before migrating, if possible.

forklift representing the limits of a lift-and-shift approach to migrating from monolith to microservices

Although lifting and shifting to the cloud may provide some cost advantages at the outset, applications refactored for a microservices architecture can take full advantage of a cloud-first approach and provide cost savings in the long run. Microservices applications comprise independent services that teams develop, deploy, and maintain separately. Because they’re separate, they allow for faster release cycles, greater scalability, and the flexibility to test new methodologies and technologies.

However, the distributed system of a microservices architecture comes with its own cost: increased application complexity and convoluted testing. Migration is time-consuming and involved. Likewise, refactoring and rewriting code takes a lot of time and effort. Therefore, it’s important to do it right.

In the past, we’ve covered various topics on how to break the monolith, define monolithic architecture, and identify the advantages and disadvantages of microservices. Since the ultimate goal is to migrate to the cloud and refactor applications to a microservices architecture, here are 10 tips for getting started on the path to a successful migration.

10 tips for migrating from monolith to microservices

1. Understand the monolith

Monolithic applications take work to understand. In fact, it can be difficult to make code changes that won’t disrupt the entire system. This is also true of splitting the application into microservices. It’s important not to disrupt a running application and user experience. Start by evaluating the monolithic application components to understand all the dependencies and their business functions. Teams can gain this understanding through topology mapping, with telemetry data from request traces, and understanding how the frontend ties to backend functions. While this sounds simple, teams often get stuck trying to map out dependencies accurately when they try to do it manually. Automatic discovery and intelligent observability are the keys to overcoming this hurdle.

2. Find the right candidates for refactoring

There will inevitably be parts of a monolith application that teams can’t fully understand. When it comes to refactoring, teams should start with what they can understand. Components that are already loosely coupled and have few dependencies will be easier to migrate, with less chance of impacting application performance.

Use domain-driven design when creating new microservices by separating microservices via their underlying business functions. This allows individual teams to own components and makes it easier to pinpoint problems with mission-critical functions. Start with components that have high business value, such as a webpage checkout action.

3. Incrementally refactor

The saying “Rome wasn’t built in a day” rings true when it comes to refactoring microservices. It is important to remember that refactoring is essentially re-architecting and rebuilding your application. This is something that will take time. The best approach is incremental, using the Strangler Fig pattern: Gradually replacing parts of the monolithic application until only the microservices architecture remains.

strangler fig model

The approach takes place in three stages: 1. create a microservice; 2. use both the microservice and monolith for the same functionality; 3. remove the dependency on the monolith after all testing is successful.

4. Ensure the microservices architecture is loosely coupled

Monolithic applications are traditionally tightly coupled, meaning dependencies among services within the app are intertwined. Because teams often can’t know or understand all dependencies, it can be difficult for them to make changes. When creating new microservices, it is better to keep them loosely coupled with minimal dependencies to allow for flexible changes and ease of deployment. One way to minimize dependencies is to use asynchronous messaging and message queues wherever possible.

5. Choose the right technology for each service

One advantage of using a microservices architecture is being able to choose different technologies for the application function at hand. Microservices can operate independently of each other. As a result, teams can leverage a polyglot architecture that uses the language and technology best suited for the job. However, there can be drawbacks to using too many different languages and technologies. It may make sense to use fewer languages depending on team bandwidth, capabilities, and size.

6. Instrument for end-to-end observability

One challenge that comes with migrating to microservices from a monolithic architecture is an increase in application complexity. Many microservices and different supporting technologies run independently of each other to support the application. With so many different components, it is easy to lose track of what is happening in which component when potential problems arise. End-to-end observability starts with tracking logs, metrics, and traces of all the components, providing a better understanding of service relationships and application dependencies.

end-to-end observability

This visibility is essential to understanding if the application is performing well and identifying where problems arise. Visibility is also the key to remediating problems and implementing automation.

There are many ways to instrument microservices for observability, including automatic instrumentation using a unified observability and security platform. Many organizations also find it useful to use an open source observability tool, such as OpenTelemetry. An observability platform approach makes it easy to combine both methods. Such an approach also considers additional important details, such as business impact and security implications.

7. Factor in security at every stage

With the increasing number and sophistication of security breaches, such as Log4Shell, integrating security into all application changes is more important than ever. This includes when teams refactor applications for microservices architecture. In fact, security risks differ among microservices. Security should be an integral part of each stage of the software delivery lifecycle, from development to monitoring in real time. Real-time security monitoring—evaluating continuously updated security data about systems, processes, and events—and runtime security monitoring—analyzing security information from a running system—can be particularly useful during migration since there will be an immediate alert if there is a security risk with this new microservices architecture.

8. Monitor the application before, during, and after migration

Migrating and changing code can be a tricky business. To ensure that the migration doesn’t affect user experience, teams should monitor application performance before, during, and after migration. Use SLAs, SLOs, and SLIs as performance benchmarks for newly migrated microservices. Repeat this process throughout the different environments before development, staging, release, and production. As each service migration is complete, continuously validate the existing code base as the team releases new code. Intelligent dependency mapping and automated baselining can help easily identify performance degradation and problems caused by new releases.

9. Automate wherever possible

Creating a system and a flow that teams can replicate and automate is a desirable objective. An automated CI/CD pipeline allows for an extremely smooth release process, accomplishing one of the goals of migrating to microservices. Every step of the way, define checks based on SLAs, SLOs, SLIs, and security scans, and automate the transitions from continuous integration, delivery, and deployment. teams can even build auto-remediation into a CI/CD pipeline, so if a problem arises, the system can trigger a fix or roll back to a previous version.

10. Optimize performance and user experience using observability data

Once the application has successfully migrated, it’s time to take advantage of all the newfound benefits a microservices architecture offers. Deploy changes fast to optimize performance and eliminate bottlenecks. Continuously update, improve, and easily add new features to provide an exceptional user experience. Utilize observability data to monitor and improve digital experiences and analyze data that can affect the business.

A unified observability and security platform approach to migrating from monolith to microservices

Migrating from monolith to microservices can be difficult, complex, and time consuming. However, making this architecture change is often the best way to take advantage of the agility of the cloud. For a simplified and smooth migration, teams need a way to assess a monolithic application’s starting state. They then need to track progress during migration to identify any drops in application performance.

As an AI-driven, unified observability and security platform, Dynatrace uses topology and dependency mapping and artificial intelligence to automatically identify all entities and their dependencies. This comprehensive view helps teams gain an initial understanding of a monolithic application so they can develop a migration strategy.

Once teams start introducing microservices, unified observability enables teams to visualize changes and the real-time performance of all the services in context. This AI platform approach automatically discovers all entities and identifies any issues developing among them. The observability extends to on-premises environments, Kubernetes infrastructure, multicloud platforms, and the multitude of proprietary and open source tools they depend on.

Keeping track of the migration stages, phases, and environments is not always easy. With real-time observability, teams can easily plan their migration and fine-tune performance as they migrate microservices. Since core observability with Dynatrace includes logs, traces, metrics, security, and user experience, teams can make decisions using these details in context.

After migration, observability continues to help teams monitor and optimize their microservices, resulting in better user experiences and, as an extension, better business outcomes.

To learn more about how migrating from monolith to microservices with real-time observability works in practice, join us for the on-demand webinar, 10 things you didn’t know about cloud migration and adoption.