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:

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:

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.