Backend Engineer interview questions & answers

17 Backend Engineer interview questions with complete model answers, spanning System design, Behavioral, Coding, Technical. The bank holds 17 Backend Engineer questions in total, tagged by round and difficulty.

BehavioralMediumBackend EngineerOnsite

1. Describe a time when you had to coordinate between two teams that were in conflict or seriously misaligned.

The full question

Describe a time when you had to coordinate between two teams that were in conflict or seriously misaligned. What caused the disagreement, how did you align stakeholders, what actions did you personally take, and what was the outcome?

Model answer

Situation

In my previous role as a project manager at a mid-sized tech company, I was tasked with overseeing a critical product launch. The engineering team and the marketing team were in conflict over the product's feature set and launch timeline. The engineering team was concerned about the feasibility and quality of the features within the given timeline, while the marketing team was pushing for a launch date that aligned with a major industry event. This misalignment threatened the success of the product launch and could have resulted in missed market opportunities and potential revenue loss.

Task

My goal was to align both teams to ensure a successful product launch that met both the technical feasibility and market timing. The key constraint was balancing the engineering team's need for quality assurance with the marketing team's deadline for the industry event.

Action

  • I initiated a series of joint meetings between the two teams to openly discuss their concerns and objectives. This helped create a platform for transparent communication and mutual understanding.
  • To address the engineering team's concerns, I worked with them to identify the critical features that could be realistically developed and tested within the timeline. I facilitated a prioritization exercise to focus on high-impact features.
  • For the marketing team, I proposed a phased launch strategy. This allowed us to meet the initial deadline with a core set of features, while planning subsequent updates to incorporate additional features post-launch.
  • I mediated discussions to ensure both teams understood the trade-offs involved and the importance of each other's objectives. I emphasized the shared goal of a successful product launch and how collaboration was essential to achieve it.
  • I also set up a regular progress tracking system to keep both teams informed and aligned on the project status, which helped in maintaining momentum and accountability.

Result

The coordinated effort led to a successful product launch that met the industry event deadline with a robust set of core features. The phased approach allowed us to continue enhancing the product post-launch, which was well-received by customers and stakeholders. This experience reinforced the importance of cross-functional collaboration and transparent communication. I learned that facilitating open dialogue and focusing on shared goals can effectively resolve conflicts and align teams towards a common objective.

BehavioralMediumBackend EngineerOnsite

2. You may be asked behavioral questions such as: Tell me about a time you had a conflict with a teammate or partner team.

The full question

You may be asked behavioral questions such as:

  • Tell me about a time you had a conflict with a teammate or partner team. What happened, how did you handle it, and what was the outcome?
  • What project are you most proud of, and why? What impact did it have?

Model answer

Situation

In my role as a software engineer at a previous company, I was part of a cross-functional team working on a high-stakes project to develop a new feature for our flagship product. The project had a tight deadline, and tensions were high. A conflict arose between our team and the design team regarding the user interface. The designers wanted to implement a visually complex design that would require significant development time, while our team was concerned about meeting the deadline without compromising the feature's functionality.

Task

As the lead developer on the project, my goal was to mediate the conflict and find a solution that balanced both the design team's vision and our technical constraints. The key challenge was to ensure that the project stayed on schedule while maintaining a high-quality user experience.

Action

  • I initiated a meeting with both teams to openly discuss the concerns and priorities. I emphasized the importance of collaboration and understanding each other's perspectives to ensure the project's success.
  • During the meeting, I actively listened to the design team's rationale for the complex interface and shared our technical limitations and timeline constraints. I encouraged both teams to express their views candidly.
  • I proposed a compromise by suggesting a phased approach: implementing a simplified version of the design for the initial release, followed by iterative enhancements in subsequent updates. This would allow us to meet the deadline while gradually incorporating the designers' vision.
  • To facilitate this, I worked closely with the design lead to identify the core elements of the interface that could be implemented within the current timeline without sacrificing quality.
  • I also coordinated with the project manager to adjust the timeline for future updates, ensuring that the design enhancements were prioritized in the development roadmap.

Result

The compromise was well-received by both teams, and we successfully launched the feature on time with a simplified yet effective user interface. The phased approach allowed us to gather user feedback, which informed the subsequent design iterations. This experience taught me the value of empathy and open communication in resolving conflicts and reinforced the importance of balancing creativity with practicality. The project not only met its deadline but also received positive feedback from users, enhancing our product's reputation in the market.

BehavioralMediumBackend EngineerTechnical Screen

3. A backend engineer prepared for a coding interview after the recruiter explicitly said the round would assess concurrency.

The full question

A backend engineer prepared for a coding interview after the recruiter explicitly said the round would assess concurrency. The candidate chose Java as the interview language. During the interview, however, the interviewer gave a web-crawler problem, insisted on a single-threaded implementation first, and did not allow time to implement the concurrent version even after the single-threaded solution passed the basic tests. The interviewer also said they were not comfortable reading Java. The candidate was later rejected.

How should a candidate handle this situation professionally?

Please address all of the following:

  • How to politely push back when the interviewer’s guidance appears to conflict with the stated evaluation focus.
  • How to make concurrency skills visible without openly ignoring the interviewer’s instructions.
  • What to do if the interviewer is unfamiliar with the chosen programming language.
  • Whether it is worth sending post-interview feedback or an appeal, and how to frame it.
  • For well-known coding problems such as a web crawler, whether the candidate should first clarify requirements and edge cases (for example URL normalization, fragments, schemes, and stopping conditions) or start coding immediately to avoid appearing slow or overly rehearsed.

Model answer

Situation

During a recent interview for a backend engineering position, I encountered a challenging situation. The recruiter had informed me that the interview would focus on assessing my concurrency skills, and I prepared accordingly, choosing Java as my language of preference. However, during the interview, the interviewer presented a web-crawler problem and insisted on a single-threaded implementation first. Additionally, the interviewer mentioned they were not comfortable with Java, which was unexpected and added complexity to the situation.

Task

My primary goal was to demonstrate my concurrency skills as initially communicated, while also addressing the interviewer's requirements for a single-threaded solution. I needed to navigate the language barrier and ensure that I still presented my skills effectively, despite the constraints.

Action

  • Clarified Expectations: I politely asked the interviewer to confirm the focus of the interview, mentioning the initial emphasis on concurrency. I framed it as a request for clarity to ensure alignment with their expectations.
  • Demonstrated Concurrency Knowledge: While implementing the single-threaded solution, I verbally explained how I would approach a concurrent version, highlighting key considerations such as thread safety and potential performance improvements. This allowed me to showcase my understanding without disregarding the interviewer's instructions.
  • Adapted to Language Constraints: Recognizing the interviewer's discomfort with Java, I offered to explain my code in pseudocode or another language they were comfortable with. This demonstrated flexibility and a willingness to communicate effectively.
  • Clarified Requirements: Before coding, I briefly clarified the problem requirements and edge cases, such as URL normalization and stopping conditions. This ensured a shared understanding and demonstrated thoroughness without appearing rehearsed.
  • Post-Interview Feedback: After the interview, I decided to send feedback to the recruitment team. I framed it constructively, expressing appreciation for the opportunity and providing specific suggestions for improving the interview process, such as ensuring alignment between interview focus and execution.

Result

Although I was not selected for the position, the experience taught me valuable lessons in adaptability and communication. By maintaining professionalism and demonstrating my skills within the given constraints, I reinforced my ability to handle unexpected challenges. This experience also highlighted the importance of clear communication and flexibility in interviews, which I have since applied to improve my approach in future interviews.

BehavioralMediumBackend EngineerOnsite

4. Pick one project you worked on and walk through the key technical decisions behind it.

The full question

Pick one project you worked on and walk through the key technical decisions behind it. Why did you choose that architecture, design, or technology stack? What alternatives did you consider? What trade-offs did you accept, and what would you change in hindsight?

A strong answer should cover your specific role, the constraints you were operating under, how you evaluated options, and the measurable impact of the final decision.

Model answer

Situation In my previous role as a lead software engineer at a mid-sized fintech company, I was tasked with developing a new payment processing system. The existing system was struggling with scalability issues, leading to frequent downtime during peak transaction periods. This was critical as it directly impacted our revenue and customer satisfaction. My role was to lead the technical design and implementation of the new system, ensuring it could handle a 10x increase in transaction volume.

Task The primary goal was to design a robust, scalable architecture that could process transactions efficiently and reliably. The key constraint was maintaining data consistency while ensuring high availability, given the financial nature of the transactions.

Action

  • I began by evaluating different architectural patterns, considering microservices and event-driven architectures due to their scalability and flexibility. After thorough analysis, I chose a microservices architecture because it allowed us to scale individual components independently, which was crucial for handling varying loads across different services.
  • I selected a technology stack that included Kubernetes for container orchestration, which provided the necessary scalability and resilience. For the database, I opted for a combination of SQL for transactional consistency and NoSQL for handling large volumes of read-heavy operations, ensuring both consistency and performance.
  • To address the trade-off between consistency and availability, I implemented a two-phase commit protocol for transactions, ensuring data consistency across services while accepting slightly increased latency as a trade-off.
  • I conducted a series of load tests to simulate peak transaction volumes, iterating on the design based on the results. This helped identify bottlenecks early and allowed us to optimize the system before going live.
  • I also led discussions with stakeholders to align on the design decisions and ensure that the business requirements were fully met. This involved presenting the trade-offs and justifying the chosen architecture with data from our testing.

Result The new payment processing system successfully handled a 12x increase in transaction volume with zero downtime during peak periods. Customer satisfaction improved significantly, and the system's reliability directly contributed to a 15% increase in revenue within the first quarter post-launch. Reflecting on the project, I learned the importance of balancing technical trade-offs with business needs and the value of thorough testing in identifying potential issues early. In hindsight, I would have incorporated more automated monitoring tools from the outset to further enhance our ability to detect and respond to issues in real-time.

BehavioralMediumBackend EngineerTechnical Screen

5. Choose a meaningful engineering project from roughly the last two years.

The full question

Choose a meaningful engineering project from roughly the last two years. Give a concise deep dive into the problem, your personal responsibilities, the most important technical decision, and the outcome. Then identify what did not go well and explain what you would do differently if you restarted the project today.

Model answer

Situation

About a year ago, I led a project at my previous company to redesign our payment processing system. The existing system was becoming a bottleneck, leading to delays in transaction processing and customer dissatisfaction. As a senior software engineer, I was responsible for overseeing the technical aspects of the project, ensuring that the new system would be scalable and reliable. This project was crucial because it directly impacted our revenue stream and customer retention.

Task

My primary goal was to design and implement a new payment processing architecture that could handle a 50% increase in transaction volume without degrading performance. A key constraint was maintaining zero downtime during the transition, as any disruption could lead to significant financial losses and damage to our reputation.

Action

  • I began by conducting a thorough analysis of the existing system to identify its limitations and bottlenecks. This involved reviewing transaction logs and performance metrics to pinpoint areas for improvement.
  • Based on the analysis, I proposed a microservices architecture to replace the monolithic design. This decision was driven by the need for scalability and fault isolation, allowing individual components to be updated or scaled independently.
  • I led a team of four engineers in developing the new system, focusing on using technologies like Kubernetes for container orchestration and Kafka for reliable message queuing. These choices were made to ensure high availability and resilience.
  • To ensure a smooth transition, I implemented a phased rollout strategy. We first deployed the new system in parallel with the old one, gradually shifting traffic to monitor performance and address any issues in real-time.
  • Throughout the project, I maintained open communication with stakeholders, providing regular updates and gathering feedback to align the technical solution with business needs.

Result

The new payment processing system was successfully deployed with zero downtime, and it exceeded the performance goals by handling a 60% increase in transaction volume. Customer satisfaction improved significantly, as evidenced by a 20% reduction in transaction-related support tickets. Reflecting on the project, I learned the importance of thorough planning and stakeholder engagement in complex system migrations.

What Did Not Go Well

One aspect that did not go as planned was the initial underestimation of the time required for testing and validation. This oversight led to a compressed timeline towards the end of the project, causing stress for the team.

What I Would Do Differently

If I were to restart the project, I would allocate more time for testing and validation in the project timeline. Additionally, I would involve the QA team earlier in the process to identify potential issues sooner, ensuring a smoother and less stressful rollout.

CodingMediumBackend EngineerTake-home Project

6. A metrics page accepts filters and returns both matching rows and summary metrics.

The full question

A metrics page accepts filters and returns both matching rows and summary metrics. Users report that the rows are filtered correctly but the summary value still describes the unfiltered data.

The endpoint has this simplified flow:

rows = repository.load_rows()
metrics = summarize(rows)
rows = apply_filters(rows, request.filters)
return {"rows": rows, "metrics": metrics}

Diagnose the defect, propose a minimal safe fix, and design tests that prevent the ordering bug from returning. Discuss how you would investigate the real code when the frontend is JavaScript but the endpoint is implemented in an unfamiliar Python web framework.

Model answer

def load_rows():
    # Simulate loading rows from a repository
    return [
        {"id": 1, "value": 10},
        {"id": 2, "value": 20},
        {"id": 3, "value": 30},
    ]

def summarize(rows):
    # Calculate the summary metric as the sum of 'value' fields
    return sum(row['value'] for row in rows)

def apply_filters(rows, filters):
    # Apply filters to the rows
    return [row for row in rows if row['id'] in filters]

def get_metrics_and_rows(request_filters):
    rows = load_rows()
    # Apply filters before summarizing
    rows = apply_filters(rows, request_filters)
    metrics = summarize(rows)
    return {"rows": rows, "metrics": metrics}

# Example request filters
request_filters = [1, 3]
result = get_metrics_and_rows(request_filters)
print(result)  # Should output: {'rows': [{'id': 1, 'value': 10}, {'id': 3, 'value': 30}], 'metrics': 40}
  • Approach:
  • The defect arises because the summary metrics are calculated before applying the filters.
  • To fix, apply the filters to the rows first, then calculate the metrics on the filtered data.
  • This ensures that both the rows and the metrics reflect the filtered dataset.
  • Complexity:
  • Time Complexity: O(n) for both filtering and summarizing, where n is the number of rows.
  • Space Complexity: O(n) for storing the filtered rows.

Investigating the Real Code

  1. Understand the Framework: Familiarize yourself with the Python web framework in use. Look for documentation or tutorials to understand routing, request handling, and middleware.
  2. Trace the Code Path: Identify where the endpoint logic is implemented. Use logging or debugging tools to trace the flow of data through the endpoint.
  3. Check Data Flow: Ensure that data transformations (like filtering and summarizing) occur in the correct order. Use print statements or a debugger to inspect intermediate results.
  4. Review Tests: Examine existing tests to see if they cover the filtering and summarizing logic. Add tests if necessary to ensure coverage of edge cases.

Designing Tests

  • Unit Tests:
  • Test that the apply_filters function correctly filters rows based on various filter criteria.
  • Test that the summarize function calculates metrics accurately for a given set of rows.
  • Integration Tests:
  • Test the entire endpoint to ensure that the response contains correctly filtered rows and accurate metrics.
  • Use different filter scenarios to verify that the metrics reflect the filtered data.
  • Edge Cases:
  • Test with no filters to ensure metrics cover all data.
  • Test with filters that match no rows to ensure metrics return zero or appropriate default values.
  • Test with filters that match all rows to ensure metrics are calculated on the entire dataset.
CodingMediumBackend EngineerTake-home Project

7. An existing library-management application shows a book table that users find too long and difficult to navigate.

The full question

An existing library-management application shows a book table that users find too long and difficult to navigate. The initial request is to add a text search input and two dropdown filters. Important details—filter fields, labels, option values, matching rules, styling, and interaction behavior—have not yet been agreed.

Explain how you would gather the missing requirements, turn them into an acceptance contract, locate the relevant code, implement the feature, and provide unit and integration coverage.

Model answer

// Assuming we have a basic HTML structure for the library management application
// and a JavaScript file where we will implement the search and filter functionality.

class LibraryManager {
  constructor(books) {
    this.books = books;
    this.filteredBooks = books;
    this.init();
  }

  init() {
    // Attach event listeners to search input and dropdowns
    document.getElementById('searchInput').addEventListener('input', (e) => this.handleSearch(e));
    document.getElementById('filterDropdown1').addEventListener('change', (e) => this.handleFilter(e));
    document.getElementById('filterDropdown2').addEventListener('change', (e) => this.handleFilter(e));
  }

  handleSearch(event) {
    const query = event.target.value.toLowerCase();
    this.filteredBooks = this.books.filter(book => book.title.toLowerCase().includes(query));
    this.renderBooks();
  }

  handleFilter(event) {
    const filter1Value = document.getElementById('filterDropdown1').value;
    const filter2Value = document.getElementById('filterDropdown2').value;

    this.filteredBooks = this.books.filter(book => {
      return (filter1Value === 'all' || book.category === filter1Value) &&
             (filter2Value === 'all' || book.author === filter2Value);
    });
    this.renderBooks();
  }

  renderBooks() {
    const booksContainer = document.getElementById('booksContainer');
    booksContainer.innerHTML = '';

    this.filteredBooks.forEach(book => {
      const bookElement = document.createElement('div');
      bookElement.textContent = `${book.title} by ${book.author}`;
      booksContainer.appendChild(bookElement);
    });
  }
}

// Example usage
const books = [
  { title: 'Book One', author: 'Author A', category: 'Fiction' },
  { title: 'Book Two', author: 'Author B', category: 'Non-Fiction' },
  // more books...
];

const libraryManager = new LibraryManager(books);
  • Approach:
  • Gather Requirements: Conduct interviews with stakeholders to determine the specific fields for filtering, the labels, option values, and the desired interaction behavior.
  • Acceptance Contract: Draft a document detailing the agreed-upon requirements, including UI mockups, user stories, and acceptance criteria. Ensure all stakeholders sign off on this document.
  • Locate Code: Identify the existing codebase related to the book table. This might involve searching through the repository for relevant files and understanding the current implementation.
  • Implement Feature: Develop the search and filter functionality using JavaScript. Ensure the UI updates dynamically based on user input.
  • Testing: Write unit tests for the search and filter logic, and integration tests to ensure the feature works as expected within the application.

Complexity:

  • Time: O(n) for filtering and searching through the list of books, where n is the number of books.
  • Space: O(n) for storing the filtered list of books.
System designEasyBackend EngineerOnsite

8. Design an object-oriented elevator control system for a building with multiple floors and multiple elevators.

The full question

Design an object-oriented elevator control system for a building with multiple floors and multiple elevators.

The system should support:

  • Hall requests from each floor, where a user can request to go up or down
  • Cabin requests from inside an elevator, where a user selects a destination floor
  • Tracking each elevator's current floor, direction, door state, and pending stops
  • Assigning hall requests to an appropriate elevator
  • Moving elevators efficiently while avoiding obviously poor scheduling decisions

Focus on clean object-oriented design and the core scheduling logic. You do not need to model low-level motor hardware, networking, or a graphical UI.

Discuss the main classes, responsibilities, state transitions, and how requests are processed. If useful, also describe a simple dispatch algorithm and how you would extend the design later.

Model answer

1. Requirements & scale

Functional Requirements:

  • Handle hall requests from each floor for up and down directions.
  • Handle cabin requests from inside an elevator to select destination floors.
  • Track each elevator's current floor, direction, door state, and pending stops.
  • Assign hall requests to the most appropriate elevator.
  • Move elevators efficiently to minimize wait and travel times.

Non-Functional Requirements:

  • System should be reliable and handle requests in real-time.
  • Scalable to accommodate buildings with varying numbers of floors and elevators.
  • Maintainable and extendable for future enhancements.

Scale Estimates:

  • Assume a building with 10 floors and 4 elevators.
  • Each elevator can handle up to 10 requests per minute.
  • Total requests per minute = 4 elevators * 10 requests = 40 requests.
  • System should handle peak loads efficiently, with potential scaling for larger buildings.

2. High-level architecture

flowchart TD
    subgraph Client
        A[User Interface]
    end

    subgraph API / Services
        B[Request Processor]
        C[Scheduler]
        D[Elevator Controller]
    end

    subgraph Datastores
        E[State Database]
    end

    A -- "Hall/Cabin Request" --> B
    B -- "Process Request" --> C
    C -- "Schedule Elevator" --> D
    D -- "Update State" --> E
    E -- "Current State" --> C
Diagram

3. API design

  • POST /request/hall: Submit a hall request from a specific floor with direction.
  • POST /request/cabin: Submit a cabin request from inside an elevator for a destination floor.
  • GET /elevator/status: Retrieve the current status of all elevators (floor, direction, door state).

4. Data model & storage

Datastore Choice:

  • Use a relational database (SQL) for structured data and ACID transactions to ensure consistency.

Key Tables:

  • Elevators: Tracks each elevator's current floor, direction, door state, and pending stops.
  • elevator_id (Primary Key)
  • current_floor
  • direction (enum: UP, DOWN, IDLE)
  • door_state (enum: OPEN, CLOSED)
  • pending_stops (list of floor numbers)
  • Requests: Stores hall and cabin requests.
  • request_id (Primary Key)
  • floor_number
  • direction (optional for cabin requests)
  • elevator_id (foreign key)

5. Deep dive

The core of this system is the scheduling logic that assigns hall requests to the most appropriate elevator. The scheduling algorithm should consider factors such as the current direction of the elevator, the proximity to the requested floor, and the current load of pending requests.

sequenceDiagram
    participant User
    participant RequestProcessor
    participant Scheduler
    participant ElevatorController
    participant StateDatabase

    User->>RequestProcessor: Submit Hall/Cabin Request
    RequestProcessor->>Scheduler: Process Request
    Scheduler->>StateDatabase: Fetch Current Elevator States
    StateDatabase-->>Scheduler: Return Elevator States
    Scheduler->>ElevatorController: Assign Request to Elevator
    ElevatorController->>StateDatabase: Update Elevator State
    StateDatabase-->>ElevatorController: Confirm Update
Diagram

6. Scale, bottlenecks & trade-offs

Scalability:

  • The system can scale horizontally by adding more elevators and processing nodes.
  • The scheduler can be distributed to handle more requests concurrently.

Bottlenecks:

  • The scheduler could become a bottleneck if not properly optimized, as it needs to make real-time decisions.
  • Database performance could degrade with a high number of concurrent requests; indexing and query optimization are crucial.

Trade-offs:

  • Consistency vs. Availability: Prioritize consistency to ensure accurate elevator states and request handling.
  • Push vs. Pull: Use a push model for real-time updates to elevators, ensuring timely response to requests.
  • Modular Design: Following modular design principles allows for easier maintenance and future extensions, such as adding predictive maintenance features or integrating with building management systems.

This design provides a robust framework for an elevator control system, focusing on efficient scheduling and real-time processing while maintaining a clean and modular architecture.

System designEasyBackend EngineerOnsite

9. Design the backend of an internal analytics system for a conversational AI product similar to ChatGPT.

The full question

Design the backend of an internal analytics system for a conversational AI product similar to ChatGPT. The goal is to power an analytical metrics dashboard used by product managers and backend engineers.

The dashboard itself and the LLM serving stack are out of scope. Focus on the data platform and backend services that collect data from existing production databases and service logs, compute product and reliability metrics, and serve those metrics to the dashboard.

Your design should address:

  • How to ingest data from existing services without overloading primary databases
  • What event and metric schemas you would use
  • How to support both near-real-time metrics and historical analysis
  • How to compute common metrics such as daily active users, number of conversations, request volume, latency percentiles, token usage, error rates, and feature adoption
  • How to slice metrics by dimensions such as time, model version, region, platform, and user segment
  • Data freshness, correctness, deduplication, and backfills
  • Privacy, access control, and operational monitoring
  • The APIs or query layer that a dashboard backend would call

Assume the system must support large-scale traffic and multi-month historical retention.

Model answer

1. Requirements & scale

Functional Requirements:

  • Ingest data from existing production databases and service logs.
  • Compute and serve metrics like daily active users, number of conversations, request volume, latency percentiles, token usage, error rates, and feature adoption.
  • Support slicing metrics by dimensions such as time, model version, region, platform, and user segment.
  • Provide APIs for the dashboard to query metrics.

Non-Functional Requirements:

  • Near-real-time metrics computation.
  • Support historical analysis with multi-month retention.
  • Ensure data freshness, correctness, deduplication, and support for backfills.
  • Implement privacy, access control, and operational monitoring.

Scale Estimates:

  • Assume 1 million DAUs generating 10 events per day: 10 million events/day.
  • Each event is approximately 1 KB, resulting in 10 GB/day of raw event data.
  • For a 6-month retention, storage needs are approximately 1.8 TB.
  • Assume a peak QPS of 100 for dashboard queries.

2. High-level architecture

flowchart TD
    subgraph Client
        A[Dashboard]
    end

    subgraph Edge/CDN
        B[API Gateway]
    end

    subgraph Load Balancer
        C[Load Balancer]
    end

    subgraph API / Services
        D[Metrics API]
        E[Ingestion Service]
    end

    subgraph Cache
        F[Redis Cache]
    end

    subgraph Datastores
        G[Event Storage (S3)]
        H[Data Warehouse (Redshift)]
        I[Metadata DB (PostgreSQL)]
    end

    subgraph Message Queue
        J[Kafka]
    end

    subgraph Workers
        K[Metrics Computation Workers]
    end

    A --> B
    B --> C
    C --> D
    C --> E
    E --> J
    J --> K
    K --> G
    K --> H
    D --> F
    F --> H
    H --> D
Diagram

3. API design

  • GET /metrics: Retrieve computed metrics for specified dimensions and time range.
  • POST /ingest: Ingest raw event data from production databases and service logs.
  • GET /status: Check the health and status of the metrics computation pipeline.

4. Data model & storage

Datastores:

  • Event Storage (S3): Used for raw event data storage. Chosen for its scalability and cost-effectiveness.
  • Data Warehouse (Redshift): Used for storing processed metrics. Chosen for its analytical capabilities and ability to handle large datasets.
  • Metadata DB (PostgreSQL): Stores metadata about data sources, schemas, and processing jobs.

Key Tables:

  • Events Table: event_id, timestamp, user_id, event_type, attributes.
  • Metrics Table: metric_id, metric_name, value, timestamp, dimensions.

Partitioning/Sharding:

  • Events and metrics tables are partitioned by time (e.g., daily) to optimize query performance.
  • Sharding by user ID or region to distribute load evenly.

5. Deep dive

The core of this system is the metrics computation pipeline. Events are ingested via the API and pushed to Kafka. Workers consume these events to compute metrics in near-real-time and store them in the data warehouse.

sequenceDiagram
    participant E as Ingestion Service
    participant J as Kafka
    participant K as Metrics Workers
    participant G as Event Storage
    participant H as Data Warehouse

    E->>J: Publish raw events
    J->>K: Consume events
    K->>G: Store raw events
    K->>H: Compute and store metrics
Diagram

6. Scale, bottlenecks & trade-offs

Scaling:

  • Horizontal Scaling: Use sharding for the data warehouse and Kafka to handle increased load.
  • Caching: Use Redis to cache frequently accessed metrics to reduce load on the data warehouse.

Bottlenecks:

  • Ingestion Pipeline: Kafka can become a bottleneck if not scaled properly. Ensure sufficient partitions.
  • Metrics Computation: Workers need to be scaled based on event volume to maintain near-real-time processing.

Trade-offs:

  • Consistency vs. Availability: Prioritize availability for the metrics API, allowing eventual consistency in metrics computation.
  • Push vs. Pull: Use a push-based model for event ingestion to reduce load on primary databases.
  • Privacy and Access Control: Implement role-based access control and data anonymization to ensure privacy.

By carefully designing the ingestion, processing, and storage layers, this system can efficiently compute and serve analytics metrics for a conversational AI product at scale.

System designMediumBackend EngineerTechnical Screen

10. Design a small scoring component for one tennis game between two players.

The full question

Design a small scoring component for one tennis game between two players. It must record which player won each point, report a human-readable score, and identify when the game has ended and who won.

Under standard scoring, the first points are displayed as Love, 15, 30, and 40. Once both players have at least three points, equal scores are Deuce, a one-point lead is Advantage, and a two-point lead wins the game. Extend the design so another scoring policy, such as no-ad scoring at Deuce, can be selected without putting rule-specific conditionals throughout the game object.

Candidate hint: Keep the facts that happened separate from the policy that interprets those facts.

Model answer

1. Requirements & scale

Functional Requirements:

  • Record which player won each point.
  • Report the current score in a human-readable format.
  • Identify when the game has ended and who won.
  • Support multiple scoring policies (e.g., standard, no-ad scoring).

Non-Functional Requirements:

  • The system should be extensible to accommodate new scoring policies.
  • Ensure high reliability and accuracy in score reporting.
  • Maintain separation between game events and scoring policies.

Estimates:

Given that this is a component for a single tennis game, the scale is minimal. No significant storage or bandwidth concerns are anticipated. The system primarily requires efficient in-memory operations.

2. High-level architecture

flowchart TD
    subgraph Client
        A[Player Input]
    end
    
    subgraph API / Services
        B[Game Service]
        C[Scoring Policy]
    end
    
    subgraph Datastores
        D["Event Store"]
    end
    
    A -->|Point Won| B
    B -->|Store Event| D
    B -->|Fetch Events| C
    C -->|Calculate Score| B
    B -->|Report Score| A
Diagram

3. API design

  • POST /game/{gameId}/point: Record a point won by a player.
  • GET /game/{gameId}/score: Retrieve the current score of the game.
  • GET /game/{gameId}/status: Check if the game has ended and who won.

4. Data model & storage

Datastore Choice:

  • Event Store: Use an event sourcing approach to store each point as an event. This allows the system to reconstruct the game state by replaying events, supporting different scoring policies without altering the underlying data.

Key Tables/Structures:

  • Events Table:
  • gameId: Unique identifier for the game.
  • playerId: Identifier for the player who won the point.
  • timestamp: Time when the point was recorded.

5. Deep dive

The core of this design is the separation of game events from scoring policies using event sourcing. Each point won is stored as an event, and the current score is derived by applying a scoring policy to these events.

sequenceDiagram
    participant Player
    participant GameService
    participant EventStore
    participant ScoringPolicy

    Player->>GameService: Record Point (playerId)
    GameService->>EventStore: Store Event (gameId, playerId)
    Player->>GameService: Request Score
    GameService->>EventStore: Fetch Events (gameId)
    EventStore-->>GameService: List of Events
    GameService->>ScoringPolicy: Calculate Score (events)
    ScoringPolicy-->>GameService: Current Score
    GameService-->>Player: Return Score
Diagram

6. Scale, bottlenecks & trade-offs

Scalability:

  • The system is designed to handle a single game, so scalability concerns are minimal. However, the architecture supports scaling by handling multiple games concurrently if needed.

Bottlenecks:

  • The main bottleneck could be the event store if it becomes large, but given the small scale of a single game, this is unlikely to be an issue.

Trade-offs:

  • Event Sourcing: Provides a complete audit trail and flexibility to apply different scoring policies. However, it requires replaying events to compute the current state, which can be inefficient if the event list grows large. This can be mitigated by creating periodic snapshots.
  • CAP Theorem: As this is not a distributed system, CAP considerations are minimal. However, if extended to a distributed system, one might prioritize consistency and availability.
  • Policy Extensibility: By decoupling the scoring policy from the game events, the system adheres to the DRY principle, allowing easy addition of new policies without modifying existing code. This promotes maintainability and flexibility.
System designMediumBackend EngineerTake-home Project

11. Extend a library-management system with two notification workflows: A patron receives a notification 24 hours before a borrowed item is due and ano…

The full question

Extend a library-management system with two notification workflows:

  1. A patron receives a notification 24 hours before a borrowed item is due and another when it becomes due.
  2. Patrons may place holds on an item. When N copies become available, notify the first N eligible patrons in FIFO hold order.

Design the backend and the related frontend states. The workflows must be idempotent, tolerate retries and worker crashes, and have meaningful unit and integration coverage.

Model answer

1. Requirements & scale

Functional Requirements:

  • Notify patrons 24 hours before a borrowed item is due.
  • Notify patrons when a borrowed item becomes due.
  • Allow patrons to place holds on items.
  • Notify the first N eligible patrons when N copies of a held item become available.
  • Ensure notifications are idempotent and tolerate retries and worker crashes.

Non-Functional Requirements:

  • High availability and reliability.
  • Scalability to handle a growing number of patrons and items.
  • Consistent and timely notifications.

Estimates:

  • Assume 1 million patrons, each borrowing an average of 5 items.
  • Daily notifications: 2 million (1 million for 24-hour notice, 1 million for due notice).
  • Holds: Assume 10% of items have holds, leading to 500,000 hold notifications daily.
  • Total notifications per day: 2.5 million.
  • QPS (Queries per second): ~30 (2.5 million / 86,400 seconds).
  • Storage: Assume 1KB per notification, leading to 2.5GB/day.

2. High-level architecture

flowchart TD
    subgraph Client
        A[Patron App]
    end

    subgraph Edge/CDN
        B[API Gateway]
    end

    subgraph Load Balancer
        C[Load Balancer]
    end

    subgraph API / Services
        D[Notification Service]
        E[Hold Service]
    end

    subgraph Cache
        F[Redis Cache]
    end

    subgraph Datastores
        G[MySQL DB]
        H["Object Storage (S3)"]
    end

    subgraph Message Queue
        I[Kafka Queue]
    end

    subgraph Workers
        J[Notification Worker]
        K[Hold Worker]
    end

    A --> B
    B --> C
    C --> D
    C --> E
    D --> I
    E --> I
    I --> J
    I --> K
    J --> F
    K --> F
    F --> G
    G --> H
Diagram

3. API design

  • POST /notifications/due: Schedule a notification for when an item is due.
  • POST /notifications/hold: Schedule a notification for when a hold becomes available.
  • GET /holds: Retrieve the list of holds for a patron.
  • POST /holds: Place a hold on an item.
  • DELETE /holds/{holdId}: Cancel a hold.

4. Data model & storage

Datastores:

  • MySQL for relational data such as patrons, items, and holds due to its ACID properties.
  • Redis for caching notification states to ensure idempotency.
  • Kafka for message queuing to handle retries and worker crashes.

Key Tables:

  • Patron: (id, email, notification_preference)
  • Item: (id, title, due_date)
  • Hold: (id, item_id, patron_id, status, created_at)

Partition Key:

  • For the Hold table, use item_id to distribute load evenly across shards.

5. Deep dive

The core of this design is the notification workflow, which involves scheduling and sending notifications reliably.

sequenceDiagram
    participant Patron
    participant API
    participant Queue
    participant Worker
    participant Cache
    participant DB

    Patron->>API: Request to place hold
    API->>DB: Store hold
    API->>Queue: Publish hold event
    Worker->>Queue: Consume hold event
    Worker->>DB: Check available copies
    alt Copies available
        Worker->>Cache: Check notification state
        alt Not notified
            Worker->>Patron: Send notification
            Worker->>Cache: Update notification state
        end
    end
Diagram

6. Scale, bottlenecks & trade-offs

Replication & Sharding:

  • Use MySQL replication for read scalability and high availability.
  • Shard the Hold table by item_id to distribute load.

Caching:

  • Use Redis to cache notification states, reducing database load and ensuring idempotency.

Single Points of Failure:

  • Ensure redundancy in the message queue (Kafka) and workers to avoid single points of failure.

Trade-offs:

  • Consistency vs. Availability: Opt for eventual consistency in notifications to ensure high availability.
  • Push vs. Pull: Use push notifications for timely alerts but ensure retries in case of failures.
  • SQL vs. NoSQL: Use SQL for structured data and relationships, ensuring data integrity.

This design ensures that notifications are sent reliably and efficiently, even in the face of system failures or retries, while maintaining scalability and high availability.

System designMediumBackend EngineerTechnical Screen

12. Design an online chess service that supports real-time, two-player games.

The full question

Design an online chess service that supports real-time, two-player games.

Users should be able to create or join a game, make legal moves, and see their opponent's moves with low latency. Games end by checkmate, timeout (flag fall), resignation, draw (agreement, stalemate, threefold repetition, fifty-move rule, or insufficient material), or abandonment. The server is the authority on rules, move ordering, and the clock — clients are never trusted.

Your design should address, in particular:

  1. State representation & persistence — how you model and store a game's position and history.
  2. Move validation & ordering — how the server rejects illegal/out-of-turn moves and keeps the two players' views totally consistent.
  3. Clock accuracy — how each player's remaining time is tracked authoritatively, including a timeout that occurs while a player is simply thinking.
  4. Failure recovery — what happens when a client disconnects, crashes, or reconnects from a different device, and when a server-side game owner fails.
  5. Scaling — how the service handles millions of concurrent games while preserving per-game correctness.

The system must feel real-time: a move should typically reach the opponent in well under a few hundred milliseconds. State any additional assumptions you make.

Model answer

1. Requirements & scale

Functional Requirements:

  • Users can create or join a real-time, two-player chess game.
  • The server validates moves and enforces chess rules.
  • Games can end by checkmate, timeout, resignation, draw, or abandonment.
  • Players should see opponent moves with low latency.

Non-Functional Requirements:

  • Low latency communication (under 200ms per move).
  • High availability and fault tolerance.
  • Scalable to handle millions of concurrent games.

Estimates:

  • Assume 1 million concurrent games with an average move every 10 seconds: 100,000 moves per second (QPS).
  • Each game state (board position, move history) is approximately 1KB, leading to 1GB of storage for active games.
  • Bandwidth: 100,000 moves/sec * 1KB = 100MB/sec.

2. High-level architecture

flowchart TD
    subgraph Client
        A[Player 1]
        B[Player 2]
    end
    
    subgraph Edge/CDN
        C[WebSocket Server]
    end
    
    subgraph Load Balancer
        D[Load Balancer]
    end
    
    subgraph API / Services
        E[Game Service]
        F[Move Validation Service]
        G[Clock Service]
    end
    
    subgraph Cache
        H[Redis Cache]
    end
    
    subgraph Datastores
        I["SQL DB (Game State)"]
        J["NoSQL DB (Move History)"]
    end
    
    subgraph Message Queue
        K[Message Queue]
    end
    
    subgraph Workers
        L[Game State Worker]
    end
    
    A -- "Move" --> C
    B -- "Move" --> C
    C -- "WebSocket" --> D
    D -- "HTTP" --> E
    E -- "Validate Move" --> F
    F -- "Legal/Illegal" --> E
    E -- "Update Clock" --> G
    G -- "Clock Update" --> E
    E -- "Game State" --> H
    H -- "Cached State" --> E
    E -- "Persist State" --> I
    E -- "Persist Move" --> J
    E -- "Notify Players" --> K
    K -- "Game Update" --> L
    L -- "Broadcast Move" --> C
Diagram

3. API design

  • POST /games: Create a new game.
  • POST /games/{gameId}/join: Join an existing game.
  • POST /games/{gameId}/move: Submit a move.
  • GET /games/{gameId}/state: Retrieve current game state.
  • POST /games/{gameId}/end: End a game (resignation, draw).

4. Data model & storage

Datastores:

  • SQL Database for game state: Ensures ACID properties for critical game state updates.
  • NoSQL Database for move history: Efficiently stores and retrieves sequential move data.

Key Tables:

  • Games: game_id (PK), player1_id, player2_id, current_state, status, start_time, end_time.
  • Moves: move_id (PK), game_id (FK), player_id, move, timestamp.

Partition Key:

  • game_id for both Games and Moves tables to distribute load evenly.

5. Deep dive

The core challenge is ensuring real-time, consistent game state updates and move validation.

sequenceDiagram
    participant A as Player 1
    participant B as WebSocket Server
    participant C as Game Service
    participant D as Move Validation Service
    participant E as Clock Service
    participant F as Player 2

    A->>B: Send Move
    B->>C: Forward Move
    C->>D: Validate Move
    D-->>C: Move Valid/Invalid
    alt Move Valid
        C->>E: Update Clock
        E-->>C: Clock Updated
        C->>F: Notify Move
    else Move Invalid
        C->>A: Notify Invalid Move
    end
Diagram

6. Scale, bottlenecks & trade-offs

Scaling Techniques:

  • Load Balancing: Distributes incoming WebSocket connections and HTTP requests across multiple servers.
  • Sharding: Game data is sharded by game_id to ensure even distribution and parallel processing.
  • Caching: Redis is used to cache game states, reducing database load and improving response times.

Bottlenecks & Trade-offs:

  • Consistency vs. Availability: Prioritize strong consistency for game state to prevent illegal moves and ensure fair play.
  • Failure Recovery: Use a gossip protocol for decentralized failure detection and recovery, ensuring players can reconnect seamlessly.
  • Latency: WebSockets provide low-latency communication, essential for real-time updates.

This design ensures that the chess service can handle high concurrency while maintaining low latency and strong consistency, crucial for a real-time gaming experience.

System designMediumBackend EngineerTechnical Screen

13. Design a production-grade rate limiter for notification sending that enforces limits at three levels of an organizational hierarchy.

The full question

Design a production-grade rate limiter for notification sending that enforces limits at three levels of an organizational hierarchy.

The system exposes a single decision call, should_send_notification(user_id, timestamp) -> bool, that returns whether a notification send should be accepted. Every user belongs to exactly one team, and every team belongs to exactly one company (a user -> team -> company mapping is available).

A send is accepted only if it stays within an exact rolling 10-minute window at all three scopes simultaneously:

  • at most 3 accepted notifications per user in any 10-minute window
  • at most 10 accepted notifications per team in any 10-minute window
  • at most 20 accepted notifications per company in any 10-minute window

If accepting the send would violate any of the three limits, it is rejected and does not count against any scope. The decision must be correct under concurrent requests across many stateless application servers.

You should first reason through the core decision algorithm (and be ready to walk a concrete test case through it), then extend it into a full distributed-systems design covering the API, storage, atomicity, scaling, hierarchy changes, and operational concerns.

Hint — Atomicity across three scopes The user, team, and company checks must s

Model answer

1. Requirements & scale

Functional Requirements:

  • Implement a rate limiter that enforces limits at three hierarchical levels: user, team, and company.
  • The rate limiter should enforce a rolling 10-minute window for each level.
  • Limits are: 3 notifications per user, 10 per team, and 20 per company within any 10-minute window.
  • Provide an API should_send_notification(user_id, timestamp) -> bool to decide if a notification can be sent.
  • Ensure atomicity across the three scopes for each decision.

Non-Functional Requirements:

  • The system must handle high concurrency and be scalable.
  • The decision process should be fast, ideally within milliseconds.
  • The system should be fault-tolerant and highly available.

Scale Estimates:

  • Assume 1 million users, with an average of 10 teams per company and 100 companies.
  • If each user sends 1 notification every 10 minutes, the system handles ~100,000 QPS.
  • Storage needs to track the last 10 minutes of notifications for each user, team, and company, leading to approximately 10 million records in memory at any time.

2. High-level architecture

flowchart TD
    subgraph Client
        A["User Device"]
    end
    subgraph Edge/CDN
        B["API Gateway"]
    end
    subgraph Load Balancer
        C["Load Balancer"]
    end
    subgraph API / Services
        D["Rate Limiter Service"]
    end
    subgraph Cache
        E["Distributed Cache (Redis)"]
    end
    subgraph Datastores
        F["User-Team-Company Mapping DB"]
    end

    A -->|HTTP Request| B
    B -->|Forward Request| C
    C -->|Route Request| D
    D -->|Check Limits| E
    D -->|Fetch Hierarchy| F
    E -->|Update Counts| D
    D -->|Response| C
    C -->|Response| B
    B -->|HTTP Response| A
Diagram

3. API design

  • GET /should_send_notification
  • Purpose: Determine if a notification can be sent for a given user.
  • Parameters: user_id (string), timestamp (ISO 8601 format).
  • Response: Returns true or false.

4. Data model & storage

Datastores:

  • Distributed Cache (Redis): Used for storing notification counts with TTL to automatically expire entries after 10 minutes.
  • User-Team-Company Mapping DB (SQL): Stores the mapping of users to teams and teams to companies for hierarchy resolution.

Key Tables:

  • UserNotificationCount: (user_id, timestamp, count)
  • TeamNotificationCount: (team_id, timestamp, count)
  • CompanyNotificationCount: (company_id, timestamp, count)

Partitioning:

  • Use user_id, team_id, and company_id as keys for sharding in Redis to ensure even distribution and quick access.

5. Deep dive

The core of the rate limiter is the rolling window algorithm. We use a sliding window counter approach with Redis to maintain counts for each scope.

sequenceDiagram
    participant U as User
    participant S as Rate Limiter Service
    participant R as Redis
    participant DB as User-Team-Company Mapping DB

    U->>S: should_send_notification(user_id, timestamp)
    S->>DB: Fetch user-team-company mapping
    DB-->>S: Return mapping
    S->>R: Get user, team, company counts
    R-->>S: Return counts
    alt If all counts within limits
        S->>R: Increment counts with TTL
        R-->>S: Acknowledge
        S-->>U: Return true
    else If any count exceeds limit
        S-->>U: Return false
    end
Diagram

6. Scale, bottlenecks & trade-offs

Scaling:

  • Horizontal Scaling: Use multiple instances of the Rate Limiter Service behind a load balancer to handle increased load.
  • Redis Clustering: Distribute the cache across multiple Redis nodes to handle high throughput and ensure low-latency access.

Bottlenecks:

  • Redis Latency: Ensure Redis is properly clustered and sharded to avoid bottlenecks in accessing and updating counts.
  • Database Access: Minimize calls to the User-Team-Company Mapping DB by caching mappings in memory with a reasonable TTL.

Trade-offs:

  • Consistency vs. Availability (CAP): Opt for eventual consistency in count updates to ensure high availability. Use Redis transactions to maintain atomicity across multiple keys.
  • Push vs. Pull: Use a pull-based approach where the service checks limits on each request rather than pushing updates to clients.

This design ensures that the rate limiter is efficient, scalable, and maintains the necessary constraints across the hierarchical levels, while also being resilient to high concurrency and failure scenarios.

System designMediumBackend EngineerOnsite

14. Design a secure document vault product similar to a cloud repository for sensitive files.

The full question

Design a secure document vault product similar to a cloud repository for sensitive files. The system should allow authenticated users to upload, store, and retrieve documents, while enforcing strong authorization rules.

Expected follow-up topics include:

  • How would you scale the system as file count and traffic grow?
  • How would you design access control and permission checks?
  • How would you handle very large file uploads?
  • If files are uploaded to object storage such as S3 in multiple chunks, how can the system verify that each uploaded chunk actually belongs to the intended file and authorized user, rather than arbitrary data uploaded by a malicious client?

Discuss APIs, metadata storage, authentication and authorization, upload workflows, integrity verification, and operational concerns.

Model answer

1. Requirements & scale

Functional Requirements:

  • Users can upload, store, and retrieve documents.
  • Strong authentication and authorization for access control.
  • Support for large file uploads, potentially in chunks.
  • Ensure data integrity and security.

Non-Functional Requirements:

  • High availability and durability.
  • Low latency for file retrieval.
  • Scalability to handle increasing file count and traffic.
  • Secure storage and transmission of files.

Scale Estimates:

  • Assume 1 million registered users, with 100,000 daily active users.
  • Average file size: 10 MB, with 1 million files uploaded per day.
  • Total storage: 10 TB per day.
  • Bandwidth: 100,000 downloads per day, averaging 10 MB each, resulting in 1 TB/day.

2. High-level architecture

flowchart TD
    subgraph Client
        A[User Device]
    end

    subgraph Edge/CDN
        B[CDN]
    end

    subgraph Load Balancer
        C[Load Balancer]
    end

    subgraph API / Services
        D[Auth Service]
        E[Upload Service]
        F[Download Service]
    end

    subgraph Cache
        G[Cache Layer]
    end

    subgraph Datastores
        H["Metadata DB (SQL)"]
        I["Object Storage (S3)"]
    end

    subgraph Message Queue
        J[Upload Queue]
    end

    subgraph Workers
        K[Chunk Verification Worker]
    end

    A -->|Upload/Download Request| B
    B -->|Forward Request| C
    C -->|Authenticate| D
    D -->|Auth Result| C
    C -->|Upload Request| E
    E -->|Store Metadata| H
    E -->|Enqueue Chunk| J
    J -->|Process Chunk| K
    K -->|Store Chunk| I
    C -->|Download Request| F
    F -->|Fetch Metadata| H
    F -->|Retrieve File| I
    F -->|Cache File| G
    G -->|Serve File| B
Diagram

3. API design

  • POST /upload: Initiate a file upload, returning a session ID for chunked uploads.
  • POST /upload/{session_id}/chunk: Upload a file chunk, with session ID and chunk index.
  • POST /upload/{session_id}/complete: Complete the upload, verifying all chunks.
  • GET /download/{file_id}: Retrieve a file by its ID.
  • POST /auth/login: Authenticate a user and return a token.
  • GET /auth/validate: Validate an authentication token.

4. Data model & storage

Datastores:

  • Metadata DB (SQL): Stores user information, file metadata (file ID, owner, size, upload date, etc.). Chosen for its strong consistency and relational capabilities.
  • Object Storage (S3): Stores file chunks. Chosen for its scalability and durability.

Key Tables:

  • Users: user_id (PK), username, hashed_password.
  • Files: file_id (PK), user_id (FK), filename, size, upload_date.
  • Chunks: chunk_id (PK), file_id (FK), chunk_index, chunk_hash.

Partitioning:

  • Metadata DB sharded by user_id.
  • Object Storage organized with hashed folder paths to avoid hotspots.

5. Deep dive

Chunk Upload and Verification:

To handle large file uploads, files are split into chunks. Each chunk is uploaded separately, and a session ID is used to track the upload process. The system verifies each chunk's integrity and ownership before storing it.

sequenceDiagram
    participant U as User
    participant S as Upload Service
    participant Q as Upload Queue
    participant W as Chunk Verification Worker
    participant O as Object Storage

    U->>S: POST /upload
    S->>U: Session ID
    loop Upload Chunks
        U->>S: POST /upload/{session_id}/chunk
        S->>Q: Enqueue Chunk
        Q->>W: Process Chunk
        W->>O: Store Verified Chunk
    end
    U->>S: POST /upload/{session_id}/complete
    S->>W: Verify All Chunks
    W->>S: Confirmation
    S->>U: Upload Complete
Diagram

6. Scale, bottlenecks & trade-offs

Scalability:

  • Use sharding in metadata DB to distribute load.
  • Object Storage (S3) inherently scales with data size and access patterns.
  • CDN integration for faster global access.

Bottlenecks:

  • Metadata DB could become a bottleneck; mitigate with read replicas.
  • Network bandwidth for large file uploads/downloads; use CDN and regional replication.

Trade-offs:

  • Consistency vs. Availability: Opt for strong consistency in metadata DB for accurate access control, accepting potential availability trade-offs.
  • Push vs. Pull: Use a push model for notifications of upload completion.
  • Sync vs. Async: Asynchronous processing of chunks for better user experience.

Security:

  • Use client-side encryption before upload to protect data.
  • Implement token-based authentication and authorization checks for access control.
System designMediumBackend EngineerOnsite

15. You may be asked one or more backend-heavy system design problems such as: Design a reserved-seat ticketing platform similar to a concert or sports…

The full question

You may be asked one or more backend-heavy system design problems such as:

  1. Design a reserved-seat ticketing platform similar to a concert or sports booking system.
  • Users should browse a venue seat map, select seats, place a temporary hold, and complete checkout.
  • Explain how you would prevent double booking when many users try to buy the same seat at once.
  • Discuss the API design, what the backend should return, and how the frontend should render seat availability and user-facing seat states.
  • Cover failure handling, hold expiration, and the user experience during contention.
  1. Design a transaction reconciliation system.
  • The system periodically receives files from a bank and from a payment processor.
  • Each file contains transactions that must be compared against internal orders/payments.
  • Detect missing, duplicate, or mismatched records, and produce outputs for operators or downstream systems.
  • Discuss file ingestion, matching logic, auditability, and fault tolerance.
  1. Design a remittance-file processing system.
  • A remittance file contains many transactions.
  • For each transaction, validate it, enrich it with additional data, store the result, and notify humans or downstream systems when needed.
  • Discuss orchestration, retries, idempotency, partial failures, and reliability.

Model answer

1. Requirements & scale

Functional Requirements:

  • Users can browse a venue seat map and view available seats.
  • Users can select seats and place a temporary hold.
  • Users can complete the checkout process to purchase tickets.
  • The system must prevent double booking of seats.
  • Holds should expire after a certain time if not purchased.

Non-Functional Requirements:

  • High availability and low latency.
  • Scalability to handle peak loads during popular event releases.
  • Consistency in seat availability data.
  • Secure payment processing.

Estimates:

  • Assume a venue with 20,000 seats and 100 events per year.
  • Peak load: 10,000 users trying to book simultaneously.
  • Average session duration: 5 minutes.
  • QPS (Queries Per Second): If 10,000 users interact within 5 minutes, QPS = 10,000 / 300 = ~33 QPS.
  • Storage: Each event's seat map and holds require minimal storage, estimated at 1 MB per event, totaling 100 MB annually.

2. High-level architecture

flowchart TD
    subgraph Client
        A[User Interface]
    end

    subgraph Edge/CDN
        B[CDN]
    end

    subgraph Load Balancer
        C[Load Balancer]
    end

    subgraph API / Services
        D[Seat Selection Service]
        E[Payment Service]
        F[Hold Management Service]
    end

    subgraph Cache
        G[Redis Cache]
    end

    subgraph Datastores
        H["SQL DB (Seats, Events)"]
        I["NoSQL DB (Holds)"]
    end

    subgraph Workers
        J[Hold Expiry Worker]
    end

    A --> B --> C --> D
    D --> G
    D --> H
    D --> I
    E --> H
    F --> I
    J --> I
Diagram

3. API design

  • GET /events/{eventId}/seats: Retrieve seat map and availability for a specific event.
  • POST /events/{eventId}/seats/hold: Place a temporary hold on selected seats.
  • POST /events/{eventId}/seats/purchase: Complete the purchase of held seats.
  • DELETE /events/{eventId}/seats/hold: Release a hold on seats (triggered by expiry or user action).

4. Data model & storage

  • SQL Database: Used for storing events and seat information due to the need for complex queries and transactions.
  • Tables:
  • Events: event_id, name, date, venue_id
  • Seats: seat_id, event_id, status (available, held, sold)
  • NoSQL Database: Used for managing seat holds to handle high write throughput and fast access.
  • Collections:
  • Holds: hold_id, user_id, event_id, seat_ids, expiry_time
  • Redis Cache: Caches seat availability to reduce database load and improve response time.

5. Deep dive

To prevent double booking, the system uses a distributed lock mechanism when placing a hold on seats. When a user selects seats, the Hold Management Service checks the Redis Cache for availability. If available, it places a lock and updates the NoSQL DB with a hold entry. The lock ensures that no other transaction can place a hold on the same seats simultaneously.

sequenceDiagram
    participant U as User
    participant UI as User Interface
    participant S as Seat Selection Service
    participant C as Redis Cache
    participant N as NoSQL DB

    U->>UI: Select seats
    UI->>S: Request hold on seats
    S->>C: Check seat availability
    alt Seats available
        C-->>S: Seats available
        S->>C: Place lock on seats
        S->>N: Create hold entry
        S-->>UI: Hold confirmed
    else Seats not available
        C-->>S: Seats not available
        S-->>UI: Hold failed
    end
Diagram

6. Scale, bottlenecks & trade-offs

Scalability:

  • Use horizontal scaling for the Seat Selection Service and Hold Management Service.
  • Redis is used for caching to reduce database load and improve response times.

Bottlenecks:

  • The SQL database can become a bottleneck during peak loads. Consider sharding by event_id to distribute load.
  • Redis can be a single point of failure; use Redis Cluster for high availability.

Trade-offs:

  • Consistency vs. Availability: Prioritize consistency to prevent double bookings, which may slightly impact availability during high contention.
  • CAP Theorem: The system is designed to be CP (Consistency and Partition Tolerance) to ensure no double bookings occur.

Failure Handling:

  • Implement retries for transient failures in network communication.
  • Use a Hold Expiry Worker to periodically clean up expired holds, ensuring seats are released back to availability.

This design ensures a robust ticketing platform capable of handling high concurrency while maintaining data consistency and providing a seamless user experience.

System designMediumBackend EngineerTechnical Screen

16. Is the load balancer a single process exercise or a distributed production service?

The full question

  • Is the load balancer a single process exercise or a distributed production service?
  • What telemetry, configuration history, and backend test access are available?
  • Should policy state survive a load-balancer restart?

Part 1: All Backends Appear Unhealthy

Immediately after a configuration change, the load balancer marks every backend unhealthy and returns HTTP 500 even though direct requests to the service succeed. Walk through how you would isolate the fault. Include the health-check hostname and port configuration in your investigation.

Candidate hint: Compare what the health checker actually sends with a known-good direct request before changing retry or routing logic.

What This Part Should Cover
  • A disciplined narrowing process using logs, metrics, and a direct reproduction
  • Verification of target resolution, port, protocol, host header, and health path
  • Safe configuration correction and regression protection

Part 2: One Backend Causes High Latency

After health checks recover, aggregate latency remains high. One backend occasionally takes much longer than the others. Explain how you would instrument the request path, attribute latency to a backend, and distinguish queueing, network, and service time.

Candidate hint: Decide which timestamps and labels are needed before deciding that the backend code is responsible.

What This Part Should Cover
  • Per-attempt timing with backend identity and outcome
  • Tail-latency analysis rather than averages alone
  • A debugging sequence that avoids adding unbounded-cardinality telemetry

Part 3: Add a Circuit Breaker

Design a circuit breaker that stops sending normal traffic to a repeatedly failing backend while still allowing it to demonstrate recovery. Define state

Model answer

1. Requirements & scale

Functional Requirements
  • Efficiently distribute incoming traffic across backend servers.
  • Perform health checks on backend servers to ensure they are operational.
  • Provide telemetry and configuration history for troubleshooting.
  • Implement circuit breakers to manage failing backends.
Non-Functional Requirements
  • High availability and reliability.
  • Low latency in request routing.
  • Scalability to handle increasing traffic.
  • Fault tolerance to handle server failures gracefully.
Scale Estimates
  • Assume 10,000 requests per second (QPS) at peak.
  • Each request is approximately 1 KB, leading to a bandwidth requirement of 10 MB/s.
  • Health check frequency of every 5 seconds for each backend server.

2. High-level architecture

flowchart TD
    subgraph Client
        A[User Devices]
    end

    subgraph Edge/CDN
        B[CDN]
    end

    subgraph Load Balancer
        C[Load Balancer]
    end

    subgraph API / Services
        D[Backend Servers]
    end

    subgraph Cache
        E[In-Memory Cache]
    end

    subgraph Datastores
        F[SQL Database]
    end

    subgraph Message Queue
        G[Queue]
    end

    subgraph Workers
        H[Background Workers]
    end

    A -->|HTTP Request| B
    B -->|Forwarded Request| C
    C -->|Load Balanced Request| D
    D -->|Data Fetch| E
    E -->|Cached Data| D
    D -->|Database Query| F
    F -->|Query Result| D
    D -->|Response| C
    C -->|HTTP Response| B
    B -->|Cached Response| A
Diagram

3. API design

  • GET /health-check: Check the health status of backend servers.
  • POST /config: Update load balancer configuration.
  • GET /metrics: Retrieve telemetry data for monitoring.
  • POST /circuit-breaker: Update circuit breaker settings.

4. Data model & storage

  • SQL Database: Used for storing configuration history and telemetry data.
  • Tables:
  • Configurations: Stores load balancer configurations with fields like id, timestamp, config_data.
  • Telemetry: Stores telemetry data with fields like id, timestamp, metric_name, value.
  • In-Memory Cache: Used for quick access to frequently requested data to reduce latency.

5. Deep dive

Part 1: All Backends Appear Unhealthy

To isolate the fault when all backends appear unhealthy after a configuration change:

  1. Verify Configuration: Check the configuration history for recent changes. Ensure the health-check hostname, port, and protocol are correct.
  2. Compare Health Check Requests: Use logs to compare what the health checker sends versus a known-good direct request. Verify the host header and health path.
  3. Safe Configuration Correction: If discrepancies are found, correct the configuration and test in a staging environment before deploying.
  4. Regression Protection: Implement automated tests to validate configurations before applying them in production.
Part 2: One Backend Causes High Latency

To diagnose high latency caused by a single backend:

  1. Instrument the Request Path: Add logging to capture timestamps at key points: request received, request sent to backend, response received, and response sent.
  2. Attribute Latency: Identify the backend responsible by correlating request IDs with backend server logs.
  3. Analyze Tail Latency: Focus on 95th and 99th percentile latencies to identify outliers.
  4. Distinguish Latency Sources: Use timestamps to separate queueing, network, and service time.
sequenceDiagram
    participant Client
    participant LoadBalancer
    participant Backend
    Client->>LoadBalancer: HTTP Request
    LoadBalancer->>Backend: Forward Request
    Backend-->>LoadBalancer: Response
    LoadBalancer-->>Client: HTTP Response
Diagram

6. Scale, bottlenecks & trade-offs

  • Replication and Sharding: Use replication for high availability and sharding for scalability in the SQL database.
  • Caching: Implement caching at the CDN and in-memory levels to reduce latency and load on backend servers.
  • Single Points of Failure: Ensure the load balancer is distributed and redundant to avoid a single point of failure.
  • Trade-offs:
  • Consistency vs Availability: Prioritize availability in the load balancer to ensure requests are always routed, even if some data may be stale.
  • Push vs Pull Health Checks: Use push-based health checks for real-time updates but ensure they do not overwhelm the system.
  • Circuit Breaker Design: Implement a circuit breaker that temporarily stops routing to a failing backend but allows periodic health checks to test recovery.
TechnicalMediumBackend EngineerOnsite

17. You may be asked several short technical-fundamentals questions such as: In a Kafka-based batch consumer, how would you prevent a batch from being…

The full question

You may be asked several short technical-fundamentals questions such as:

  • In a Kafka-based batch consumer, how would you prevent a batch from being processed twice?
  • What happens end-to-end when a user opens a browser, types a query into Google, and presses Search?
  • If a large web service's API gateway goes down, how should the system degrade gracefully or recover?
  • What kind of database or storage engine would you choose for search results or searchable documents, and why?
  • What is the difference between a compiler and an interpreter?

Model answer

Kafka-based Batch Consumer: Preventing Double Processing

  1. Idempotency: Ensure that the processing of each message is idempotent. This means that even if a message is processed more than once, the outcome remains the same. Implement this by using unique identifiers for each message and checking if the message has already been processed before proceeding.
  2. Offset Management: Use Kafka's offset management to track which messages have been processed. Commit offsets only after a batch is successfully processed. This ensures that if a failure occurs before the commit, the batch will be reprocessed.
  3. Transactional Processing: Leverage Kafka's support for transactions. This allows you to produce and consume messages within a transaction, ensuring that either all operations succeed or none do. This prevents partial processing of batches.
  4. Error Handling: Implement robust error handling and retries. If a batch fails to process, retry it a limited number of times before moving it to a dead-letter queue for manual inspection.

End-to-End Process When a User Searches on Google

  1. DNS Resolution: When a user types a query and presses search, the browser first resolves the domain name (e.g., google.com) to an IP address using DNS.
  2. HTTP Request: The browser sends an HTTP request to Google's servers, including the search query.
  3. Load Balancing: The request is routed through Google's load balancers to distribute the load evenly across their servers.
  4. Query Processing: Google's backend processes the query, retrieving relevant results from their index and ranking them based on algorithms.
  5. Response: The search results are sent back to the user's browser, which renders the results page for the user to view.

API Gateway Failure and Graceful Degradation

  1. Redundancy: Deploy multiple instances of the API Gateway across different regions to prevent a single point of failure.
  2. Failover Mechanism: Implement automatic failover to redirect traffic to a standby gateway in case the primary one fails.
  3. Graceful Degradation: If the gateway is down, serve cached responses for certain requests or provide a fallback service that offers limited functionality.
  4. Monitoring and Alerts: Use monitoring tools to detect failures quickly and alert the operations team for immediate action.

Database Choice for Searchable Documents

  1. Search Engine: Use a search engine like Elasticsearch, which is optimized for full-text search and provides powerful querying capabilities.
  2. NoSQL Datastore: Consider a NoSQL database like MongoDB if the data is semi-structured and requires flexible schema design.
  3. Rationale: These systems are designed to handle large volumes of data and provide fast search capabilities, making them suitable for storing and querying searchable documents.

Compiler vs. Interpreter

  1. Compiler: Translates the entire source code into machine code before execution. This results in faster execution time but longer initial compilation time.
  2. Interpreter: Translates and executes code line-by-line at runtime. This allows for immediate execution but can be slower due to the overhead of interpreting each line.
  3. Use Cases: Compilers are suitable for performance-critical applications, while interpreters are useful for scripting and rapid development environments.

Practice these out loud, don't memorise them

Reading an answer is not the same as being able to give one under pressure. ChannelPulse plays the interviewer, asks the follow-ups, and scores each answer with feedback and a model answer so you can hear the gap between what you said and what lands.

Get ChannelPulse Browse all questions