The interview question: "Your API answers price as a number. A new app needs the currency too. How do you change the API without breaking the apps that already use it?"
The expected answer: changing the type of a field that clients already read is a breaking change, so you don't do it in place. You publish a new version: /v1/products keeps answering exactly what it always did, and /v2/products answers the new shape. Inside the app, one function reads the data and one small function per version shapes each row, so the query lives in one place.
For example, a user whose email gets more detail in version 2. Version 2 starts from version 1's dictionary and replaces only that field:
def user_v1(row):
return {"id": row["id"], "email": row["email"]}
def user_v2(row):
user = user_v1(row)
user["email"] = {"address": row["email"], "verified": row["verified"] == 1}
return user
The shop's products table has 8 products. The finished app answers:
GET /v1/products → 200 [{"id": 1, "name": "Mug", "price": 12}, {"id": 2, "name": "Notebook", "price": 7}, …]
GET /v2/products → 200 [{"id": 1, "name": "Mug", "price": {"amount": 12, "currency": "EUR"}}, {"id": 2, "name": "Notebook", "price": {"amount": 7, "currency": "EUR"}}, …]
GET /v1/products → 200 [{"id": 1, "name": "Mug", "price": 12}, …] (still plain numbers)
GET /v3/products → 404
Your task: load_products, product_v1 and GET /v1/products are already written, and GET /v2/products answers an empty list. Write product_v2, which starts from the version 1 dictionary and replaces its price with {"amount": …, "currency": "EUR"}, and make /v2/products answer every product in that shape.
Assignment
Follow the Coding Tutorial to write product_v2 and the loop that fills list_v2.
Hint
Don't copy version 1: call product_v1(row) and change only the price. It builds a new dictionary on every call, so changing the price in product_v2 doesn't change what /v1/products answers.
Free plan: 30 tutor messages. PRO is unlimited.
