Interview questions, answered in depth

A growing base of interview questions with real technical explanations and guidance on how to answer them, plus articles on backend, cloud and AI.

In-depth Interview Question Breakdowns

The key difference from similar services — this is not dry information (which you can just ask ChatGPT). I've written about how to correctly answer questions to pass the interview. Additionally, most questions include topics you can bring up to steer the interview in your direction.

TypeScript

What is the difference between type and interface in TypeScript?

This is a common interview question, especially for Junior and Middle positions. In general, what they usually want to hear in an interview is interface merging.

Let’s break it down with an example:

interface IUser {
name: string;
email: string;
}

interface IUser {
age: number;
}

// TypeScript will merge these two interfaces
// This is now a single interface, so IUser will also expect the age field.
const user: IUser = {
name: 'Test',
email: 'test@test.com',
age: 30,
}

If you try the same approach with a type, you will get an error. When answering this question, it would be a bonus to mention when it’s better to use type vs interface:

  1. Interface is better used to describe the structure of objects and classes, as intended in OOP.
  2. For everything else, it’s more appropriate to use type, especially in the context of building complex types: union, utility types, function signatures…
Databases

How does a LEFT JOIN work?

LEFT JOIN is a type of join that returns all rows from the left table, even if there are no matching rows in the right table. If a match exists in the right table, its data is included. If there is no match, the columns from the right table will contain NULL.

Let’s look at an example

We have two tables: users and orders. Our goal is to get all users along with their orders. If a user has no orders, we still want to include them in the result, so we use a LEFT JOIN.

-- users
id | name
------------
1 | Alice
2 | Bob
3 | Charlie

-- orders
id | user_id | product
------------------------
1 | 1 | Laptop
2 | 1 | Mouse
3 | 2 | Keyboard

The query will look like this. Let’s break it down in more detail:

- Select the columns we want to see in the result:
-- name — from the users table
-- product — from the orders table
SELECT
users.name,
orders.product
FROM
users
-- We do a LEFT JOIN, meaning we take ALL rows from the `users` table (the left table)
-- and add data from the `orders` table (the right table) if a match is found.
LEFT JOIN
orders
-- Define the condition that links rows from both tables:
-- if `users.id` matches `orders.user_id`, the rows are joined.
ON
users.id = orders.user_id;

As a result of running the query, we’ll get the following output.

name | product
-------------------
Alice | Laptop
Alice | Mouse
Bob | Keyboard
Charlie | NULL -- even though Charlie has no matches in the right table, the LEFT JOIN still returns him, but the columns from the right table will be NULL.
Algorithms and Data Structures

How can you determine an algorithm’s complexity?

First, let’s clarify what algorithmic complexity is. It’s a way to estimate how many resources an algorithm uses as the input size grows. There are two ways to measure an algorithm’s complexity.

Time Complexity

The time complexity of an algorithm is a measure of how much it slows down as the amount of data grows. Big O notation is used here, and it provides an upper bound.

| Нотація | Назва | Пояснення |
|--------------|----------------------|--------------------------------------------------------|
| O(1) | Константна | Always takes the same amount of time. |
| O(log n) | Логарифмічна | Grows very slowly as n increases. |
| O(n) | Лінійна | Time grows in direct proportion to the input size (n). |
| O(n log n) | Лінійно-логарифмічна | Slightly slower than linear, but still efficient. |
| O(n²) | Квадратична | Slow because it has two nested loops. |
| O(2ⁿ) | Експоненційна | Very slow for large n. |

Let’s break it down in more detail.

O(1) - A good example of constant time complexity is an assignment operation or accessing an object property by key.

O(log n) - With this complexity, execution time grows very slowly, even as the input size increases significantly. There are many examples of this, and the simplest one is binary search.

O(n) - With this complexity, the execution time grows in direct proportion to the number of elements. For example, imagine iterating over an array of 100 elements. With a logarithmic-time algorithm, increasing the number of elements to 100,000 would barely affect the runtime, whereas an O(n) algorithm would slow down significantly.

O(n log n) - In simple terms, this is a combination of linear and logarithmic complexity. For example, an algorithm may use a divide-and-conquer strategy O(log n) to split an array and then use iteration O(n) to process all elements.

O(n²) - This happens when, for each element, the algorithm checks all other elements - most commonly when there are two nested loops.

O(2ⁿ) - Exponential complexity is one of the slowest. Put simply, it doubles the number of operations with each additional element. For example, with 10 elements, you’d need roughly 1,024 operations.

A simple example of determining an algorithm’s complexity

We have a function that returns the minimum value in an array. Essentially, we have two operations: assigning let min = arr[0] and iterating over the array arr. The assignment operation always takes constant time, O(1), because it doesn’t depend on the number of elements, while iterating over the array depends on how many elements it contains, so the complexity is O(n). The overall complexity of an algorithm is determined by the most expensive operation. In our case, that’s iterating over the array, so the time complexity of the findMin function is O(n).

function findMin(arr) {
let min = arr[0]; // O(1)
for (let i = 1; i < arr.length; i++) // O(n)
if (arr[i] < min) min = arr[i];
}
return min;
}

Space Complexity

This is the amount of additional memory an algorithm needs, beyond the input data. For example, creating extra variables, arrays, objects, and so on. To determine space complexity, we use the same Big O notation, but instead of counting operations, we focus on how much extra memory (data structures) is being used.

After answering the question, it’s a big plus to emphasize the importance of understanding complexity, since it directly relates to understanding data structures. And data structures are a foundation for understanding modern distributed systems, databases, caching, and many other important topics.

System Design

What is idempotency?

So, let’s start with the definition of idempotency. It’s a concept that guarantees that repeating an operation multiple times has the same effect as performing it once. Most often, this question comes up in the context of REST API design, so it makes sense to discuss this concept in terms of HTTP methods.

For example, let’s look at the PUT method

According to REST, it performs a full update of a resource. And if we send it 10 times with the same body, all those calls will produce the same result as a single call.

The same applies to GET, DELETE, and PATCH. But if we look at the POST method, according to the REST concept, each call creates a new record in the database, so repeating the call will NOT have the same effect - therefore, the method is not idempotent. Additionally, I’d recommend reading about idempotency in distributed systems and talking about it in an interview. It’s also good practice to mention an idempotency key and give an example of how it’s used.

Go to question database
Loading...
Loading...
Loading...

Need 1:1 help?

Mentorship, mock interviews and a development plan built around where you actually are.

See how mentoring works