Breaking down the Monolith. Considering Serverless!
This short article describes key strategic steps you need to take in your journey to Serverless migration
What business value you are creating?
The first question you ask is what is the business value you will create by moving to serverless. Are you looking for an accelerated delivery model for features? Are you annoyed with slow features delivery and losing your market share to competitors? Is your current backlog leave no room for new developments? Is scalability of your current application concerns you? Is your brand reputation is at stake due to unstable environments? Is your finance department crying badly on your server and infrastructure maintenance cost? Do you want to create focus in your development team so that they can focus on business functionality and not get bothered with the infrastructure of several environments one for development, other for testing and the one for UAT and the one for production?
If your answer to any one of the above questions is yes, you are creating great business value by breaking down your monolith and migrating to serverless. You have in your hands a great business case to convince your stakeholders and set right business objectives against main business goals and KPIs.
The shared understanding of this business goal is the key pre-requisite for your serverless migration strategy.
Start with low hanging fruits
The first step is to identify low-risk and non-critical features of your application or data processing or your business workflow. You can start first migrating these to avoid expensive failures and minimize business risk. A big migration step will be fatal as it might be risky and very difficult to find the bug. Get support from your team and management to keep them on your side to manage and avoid the big-bang change.
Accelerated Prototyping with an active feedback loop
Start building accelerated prototypes, test, learn and validate your choices, assumptions, and mistakes. These are perfect learning opportunities to understand the behavior of your applications. With this in mind, you learn fast but at least cost. The journey to production deployment becomes easy when you learn and repeat until you are fully convinced of the next steps. The main business values you focus during this phase is on scalability, parallelism, resilience, and cost.
Search out-of-the-box excellence and follow Serverless continuous delivery
Let the heavy lifting for the deployment, release, tests and other environment overheads be done by serverless. The Serverless framework is the most popular and takes care of most of the heavy lifting for you. It supports many cloud providers and gives you many choices out of the box. It’s a very extensible framework with powerful plugin systems developed by the serverless community. Your CD pipeline model should consider controlled code versions. Builds should be reproducible. Dependencies, including transient dependencies, should be locked down to exact versions. If minor/patch version updates can creep in between 2 builds, then the build is not reproducible!
Automate the Testing
Testing paradigm takes a new shift in the serverless era. There is a great amount of diversification in the code complexity as now it is in the configuration and security of your functions and how they interact with external dependencies. These are the things that are most likely to break your application now. Unit tests should no longer be the workhorse of your testing strategy. They have a lower return on investment (ROI) than ever before. Instead, we should focus on integration and acceptance tests.
Integration tests should exercise our functions locally and talk to the real downstream systems. Acceptance tests should exercise the system end-to-end without calling into its internal code. That means, if you’re testing an API then the tests should talk to the system via its HTTP interface.
Build observability into the system
Ship your logs to a log aggregation service where they can be easily searched. Use structured logging with JSON. Complement log messages with contextual data that are useful for debugging and finding related logs. For example, include the order ID as an attribute in every log message related to an order.
Disable debug logging in production. Instead, sample debugs logs for, say, one out of every thousand function invocations. Log an error message with the invocation event as an attribute for failed invocations. This lets you capture and replay failed invocations.
Record custom, application-level metrics.
Create dashboards and alarms for key performance indicators. Connect alarms to an alerting & incident management system such as OpsGenie or PagerDuty.
Capture and forward correlation IDs through both synchronous and asynchronous event sources, and include the captured correlation IDs in every log message. This way, you can find all logs related a user action even when the action spans over multiple functions. You need this for debugging complex interactions inside a serverless application.
Also, sample debugs logs for the entire call chain. Make the sampling decision at the edge, and pass the decision along as correlation ID. The receiving function would respect this decision and enable debug logging for the invocation as well. Capture performance traces for your function invocations. For example, by using Amazon X-Ray with Lambda.
Don’t be shy to talk about security
Follow the principle of least privilege. Each function should have only the permissions it needs, nothing more, nothing less. Apply account level isolation. Each environment should have a separate account, so as to contain a security breach. Use git commit hooks to stop account credentials from leaking. Sensitive data such as API keys and credentials should never be checked into source control in plain text. Use automated services such as Snyk to continuously scan dependencies for known vulnerabilities.
Sanitize and validate user input as well as a function output. Sanitizing function output stops unintended data from leaks. If multiple functions are chained together then it also prevents problems from being passed down the chain.
If you know a function is no longer used, delete it. Even if they’re not used they will continue to exist as an attack surface. Unused functions are also more likely to become vulnerable over time due to lack of patching.
You’re in production, congratulations! But don’t stop there. Keep experimenting, learn and iterate on your designs. Share your learnings with other teams and with the broader developer community. Help establish a virtuous circle of learning and improvement for everyone.
Identify common patterns and cross-cutting concerns. Look for ways to standardize how you deal with these concerns. Using middleware engines such as middy is a good way for you to standardize how you do things. For example, how you handle errors, or how to capture and forward correlation IDs.
Start building a platform to provide features all your teams would want to use.
Automate everything possible
Use the power of serverless to automate ops and security monitoring. For example, you can adopt ChatOps using AWS Lambda with Slack integration. You can use CloudTrail, CloudWatch Events and Lambda to alert on suspicious activities. For example, console logins at weird times of the day or from locations where you have no employee. Or alert on EC2 activities in remote regions that you’re not using.
Depending on your starting point, you might face very different challenges during migration.
A serverless architecture is almost always a microservices architecture too. It means people migrating from a monolithic system would have to migrate to a new execution environment as well as a new style of architecture.