๐ฆ SquidLeet: A Command-Line LeetCode Practice Tool
A deep dive into the low-level design of SquidLeet, a command-line tool for practicing LeetCode problems.

SquidLeet is a command-line tool designed to enhance the experience of practicing LeetCode problems. This article explores its low-level design, highlighting the modular architecture, key components, and how they interact to create a seamless user experience.
Overview of the Architecture
SquidLeet's architecture is modular, with distinct layers for API interactions, input handling, practice management, and utility functions. Here's a high-level overview of its architecture:
Key Components and Their Roles
1. CommandParser
The CommandParser
component processes CLI arguments and validates inputs. It determines the user's desired practice mode, problem difficulty, and other configuration options.
Input Arguments:
--practice-mode
: Specifies the mode (e.g., daily, random, custom, study-plan).--difficulties
: Filters problems by difficulty (easy, medium, hard).--problems
: Accepts specific problem slugs for custom mode.--plan-name
: Selects a study plan for study-plan mode.
2. PracticeModeManager
This module determines the appropriate practice mode based on the parsed inputs and delegates the request to the corresponding handler.
3. PracticeHandler
PracticeHandler
serves as the core logic for managing different practice modes. It interfaces with CachedLeetCodeAPI
to fetch problems and passes problem details to the SolutionHandler
for further action.
4. CachedLeetCodeAPI
The CachedLeetCodeAPI
module acts as a caching layer to store fetched problems temporarily. It helps reduce redundant API calls and speeds up problem retrieval.
In order to provide universal access to the cache, I followed the singleton pattern. This ensures that all components share the same cache instance and avoid redundant cache reads and writes.
In Python this is achieved by creating a file called CacheHandler.py
with the following content:
from api.CachedLeetCodeAPI import CachedLeetCodeAPI
cached_api = CachedLeetCodeAPI(cache_expiry=3600)
This file is then imported wherever the cache is needed, ensuring a single cache instance across the application.
from handlers.CacheHandler import cached_api
How did I design the cache?
The cache design for CachedLeetCodeAPI
involves several key components and processes, which are outlined below:
- Cache Directory:
- The cache is stored in a directory, which defaults to the system's temporary directory if not specified. This directory is named "cached_leetcode_api" and is created if it doesn't exist.
- Cache Key:
- A unique cache key is generated for each API request. This key is derived from a unique identifier, such as the query or API parameters, by computing the MD5 hash of the identifier. This ensures that each request has a distinct cache entry.
- Cache Expiry:
- The cache has an expiration time, set by default to 3600 seconds (1 hour). This means cached data older than this duration is considered stale and will be refreshed by making a new API call.
- Cache Read/Write Operations:
- Reading from Cache: When a request is made, the system first checks if a valid cache entry exists. If the cache file is present and not expired, the data is read from the cache and returned.
- Writing to Cache: If there is a cache miss or the cache is expired, the data fetched from the API is written to the cache for future use.
- Fetching with Cache:
- The
_fetch_with_cache
method orchestrates the caching logic. It attempts to read from the cache first and, upon a cache miss, invokes the API function to fetch the data and then writes the data back to the cache.
- Cached API Methods:
- The class provides cached versions of several API methods, such as
fetch_problems
,fetch_daily_challenge
,fetch_problem
, andget_study_plan
. Each method uses a unique identifier to manage its cache entries.
Here is a visual representation of the caching process using PlantUML:
This diagram illustrates the interaction between the user, the CachedLeetCodeAPI
, the LeetCodeAPI
, and the cache directory, highlighting the decision-making process for using cached data versus fetching new data from the API.
5. LeetCodeAPI
The LeetCodeAPI
module encapsulates all interactions with the LeetCode platform using GraphQL. It supports fetching problems, study plans, and submitting solutions.
Core Methods:
fetch_problems
: Retrieves a list of problems based on difficulty and filters.fetch_daily_challenge
: Fetches the daily coding challenge.fetch_problem
: Retrieves detailed information about a specific problem.submit_solution
: Submits user solutions and handles responses.
6. SolutionHandler
SolutionHandler
manages the solution workflow, including creating template files with the starter code provided by the LeetCodeAPI
, opening them in the user's preferred editor, and setting up file watchers for automatic submission.
7. Utilities
SquidLeet includes several utility modules for logging, file handling, and timer management:
- Logger: Customizable logging levels (DEBUG, INFO, WARN, ERROR).
- File Handler: Manages file creation for solutions.
- Timer: Tracks time limits for solving problems.
- Editor Resolver: Determines the appropriate command to open files in the user's preferred editor.
Interaction Flow
Here is a step-by-step breakdown of how SquidLeet processes a --mode daily
command:
- User Input: The user enters
python3 main.py --mode daily
. - Command Parsing:
CommandParser
parses inputs and validates them. - Session Initialization:
SessionManager
initializes the LeetCode session. - Mode Handling:
PracticeModeManager
delegates the request toDailyChallengeMode
. - Problem Fetching:
LeetCodeAPI
fetches the daily challenge if it is not found inCachedLeetCodeAPI
. - Solution Workflow:
SolutionHandler
creates a solution file with the starter code provided by theLeetCodeAPI
.- Opens the file in the editor.
- Sets up a file watcher to monitor changes.
- Submits the solution upon file save.
Design Considerations
1. Modular Design
Each component is designed to perform a specific task, making the codebase maintainable and extensible. For instance, adding a new practice mode requires implementing a new subclass of PracticeHandler
.
2. Asynchronous API Calls
The use of ThreadPoolExecutor
in LeetCodeAPI
allows fetching problems in parallel, improving performance for multi-difficulty queries.
3. Extensibility
New features like additional practice modes or support for more languages can be added without disrupting existing functionality.
4. Error Handling
Graceful error handling ensures that the user is informed of any issues, such as missing session tokens or invalid problem slugs.
Challenges and Future Enhancements
Challenges
- Managing authentication securely.
- Handling API rate limits and network failures.
Future Enhancements
- Detailed Analytics: Track user performance and provide insights.
- Enhanced Logging: Support structured logging for better debugging.
Conclusion
SquidLeet demonstrates the power of modular design and efficient API integration. By providing a command-line interface for practicing LeetCode problems, it eliminates distractions and optimizes the coding experience. With its extensible design, SquidLeet is well-positioned to evolve into an even more powerful tool for developers.