For us at Solidify Cloud Native is the first choice when it comes to building modern software. In this article we will look at our approach to Cloud Native the Solidify way Cloud Native computing is an idea that is talked about a lot right now and likely means something slightly different to whoever we talk to. So, what do we mean with Cloud Native? The term was introduced by companies like Netflix and Uber as part of their journey to digitalize their business. The goals then were to be quick and agile, to deliver what the customers want faster than the competition. Ideas that are just as relevant now of course. To achieve this, we need to rethink how our applications are designed so that they can be updated often without risk of downtime or errors. The Cloud Native Computing Foundation’s mission statement defines Cloud Native in a great way:
“Cloud-native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach. These techniques enable loosely coupled systems that are resilient, manageable, and observable. Combined with robust automation, they allow engineers to make high-impact changes frequently and predictably with minimal toil.”
With this definition a Cloud Native application brings together the best practices of software architecture, modern infrastructure, and development culture. With our mission to help our customers deliver value using software faster, Cloud Native is a great means to meet that objective.
Before we explore how we create a Cloud Native application we should look at what makes good a use-case for the new way of thinking. What properties does Cloud Native solutions have that makes them more likely to succeed? The first group of applications are the ones where there is a window of opportunity. Here time to market and how quickly we can release new features are the success factors that possibly will make these apps the next success stories.
Next, we have applications where there is a need for modernization. Cloud Native can be an enabler to reduce maintenance or operational costs. It is also a great way to improve the motivation of your staff by modernizing their skills, shifting tasks from maintenance to greenfield development and learning new stuff.
The third set of apps we see fit for Cloud Native are those where there is a platform thinking. These solutions typically require open apis, flexibility in configuration, multi-tenancy, and scalability to be successful.
Regardless of where your applications fit there are of course value in the practices we will look at. Of course, not all software is built to fit in a Cloud Native world, but fear not, you can modernize your app with these practices in mind. For this we do not believe that a big re-write of software is the best solution since it is contradicts agile practices. Starting new is nice but unless you start doing things differently it is likely to result in a new solution that is great when the project is completed but then needs to go through the same exercise a couple of years later. Instead, we want to shift to a mindset where we continuously rearchitect, replatform, and rewrite the parts of the system that makes most sense at a particular time. This way we continue to deliver and the better we get at improving the faster we can get the benefits of a Cloud Native solution.
Now let us look at how we would create a Cloud Native application. We will start by looking at how we architect the application in a way that makes it run well in the cloud. Closely related to that we look at infrastructure next and how we leverage the powers of the cloud to run and operate our applications. Last (but in reality, first) we will focus on the importance of a great development platform and related practices. Without ideas from DevOps, SRE and other engineering practices Cloud Native will fail.
Developing Cloud Native software
Developing a Cloud Native application is different from building a traditional multi-tier application. To build a system that is loosely coupled, where the parts are resilient and can be managed and monitored separately, we should think in terms of domains and services. Each domain should define services that are self-contained, they should have clear interfaces, own its own code, data, and dependencies. Each service should be implemented using languages and techniques that allows the team to build, test, deploy and run the service in the most efficient way. So, one service can be written in c#, another in Go. One can be hosted in a container, another in a web app. If the team can run it and it is easy to change and release updates the choice of tools should be of lesser importance.
When developing for Cloud Native we value
- Design principles, such as DDD and SOLID, to design a system of services that can evolve independently.
- Test driven development to keep developing with confidence as well as making sure we have good separation between layers and dependencies.
- Implementing microservices with open apis for extensibility and integration.
- Implementing health checks so the runtime can detect malfunctioning services.
- Providing telemetry for performance and usage data.
- Designing for resiliency using patterns like circuit breaking to handle problems in service and dependencies.
Working with Cloud Infrastructure
In a Cloud Native application, we want to view infrastructure a little different than we might be used to. To reach the goals of scalability, autonomy, and automation we must design the infrastructure for our services to work much more independently and loosely coupled that we traditionally have. It will be necessary to think about infrastructure when we design our services so that we can leverage the platform in a good way. We can potentially use any technology to develop and run our services but if we want to develop on the same technology as in production the predominant solution is to use Docker containers. Containers also lets us avoid tight coupling with the operating system, so we get portability when we need it. When developing we probably run single containers whereas in production, we use an orchestrator, such as Azure Kubernetes Service, to manage and operate our services.
With Cloud infrastructure we
- Model infrastructure in code. By defining models of our infrastructure in code we can treat infrastructure as dynamic resources, that gets created and cleaned up as need requires. With proper handling of configuration, we should aim to create new environments instead of reusing, minimizing risks for configuration drift and unclear dependencies. With frameworks like Helm and Pulumi we can further extend how we describe the infrastructure our services expect.
- Use abstractions over infrastructure. The more we can avoid assuming things about our runtime environment the easier it is to scale and move our applications. Using Docker and Kubernetes gives us separation from the operating system, a management system for our services, operational insights into the state of the system, and much more. Kubernetes lets us describe models for how we scale our service to handle load, when needed Kubernetes will take care of scaling up or down without interaction.
- Collect logs and telemetry. By streaming logs, we can react to errors but also do much more. If we store our logs, we get to do postmortem analysis to track back the root cause of an incident. If we process our logs and look for patterns, we can build intelligence that will help us predict issues before they happen. Log analytics is a key ingredient so successful operation.
- Site Reliability Engineering. The practices around SRE brings operations and development together. A site reliability engineer will do ops work such as overseeing the status of a system, investigating issues or do root cause analysis. Unlike traditional operations, the rest of the time SREs develop features, automation, or work on improving other practices.
With DevOps as a guiding star the teams get to work in autonomy with agreed levels of alignment. People, processes, and tools together helps us deliver value. In a Cloud Native environment, we have the luxury to have virtually endless resources to make collaboration and automation work for us to deliver features frequently. Combined with agile practices we can develop on cadence and release on demand.
When practicing DevOps for Cloud Native we
- Version control everything. Using Git we can manage code and development workstreams using a trunk based development approach. We also manage test data as code, pipelines as code and infrastructure as code, keeping as much as we can close to the application we develop.
- Optimize the inner loop of development. Shifting as much as possible up stream improves velocity and quality. Use techniques like GitHub Codespaces to get control over development environments and Git repo polices for governance. Add as many security practices at repository level as possible, for instance dependency vulnerability and credential scanning.
- Optimize the outer loop of development. Using dynamic infrastructure, we create environments on demand which are as production-like as possible. Separate configuration from builds and infer at deployment or run-time using secure secrets.
- Automate as much as makes sense of the CI/CD pipeline, including environment provisioning and health checks. Strive for continuous delivery where code is automatically built, tested and installed all the way to production. And the pipelines will build trust in the release process, anyone should be able to release with confidence, regardless of role or seniority.
- Practice proactive operation where the development team finds issues and not the users. If things go wrong make sure to have procedures to quickly understand the root cause of the issue and have ways to roll out a mitigation fast.
To have the best product for your users when they have use for it requires rethinking software. Cloud Native gives us a framework for building applications that are faster to deliver by using modern architecture, cloud-based infrastructure, and DevOps practices. Using the three dimensions we have discussed we create an environment where we build Cloud Native applications. Want to learn more about how Solidify can help you move to a Cloud Native world? Get in touch so we can start a discussion! You can reach us at firstname.lastname@example.org.