Karthick's Sunday Learning (15/12)

Karthick's Sunday Learning (15/12)

As I strive to learn every day, here are my this week's learning and deep reading as I took some time to recap/reflect on those topics for my network and newsletter. Are you motivated to learn more every day? If not, ask yourself what is missing and go after it.

1. Strangler Pattern

2. Sidecar pattern

Strangler Pattern

The Strangler Pattern (a.k.a. Strangler Fig pattern) is a design pattern used in software development to gradually replace a legacy system with a new system while ensuring that the application remains functional throughout the transition.

This pattern is particularly useful in scenarios where a complete rewrite of the legacy system is impractical due to time, cost, or risk constraints. The term "Strangler Pattern" was first coined by Martin Fowler, drawing an analogy to the natural phenomenon of the strangler fig, a type of plant that grows around a tree and eventually replaces it.

Stranger pattern. Courtesy - Openlegacy

For example, take modernising an application by incrementally developing a new (strangler) application around the legacy application. In this scenario, the strangler application has a Microservice Architecture.

Strangling the monolith. Courtesy - Microservices.io

The strangler application consists of two types of services. First, there are services that implement functionality that previously resided in the monolith. Second, there are services that implement new features. The latter are particularly useful since they demonstrate to the business, the value of using microservices.

What is Strangler pattern

Imagine you have a really old tree in your backyard. This tree is very special to you, but over time, some of its branches have become weak and don't grow leaves anymore. If you cut down the whole tree, you would lose this special place where you love to play.

Now, instead of cutting down the tree, you decide to plant a new, stronger tree right next to it. As the new tree grows, it starts to wrap around the old tree’s weak branches, slowly taking their place. Eventually, the new tree becomes strong and healthy, while the old tree's weak parts are completely covered. You still have a tree in your backyard, but now it's much stronger and healthier.

The strangler pattern in computing is a bit like that. Instead of throwing away an old computer program (the old tree) that many people use, we build a new and improved program (the new tree) alongside it. Slowly, we move small parts of the old program into the new one. Eventually, the new program takes over, and we don’t need the old one anymore. This way, the change happens smoothly, and nothing is lost all at once.

This method is called the "strangler pattern" because it gently replaces the old with the new, just like how a new tree wraps around and replaces the old tree's branches.

Core Principles

The Strangler Pattern revolves around the following core principles:

  • Incremental Replacement: Instead of rewriting the entire legacy system in one go, the Strangler Pattern suggests replacing it incrementally. This means that new features and functionalities are developed in the new system while the old system continues to operate.

  • Coexistence: During the transition phase, both the legacy system and the new system coexist. This allows for continuous delivery and deployment without disrupting the user experience.

  • Interception: The pattern often involves intercepting calls to the legacy system and redirecting them to the new system as appropriate. This can be achieved through techniques such as proxy patterns, API gateways, or middleware.

  • Gradual Decommissioning: As more functionalities are moved to the new system, corresponding components of the legacy system are gradually decommissioned until the old system is entirely replaced.

How does it work

Refactoring a monolith into microservices with the Strangler Pattern consists of 3 main steps: Transform, Coexist, and Eliminate.

  • Transform: You need to start by identifying the main components of the monolithic application. This step involves identifying the boundaries between the existing application and the new components being developed.

  • Coexist: Then, build a wrapper around the monolith to allow the new components to coexist with the existing application.

  • Eliminate: Finally, eliminate the monolith by replacing parts with new components. However, you must ensure that each microservice works as expected before integrating it into the system.

T, C, E of Stranger pattern overview

Implementation Steps

Implementing the Strangler Pattern typically involves the following steps:

  • Assessment: Begin by assessing the legacy system to identify which parts are critical and need immediate attention. Determine the dependencies and the parts that can be incrementally replaced.

  • Design the New System: Architect the new system in a way that it can gradually take over the functionalities of the legacy system. Ensure that it is scalable, maintainable, and aligns with modern best practices.

  • Create Interceptors: Implement interceptors to direct traffic from the legacy system to the new system. This might involve setting up API gateways, proxy patterns, or middleware to manage the redirection.

  • Incremental Migration: Start migrating functionalities from the legacy system to the new system in small, manageable increments. Test each increment thoroughly to ensure that it meets the required standards and does not disrupt existing services.

  • Monitor and Optimize: Continuously monitor the performance and functionality of both the legacy and new systems. Optimise the new system based on feedback and gradually increase the scope of the migration.

  • Decommission the Legacy System: Once all critical functionalities have been successfully migrated, begin the process of decommissioning the legacy system. Ensure that all dependencies are resolved and that the new system is fully capable of handling the load.

Consider an e-commerce application with a monolithic architecture. To migrate the order management functionality to microservices using the Strangler pattern, follow these implementation steps:

  1. Identify the order management functionality within the monolithic application.

  2. Create an order management microservice.

  3. Configure the API gateway to route order management requests to the microservice.

  4. Migrate specific functionalities from the monolithic application to the microservice.

  5. Repeat steps 1-4 until the monolithic application is fully replaced.

Strangler Pattern in action. Courtesy - Geeksforgeeks

Advantages

The Strangler Pattern offers several advantages:

  • Reduced Risk: By replacing the legacy system incrementally, the risk of system failure or significant downtime is minimised.

  • Cost Efficiency: The incremental approach allows for better allocation of resources and avoids the high costs associated with a complete system rewrite.

  • Continuous Delivery: The pattern supports continuous delivery and deployment, ensuring that the system remains functional and up-to-date throughout the transition.

  • Flexibility: The gradual migration process provides flexibility to adapt to changing requirements and technologies.

Challenges

Despite its benefits, the Strangler Pattern also presents some challenges:

  • Complexity: Managing the coexistence of the legacy and new systems can be complex and requires careful planning and coordination.

  • Interception Overhead: Implementing interceptors and managing traffic redirection can introduce additional overhead and latency.

  • Technical Debt: There is a risk of accumulating technical debt if the migration process is not properly managed and documented.

Use case

Let us take a real world example from Shopify, as to how they refactored legacy code with the Strangler pattern - their experience with refactoring their Ruby on Rails codebase, specifically the Shop model.

  • Problem: Large objects in code, like Shopify’s Shop model, become difficult to manage and maintain.

  • Solution: The Strangler Fig Pattern, which allows incremental refactoring by gradually replacing old code with new code.

  • Steps Involved:Define a new interface for the functionality to be extracted.Redirect calls from the old system to the new system.Create a new data source if needed.Implement writing to both old and new data sources.Backfill the new data source with existing data.Change methods to read from the new data source.Remove the old code once the new system is fully operational.

This allowed Shopify to:

  • Incrementally changes which allowed for continuous monitoring and reduces the risk of breaking the system.

  • Improved Code Quality which lead to better-defined boundaries and responsibilities within the codebase.

More info here: https://shopify.engineering/refactoring-legacy-code-strangler-fig-pattern

Resources

The Strangler Pattern is a powerful strategy for modernising legacy systems while minimising risk and ensuring continuous functionality. By adopting an incremental approach and carefully managing the coexistence of the old and new systems, organisations can achieve a seamless transition and leverage the benefits of modern technologies and practices.

Sidecar pattern

The world of software architecture is ever-evolving, and as applications grow in complexity, new patterns emerge to address specific challenges. One such pattern that has gained prominence in recent years is the sidecar pattern. This architectural design pattern is particularly relevant in the context of microservices and cloud-native applications.

The sidecar pattern involves deploying a helper application, known as a sidecar, alongside a primary application. The sidecar runs in a separate container but within the same pod or virtual machine as the main application. This setup allows the sidecar to operate independently while still being closely coupled with the primary application. The sidecar typically handles responsibilities that enhance the primary application's functionality, such as logging, monitoring, networking, or security.

Sidecar pattern overview. Courtesy - Microsoft

What is Sidecar pattern

Imagine you have a bicycle, and you want to carry your favourite teddy bear everywhere you go. But your teddy bear is too big to fit in your backpack comfortably. What do you do? You attach a little sidecar to your bicycle! Your teddy bear sits in the sidecar, and you ride your bike. This way, you both get to go on adventures together.

Now, let's take this idea and apply it to computers. In the world of computers, a "sidecar pattern" works in a similar way. Think of a computer program (like a game or an app) as the bicycle. Sometimes, this program needs a helper to do extra things, like keeping it safe from hackers or storing important information. Instead of putting everything into one big program (which can be complicated and messy), we bring along a helper program, just like the sidecar for the teddy bear.

Courtesy - blog.bitsrc.io

This helper program, called a "sidecar," stays close to the main program and helps it do its job better. They both work together as a team. The main program (the bicycle) focuses on what it's really good at, and the sidecar (the helper) takes care of other important tasks.

So, the sidecar pattern is a way to make sure our programs work better together by having a special helper program ride along with them, just like how your teddy bear gets to ride in the sidecar with you on your bicycle adventures!

Example of the Sidecar pattern usage. Courtesy - Distributedsystemmadeeasy

Implementation of Sidecar Design Pattern

Implementing the Sidecar Design Pattern involves several steps:

  • Step 1: Identify Secondary Functionalities for services that can be separated from the main application logic and implemented as sidecar containers. These functionalities may include logging, monitoring, security, service discovery, communication proxies, or other cross-cutting concerns.

  • Step 2: Design Sidecar Containers to containers to encapsulate the identified secondary functionalities. Each sidecar container should be self-contained, providing a specific set of functionalities or services that support the operation of the primary application container.

  • Step 3: Define Inter-container Communication for inter-container communication between the primary application container and the sidecar containers. This may involve local networking, shared volumes, IPC (Inter-Process Communication), or other communication channels provided by the container runtime or orchestration platform.

  • Step 4: Implement Sidecar Containers to containers to implement the identified secondary functionalities. Each sidecar container should be packaged as a separate container image and deployed alongside the primary application container within the same execution environment.

  • Step 5: Configure and Coordinate Sidecar Containers to ensure they are properly synchronised and coordinated with the primary application container. This may involve dynamic configuration updates, service registration and discovery, or coordination through APIs provided by the container platform.

  • Step 6: Handle Lifecycle Management mechanisms to ensure that both the primary application container and the sidecar containers are properly started, stopped, and managed throughout their lifecycle. This may involve container orchestration tools, lifecycle hooks, or custom scripts to coordinate their operation.

  • Step 7: Integrate Observability and Monitoring components into the sidecar containers to collect data about the microservice's behaviour, performance, and health. This may include log collectors, metrics collectors, distributed tracing agents, or other monitoring tools.

By following these steps, you can effectively implement the Sidecar Design Pattern to enhance the modularity, scalability, and maintainability of your microservices-based applications.

Understanding Sidecar pattern. Courtesy - levelup.gitconnected.com

Key Benefits of the Sidecar Pattern

1. Separation of Concerns

One of the most significant advantages of the sidecar pattern is the separation of concerns. By offloading specific tasks to the sidecar, the primary application can remain focused on its core business logic. This modular approach simplifies the development and maintenance of both the main application and the sidecar, leading to cleaner and more manageable codebases.

2. Scalability and Flexibility

The sidecar pattern enhances the scalability and flexibility of applications. Since the sidecar runs in a separate container, it can be updated, restarted, or replaced independently of the main application. This isolation allows for more granular control over the deployment and scaling processes, enabling teams to address issues or introduce new features without impacting the primary application.

3. Enhanced Observability and Security

Sidecars are often used to implement logging, monitoring, and security features. By centralizing these concerns in a sidecar, organizations can achieve consistent observability and security practices across all microservices. This consistency is crucial in dynamic and distributed environments where visibility and protection are paramount.

4. Simplified Service Mesh Integration

The sidecar pattern is a fundamental building block of service mesh architectures. In a service mesh, sidecars act as proxies that manage communication between microservices. This approach decouples the communication logic from the application code, making it easier to implement advanced networking features such as load balancing, retries, and circuit breaking.

Common Use Cases for the Sidecar Pattern

1. Service Discovery and Load Balancing

Sidecars can be used to handle service discovery and load balancing within a microservices architecture. By managing these responsibilities in a sidecar, the main application can remain agnostic to the underlying network topology, leading to more resilient and adaptable systems.

2. Logging and Monitoring

Implementing logging and monitoring as a sidecar ensures that these critical functions are uniformly applied across all microservices. The sidecar can collect, process, and forward logs and metrics to centralised systems, providing valuable insights into the application's performance and health.

3. Security

Security is a top priority in modern applications, and the sidecar pattern can play a pivotal role in enforcing security policies. Sidecars can manage authentication, authorization, encryption, and other security-related tasks, ensuring that these practices are consistently applied across the entire application.

Challenges and Considerations

While the sidecar pattern offers numerous benefits, it is not without its challenges. Understanding these potential pitfalls is essential for successful implementation.

1. Complexity

Introducing a sidecar adds another layer of complexity to the system. Teams must carefully design and manage the interaction between the primary application and the sidecar to avoid introducing new points of failure or performance bottlenecks.

2. Resource Overhead

Running additional containers for sidecars can increase the overall resource consumption of the application. Organizations must account for the extra CPU, memory, and storage requirements when planning their infrastructure.

3. Latency

The communication between the primary application and the sidecar can introduce latency, especially if the sidecar performs resource-intensive tasks. Optimizing the performance of both the main application and the sidecar is crucial to minimize any negative impact on the user experience.

Best Practices for Implementing the Sidecar Pattern

1. Clearly Define Responsibilities

Ensure that the roles and responsibilities of the primary application and the sidecar are well-defined and documented. This clarity will help avoid overlaps and ensure that each component operates efficiently within its scope.

2. Monitor Performance and Resource Usage

Continuously monitor the performance and resource consumption of both the main application and the sidecar. Use this data to make informed decisions about scaling, optimizing, and troubleshooting the system.

3. Implement Robust Communication Channels

Design efficient and reliable communication channels between the primary application and the sidecar. Consider using lightweight protocols and optimizing data serialization to minimize latency and overhead.

4. Automate Deployment and Management

Leverage automation tools and frameworks to streamline the deployment and management of sidecars. Kubernetes, for example, provides native support for sidecar containers, making it easier to orchestrate and manage them within a cluster.

Resources

In Summary...

The sidecar pattern is a powerful architectural design that offers numerous benefits for modern applications, particularly in microservices and cloud-native environments. By separating concerns, enhancing scalability, and improving observability and security, the sidecar pattern helps organisations build more resilient, flexible, and maintainable systems.

However, successful implementation requires careful consideration of the associated challenges and adherence to best practices. With the right approach, the sidecar pattern can significantly enhance the functionality and manageability of applications, paving the way for innovation and growth in the ever-evolving world of software development.

What are you learning? May be we can learn together!

Happy learning!

To view or add a comment, sign in

Others also viewed

Explore content categories