In today's development landscape, local testing of code has become increasingly challenging, especially in setups that involve microservices. As the architecture grows more intricate, dependencies multiply, and applications become more challenging to run locally, developers face a daunting task to ensure their code works seamlessly in such environments.
To address this issue, it is important to understand the challenges developers face in testing their code locally. These challenges are broadly classified into three categories for OLTP systems:
- Frontend applications: These are static SPAs or mobile applications that access a publicly exposed API server. Local development is relatively straightforward for these applications, as developers can easily point to any test environment and test all features.
- REST or SOAP APIs: These services sit behind load balancers and receive direct API calls. They call other microservices, databases, caches, and sometimes cloud resources, making it difficult to establish connectivity to all these dependencies from a local environment. Even if developers manage to run the service locally, mocking all the dependencies can be a tedious and inconsistent process.
- Private RPC microservices: These services may have RPC or HTTP endpoints but are called from other microservices internally and can have dependencies like Public APIs. Testing these APIs locally is even more challenging, as developers need to run other microservices that call them to test the integration effectively. Although calls can be mocked, the challenges mentioned for Public APIs still apply.
Ephemeral Environments or Environment-as-a-Service (EaaS) are often proposed as solutions to the challenges of local testing in microservices. EaaS solutions offer distinct setups for each developer or pull request. This can be a scalable approach to address the issue, as it allows developers to test their code in a consistent environment that mirrors the production environment.
While this method might seem straightforward at first glance, implementing it effectively can be complex. Solutions, such as launching copies of microservices in Kubernetes for every pull request, might not fully address all the complexities involved. The substantial challenge comes in managing aspects like routing, service discovery, shared databases, and complex dependencies. Handling these tasks individually can be a significant undertaking for developers, making it crucial to explore other potentially more manageable solutions.
Environment leasing
An alternative solution, especially for those using Kubernetes, involves setting up a shared Dev environment. In this environment, all services run versions that mirror their Quality Assurance (QA) counterparts – essentially stable, testable versions of the services. It is the joint responsibility of the team to keep this environment stable.
All feature development should occur locally, with developers performing rigorous testing until their work is ready to merge into the mainline codebase, often referred to as the 'master' branch. This approach is key to maintaining a shared Dev environment.
With that in mind, let's explore how local development would unfold within this shared Dev environment:
- Frontend applications: These static SPAs or mobile applications that accessed a publicly exposed API server. With local development, developers could easily point these to the Shared Dev environment for feature testing. So, the process remains largely the same
- REST or SOAP APIs: These services posed a challenge as they sat behind load balancers and received direct API calls. They called other microservices, databases, caches, and sometimes cloud resources, making it difficult to establish connectivity to all these dependencies from a local environment. However, with tools like Telepresence , developers can now create a two-way network proxy between the local machine and the remote Kubernetes cluster. This allows them to develop and debug services as if they were running in the shared Dev environment, simplifying the process significantly.
- Private RPC microservices: These services may have RPC or HTTP endpoints but are primarily called from other microservices internally and can have dependencies like Public APIs. Earlier, testing these APIs locally was a difficult as it required running other microservices that called them. But now, with Telepresence, developers can set their services as sinks and call downstream microservices in the shared Dev Environment to receive calls on their laptops, enhancing debugging capabilities and reducing local setup.
In the shared Dev environment, the ability for concurrent, non-interfering development is a significant advantage, especially with Frontend applications and REST or SOAP APIs. Multiple developers can interact with the shared Dev cluster simultaneously. However, this is different for Private RPC microservices. Given the fact that only one sink can be active at a time, it necessitates an environment leasing process per such service, requiring coordination among team members. Despite this, the significant advantages - such as enhanced debugging capabilities and a streamlined local setup - make this approach a worthwhile strategy for managing these complex services..
Another potential challenge with this approach arises when multiple features in backend development might necessitate different database schemas. However, it's important to remember two things: firstly, not every feature will induce changes to the schema. Secondly, any schema-level changes should be thoroughly considered and implemented before the development itself and should reach the QA environment ahead of the development. Adhering to these principles ensures that there will be no conflicts when merging code from different features down the line, thereby maintaining the integrity and efficiency of the development process.
To Summarize...
While Ephemeral environments or Environment-as-a-Service (EaaS) offer a solution for local testing, they may not be the best fit for all, due to potential complexity and cost considerations. By understanding the challenges associated with different types of applications and adopting a shared development environment approach in Kubernetes, developers can effectively test their code locally, streamline the development process, and enhance collaboration within their teams. This approach, combined with tools like Telepresence, offers a more cost-effective and manageable solution for local code testing and debugging.