Hands-on coding example
Practice Exercise: Todo List
This exercise was used in Perplexity technical interviews throughout much of 2025 and 2026. We’re releasing it to provide a sense of the format and style of our hands-on coding interviews. If you’d like to try the exercise in CoderPad, please request a practice session link from your recruiter.
Part 1 of 4 (setup)
Please read the README.md file, which contains the general instructions and interview agreement used for Perplexity live technical rounds. In an actual interview, you would be asked to review, sign, and date this agreement in CoderPad before beginning.
README.md
Reminders
- If performing this interview remotely, please share your full computer screen via Google Meet.
- This is a coding exercise with multiple parts. Each part will ask you to implement some functionality, and you may implement helper functions and/or classes if useful.
- It’s not unusual to reach the end of the interview time before finishing all parts. You should work efficiently with decent (but not necessarily production-level) code quality.
Rules on Outside Assistance
You must not use AI or other outside assistance (such as the internet or other humans) during this interview.
- Exception 1: you may refer to online language documentation.
- Exception 2: you may use Perplexity, a search engine, or another AI tool to ask questions about your language’s libraries (including to see example usage). However, do not ask the tool to produce any significant part of your answer.
If needed, your interviewer can provide further guidance about what is and is not permissible.
Perplexity Interview Agreement (Technical Round)
In connection with this opportunity to interview for a job at Perplexity, I agree to the following terms:
- I affirm that before this interview, I have not sought out or accessed any of Perplexity’s proprietary interview materials on the Internet or other sources.
- I agree to comply with the Rules on Outside Assistance.
- I agree not to cheat, deceive, or defraud.
- I agree not to record this interview, copy or take screenshots of any interview materials, or save any other records of proprietary Perplexity information.
- I agree not to disclose the interview question (and/or any details about the question) to anyone else, both during and after this interview.
- I acknowledge that violations of this Agreement are grounds for termination of my candidacy or employment, along with serious legal consequences under applicable law.
By signing my name below, I indicate that I have read and understood this entire document and I agree to its terms:
Signed: /MY FULL NAME/
Date: YEAR MONTH DATE
Introduction
LLM-based agents often get sidetracked from their original goal. To address this, many AI systems feature some state-tracking abstraction (such as a todo list) for the LLM to use.
In this exercise, you’ll implement a simplified version of the todo list used by Perplexity’s agentic systems.
Part 2 of 4
Please open src/todo_list.py and carefully review the starter code. Then, complete the implementation of the TodoList class by filling in all methods that are currently stubbed with pass.
Starter code
This starter code is for the Python version; starter code and problem instructions for other languages are available from your recruiter.
src/todo_list.py
import enum
from dataclasses import dataclass
class TaskStatus(enum.IntEnum):
"""Possible states for an individual task."""
BLOCKED = 1
READY_TO_EXECUTE = 2
IN_PROGRESS = 3
SUCCEEDED = 4 # Terminal state
FAILED = 5 # Terminal state
@dataclass
class Task:
"""
A task for an AI agent to execute.
Example:
#### Creating a new task ####
my_task = Task(
task_id=1234,
description="check the weather in the user's current city",
status=TaskStatus.READY_TO_EXECUTE
)
#### Editing a field ####
my_task.status = TaskStatus.IN_PROGRESS
print(my_task.status.name) # prints "IN_PROGRESS"
"""
task_id: int
description: str
status: TaskStatus
class TodoList:
def __init__(self):
"""
Initializes internal state for an empty todo list.
"""
pass
def add_task(self,
description: str) -> Task:
"""
Adds a new task to the todo list and returns the newly-created
Task object.
task_id should be initialized to a unique integer value.
Earlier-created tasks should have lower IDs than later-created tasks.
status should be initialized to READY_TO_EXECUTE.
"""
pass
def get_task(self, task_id: int) -> Task | None:
"""
Retrieves an existing task given a task_id, or None if no such
task exists.
"""
pass
def change_task_status(self, task_id: int, new_status: TaskStatus) -> bool:
"""Changes the status of a task given its task_id.
Note that tasks can only progress in status. A task may not regress
from a higher to a lower status.
This function should return True on success.
This function should do nothing and return False if
any of these failure conditions occur:
- The task_id does not correspond to a task that exists.
- The task status is already in a terminal state (SUCCEEDED or FAILED).
- The new task status is not higher than the task's current status.
"""
pass
Testing
You can test your code by clicking on the green dropdown menu in CoderPad and selecting “Run Tests (Part 2)”.
Once the tests pass, proceed to the next part.
Part 3 of 4
In this part, you will implement support for dependencies between tasks.
A task may be created with dependencies on other preexisting tasks. At creation time, a new task is considered READY_TO_EXECUTE if it has no dependencies or if all dependencies have status SUCCEEDED. Otherwise, the new task is considered BLOCKED.
We’d like you to:
- Modify the
add_taskmethod by adding a new parameterdependency_ids: list[int].- This parameter represents the task ids of all existing tasks on which the new task depends.
- Make sure that new tasks are correctly set to either
BLOCKEDorREADY_TO_EXECUTE. - You may assume that if
dependency_idsis non-empty, then all task IDs contained therein are valid. - You may save the dependency state either in the
Taskdataclass or directly within a newTodoListinstance variable. Either is acceptable.
- Modify the
change_task_statusmethod such that the following is true after each status update:- Any
BLOCKEDtasks whose direct dependencies have all completed successfully are transitioned to theREADY_TO_EXECUTEstatus. - Any
BLOCKEDtasks that directly or indirectly depend on a newly failed task are transitioned to theFAILEDstate. - Note: make sure that these state transitions are efficient.
- Any
Testing
You can test your code by clicking on the green dropdown menu in CoderPad and selecting “Run Tests (Part 3)”.
Once the tests pass, proceed to the next part.
Part 4 of 4
In this part, you will render the todo list so that an LLM can read it.
In the TodoList class, please implement a new method called render_for_llm which returns a string. The string should be an LLM-friendly representation of the todo list’s state. This should include the ID/status/description of each task as well as the dependencies between tasks. You do not need to filter out completed tasks (that is, you can show all tasks). Moreover, you do not need to write a natural language prompt—the focus is on the substantive representation of the todo list information.
There is no single “correct” representation format. Feel free to choose a representation format that you think will work well for LLMs, and consider what makes your chosen representation easy for an LLM to interpret.
Testing
You can test your code by clicking on the green dropdown menu in CoderPad and selecting “Run Tests (Part 4)”. You will also want to inspect the actual string (which will be available in the captured stdout) to make sure that the string looks as expected.
What to focus on
This exercise is designed to reveal practical engineering judgment. As you work, prioritize:
- Correctly translating the requirements into behavior.
- Choosing clear data structures for task lookup and dependency traversal.
- Preserving the behavior from earlier parts as the design evolves.
- Writing code that is easy to explain, test, and extend.
It is not unusual to reach the end of a live interview before completing every part. Aim for precise, working code rather than rushing through the entire exercise.
Refer to our hands-on coding interview guide for more tips and guidance.