Revolut interview questions & answers

20 real Revolut interview questions with full model answers — System design, Coding, Technical, Product & growth. Drawn from the same verified bank ChannelPulse drills from (52 Revolut questions in total).

BehavioralEasyRevolut

1. Tell me about a time when you had to work with a team to solve a technical problem under a tight deadline.

Model answer

Situation

In my role as a software engineer at a fintech company, our team encountered a critical situation when a key feature in our mobile app malfunctioned just days before a major release. This feature was essential for a new financial product launch that had been heavily marketed, and any delay could have resulted in significant reputational damage and financial loss.

Task

I was responsible for leading the technical team to diagnose and resolve the issue under a tight deadline. The challenge was to ensure a seamless fix without compromising the quality of the application, all within a 48-hour window.

Action

  • I immediately organized a team meeting to assess the situation and prioritize tasks. We broke down the problem into smaller components, assigning each to team members based on their expertise.
  • To maintain focus and efficiency, I set up a war room environment where the team could collaborate closely and communicate in real-time, minimizing delays in decision-making.
  • I personally took on the task of reviewing the codebase to identify potential sources of the bug. By leveraging automated testing tools, I was able to quickly pinpoint the problematic areas.
  • Throughout the process, I maintained open lines of communication with stakeholders, providing regular updates on our progress and any potential risks. This transparency helped manage expectations and allowed us to focus on the most critical aspects of the fix.
  • To ensure we met the deadline, I coordinated with the QA team to run parallel testing on the fixes, allowing us to catch any new issues early and iterate quickly.

Result

We successfully resolved the issue well within the 48-hour deadline. The fix was deployed without any further complications, and the product launch proceeded as planned. Our swift and coordinated response not only preserved our company's reputation but also strengthened our client relationships. This experience reinforced the importance of teamwork, clear communication, and a structured approach to problem-solving under pressure.

BehavioralMediumRevolut

2. Describe a situation where you had to make a trade-off between system performance and data consistency.

The full question

Describe a situation where you had to make a trade-off between system performance and data consistency. How did you approach the decision?

Model answer

Situation

In my role as a software developer at a fintech company, we were tasked with enhancing the performance of our transaction processing system. This system was crucial for handling thousands of transactions per second, and any downtime or inconsistency could lead to significant financial losses and customer dissatisfaction. However, we were also experiencing issues with data consistency due to the distributed nature of our database, which occasionally led to discrepancies in transaction records.

Task

My primary responsibility was to find a solution that would balance the need for high system performance with the requirement for strong data consistency. The key constraint was that we needed to implement this solution without causing significant downtime or impacting the user experience.

Action

  • I began by conducting a thorough analysis of the current system architecture to identify the root causes of the data inconsistency issues. This involved reviewing logs, monitoring transaction flows, and consulting with database administrators.
  • After identifying that the inconsistencies were primarily due to eventual consistency in our NoSQL database, I proposed implementing a stronger consistency model for critical transaction data. This would involve using a hybrid approach where critical data would be stored in a relational database with ACID properties, while less critical data could remain in the NoSQL database for performance reasons.
  • To validate this approach, I developed a proof of concept that simulated the transaction processing under the new model. This allowed us to measure the impact on both performance and consistency.
  • I presented my findings and the proof of concept results to the team, highlighting the trade-offs between performance and consistency. I emphasized that while there might be a slight reduction in throughput, the increase in data reliability was crucial for maintaining customer trust and compliance with financial regulations.
  • After gaining consensus, I led the implementation of the new architecture, ensuring that the transition was smooth and that all team members were aligned on the changes.

Result

The implementation of the hybrid database model resulted in a significant improvement in data consistency without a noticeable impact on system performance. The synchronization errors were eliminated, and the system's reliability increased, leading to higher customer satisfaction and fewer support tickets related to transaction discrepancies. This experience taught me the importance of balancing technical trade-offs and reinforced the value of a data-driven approach to decision-making.

BehavioralMediumRevolut

3. Explain the importance of DevOps culture at Revolut.

Model answer

Situation At Revolut, where I worked as a software engineer, the company was undergoing a significant shift towards adopting a DevOps culture. This transition was crucial as it aimed to enhance collaboration between development and operations teams, improve deployment frequency, and reduce lead time for changes. The stakes were high because efficient and reliable software delivery is critical to maintaining Revolut's competitive edge in the fast-paced fintech industry.

Task My specific goal was to facilitate this cultural shift within my team by promoting DevOps practices and ensuring a smooth transition. The key challenge was overcoming resistance from team members who were accustomed to traditional development and operations silos.

Action

  • I began by organizing a series of workshops and training sessions to educate the team on the benefits of DevOps, such as faster time-to-market and improved system reliability. I highlighted how these practices could directly impact our product's success and customer satisfaction.
  • To address the resistance, I shared success stories from other teams within the company that had already adopted DevOps practices. This helped in illustrating tangible benefits and reducing skepticism.
  • I introduced tools and processes that facilitated collaboration, such as continuous integration/continuous deployment (CI/CD) pipelines and infrastructure as code (IaC). I ensured that these tools were user-friendly and provided hands-on support to team members during the initial setup.
  • I also fostered a culture of open communication and feedback, encouraging team members to voice concerns and suggest improvements. This approach helped in building trust and making everyone feel invested in the transition.
  • To maintain motivation, I set up a system of small, achievable milestones with visible rewards. Celebrating these small wins helped in maintaining momentum and enthusiasm for the change.

Result As a result of these efforts, our team successfully transitioned to a DevOps culture, leading to a 30% increase in deployment frequency and a 20% reduction in lead time for changes. This improvement not only enhanced our team's efficiency but also contributed to a more reliable and scalable product. Reflecting on this experience, I learned the importance of clear communication, education, and incremental change in driving cultural shifts within an organization.

BehavioralMediumRevolut

4. Can you give an example of a time when you implemented a new technology or process in your team?

The full question

Can you give an example of a time when you implemented a new technology or process in your team? What challenges did you face?

Model answer

Situation At my previous company, I was a software engineer on a team responsible for maintaining a legacy payment processing system. The system was becoming increasingly difficult to manage and scale, which was a significant concern as our transaction volume was projected to double within the next year. The outdated technology stack was also causing frequent downtimes, impacting our service reliability and customer satisfaction.

Task I was tasked with leading the initiative to modernize our payment processing system. The goal was to implement a more scalable and reliable solution without disrupting ongoing operations. The key challenge was to ensure a seamless transition with minimal downtime and risk.

Action

  • I began by conducting a thorough analysis of our current system to identify bottlenecks and areas for improvement. This involved reviewing system logs, performance metrics, and user feedback.
  • After evaluating several options, I proposed migrating to a microservices architecture using Kubernetes for orchestration. This would allow us to scale components independently and improve fault tolerance.
  • I organized a series of workshops to educate the team on microservices and Kubernetes, ensuring everyone understood the benefits and challenges of the new architecture.
  • To mitigate risks, I implemented a phased rollout strategy. We started by migrating non-critical services to the new architecture, allowing us to test and refine the process before tackling core components.
  • I worked closely with the DevOps team to automate the deployment pipeline, which reduced manual errors and improved deployment speed.
  • Throughout the project, I maintained open communication with stakeholders, providing regular updates and addressing concerns promptly.

Result The migration was completed successfully within six months, resulting in a 40% reduction in system downtime and a 30% improvement in transaction processing speed. The new architecture provided the scalability needed to handle increased transaction volumes, and customer satisfaction scores improved significantly. This experience taught me the importance of thorough planning and stakeholder communication when implementing new technologies.

CodingEasyRevolut

5. Given an array of integers, return indices of the two numbers such that they add up to a specific target.

Model answer

function twoSum(nums, target) {
    // Create a map to store the difference and its index
    const numMap = new Map();

    // Iterate through the array
    for (let i = 0; i < nums.length; i++) {
        // Calculate the difference needed to reach the target
        const complement = target - nums[i];

        // Check if the complement exists in the map
        if (numMap.has(complement)) {
            // If found, return the indices of the two numbers
            return [numMap.get(complement), i];
        }

        // Otherwise, add the current number and its index to the map
        numMap.set(nums[i], i);
    }

    // Return an empty array if no solution is found
    return [];
}

// Example usage:
// const result = twoSum([2, 7, 11, 15], 9);
// console.log(result); // Output: [0, 1]
  • Approach:
  • Use a hash map to store each number's complement (target - current number) and its index.
  • Iterate through the array, checking if the current number's complement exists in the map.
  • If it exists, return the indices of the current number and its complement.
  • If not, store the current number and its index in the map.
  • Complexity:
  • Time: O(n), where n is the number of elements in the array. We traverse the list containing n elements only once.
  • Space: O(n), as we store up to n elements in the hash map.
CodingEasyRevolut

6. Given a list of transactions, write a function to identify the total amount of money spent by each user.

The full question

Given a list of transactions, write a function to identify the total amount of money spent by each user. Each transaction is represented as a tuple (user_id, amount).

Model answer

function calculateTotalSpent(transactions) {
  const userSpending = {};

  // Iterate over each transaction
  for (const [userId, amount] of transactions) {
    // If the userId is not in the userSpending object, initialize it
    if (!userSpending[userId]) {
      userSpending[userId] = 0;
    }
    // Add the transaction amount to the user's total spending
    userSpending[userId] += amount;
  }

  return userSpending;
}

// Example usage:
const transactions = [
  [1, 100],
  [2, 150],
  [1, 200],
  [3, 250],
  [2, 100],
];

console.log(calculateTotalSpent(transactions));
// Output: { '1': 300, '2': 250, '3': 250 }
  • Approach:
  • Initialize an empty object userSpending to store the total amount spent by each user.
  • Iterate over each transaction tuple (user_id, amount).
  • For each transaction, check if the user_id exists in userSpending. If not, initialize it to 0.
  • Add the amount to the existing total for that user_id.
  • Return the userSpending object containing the total spent by each user.
  • Complexity:
  • Time Complexity: O(n), where n is the number of transactions. Each transaction is processed once.
  • Space Complexity: O(u), where u is the number of unique users. This accounts for storing each user's total spending.
CodingEasyRevolut

7. Check if a string contains all unique characters.

Model answer

function hasAllUniqueCharacters(s) {
    // Use a Set to track characters we've seen
    const charSet = new Set();

    // Iterate over each character in the string
    for (let i = 0; i < s.length; i++) {
        const char = s[i];
        // If the character is already in the set, it's a duplicate
        if (charSet.has(char)) {
            return false;
        }
        // Add the character to the set
        charSet.add(char);
    }
    // If no duplicates were found, the string has all unique characters
    return true;
}

// Example usage:
console.log(hasAllUniqueCharacters("abcdef")); // true
console.log(hasAllUniqueCharacters("aabbcc")); // false
  • Approach: Use a Set to track characters. Iterate through the string, checking if each character is already in the set. If it is, return false as the string contains duplicates. If not, add the character to the set. If the loop completes without finding duplicates, return true.
  • Complexity:
  • Time: O(n), where n is the length of the string, since each character is processed once.
  • Space: O(min(n, m)), where m is the size of the character set (e.g., 26 for lowercase English letters). The space is used by the set to store unique characters.
CodingMediumRevolut

8. Implement a function that simulates a bank account with methods to deposit, withdraw, and check the balance.

The full question

Implement a function that simulates a bank account with methods to deposit, withdraw, and check the balance. Ensure that concurrent access to the account is handled correctly.

Model answer

class BankAccount {
  constructor() {
    this.balance = 0;
    this.lock = new Promise(resolve => resolve()); // A simple lock to handle concurrency
  }

  // Deposit method
  deposit(amount) {
    if (amount <= 0) {
      throw new Error("Deposit amount must be positive");
    }
    return this._withLock(() => {
      this.balance += amount;
      return this.balance;
    });
  }

  // Withdraw method
  withdraw(amount) {
    if (amount <= 0) {
      throw new Error("Withdrawal amount must be positive");
    }
    return this._withLock(() => {
      if (amount > this.balance) {
        throw new Error("Insufficient funds");
      }
      this.balance -= amount;
      return this.balance;
    });
  }

  // Check balance method
  checkBalance() {
    return this._withLock(() => {
      return this.balance;
    });
  }

  // Private method to handle locking
  _withLock(fn) {
    const unlock = this.lock;
    this.lock = new Promise(resolve => {
      unlock.then(() => {
        fn();
        resolve();
      });
    });
    return this.lock;
  }
}

// Example usage:
const account = new BankAccount();
account.deposit(100).then(() => console.log(account.checkBalance())); // 100
account.withdraw(50).then(() => console.log(account.checkBalance())); // 50
  • Approach:
  • Use a simple locking mechanism with Promises to ensure that operations on the bank account are atomic and thread-safe.
  • The deposit, withdraw, and checkBalance methods are wrapped in a _withLock function to ensure mutual exclusion.
  • The _withLock function chains operations to maintain order and prevent race conditions.
  • Complexity:
  • Time Complexity: O(1) for each operation, as they involve simple arithmetic and promise chaining.
  • Space Complexity: O(1), since no additional data structures are used beyond the lock mechanism.
Product & growthEasyRevolutProduct Manager

9. What is your favorite financial app, and how would you improve it?

Model answer

Favorite financial app: My favorite financial app is Mint because of its comprehensive financial tracking and budgeting capabilities.

Improvement suggestion: One area of improvement is providing more personalized financial advice.

Clarify & scope: The goal is to offer tailored financial advice to help users make better financial decisions.

User segments & pain points: Focus on users who feel overwhelmed by financial decisions and need guidance.

Goals & success metrics: The North Star metric is increased user retention. Guardrails include maintaining user trust and data privacy.

Solutions:

  1. AI-Driven Recommendations: Use AI to analyze spending patterns and suggest ways to save or invest.
  2. Financial Health Score: Introduce a financial health score with tips to improve it.
  3. Personalized Alerts: Send alerts for unusual spending or savings opportunities.

Recommendation: Implement AI-driven recommendations as it directly helps users with actionable insights.

Prioritization & trade-offs: Prioritize AI-driven recommendations due to its potential high impact on user satisfaction and retention.

MVP, measurement & rollout: Launch an MVP with basic AI recommendations. Measure success through user engagement and feedback, then iterate to enhance accuracy and personalization.

Product & growthMediumRevolutProduct Manager

10. How would you improve Revolut's budgeting feature to enhance user engagement?

Model answer

Clarify & scope: The goal is to enhance user engagement with Revolut's budgeting feature. Assume the current feature allows users to set budgets and track spending, but engagement is low.

User segments & pain points: Focus on young professionals who want to manage their finances better but find current tools too generic or cumbersome.

Goals & success metrics: The North Star metric is increased daily active users (DAU) of the budgeting feature. Guardrails include maintaining user satisfaction and ensuring data privacy.

Solutions:

  1. Personalized Insights: Offer personalized spending insights and recommendations.
  2. Gamification: Introduce gamified elements like badges or challenges for meeting budgeting goals.
  3. Collaborative Budgeting: Allow users to share budgets with friends or family for joint financial goals.

Recommendation: Implement personalized insights as it directly addresses the user's need for tailored financial advice.

graph TD;
  A[User sets budget] --> B[Personalized insights generated];
  B --> C[User receives notifications];
  C --> D[User adjusts spending]
Diagram

Prioritization & trade-offs: Using RICE, personalized insights score highest due to high impact and moderate effort.

MVP, measurement & rollout: Launch an MVP with basic personalized insights. Measure success through DAU and user feedback, then iterate based on user data.

Product & growthMediumRevolutProduct Manager

11. How would you design a feature for Revolut that encourages responsible spending among users?

Model answer

Clarify & scope: The goal is to design a feature that encourages responsible spending. Assume users are currently overspending or not tracking expenses effectively.

User segments & pain points: Target users who struggle with impulse spending and lack of financial discipline.

Goals & success metrics: The North Star metric is reduced monthly overspending. Guardrails include user satisfaction and financial well-being.

Solutions:

  1. Spending Limits: Allow users to set daily or weekly spending limits with alerts.
  2. Spending Insights: Provide detailed insights on spending patterns and categories.
  3. Delayed Purchase Option: Introduce a "cooling-off" period for discretionary purchases.

Recommendation: Implement spending limits as they provide direct and actionable control over spending.

graph TD;
  A[User sets spending limit] --> B[Receives alerts];
  B --> C[Adjusts spending behavior]
Diagram

Prioritization & trade-offs: Spending limits score high on RICE due to high impact and low effort.

MVP, measurement & rollout: Launch an MVP with basic spending limits. Measure success through user feedback and reduction in overspending, then iterate to add more features.

Product & growthMediumRevolutProduct Manager

12. What approach would you take to increase user retention for Revolut's premium subscription service?

Model answer

Clarify & scope: The goal is to increase user retention for Revolut's premium subscription service. Assume users are churning due to perceived lack of value.

User segments & pain points: Focus on users who subscribed for specific features but are not fully utilizing them.

Goals & success metrics: The North Star metric is the reduction in churn rate. Guardrails include maintaining user satisfaction and service quality.

Strategy:

  1. Feature Education: Enhance user onboarding and education about premium features.
  2. Exclusive Benefits: Introduce new exclusive benefits that add tangible value.
  3. Feedback Loop: Implement a robust feedback mechanism to continuously improve the service.

Recommendation: Prioritize feature education as it directly addresses the awareness gap and encourages usage.

Prioritization & trade-offs: Feature education scores high on RICE due to its potential impact and moderate effort.

MVP, measurement & rollout: Start with an improved onboarding experience. Measure success through churn rate reduction and user feedback, then iterate to enhance educational content.

System designEasyRevolut

13. Design a simple transaction processing system for a banking application.

Model answer

1. Requirements & scale

Functional Requirements:

  • Process financial transactions between accounts.
  • Ensure transactions are atomic, consistent, isolated, and durable (ACID properties).
  • Support querying of transaction history.
  • Handle concurrent transaction requests.

Non-Functional Requirements:

  • High availability and fault tolerance.
  • Low latency transaction processing.
  • Secure transactions with encryption.
  • Scalability to handle increasing transaction volumes.

Estimates:

  • Assume 10 million users with an average of 10 transactions per day.
  • Transactions per second (QPS): \( \frac{10 \text{ million users} \times 10 \text{ transactions}}{24 \times 60 \times 60} \approx 1,157 \) QPS.
  • Storage: Assume each transaction record is 1 KB. For 100 million transactions, storage required is approximately 100 GB.
  • Bandwidth: For 1,157 QPS with 1 KB per transaction, bandwidth is approximately 1.1 MB/s.

2. High-level architecture

flowchart TD
    subgraph Client
        A[Mobile App]
        B[Web App]
    end

    subgraph Edge/CDN
        C[CDN]
    end

    subgraph Load Balancer
        D[Load Balancer]
    end

    subgraph API / Services
        E[Transaction Service]
        F[Authentication Service]
    end

    subgraph Cache
        G[Redis Cache]
    end

    subgraph Datastores
        H["SQL DB (PostgreSQL)"]
        I["NoSQL DB (Cassandra)"]
    end

    subgraph Message Queue
        J[Kafka]
    end

    subgraph Workers
        K[Transaction Processor]
    end

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

3. API design

  • POST /transactions: Initiate a new transaction between accounts.
  • GET /transactions/{transactionId}: Retrieve details of a specific transaction.
  • GET /accounts/{accountId}/transactions: Retrieve transaction history for an account.

4. Data model & storage

Datastores:

  • SQL DB (PostgreSQL): For storing transaction records and ensuring ACID compliance.
  • NoSQL DB (Cassandra): For fast retrieval of transaction history and scalability.

Key Tables:

  • Transactions Table (SQL):
  • transaction_id (Primary Key)
  • from_account_id
  • to_account_id
  • amount
  • status
  • timestamp
  • Accounts Table (SQL):
  • account_id (Primary Key)
  • balance
  • user_id

Partition Key:

  • Use transaction_id for partitioning in the SQL database to distribute load evenly.

5. Deep dive

The core of this system is ensuring atomicity and consistency of transactions. We can use the Two-Phase Commit (2PC) protocol to coordinate transactions across distributed databases, but given its limitations, the Saga pattern is a more resilient choice for handling distributed transactions.

sequenceDiagram
    participant A as Client
    participant B as Transaction Service
    participant C as SQL DB
    participant D as NoSQL DB
    participant E as Kafka
    participant F as Transaction Processor

    A->>B: POST /transactions
    B->>C: Begin Transaction
    B->>C: Update Accounts
    C-->>B: Acknowledge
    B->>E: Publish to Kafka
    E-->>F: Consume Message
    F->>D: Update Transaction History
    D-->>F: Acknowledge
    F->>C: Commit Transaction
    C-->>B: Commit Acknowledged
    B->>A: Transaction Success
Diagram

6. Scale, bottlenecks & trade-offs

Replication and Sharding:

  • SQL DB: Use master-slave replication for high availability. Shard based on transaction_id to distribute load.
  • NoSQL DB: Use consistent hashing for partitioning to ensure even data distribution.

Caching:

  • Use Redis to cache frequently accessed transaction data to reduce load on databases and improve latency.

Single Points of Failure:

  • Ensure redundancy in the load balancer and use multiple instances of services to avoid single points of failure.

Trade-offs:

  • Consistency vs Availability: Prioritize consistency for financial transactions, accepting potential trade-offs in availability.
  • Push vs Pull: Use push-based updates (via Kafka) to ensure real-time transaction processing.
  • SQL vs NoSQL: Use SQL for transactions requiring ACID properties and NoSQL for scalable read-heavy operations.

This design provides a robust framework for processing transactions in a banking application, ensuring both consistency and scalability while addressing potential bottlenecks and failure points.

System designMediumRevolut

14. Design a system to handle currency exchange rates in a multi-currency banking application.

Model answer

1. Requirements & scale

Functional Requirements:

  • Fetch real-time exchange rates for multiple currencies.
  • Support conversion between any two currencies.
  • Update exchange rates at regular intervals.
  • Provide historical exchange rate data for analysis.

Non-Functional Requirements:

  • High availability and low latency for fetching and updating rates.
  • Scalability to handle increasing numbers of currencies and users.
  • Consistency in exchange rates across the system.

Estimates:

  • Assume 1000 requests per second (QPS) for fetching exchange rates.
  • Exchange rate updates every minute for 100 currency pairs.
  • Storage for historical data: If each rate update is 1 KB, then 100 pairs 60 updates/hour 24 hours * 30 days = ~432 MB/month.
  • Bandwidth: For 1000 QPS, assuming each response is 1 KB, ~1 MB/s.

2. High-level architecture

flowchart TD
    subgraph Client
        A[Mobile App]
        B[Web App]
    end

    subgraph Edge/CDN
        C[CDN]
    end

    subgraph Load Balancer
        D[Load Balancer]
    end

    subgraph API / Services
        E[Exchange Rate API]
    end

    subgraph Cache
        F[Redis Cache]
    end

    subgraph Datastores
        G[SQL Database]
        H["NoSQL DB (Historical)"]
    end

    subgraph Workers
        I[Rate Updater]
    end

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

3. API design

  • GET /exchange-rate?from={currency1}&to={currency2}: Fetch the current exchange rate between two currencies.
  • GET /historical-rates?from={currency1}&to={currency2}&date={date}: Fetch historical exchange rates for a specific date.
  • POST /update-rates: Trigger an update of exchange rates (used internally).

4. Data model & storage

Datastores:

  • SQL Database: For storing current exchange rates. Chosen for ACID properties to ensure consistency.
  • NoSQL Database: For storing historical exchange rates. Chosen for scalability and flexibility in handling large volumes of time-series data.

Key Tables:

  • ExchangeRates:
  • currency_pair (Primary Key)
  • rate
  • last_updated
  • HistoricalRates:
  • currency_pair (Partition Key)
  • date (Sort Key)
  • rate

5. Deep dive

The core challenge is ensuring that exchange rates are updated consistently and efficiently. The system uses a rate updater worker that fetches new rates from external providers and updates the SQL database and cache.

sequenceDiagram
    participant I as Rate Updater
    participant E as Exchange Rate API
    participant F as Redis Cache
    participant G as SQL Database
    participant H as NoSQL DB

    I->>E: Fetch new rates
    E->>I: Return new rates
    I->>G: Update current rates
    I->>F: Update cache
    I->>H: Store historical rates
Diagram

6. Scale, bottlenecks & trade-offs

Scalability:

  • Caching: Redis is used to cache exchange rates to reduce database load and improve response times.
  • Sharding: Historical data is partitioned by currency pair and date to distribute load across multiple nodes.

Bottlenecks:

  • Single Point of Failure: The rate updater could become a bottleneck if it fails. Implement redundancy and failover mechanisms.
  • Consistency vs Availability: Prioritize consistency for exchange rates to ensure users see the same rates across the system.

Trade-offs:

  • CAP Theorem: The system prioritizes consistency and partition tolerance over availability. In case of network partition, stale rates might be served until resolved.
  • Push vs Pull: Rates are pulled from external providers and pushed to clients via APIs. This ensures clients always get the latest rates without polling.
  • SQL vs NoSQL: SQL is used for current rates due to the need for strong consistency, while NoSQL handles the large volume of historical data efficiently.
System designMediumRevolut

15. How would you design a system to handle high-frequency transactions in a banking application?

Model answer

1. Requirements & scale

Functional Requirements:

  • Process high-frequency transactions with low latency.
  • Ensure transactional integrity and consistency.
  • Support concurrent transactions from multiple users.
  • Provide real-time balance updates and notifications.

Non-Functional Requirements:

  • High availability and fault tolerance.
  • Scalability to handle peak loads.
  • Strong security measures to protect sensitive data.
  • Compliance with financial regulations.

Estimates:

  • Assume 10 million users with an average of 10 transactions per user per day.
  • Peak load: 1,000 transactions per second (QPS).
  • Average transaction size: 1 KB.
  • Daily storage requirement: 100 million transactions * 1 KB = 100 GB.
  • Bandwidth: 1,000 QPS * 1 KB = 1 MB/s.

2. High-level architecture

flowchart TD
    subgraph Client
        A[Mobile App]
        B[Web App]
    end

    subgraph Edge/CDN
        C[CDN]
    end

    subgraph Load Balancer
        D[Load Balancer]
    end

    subgraph API / Services
        E[Transaction Service]
        F[Notification Service]
    end

    subgraph Cache
        G[Redis Cache]
    end

    subgraph Datastores
        H["SQL Database (PostgreSQL)"]
        I["NoSQL Database (Cassandra)"]
    end

    subgraph Message Queue
        J[Kafka]
    end

    subgraph Workers
        K[Transaction Processor]
    end

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

3. API design

  • POST /transactions: Initiate a transaction.
  • GET /transactions/{id}: Retrieve transaction details.
  • GET /accounts/{id}/balance: Get current account balance.
  • POST /notifications: Send transaction notifications.

4. Data model & storage

Datastores:

  • SQL Database (PostgreSQL): For transactional integrity and ACID compliance.
  • NoSQL Database (Cassandra): For high availability and horizontal scalability.

Key Tables:

  • Transactions Table (SQL):
  • transaction_id (Primary Key)
  • account_id
  • amount
  • timestamp
  • status
  • Accounts Table (SQL):
  • account_id (Primary Key)
  • balance
  • user_id
  • Transaction History (NoSQL):
  • Partition Key: account_id
  • Clustering Key: timestamp

5. Deep dive

The core challenge is ensuring transactional integrity while maintaining high throughput. We use a combination of optimistic concurrency control and distributed transactions.

sequenceDiagram
    participant Client
    participant API
    participant Cache
    participant SQL
    participant NoSQL
    participant Queue
    participant Worker

    Client->>API: POST /transactions
    API->>Cache: Check balance
    Cache-->>API: Return balance
    API->>SQL: Begin transaction
    SQL-->>API: Lock account row
    API->>SQL: Update balance
    API->>NoSQL: Log transaction
    API->>Queue: Publish transaction event
    SQL-->>API: Commit transaction
    API-->>Client: Transaction success
    Worker->>SQL: Process event
    Worker->>NoSQL: Update transaction history
Diagram

6. Scale, bottlenecks & trade-offs

Replication & Sharding:

  • SQL Database: Use master-slave replication for read scalability and data redundancy. Shard based on account_id to distribute load.
  • NoSQL Database: Use consistent hashing to distribute data across nodes, ensuring high availability and fault tolerance.

Caching:

  • Use Redis to cache account balances for quick access and reduce load on the SQL database.

Single Points of Failure:

  • Ensure redundancy at every layer (e.g., multiple load balancers, replicated databases) to prevent single points of failure.

Trade-offs:

  • Consistency vs. Availability: Prioritize consistency for financial transactions, accepting potential delays in availability during network partitions.
  • Push vs. Pull Notifications: Use push notifications for real-time updates, but ensure pull mechanisms are available for reliability.
  • SQL vs. NoSQL: Use SQL for transactions requiring ACID properties and NoSQL for scalable, distributed storage of transaction history.

This design balances the need for high-frequency transaction processing with the requirements for consistency, scalability, and fault tolerance, leveraging both SQL and NoSQL technologies to meet the system's demands.

System designMediumRevolut

16. Design a system that can process and validate transactions for a banking application.

The full question

Design a system that can process and validate transactions for a banking application. Consider factors such as concurrency, data integrity, and performance.

Model answer

1. Requirements & scale

Functional Requirements:

  • Process and validate financial transactions.
  • Ensure data integrity and consistency.
  • Support concurrent transaction processing.
  • Provide secure and encrypted communication.
  • Offer real-time transaction status updates.

Non-Functional Requirements:

  • High availability and reliability.
  • Low latency to ensure quick transaction processing.
  • Scalability to handle increased transaction volume.
  • Strong security measures, including authentication and encryption.

Estimates:

  • Assume 10 million users with an average of 1 transaction per user per day.
  • Peak load: 100 transactions per second (QPS).
  • Each transaction record: ~1 KB.
  • Daily storage requirement: 10 million KB or ~10 GB.
  • Bandwidth: 100 KB/s during peak times.

2. High-level architecture

flowchart TD
    subgraph Client
        A[Mobile App]
        B[Web App]
    end

    subgraph Edge/CDN
        C[CDN]
    end

    subgraph Load Balancer
        D[Load Balancer]
    end

    subgraph API / Services
        E[Transaction Service]
        F[Auth Service]
    end

    subgraph Cache
        G[Redis Cache]
    end

    subgraph Datastores
        H["SQL DB (PostgreSQL)"]
        I["NoSQL DB (Cassandra)"]
    end

    subgraph Message Queue
        J[Kafka]
    end

    subgraph Workers
        K[Transaction Validator]
    end

    A -->|HTTPS Request| C
    B -->|HTTPS Request| C
    C -->|Forward Request| D
    D -->|Route Request| E
    E -->|Authenticate| F
    F -->|User Data| E
    E -->|Read/Write| G
    E -->|Read/Write| H
    E -->|Publish Event| J
    J -->|Consume Event| K
    K -->|Validate & Update| I
Diagram

3. API design

  • POST /transactions: Initiate a new transaction.
  • GET /transactions/{id}: Retrieve the status of a transaction.
  • POST /auth/login: Authenticate a user.
  • GET /balance: Retrieve the current balance of a user.

4. Data model & storage

Datastores:

  • SQL DB (PostgreSQL): Used for storing transaction records due to ACID compliance and complex queries.
  • NoSQL DB (Cassandra): Used for storing user balances and transaction logs to handle high write throughput and scalability.

Key Tables:

  • Transactions Table (SQL):
  • transaction_id (Primary Key)
  • user_id
  • amount
  • status
  • timestamp
  • User Balances (NoSQL):
  • Partition Key: user_id
  • Columns: balance, last_updated

5. Deep dive

The core of processing and validating transactions involves ensuring data integrity and concurrency control. We employ optimistic concurrency control to handle concurrent transactions efficiently.

sequenceDiagram
    participant User
    participant API
    participant Auth
    participant Cache
    participant DB
    participant Queue
    participant Validator

    User->>API: POST /transactions
    API->>Auth: Validate User
    Auth-->>API: User Validated
    API->>Cache: Check Transaction Cache
    Cache-->>API: Cache Miss
    API->>DB: Write Transaction Record
    DB-->>API: Record Written
    API->>Queue: Publish Transaction Event
    Queue-->>Validator: Consume Event
    Validator->>DB: Validate Transaction
    DB-->>Validator: Validation Success
    Validator->>DB: Update User Balance
    DB-->>Validator: Balance Updated
    Validator-->>API: Transaction Validated
    API-->>User: Transaction Success
Diagram

6. Scale, bottlenecks & trade-offs

Scalability:

  • Horizontal scaling of the API and database layers to handle increased load.
  • Use of a distributed message queue (Kafka) to decouple transaction processing and validation.

Bottlenecks:

  • Database write operations can become a bottleneck; use of NoSQL for high write throughput helps mitigate this.
  • Cache misses can increase latency; ensure hot data is cached effectively.

Trade-offs:

  • Consistency vs. Availability: Opt for eventual consistency in the NoSQL database to ensure high availability.
  • Security vs. Performance: Strong encryption and authentication can add latency but are necessary for security.
  • Optimistic vs. Pessimistic Concurrency: Optimistic concurrency is chosen for better performance under high contention scenarios, accepting the risk of occasional retries.

By carefully considering these factors, the system can efficiently process and validate transactions while ensuring data integrity and performance.

TechnicalEasyRevolut

17. What is the difference between optimistic and pessimistic locking in database transactions?

Model answer

Optimistic vs. Pessimistic Locking in Database Transactions

  1. Optimistic Locking
  • Concept: Assumes that multiple transactions can frequently complete without interfering with each other. It checks for conflicts only at the time of committing the transaction.
  • Mechanism: Each transaction reads a version number or timestamp of the data. Before committing, it checks if the version number has changed. If it has, the transaction is rolled back.
  • Use Cases: Suitable for scenarios with low contention, where conflicts are rare. It is often used in web applications where users mostly read data and less frequently update it.
  • Pros:
  • Reduces the need for locking resources, leading to better performance in low-contention environments.
  • Allows for higher concurrency as transactions do not block each other.
  • Cons:
  • May lead to higher rollback rates if conflicts occur frequently.
  • Requires additional logic to handle retries and conflict resolution.
  1. Pessimistic Locking
  • Concept: Assumes that conflicts between transactions are likely. It locks the data when a transaction starts, preventing other transactions from accessing it until the lock is released.
  • Mechanism: When a transaction reads or writes data, it acquires a lock. Other transactions attempting to access the locked data are blocked until the lock is released.
  • Use Cases: Ideal for high-contention environments where data is frequently updated, such as financial systems or inventory management.
  • Pros:
  • Prevents conflicts by ensuring that only one transaction can access the data at a time.
  • Simplifies transaction management as conflicts are avoided upfront.
  • Cons:
  • Can lead to decreased system throughput due to blocking.
  • May cause deadlocks if not managed carefully.

Complexity

  • Optimistic Locking:
  • Time Complexity: O(1) for version check, but potential retries increase complexity.
  • Space Complexity: O(1) for storing version numbers.
  • Pessimistic Locking:
  • Time Complexity: O(1) for acquiring and releasing locks, but blocking can increase wait times.
  • Space Complexity: O(1) for lock management.
TechnicalMediumRevolut

18. How does Revolut handle API versioning?

Model answer

How Revolut Handles API Versioning

Revolut, like many modern financial technology platforms, needs to manage its APIs efficiently to ensure backward compatibility and seamless integration with various clients. API versioning is a critical aspect of this management, allowing Revolut to introduce changes without disrupting existing services. Here's how Revolut might handle API versioning:

  1. URL Path Versioning: - Revolut likely uses URL path versioning, where the version number is included in the API endpoint path. For example, /api/v1/accounts for version 1 and /api/v2/accounts for version 2. This approach makes it clear which version of the API is being accessed and allows clients to specify the version they need.
  2. Backward Compatibility: - Maintaining backward compatibility is crucial. Revolut ensures that newer versions of the API do not break existing client integrations. This is achieved by keeping older versions available and stable while introducing new features or changes in subsequent versions.
  3. Deprecation Policy: - Revolut likely has a clear deprecation policy. When a new version is released, the older version is marked as deprecated, and clients are notified. They are given a grace period to migrate to the newer version before the old version is retired.
  4. Documentation and Communication: - Comprehensive documentation is provided for each API version. Revolut ensures that clients are well-informed about changes, new features, and migration paths. Effective communication channels are established to assist clients during transitions.
  5. Testing and Validation: - Before releasing a new API version, extensive testing is conducted to ensure that it meets the required standards and does not introduce regressions. Automated tests and validation processes are in place to verify the functionality of both new and existing versions.
  6. Use of Feature Flags: - Feature flags might be used to introduce new features gradually. This allows Revolut to test new functionalities with a subset of users before rolling them out to all clients, reducing the risk of widespread issues.
  7. Event Sourcing for State Management: - As referenced, Revolut might employ event sourcing to manage state changes across different API versions. This approach allows the system to maintain a complete audit trail and rebuild the current state by replaying events, which can be particularly useful when transitioning between API versions.

By implementing these strategies, Revolut can effectively manage API versioning, ensuring a balance between innovation and stability, and providing a seamless experience for its clients.

TechnicalMediumRevolut

19. What database technologies does Revolut use, and why?

Model answer

Database Technologies Used by Revolut

Revolut, a financial technology company, employs a combination of database technologies to meet its diverse needs for scalability, performance, and reliability. Here is an overview of the key database technologies used by Revolut and the reasons behind their choices:

  1. PostgreSQL: - Usage: Revolut uses PostgreSQL as its primary relational database management system (RDBMS). - Reasons: - ACID Compliance: PostgreSQL provides strong ACID (Atomicity, Consistency, Isolation, Durability) guarantees, which are crucial for financial transactions where data integrity and consistency are paramount. - Rich Feature Set: It supports advanced features like full-text search, JSONB for semi-structured data, and robust indexing capabilities, making it versatile for various use cases. - Scalability: PostgreSQL can handle large volumes of data and complex queries efficiently, which is essential for a growing fintech company like Revolut.
  2. NoSQL Databases: - Usage: Revolut also leverages NoSQL databases for specific use cases that require high throughput and flexible data models. - Reasons: - Scalability and Performance: NoSQL databases are designed to scale horizontally, providing high performance for read and write operations, which is beneficial for handling large-scale data and real-time analytics. - Flexibility: The schema-less nature of NoSQL databases allows for rapid iteration and adaptation to changing data requirements, which is advantageous in a fast-paced environment.
  3. In-Memory Databases: - Usage: For caching and fast data retrieval, Revolut uses in-memory databases like Redis. - Reasons: - Low Latency: In-memory databases provide extremely fast data access times, which is critical for improving the performance of user-facing applications. - Efficient Caching: They are used to cache frequently accessed data, reducing the load on primary databases and enhancing overall system efficiency.

Conclusion

Revolut's choice of database technologies reflects its need to balance consistency, scalability, and performance. By using PostgreSQL for its core transactional data, NoSQL databases for scalability and flexibility, and in-memory databases for caching, Revolut ensures it can handle the demands of a modern fintech platform while maintaining data integrity and performance.

TechnicalMediumRevolut

20. What practices does Revolut follow to ensure code quality?

Model answer

Revolut employs several best practices to ensure high code quality, focusing on robust testing methodologies, efficient communication protocols, and streamlined deployment processes. Here’s how they achieve this:

  1. Comprehensive Testing Strategies - Unit Testing: Revolut ensures that individual components of the software are tested in isolation. This helps in identifying bugs at an early stage and ensures that each unit of the application functions correctly. - Integration Testing: This is used to verify that different modules or services work together as expected. It helps in detecting interface defects and ensures that the integrated components function as intended. - Load and Stress Testing: These are crucial for understanding the system's behavior under heavy loads and stress conditions. Load testing checks the system’s performance under expected usage, while stress testing pushes the system beyond normal operational capacity to identify breaking points. - Continuous Integration/Continuous Deployment (CI/CD) Pipelines: Automated pipelines are employed to ensure that code changes are automatically tested and deployed. This reduces manual errors and accelerates the release process.
  2. Efficient Communication Protocols - REST and gRPC: Revolut uses REST for public APIs due to its simplicity and statelessness, making it ideal for external client interactions. For internal microservices, gRPC is preferred because of its high performance and efficient binary serialization with Protobuf, which reduces latency and bandwidth usage. - GraphQL: This is used to optimize data fetching, allowing clients to request only the data they need, which minimizes over-fetching and enhances performance in complex applications.
  3. Deployment and Monitoring - Revolut’s deployment strategy involves automated monitoring and alerting systems that help in identifying issues in real-time. This proactive approach ensures that any potential problems are addressed swiftly, maintaining system reliability and uptime.

By integrating these practices, Revolut maintains a high standard of code quality, ensuring that their systems are reliable, scalable, and efficient. These strategies not only enhance the development process but also improve the overall user experience by delivering robust and performant applications.

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