From Monolith to Event Driven MicroServices via Polyglot Monorepo
InformedIQ’s journey on the road to distributed nirvana
This is the first of several series of posts on how Informed transitioned from a distributed monolith that successfully served our early stages. Like many successful startups, our software and infrastructure embodied our understanding of market needs. And like the initial implementations of many startups, it also accumulated technical debt that made it harder to incorporate the additional innovations we had in store for our customers.
In early 2022 we decided to migrate our SaaS to a Microservice Event Driven Architecture leveraging AWS Serverless technologies. This was new for us so we gradually transitioned using the Strangler Pattern and slowly dissolved the legacy monolith into various microservices.
This series explores and documents the tools, techniques and technologies that we tried and implemented. We’ll share what worked and what is still improving.
We will cover:
- Section 1 Series: Intro to CI/CD, Monorepo and DevSecOps
- Section 2 Series: The nitty-gritty of Polyglot Monorepos
Intro to CICD: Foundation of Monorepo Success
CI/CD is a method of delivering apps by introducing automation into the development process. CI/CD includes continuous integration, continuous delivery, and continuous deployment. It solves problems that integrating new code can cause in development and operations.
Specifically, CI/CD introduces ongoing automation and continuous monitoring throughout the app life-cycle, from integration and testing to delivery and deployment. These connected practices are referred to as a “CI/CD pipeline” and are supported by development and operations working together.
CI/CD is a combination of two different concepts.
- Continuous Integration (CI)
- Continuous Delivery (CD)
Developers build, run, and test code on their machines before committing changes to a version control repository (such as GitHub). Once changes are made to the repository, a series of events are initiated with Continuous Integration (CI). The first step is creating the latest version of the source code, followed by unit testing if the build is successful. If the unit tests pass, the build is deployed to test environments for system testing (often automated). The team is notified via Slack or other messaging platform about the progress, and a report is provided with details such as the build number, defects, and number of tests.
The typical CI pipeline includes the following tasks:
- Detecting changes to the source code repository
- Analyzing the quality of source code
- Building the project
- Running all unit tests
- Executing integration tests
- Generating deployable artifacts
- Generating a status report
If any of the above steps fail,
- The integration may stop or continue depending on the severity and configuration of the defect.
- Results are reported to the team via email or chat.
- The team fixes the defect and commits the code again.
- The tasks are repeated.
The benefits of CI include automated execution of test suites triggered by the Source Code Management (SCM) such as git, which leads to catching issues early, reducing the number of production rollbacks. CI ensures that the software is consistently integrated and ready for deployment.
Continuous Delivery (CD) is the next step. While CI is focused on building and testing code, CD focuses on deploying code to the testing or staging environment. CD is a powerful way to continuously deploy code to end-users or various environments such as dev, dev-api, qa, staging, and production. The process is triggered by an update to an SCM, just like CI, and involves a series of customizable operations, usually packaged into a series of bash scripts that interact with service providers.
In CD, every stage from merging code changes to delivering production-ready builds, involves code test and release automation. Once complete, the team can quickly and easily deploy an app to production.
The benefits of CD include:
- An automated deployment process that reduces the chance of human errors
- Quick release of new versions of applications.
By combining CI and CD, developers can automatically test and deploy their code in one go. This approach offers the flexibility to break builds if the code fails in testing before being deployed and replacing the live application.
CD streamlines the process of getting code changes into production, ensuring that applications are delivered quickly and reliably. If this explanation hasn’t convinced you to use CI/CD let’s look at a scenario with and without.
Developers write new code, test locally and push code to the repository. The code is reviewed and merged. Once merged, the code is manually deployed, replacing existing applications.
A problem occurs, and it turns out the newly deployed code is preventing users from signing up. Unfortunately, the code is already live.
To debug the issue,
- The team checks the logs and discovers that a third-party database API is complaining about a bad connection string.
- They review the latest code and confirm that the connection string is slightly off.
- They make the required connection string change, rerun the tests, including the missing integration tests, and push the updated code to the repository.
- They manually confirm that users can now sign up.
With CI/CD, the test and deployment process is automated, increasing visibility and allowing every member of the team to understand the full process. As potential problems pop up, fixes and learnings increase the overall robustness of the service. CI/CD provides a single source of truth for expectations at each phase in the application deployment lifecycle.
For instance, if a developer has a problem where unit/integration tests locally don’t account for issues that pop up in the live application, the CI/CD would have a hardening process for testing. This would be applied every time new code is introduced. Regardless of what the developer has or hasn’t done locally, the CI/CD would catch these issues and prevent them from being deployed.
Let’s say that the same problem with the connection string arises in the CI/CD pipeline.
- The pipeline automatically detects the issue and prevents the code from being deployed until the issue is resolved.
- The pipeline then automatically triggers a series of operations, such as notifying the team of the issue, rerunning all the tests, and eventually deploying the updated code to the testing environment.
- Once the tests pass in the testing environment, the pipeline can automatically deploy the code to production, ensuring that the live application remains unaffected.
- This whole process occurs without any manual intervention, reducing the risk of human error and speeding up the deployment process.
Implementing a CI/CD process provides numerous benefits, such as the ability to catch errors early in the development cycle and automate the deployment process. With CI/CD, code changes are tested and validated before being deployed to production, helping reduce the likelihood of errors and downtime.
CI/CD allows for implementation of additional testing after deployment, such as smoke tests, ensuring the deployment was successful. In the event of a failure, the ability to rollback to the previous version minimizes downtime and impact on users.
While building a self-healing system can be challenging, proper automation and a supportive culture make it possible. By removing the potential for human error, we can build more reliable and efficient systems, reducing costs and increasing confidence in the process. Ultimately, adopting a CI/CD approach accelerates development and helps teams better achieve their goals. You can continue reading about our monorepo journey here.
Darshan is an experienced platform engineer with a strong background in software development and over 6 years of experience. He specializes in Infrastructure-as-Code (IaC) and optimizing system performance to deliver reliable and scalable infrastructure.