Implement a Multi-Level Recipe Management Service
AirbnbSoftware Engineer · Online AssessmentLast updated
Design and implement an in-memory recipe management service supporting deterministic querying, defensive data isolation, atomic updates, and secondary index maintenance.
Problem Description
Design an in-memory recipe management service, RecipeService, that supports normalized record storage, atomic updates, and deterministic search queries.
Requirements
-
Data Normalization & Monotonic IDs:
- Each created recipe is assigned an integer ID starting from
1, incrementing by1for each subsequent recipe. - Ingredients and tags must be normalized: stripped of leading/trailing whitespace and converted to lowercase. Duplicate entries within a recipe must be deduplicated while preserving their first occurrence's order.
- Input and output data collections must be defensively copied so that external mutations do not affect internal state.
- Each created recipe is assigned an integer ID starting from
-
Atomic Updates:
- Updates to a recipe must be atomic. If any validation fails (e.g., negative
prep_timeor nonexistentrecipe_id), the update must fail entirely without modifying the recipe or secondary indexes.
- Updates to a recipe must be atomic. If any validation fails (e.g., negative
-
Deterministic Search:
- Filter recipes by an optional
tag(case-insensitive) and an optionalmax_prep_time(prep_time <= max_prep_time). - Sort results by
sort_by:"prep_time_asc": sort byprep_timeascending, tie-breaking byidascending."prep_time_desc": sort byprep_timedescending, tie-breaking byidascending."title_asc": sort bytitlelexicographically ascending, tie-breaking byidascending.
- Returns a list of matching recipe IDs.
- Filter recipes by an optional
Interface
class RecipeService:
def __init__(self):
pass
def create_recipe(self, title: str, ingredients: list[str], tags: list[str], prep_time: int) -> int:
"""Creates a recipe and returns its assigned ID."""
pass
def get_recipe(self, recipe_id: int) -> dict | None:
"""Returns a dictionary with keys: 'id', 'title', 'ingredients', 'tags', 'prep_time'. Returns None if not found."""
pass
def update_recipe(self, recipe_id: int, updates: dict) -> bool:
"""Updates allowed fields ('title', 'ingredients', 'tags', 'prep_time'). Returns True if successful, False otherwise."""
pass
def delete_recipe(self, recipe_id: int) -> bool:
"""Deletes a recipe by ID. Returns True if deleted, False if not found."""
pass
def search_recipes(self, tag: str | None = None, max_prep_time: int | None = None, sort_by: str = "prep_time_asc") -> list[int]:
"""Returns matching recipe IDs ordered according to sort_by."""
pass
Examples
service = RecipeService()
id1 = service.create_recipe("Pancakes", ["Flour", " Milk ", "flour"], ["Breakfast", " quick "], 20)
# id1 == 1
# Ingredients stored: ["flour", "milk"]
# Tags stored: ["breakfast", "quick"]
id2 = service.create_recipe("Waffles", ["flour", "eggs"], ["breakfast"], 20)
# id2 == 2
service.search_recipes(tag="breakfast", max_prep_time=25, sort_by="prep_time_asc")
# Returns: [1, 2] (both have prep_time 20, broken by ID ascending)
service.update_recipe(1, {"prep_time": -5})
# Returns: False (invalid prep_time; state is unchanged)
service.delete_recipe(1)
# Returns: True
service.search_recipes(tag="breakfast")
# Returns: [2]
Constraints
prep_time >= 0for all valid records.titleis non-empty.- Total operations <=
2 * 10^4. - Number of recipes <=
5,000.
Hints
Hint 1
Normalize and deduplicate tags and ingredients in a helper function used by both creation and update operations.
Hint 2
Validate all update keys and values upfront before modifying any internal record or inverted index to ensure atomicity.
Hint 3
Ensure defensive copies are created both when ingesting lists and when returning records from `get_recipe`.
Follow-up questions
- How would you optimize `search_recipes` if the dataset grew to millions of records with high write concurrency?
- How would you extend this service to support pagination with stable cursors across concurrent edits?
- What changes would be necessary to support multi-tag boolean filtering (AND / OR / NOT) efficiently?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.