Common Follow-Up Questions, with Model Answers
50 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)After the story come follow-up questions. They are not traps. The interviewer wants to see whether you made the choices or only copied them. A good answer has a reason, a trade-off and one fact from your own project.
Why FastAPI?
A trade-off is what you give up to get something else. Naming one with your choice shows you compared options instead of following a tutorial.
A model answer has three parts: the need, the choice and the cost. The need was a small JSON API with validation. FastAPI checks input from type hints and writes the API docs for you.
Say it in your words: "I needed validation and docs without extra code. FastAPI gives both from type hints. The cost is that it is younger than Django, and I wrote auth and admin myself." Never say a tool is "the best".
How do you prevent double booking?
A unique constraint is a database rule that rejects a second row with the same key. For bookings the key is the show and the seat, so two bookings for one seat cannot both be saved.
Checking "is the seat free?" and then saving leaves a gap where two requests both see "free". The fix is to let the database decide: one unique rule, and the second insert fails.
import sqlite3
db = sqlite3.connect(":memory:")db.execute("CREATE TABLE booking (show_id INT, seat TEXT, UNIQUE (show_id, seat))")for user in ("asha", "ravi"): try: db.execute("INSERT INTO booking VALUES (1, 'A1')") print(user, "booked A1") except sqlite3.IntegrityError: print(user, "lost: seat taken")Example 1 · A unique constraint stops the second booking
import sqlite3
db = sqlite3.connect(":memory:")db.execute("CREATE TABLE booking (show_id INT, seat TEXT, user TEXT, UNIQUE (show_id, seat))")
def book(show_id, seat, user): try: db.execute("INSERT INTO booking VALUES (?, ?, ?)", (show_id, seat, user)) return "confirmed" except sqlite3.IntegrityError: return "conflict"
print(book(1, "A1", "asha"))print(book(1, "A1", "ravi"))print(book(1, "A2", "ravi"))confirmed conflict confirmed
What this code does
- The table allows each (show, seat) pair only once.
- The second request for A1 breaks that rule, so
bookreturns "conflict" and nothing is saved. - A different seat is a different key, so Ravi still gets A2.
Example 2 · A race without a lock, and the fix
import threading
seats = {"A1": None}lock = threading.Lock()winners = []
def book(user): with lock: if seats["A1"] is None: seats["A1"] = user winners.append(user)
threads = [threading.Thread(target=book, args=(f"user{i}",)) for i in range(50)]for t in threads: t.start()for t in threads: t.join()print("Bookings for A1:", len(winners))Bookings for A1: 1
What this code does
- Fifty threads try to book the same seat at once.
- The lock makes "check, then set" one step, so only one thread can pass the check.
- The count is always 1. Without the lock it can be higher. A database lock plays the same role across many app copies.
In the service I catch that error and return "409 Conflict". For stock and similar counters I also use a row lock or one atomic update, so the check and the change happen together.
What would you change?
This question tests honesty and judgement. Pick two or three real gaps, say why they matter, and say what you would do first. Do not say "nothing", and do not list ten things.
Example 3 · Pagination keeps list endpoints small
orders = list(range(1, 26))
def page(items, number, size=10): start = (number - 1) * size return items[start:start + size]
print(page(orders, 1))print(page(orders, 3))print(len(page(orders, 4)))[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [21, 22, 23, 24, 25] 0
What this code does
- A list endpoint should return one page, not every row.
- The slice starts at
(number - 1) * sizeand takessizeitems. - Page 3 has the last 5 orders and page 4 is empty, which the API must handle too.
Good answers for these projects: add database migrations, add pagination to list endpoints, hash and rotate secrets properly, add rate limiting, and test the failure paths. Always say which one comes first and why.
How would it scale?
Horizontal scaling means running more copies of the same app behind a load balancer. It works when each request is independent and shared state lives in the database or a cache.
Answer in the order things break. First measure. Then add indexes, pagination and caching. Then run more app copies. Last, split the database. Say you have not run the project at that load.
Example 4 · A cache saves repeated work
from functools import lru_cache
calls = 0
@lru_cache(maxsize=100)def product_name(product_id): global calls calls += 1 return f"Product {product_id}"
for pid in (1, 2, 1, 1, 2): product_name(pid)print("Database reads:", calls)Database reads: 2
What this code does
lru_cacheremembers results by argument.- Five lookups need only two reads, because ids 1 and 2 repeat.
- A cache can show old data, so say how long data may be stale. In a real system with many copies use a shared cache.
Never invent traffic numbers. Say "I tested with 50 threads on my laptop. For real traffic I would first load-test and find the slowest query."
Next step
Check your understanding
10 quick questions