Design Patterns in Action: Refactoring activity card badges for scalability and flexibility
Learn how GetYourGuide used the Chain of Responsibility design pattern to refactor activity card badges for improved scalability, flexibility and experiment support, making badge logic easier to maintain, extend and clean up.

Key takeaways:
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}}
Abstract
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.
Revisit the old architecture
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.
Our goals
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:
- Add new badges easily: Adding a new badge should be simple and not require significant modifications to existing code.
- Achieve better flexibility when ordering experiments: Changing the order of badges should be straightforward and isolated.
- Preserve code post-cleanup: After experiments conclude, the badge logic should remain cleanly preserved without interfering with other parts of the system.
Applying the Chain of Responsibility
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.

How did we apply the Chain of Responsibility?
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:
- Should the badge be rendered based on the preset conditions?
- If not, pass the request to the next handler in the chain.
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.
Coding
Let’s say we have three different types of badges:
- Badge X
- Badge Y
- Badge Z
Step 1: Create individual badge handlers
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
Step 2: Create a badge 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.
Step 3: Dynamic chain of responsibility
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.
Key benefits of this architecture
1. Reordering badges for experiments
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.
2. Adding a new badge
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
3. Removing a badge
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.
4. Cleaning up experiments
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.
Conclusion
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.