All you need for System Design Interviews 📌
github.com/liquidslr/system-…
sharding, partitioning and replication are often confused in system design, but they solve different problems
partitioning: splits a large table into smaller parts to improve query performance and data management
sharding: distributes those partitions across multiple servers to scale storage and write capacity
replication: maintains copies of data across multiple servers to improve availability and read scalability
simple way to remember:
partitioning = split data
sharding = distribute data
replication = copy data
understanding when to use each one is an important part of designing scalable databases
LLD Interview Notes: Day 18/50
Topic: Adapter Pattern
Imagine your application expects data in one format, but an existing third-party service provides it in a completely different format.
Do you modify the existing service or change your entire application?
Neither. You can use the Adapter Pattern.
1. What Is the Adapter Pattern?
A structural design pattern that allows two incompatible interfaces to work together.
It acts as a bridge between the client and an existing class without modifying their original code.
2. How It Works
Client expects a specific interface.
Existing service provides a different interface.
Create an Adapter that implements the expected interface.
Adapter translates client requests into the format understood by the existing service.
The existing service performs the operation and returns the result.
3. Real-World Example: Payment Integration
Imagine your application expects:
makePayment(amount)
But a third-party payment provider exposes:
processTransaction(value)
Instead of modifying your application or the payment provider:
Create a PaymentAdapter.
Implement the expected makePayment() interface.
Internally call processTransaction().
Return the result in the format expected by your application.
4. UML Structure
Target: Interface expected by the client.
Client: Uses the Target interface.
Adaptee: Existing class with an incompatible interface.
Adapter: Implements Target and wraps the Adaptee.
5. Types of Adapter
Object Adapter:
Uses composition.
Holds a reference to the Adaptee.
Delegates calls to the existing object.
More flexible and commonly preferred.
Class Adapter:
Uses inheritance.
Inherits from the Adaptee and implements the Target interface.
Depends on language support for multiple inheritance.
6. Where Is It Used?
Integrating third-party payment gateways.
Working with legacy systems.
Connecting incompatible APIs.
Database and external service integrations.
Converting data formats between components.
7. Adapter vs Decorator vs Facade
Adapter: Converts one interface into another.
Decorator: Adds new behavior to an existing object.
Facade: Provides a simplified interface to a complex subsystem.
8. Common Interview Mistakes
Confusing Adapter with Decorator or Facade.
Modifying the Adaptee instead of creating an adapter.
Creating unnecessary adapter layers.
Ignoring error handling and data conversion.
Using inheritance when composition would be more flexible.
FAANG Takeaway
Adapter Pattern helps integrate incompatible systems without changing their existing implementations.
Remember:
Adapter changes the interface, Decorator adds behavior, and Facade simplifies access.
Next: Day 19/50
Facade Pattern in LLD
HLD Interview Notes: Day 12/30
Topic: API Gateway: Architecture, Routing and Responsibilities
Imagine your application has 20 microservices.
Users need to access different services for authentication, payments, orders and user profiles.
Should clients communicate directly with every microservice?
Or should there be a single entry point that manages incoming requests?
1. What Is an API Gateway?
An API Gateway acts as a single entry point between clients and backend services.
It receives client requests, applies common policies and routes requests to the appropriate microservice.
Example:
An e-commerce application has:
User Service
Product Service
Order Service
Payment Service
Instead of calling each service directly, clients send requests through the API Gateway.
2. Request Routing
The gateway forwards incoming requests to the correct backend service based on the URL, HTTP method or routing rules.
Example:
/users → User Service
/products → Product Service
/orders → Order Service
/payments → Payment Service
Best for: Managing access to multiple backend services through one entry point.
3. Authentication and Authorization
The API Gateway can validate authentication tokens and enforce access policies before forwarding requests.
How it works:
Client sends a request with an access token.
Gateway validates the token.
Unauthorized requests are rejected.
Valid requests are forwarded to the backend service.
Best for: Centralizing common authentication checks.
4. Rate Limiting and Throttling
The gateway can control incoming traffic to protect backend services.
Example:
Allow 100 requests per minute per user.
Requests beyond the limit can receive HTTP 429.
Best for: Preventing excessive traffic from overwhelming backend services.
5. Load Balancing and Service Discovery
The gateway can distribute requests across healthy instances of a service.
Example:
An Order Service has three instances.
The gateway routes incoming requests across available instances instead of sending all traffic to one server.
Best for: Improving availability and distributing traffic.
6. Request and Response Transformation
The gateway can modify requests before forwarding them and transform responses before returning them to clients.
Examples:
Adding common headers.
Validating request formats.
Combining responses from multiple services.
Converting response formats.
Best for: Handling cross-cutting communication requirements.
7. API Gateway Architecture
Typical request flow:
Client → API Gateway → Authentication → Routing → Backend Service → Response
The gateway can also integrate:
Logging and monitoring
Rate limiting
Caching
TLS termination
Request validation
8. Challenges and Trade-offs
Single point of failure if not deployed redundantly.
Additional network latency.
Can become a performance bottleneck.
Complex routing and configuration management.
Overloading the gateway with business logic makes it difficult to maintain.
9. Interview Questions
Why do we need an API Gateway in microservices?
API Gateway vs Load Balancer?
API Gateway vs Reverse Proxy?
How does the gateway discover backend services?
What happens if the gateway goes down?
How do you scale an API Gateway?
Should authentication happen at the gateway or individual services?
FAANG Takeaway
An API Gateway simplifies communication between clients and microservices by centralizing routing and common infrastructure concerns.
But it should not become a place for business logic.
Keep it lightweight, scalable and highly available.
Next: Day 13/30
Service Discovery: Client-Side vs Server-Side Discovery
Blind 75: Day 39/75
Problem: Minimum Window Substring
LeetCode 76: leetcode.com/problems/minimu…
Given two strings s and t, return the minimum window substring of s that contains every character from t, including duplicate characters.
If no such window exists, return an empty string.
Example 1
Input: s = "ADOBECODEBANC", t = "ABC"
Output: "BANC"
Explanation: The smallest substring containing A, B and C is "BANC".
Example 2
Input: s = "a", t = "a"
Output: "a"
Example 3
Input: s = "a", t = "aa"
Output: ""
Explanation: The string does not contain enough occurrences of a.
Approach: Sliding Window + Frequency Count
Use two pointers to maintain a window and track character frequencies.
Store the frequency of every character in t.
Expand the window by moving the right pointer.
Update the frequency of the character entering the window.
Track how many required characters have been matched.
Once the window contains all required characters, try shrinking it from the left.
Update the minimum window whenever a valid smaller window is found.
Continue until the right pointer reaches the end.
Key Observation
A valid window must contain every character in t with the required frequency, including duplicates.
For example:
s = "ADOBECODEBANC"
t = "ABC"
The minimum valid window is "BANC".
Complexity
Let n be the length of s and m be the length of t.
Time: O(n + m)
Space: O(1) for a fixed-size character set.
FAANG Takeaway
This problem takes Sliding Window to the next level.
Instead of maintaining a simple condition like window length or maximum frequency, we track whether the window satisfies all character-frequency requirements.
The key is to expand until the window becomes valid, then shrink it to find the smallest possible answer.
Next: Day 40/75
Group Anagrams (LC 49)
HLD Interview Notes: Day 11/30
Topic: Rate Limiting: Token Bucket, Leaky Bucket and Sliding Window
Imagine your API suddenly receives 100,000 requests in one minute.
Your servers are overloaded. Legitimate users are experiencing failures.
How do you control incoming traffic without bringing down the system?
1. What Is Rate Limiting?
Rate limiting controls how many requests a client can make within a specific time period.
Example:
An API allows:
100 requests per minute per user.
Requests beyond the limit receive HTTP 429 (Too Many Requests).
2. Fixed Window Counter
Divide time into fixed intervals and count requests in each window.
Example: Maximum 100 requests per minute.
Trade-off: Traffic can spike at window boundaries, allowing nearly 200 requests in a short period.
3. Sliding Window Log
Store timestamps of incoming requests and remove entries outside the current time window.
Trade-off: Accurate, but storing individual timestamps consumes more memory.
4. Sliding Window Counter
Combine two adjacent fixed windows using a weighted request count.
Trade-off: More efficient than a full sliding window log, but provides an approximation.
5. Token Bucket Algorithm
Tokens are added to a bucket at a fixed rate.
Each request consumes one token.
Requests are rejected when no tokens remain.
The bucket has a maximum capacity.
Best for: APIs that need to allow short bursts of traffic.
6. Leaky Bucket Algorithm
Incoming requests enter a queue.
Requests are processed at a steady rate.
If the queue becomes full, new requests are rejected.
Best for: Systems that need smooth, predictable request processing.
7. Distributed Rate Limiting
In distributed systems, multiple application instances must share rate-limit state.
Common approach:
Use Redis to maintain counters or token state.
Apply atomic operations or Lua scripts.
Set expiration for time-based counters.
Define consistent limits across application instances.
8. Interview Questions
Fixed window vs sliding window?
Token bucket vs leaky bucket?
How do you implement rate limiting across multiple servers?
What happens if Redis becomes unavailable?
How do you handle different limits for different users?
Where should rate limiting happen: API Gateway or application?
FAANG Takeaway
Rate limiting isn't just about rejecting requests.
It's about protecting system resources, ensuring fair usage and keeping services available during unexpected traffic spikes.
Next: Day 12/30
API Gateway: Architecture, Routing and Responsibilities
china has deepseek
europe has mistral
japan is building its own AI models
russia is building its own AI ecosystem
and now india has sarvam AI aiming for a trillion-parameter model
that's actually exciting
imagine india not just using AI built by other countries, but building models that can compete globally and understand our languages, businesses and people
would be great to see an indian AI model make it big on the world stage
India needs this!!!!
Blind 75: Day 38/75
Problem: Longest Repeating Character Replacement
LeetCode 424: leetcode.com/problems/longes…
Given a string s and an integer k, return the length of the longest substring containing the same letter after performing at most k character replacements.
You can replace any character with any other uppercase English letter.
Example 1
Input: s = "ABAB", k = 2
Output: 4
Explanation: Replace the two As with B to get "BBBB".
Example 2
Input: s = "AABABBA", k = 1
Output: 4
Explanation: Replace one B with A to get "AAAA" or replace one A with B to get "BBBB".
Approach: Sliding Window + Frequency Count
Use two pointers to maintain a window.
Expand the window by moving the right pointer.
Track the frequency of each character.
Maintain the maximum frequency of any character in the current window.
Calculate the replacements needed:Window Length - Maximum Character Frequency
If replacements needed exceed k, shrink the window from the left.
Update the maximum window length whenever the window is valid.
Key Observation
A window is valid when:
Window Length - Max Frequency <= k
We only need to replace characters that are different from the most frequent character.
Complexity
Let n be the length of the string.
Time: O(n)
Space: O(1), since the frequency array stores only 26 uppercase English letters.
FAANG Takeaway
This problem teaches an important Sliding Window pattern:
Maintain a window using character frequencies and shrink it only when the replacement constraint is violated.
Recognizing how to calculate the number of required changes is the key to solving this problem efficiently.
Next: Day 39/75
Minimum Window Substring (LC 76)
HLD Interview Notes: Day 11/30
Topic: Rate Limiting: Token Bucket, Leaky Bucket and Sliding Window
Imagine your API suddenly receives 100,000 requests in one minute.
Your servers are overloaded. Legitimate users are experiencing failures.
How do you control incoming traffic without bringing down the system?
1. What Is Rate Limiting?
Rate limiting controls how many requests a client can make within a specific time period.
Example:
An API allows:
100 requests per minute per user.
Requests beyond the limit receive HTTP 429 (Too Many Requests).
2. Fixed Window Counter
Divide time into fixed intervals and count requests in each window.
Example: Maximum 100 requests per minute.
Trade-off: Traffic can spike at window boundaries, allowing nearly 200 requests in a short period.
3. Sliding Window Log
Store timestamps of incoming requests and remove entries outside the current time window.
Trade-off: Accurate, but storing individual timestamps consumes more memory.
4. Sliding Window Counter
Combine two adjacent fixed windows using a weighted request count.
Trade-off: More efficient than a full sliding window log, but provides an approximation.
5. Token Bucket Algorithm
Tokens are added to a bucket at a fixed rate.
Each request consumes one token.
Requests are rejected when no tokens remain.
The bucket has a maximum capacity.
Best for: APIs that need to allow short bursts of traffic.
6. Leaky Bucket Algorithm
Incoming requests enter a queue.
Requests are processed at a steady rate.
If the queue becomes full, new requests are rejected.
Best for: Systems that need smooth, predictable request processing.
7. Distributed Rate Limiting
In distributed systems, multiple application instances must share rate-limit state.
Common approach:
Use Redis to maintain counters or token state.
Apply atomic operations or Lua scripts.
Set expiration for time-based counters.
Define consistent limits across application instances.
8. Interview Questions
Fixed window vs sliding window?
Token bucket vs leaky bucket?
How do you implement rate limiting across multiple servers?
What happens if Redis becomes unavailable?
How do you handle different limits for different users?
Where should rate limiting happen: API Gateway or application?
FAANG Takeaway
Rate limiting isn't just about rejecting requests.
It's about protecting system resources, ensuring fair usage and keeping services available during unexpected traffic spikes.
Next: Day 12/30
API Gateway: Architecture, Routing and Responsibilities
LLD Interview Notes: Day 17/50
Topic: Decorator Pattern
How do you add new features to an object without modifying its existing class or creating a huge inheritance hierarchy?
That's where the Decorator Pattern comes in.
1. What Is the Decorator Pattern?
A structural design pattern that dynamically adds behavior to an object by wrapping it with another object that implements the same interface.
It follows the principle of composition over inheritance.
2. How It Works
Define a common interface.Create a concrete component with the basic behavior.Create a decorator that implements the same interface and wraps the component.Add extra behavior before or after delegating to the wrapped object.
3. Real-World Example: Coffee Shop
Imagine ordering a basic coffee and adding extras.
Basic Coffee: ₹100
Milk: ₹20
Caramel: ₹30Total: ₹150
Each decorator adds its own cost and behavior without changing the original coffee class.
4. UML Structure
Component: Common interface, such as getCost()ConcreteComponent: The original object.Decorator: Holds a reference to a Component.ConcreteDecorator: Adds behavior, such as milk or caramel.
5. Where Is It Used?
Java I/O streamsMiddleware and request processingLogging and monitoring wrappersAdding features to UI componentsDynamically extending object behavior
6. Decorator vs Inheritance
Inheritance: Adds behavior through class hierarchies.Decorator: Adds behavior by wrapping objects at runtime.Decorator makes combinations more flexible without creating a separate subclass for every combination.
7. Common Interview Mistakes
Confusing Decorator with Proxy.Forgetting that the decorator should preserve the component's interface.Creating unnecessary layers of wrappers.Using decorators when a simple method would be enough.
FAANG Takeaway
Decorator Pattern answers:
How can you extend an object's behavior without modifying its class?
Use it when features need to be combined flexibly at runtime. Remember that too many wrappers can make debugging and tracing execution harder.
Next: Day 18/50
Adapter Pattern in LLD
35 LPA internship PPO to a 6.5 LPA offer. All in two days.
A 2026 graduate spent 6 months interning, received positive feedback from the manager, and expected a full-time offer.
Then, just 2 days before the internship ended, all 90 interns received rejection emails.
Now they're left with a 6.5 LPA backup offer and struggling to get interview calls.
A harsh reality of today's job market:
Good performance doesn't guarantee a PPO.
Companies can change hiring plans overnight.
Even strong candidates are struggling to get interview calls.
Never depend on a single company or opportunity.
It's not a reflection of your worth. One company's decision shouldn't define your career.
LLD Interview Notes: Day 17/50
Topic: Decorator Pattern
How do you add new features to an object without modifying its existing class or creating a huge inheritance hierarchy?
That's where the Decorator Pattern comes in.
1. What Is the Decorator Pattern?
A structural design pattern that dynamically adds behavior to an object by wrapping it with another object that implements the same interface.
It follows the principle of composition over inheritance.
2. How It Works
Define a common interface.Create a concrete component with the basic behavior.Create a decorator that implements the same interface and wraps the component.Add extra behavior before or after delegating to the wrapped object.
3. Real-World Example: Coffee Shop
Imagine ordering a basic coffee and adding extras.
Basic Coffee: ₹100
Milk: ₹20
Caramel: ₹30Total: ₹150
Each decorator adds its own cost and behavior without changing the original coffee class.
4. UML Structure
Component: Common interface, such as getCost()ConcreteComponent: The original object.Decorator: Holds a reference to a Component.ConcreteDecorator: Adds behavior, such as milk or caramel.
5. Where Is It Used?
Java I/O streamsMiddleware and request processingLogging and monitoring wrappersAdding features to UI componentsDynamically extending object behavior
6. Decorator vs Inheritance
Inheritance: Adds behavior through class hierarchies.Decorator: Adds behavior by wrapping objects at runtime.Decorator makes combinations more flexible without creating a separate subclass for every combination.
7. Common Interview Mistakes
Confusing Decorator with Proxy.Forgetting that the decorator should preserve the component's interface.Creating unnecessary layers of wrappers.Using decorators when a simple method would be enough.
FAANG Takeaway
Decorator Pattern answers:
How can you extend an object's behavior without modifying its class?
Use it when features need to be combined flexibly at runtime. Remember that too many wrappers can make debugging and tracing execution harder.
Next: Day 18/50
Adapter Pattern in LLD
35 LPA internship PPO to a 6.5 LPA offer. All in two days.
A 2026 graduate spent 6 months interning, received positive feedback from the manager, and expected a full-time offer.
Then, just 2 days before the internship ended, all 90 interns received rejection emails.
Now they're left with a 6.5 LPA backup offer and struggling to get interview calls.
A harsh reality of today's job market:
Good performance doesn't guarantee a PPO.
Companies can change hiring plans overnight.
Even strong candidates are struggling to get interview calls.
Never depend on a single company or opportunity.
It's not a reflection of your worth. One company's decision shouldn't define your career.
HLD Interview Notes: Day 9/30
Topic: Cache Stampede Prevention: Locks, Request Coalescing and Refresh-Ahead
Your cache works perfectly until a popular key expires and thousands of requests hit your database at once.
How do you prevent a cache miss from turning into a production incident?
1. Cache Stampede: The Problem
A cache stampede happens when many requests try to rebuild the same expired cache entry simultaneously.
Example:
A shopping website caches popular product details.The cache expires during a big sale.Thousands of requests miss the cache and query the database.
Database load spikes, response times increase, and the service may become unavailable.
2. Distributed Locking
Allow only one worker to refresh an expired cache entry at a time.
First request acquires a lock.It fetches fresh data from the database.It updates the cache and releases the lock.Other requests wait briefly or use a fallback.
Best for: Preventing multiple workers from rebuilding the same key.
Trade-off: Lock timeouts, failed workers and incorrect lock handling can create problems.
3. Request Coalescing
Combine concurrent requests for the same missing key into one database fetch.
First request triggers the fetch.Other requests for the same key wait for the result.The fetched value is shared among waiting requests.
Best for: Reducing duplicate work for popular keys.
Trade-off: Waiting requests may experience additional latency.
4. Refresh-Ahead
Refresh frequently accessed cache entries before they expire.
Identify popular cache entries.Refresh them in the background before expiration.Keep serving cached data while the refresh happens.
Example: A product page refreshes its cached details before a major sale begins.
Best for: Predictable, read-heavy workloads.
Trade-off: Some refreshed data may never be used, increasing unnecessary database work.
5. Quick Comparison
Distributed locking: Controls which worker refreshes a key.Request coalescing: Shares one in-flight fetch among concurrent requests.Refresh-ahead: Refreshes data before it expires.
6. Interview Tip
When designing cache stampede protection, ask:
Can multiple requests rebuild the same key?Should requests wait, or can they use stale data?What happens if the refresh worker crashes?How do we prevent a lock from becoming a bottleneck?
FAANG Takeaway
Preventing cache stampedes is about controlling concurrent work, not just increasing cache hit rates.
Combine the right strategy with timeouts, TTL jitter and database protection to make caching more resilient under heavy traffic.
Next: Day 10/30
Cache Warming and Preloading Strategies
HLD Interview Notes: Day 9/30
Topic: Cache Stampede Prevention: Locks, Request Coalescing and Refresh-Ahead
Your cache works perfectly until a popular key expires and thousands of requests hit your database at once.
How do you prevent a cache miss from turning into a production incident?
1. Cache Stampede: The Problem
A cache stampede happens when many requests try to rebuild the same expired cache entry simultaneously.
Example:
A shopping website caches popular product details.The cache expires during a big sale.Thousands of requests miss the cache and query the database.
Database load spikes, response times increase, and the service may become unavailable.
2. Distributed Locking
Allow only one worker to refresh an expired cache entry at a time.
First request acquires a lock.It fetches fresh data from the database.It updates the cache and releases the lock.Other requests wait briefly or use a fallback.
Best for: Preventing multiple workers from rebuilding the same key.
Trade-off: Lock timeouts, failed workers and incorrect lock handling can create problems.
3. Request Coalescing
Combine concurrent requests for the same missing key into one database fetch.
First request triggers the fetch.Other requests for the same key wait for the result.The fetched value is shared among waiting requests.
Best for: Reducing duplicate work for popular keys.
Trade-off: Waiting requests may experience additional latency.
4. Refresh-Ahead
Refresh frequently accessed cache entries before they expire.
Identify popular cache entries.Refresh them in the background before expiration.Keep serving cached data while the refresh happens.
Example: A product page refreshes its cached details before a major sale begins.
Best for: Predictable, read-heavy workloads.
Trade-off: Some refreshed data may never be used, increasing unnecessary database work.
5. Quick Comparison
Distributed locking: Controls which worker refreshes a key.Request coalescing: Shares one in-flight fetch among concurrent requests.Refresh-ahead: Refreshes data before it expires.
6. Interview Tip
When designing cache stampede protection, ask:
Can multiple requests rebuild the same key?Should requests wait, or can they use stale data?What happens if the refresh worker crashes?How do we prevent a lock from becoming a bottleneck?
FAANG Takeaway
Preventing cache stampedes is about controlling concurrent work, not just increasing cache hit rates.
Combine the right strategy with timeouts, TTL jitter and database protection to make caching more resilient under heavy traffic.
Next: Day 10/30
Cache Warming and Preloading Strategies
Blind 75: Day 37/75
Problem: Palindromic Substrings
LeetCode 647: leetcode.com/problems/palind…
Given a string s, return the number of palindromic substrings in it.
Every occurrence counts separately, even if the same substring appears at different positions.
Example 1
Input: s = "abc"Output: 3Explanation: "a", "b", "c"
Example 2
Input: s = "aaa"Output: 6Explanation: "a", "a", "a", "aa", "aa", "aaa"
Approach: Expand Around Center
Every palindrome has a center, which can be a character or a gap between two characters.
Iterate through every possible center.Expand left and right while the characters match.Increment the count for every valid palindrome found.Check both odd-length and even-length centers.
Complexity
Let (n) be the length of the string.
Time: O(n²)Space: O(1) auxiliary space
FAANG Takeaway
This problem uses the same Expand Around Center technique as Longest Palindromic Substring.
The difference: instead of tracking the longest palindrome, count every valid palindrome found during expansion.
Next: Day 38/75
Longest Repeating Character Replacement (LC 424)
Cheating might get you a rank, but it won't get you the skills to back it up!
Solving all 4 problems in a LeetCode contest in just 1–4 minutes is wild.
Reading, understanding, finding the optimal approach, coding and debugging all in that time?
Competitive programming is getting intense. If the results are legitimate, that's an incredible level of problem-solving speed. But suspicious patterns deserve proper scrutiny too.
Fair competition matters. Rankings only mean something when everyone plays by the same rules.
Are vibe coders even aware of these 50 things?
Database Indexes
Race Conditions
API Rate Limiting
Database Transactions
Cache Stampedes
Connection Pooling
Dead Letter Queues
ACID Properties
Webhook Verification
Distributed Tracing
N+1 Queries
Circuit Breakers
Database Sharding
Optimistic Locking
Exponential Backoff
Zero-Downtime Migrations
Message Queues
Isolation Levels
Idempotency
Load Balancing
Query Optimization
Graceful Shutdown
Redis Eviction Policies
Foreign Key Constraints
Replication Lag
Background Workers
Deadlocks
Eventual Consistency
Read Replicas
Retry Mechanisms
Database Migrations
Backpressure
Pessimistic Locking
Horizontal vs Vertical Scaling
Webhook Retries
EXPLAIN ANALYZE
Distributed Caching
Unique Constraints
Outbox Pattern
Health Checks
Database Partitioning
Job Scheduling
Reverse Proxies
Distributed Transactions
Cache Invalidation
Event-Driven Architecture
Jitter
CQRS
Event Sourcing
Circuit Breaker Recovery Strategies
Building a working demo is one thing.
Building a system that survives real users, failures, traffic spikes, and messy data is a completely different game.














