Cant find the role you’re looking for?
Sign up to be the first to know about future job openings at GetYourGuide
Stay notified of new jobsCopyrighted 2008 – 2023 GetYourGuide. Made in Zurich & Berlin.
Vipan Kumar is a software engineer at GetYourGuide, part of the Comparison and Attractions Team (CAAT). His team is responsible for maintaining and improving activity cards across all platforms. In this blog, he will demonstrate how to utilize design patterns to make code cleaner and more readable while optimizing experiment development, using the Chain of Responsibility design pattern as an example.
{{divider}}
At GetYourGuide, we constantly conduct experiments to enhance activity cards and increase the value we offer to our users. However, the fast pace of launching experiments can sometimes lead to less maintainable and readable code. At such times, it becomes essential to step back and refactor the code to make it scalable and maintainable. In this blog, I’ll demonstrate how we refactored the badges displayed on activity cards, centralized their logic, and optimized them for developing new experiments using the Chain of Responsibility design pattern.
The original activity cards building logic consisted of a complex function responsible for assembling various components such as reviews, badges, and pricing. Over time, the badge logic became fragmented across multiple files, making it difficult to maintain, debug, and extend, and often leading to issues with rendering order due to conflicting overrides. Adding new badges under new experiments required modifying unrelated files, violating core design principles, and increasing the risk of errors.
Experiment cleanup to preserve features became a significant challenge as experiment logic was frequently intertwined with non-experiment code. This made changes more complex and the system harder to scale. We decided to refactor the architecture using design patterns to address these issues.
We have multiple certification badges, such as ‘Official Provider’ and ‘GetYourGuide Originals,’ displayed on activity cards to help categorize our experiences. Our objectives were as follows:
After evaluating our options, we implemented the Chain of Responsibility design pattern to refactor the badges and improve code modularity.
The Chain of Responsibility is a behavioral design pattern that structures a request to pass through a sequence of handlers. Each handler in the chain has two options: either process the request or pass it along to the next handler. This approach is efficient when multiple objects can handle a request, but the exact handler is determined dynamically at runtime, and multiple handlers might be eligible to respond.

We introduced a BadgeHandler, which acts as the core of the chain. This handler is responsible for determining the order of badge rendering. Each badge’s logic is encapsulated in its own handler file, adhering to the Single Responsibility Principle. The handler file’s sole purpose is to decide:
The chain operates based on sequential processing. If a badge can render, it returns it and stops further processing. If a badge cannot render, it passes the request to the next handler. This modular and sequential logic ensures clean separation of concerns, making it easier to extend, reorder, or disable badges without touching unrelated code.
Let’s say we have three different types of badges:
Each badge has its own handler file, ensuring that the logic for rendering that badge is isolated and adheres to the Single Responsibility Principle.
Badge X handler
Badge Y handler
Badge Z handler
The BadgeHandler is responsible for orchestrating the badge rendering process. It defines the order of badges based on conditions, such as whether an experiment is active.
The badge handlers are linked together dynamically, forming a chain. The chain processes badges sequentially, with each handler deciding whether to render its badge or pass the request to the next handler.
Reordering badges based on experiments becomes straightforward. If you want to change the order of badges for an experiment, simply add a condition in the BadgeHandler and specify the new order in the chain.
Let’s say we want to add another badge, ‘NewBadge’. Adding a new badge is efficient and straightforward. You just need to create a new handler file for the badge logic and specify its position in the chain within the BadgeHandler.
Create the handler for ‘Newbadge’
Integrate this new handler in the BadgeHandler
Let’s say we want to remove the newly created badge. It is very simple to do. You just exclude its handler from the chain in the BadgeHandler, and the badge logic remains preserved in its handler file. This makes it easier to restore or reuse the badge in the future.
Let’s say we want to remove the experiment we added in the first step. We simply remove the experiment-specific condition from the BadgeHandler. The badge logic remains intact in its handler file, ensuring no rework is required if we decide to revisit the experiment in future. This pattern helps us isolate experiment-specific conditions from core rendering logic, making it easier to remove, reuse, or update them without side effects.
The Chain of Responsibility design pattern has made our badge rendering system scalable, maintainable, and flexible. It allows for seamless reordering of badges for experiments, easy addition of new badges, and straightforward removal without interfering with the rest of the system. Cleanup is effortless, preserving badge logic for future use while ensuring the architecture remains clean and efficient. This approach prepares the system for future scalability and adaptability, saving time and reducing risks.

