Summary
The provided codebase implements a quiz application with a gambling-based difficulty selector and a timed quiz. The primary technical debt is the complete lack of a persistence layer. The user intends to implement a leaderboard using flat files, but the current architecture lacks a data serialization strategy, which will inevitably lead to race conditions and data corruption as the project scales or if multiple instances of the app are run.
Root Cause
The current system state is volatile memory only. The “Root Cause” of the impending failure is the reliance on manual file I/O without synchronization. Using a simple text file to store scores leads to several failure modes:
- Atomicity Failure: Overwriting a file without a temporary backup means a crash during a write operation results in a total loss of all historical data.
- Linear Search Complexity: Sorting a raw text file in memory every time a new score is added results in O(N log N) time complexity for every save operation.
- Lack of Schema: Storing data as raw strings without a structured format (like JSON or CSV) makes the system fragile to input changes.
Why This Happens in Real Systems
In production environments, this is known as the “Local State Trap”. Developers often start with local files for simplicity, but this fails in real-world systems because:
- Concurrency: Multiple users (or threads) attempting to write to the same file simultaneously cause FileLock errors or interleaved, corrupted text.
- I/O Bottlenecks: Disk I/O is orders of magnitude slower than RAM; reading a whole file to find the “top 10” scores becomes a bottleneck as the dataset grows.
- Lack of ACID Compliance: Without Atomicity, Consistency, Isolation, and Durability, the system cannot guarantee that a high score is actually saved.
Real-World Impact
If this logic were deployed to a production environment:
- Data Loss: A system crash during the
file.write()call would wipe the entire leaderboard. - Performance Degradation: As the “history” file grows, the application’s startup and save times would increase linearly.
- Corruption: If the
threadinglogic used for the timer is expanded to the saving logic, the leaderboard file would likely suffer from overlapping writes, rendering the file unreadable.
Example or Code (if necessary and relevant)
The following implementation demonstrates the Senior Engineer approach: using JSON for structured data and a sort-on-write strategy to maintain the leaderboard.
import json
import os
LEADERBOARD_FILE = "leaderboard.json"
def save_score(username, score):
# Load existing data or initialize empty list
if os.path.exists(LEADERBOARD_FILE):
with open(LEADERBOARD_FILE, "r") as f:
try:
data = json.load(f)
except json.JSONDecodeError:
data = []
else:
data = []
# Append new score
data.append({"name": username, "score": score})
# Sort by score descending and slice top 10
data.sort(key=lambda x: x["score"], reverse=True)
top_scores = data[:10]
# Atomic-like write: write to temp file then rename
with open(LEADERBOARD_FILE + ".tmp", "w") as f:
json.dump(top_scores, f, indent=4)
os.replace(LEADERBOARD_FILE + ".tmp", LEADERBOARD_FILE)
def get_leaderboard():
if not os.path.exists(LEADERBOARD_FILE):
return []
with open(LEADERBOARD_FILE, "r") as f:
return json.load(f)
How Senior Engineers Fix It
A senior engineer moves away from raw text files and implements the following patterns:
- Structured Serialization: Use JSON or SQLite to ensure data has a defined schema.
- Atomic Writes: Instead of writing directly to the main file, write to a temporary file and use
os.replace(). This ensures that if the system crashes, the old leaderboard remains intact. - Complexity Management: Keep the leaderboard size capped (e.g., Top 10). This keeps the time complexity constant O(1) regardless of how many thousands of games are played.
- Separation of Concerns: Move the leaderboard logic into a dedicated Data Access Object (DAO) class rather than mixing it with the game loop.
Why Juniors Miss It
Junior developers typically focus on the “Happy Path” (the code works when one person plays it once). They miss these points because:
- Ignorance of Edge Cases: They assume the
write()operation is instantaneous and infallible. - Lack of Complexity Analysis: They do not consider how the system behaves when the file size grows from 1KB to 100MB.
- Monolithic Design: They place business logic (the quiz) and persistence logic (the file saving) in the same function, making the code untestable and brittle.