Build a Merkle Tree over a Directory with Hash Lookup and Tree Diff

Read the full interview experience this question came from →

Quick Overview

Implement a Merkle tree over a real directory in Python, with N-ary directory nodes, a build method, a hash lookup by path, and a diff that compares two trees, and write the tests yourself. It tests deterministic hashing design, file-system traversal, pruning identical subtrees, and disciplined testing under time pressure.

Build a Merkle Tree over a Directory with Hash Lookup and Tree Diff

Company: Cursor

Role: Software Engineer

Category: Software Engineering Fundamentals

Difficulty: medium

Interview Round: Technical Screen

Build a Merkle tree that represents a directory on the local file system, so that two snapshots of a directory can be compared quickly. Every file is a leaf whose hash depends on the file's content. Every directory is an internal node whose hash is computed from its children, and a directory can have any number of children rather than exactly two as in a binary Merkle tree. Implement the class below in Python, working against the real file system. No test harness is provided: write your own tests for all three methods. The interview lasts 60 minutes, and the three methods and the tests are all expected to be written and working within it. ```python class MerkleTree: def build(self, path): """Build the tree for the directory at `path` and return it.""" def get_hash(self, path, tree): """Return the hash of the file or directory at `path` inside `tree`.""" def diff(self, tree_a, tree_b): """Compare two trees and report what differs between them.""" ``` ### Constraints and Clarifications - The tree is built by reading an actual directory from disk, not an in-memory description of one. - Directory nodes are N-ary: a directory may hold any number of files and subdirectories. - The tree returned by `build` is the value passed to `get_hash` and `diff`, so its representation is your choice. ### Clarifying Questions - Is the `path` given to `get_hash` absolute, or relative to the directory the tree was built from? - What should `diff` return: a flat list of changed paths, or paths labeled as added, removed and modified? When a whole directory is added, should `diff` report the directory once or every file inside it? - Should an entry's name contribute to the hashes, so that renaming a file changes its parent directory's hash? - How should empty directories, symbolic links, hidden files and unreadable files be treated? - Does only file content matter, or also metadata such as permissions and modification times? - Which hash function should be used, and in what form should hashes be returned? - What should `get_hash` do for a path that is not in the tree? ### Part 1 — Build the tree Implement `build(path)`: walk the directory, create a leaf for every file and an internal node for every directory, and compute every node's hash from the bottom up. ```hint Make directory hashes order-independent The file system does not promise any particular order when it lists a directory, yet the same contents must always produce the same hash. ``` ```hint Keep the encodings unambiguous Consider whether a file and a directory, or two different sets of children, could ever feed exactly the same bytes into the hash function, and how to rule that out. ``` #### What This Part Should Cover - A node structure that holds the name, the kind of entry, the hash and the children - Deterministic directory hashes that do not depend on listing order - What goes into each hash (content, names, kind of entry) and why - Reading large files without loading them into memory at once ### Part 2 — Look up a hash Implement `get_hash(path, tree)`: return the hash stored for the file or directory at `path`. ```hint Walk, do not rebuild The tree already holds every hash. The path only says which children to follow, starting from the root. ``` #### What This Part Should Cover - Resolving the path into components relative to the tree's root - Behavior for the root itself and for paths that are not in the tree - Lookup cost in terms of the path's depth ### Part 3 — Diff two trees Implement `diff(tree_a, tree_b)`: report the files and directories that were added, removed or changed between the two trees. ```hint Let equal hashes end the search Ask what two equal hashes tell you about everything underneath those two nodes. ``` #### What This Part Should Cover - Skipping identical subtrees without visiting them - Matching children by name and classifying each difference - A path that is a file in one tree and a directory in the other - Deterministic output order, and cost in terms of how much actually changed ### Part 4 — Test it Write tests showing that all three methods work. ```hint Build fixtures on disk Create small directory trees in a temporary location, so that each test controls exactly what differs between two builds. ``` #### What This Part Should Cover - Temporary-directory fixtures that are always cleaned up - Identical trees, a single edited file, added and removed entries, nested changes, and empty directories - Assertions on which hashes change and which stay the same ### What a Strong Answer Covers - The Merkle property: editing one file changes exactly the hashes on the path from that file to the root - A clean split between walking the disk, hashing, and comparing trees - Complexity of `build`, `get_hash` and `diff`, stated and justified - Complete, working code and passing tests within the time limit ### Follow-up Questions - One file changes after the tree is built. How do you update the tree without rebuilding all of it? - The two directories live on different machines. How would you use the hashes to find the differences while sending as little data as possible? - The directory holds millions of files. What would you change in `build` to make it fast? - How would you detect that a file was moved or renamed, rather than reporting a deletion and an addition?

Overview: Implement a Merkle tree over a real directory in Python, with N-ary directory nodes, a build method, a hash lookup by path, and a diff that compares two trees, and write the tests yourself. It tests deterministic hashing design, file-system traversal, pruning identical subtrees, and disciplined testing under time pressure.

Read the full Cursor Software Engineer interview experience this question came from

|Home/Software Engineering Fundamentals/Cursor
Cursor logo
Cursor
Sep 16, 2026
mediumSoftware EngineerTechnical ScreenSoftware Engineering Fundamentals
0
0

Build a Merkle tree that represents a directory on the local file system, so that two snapshots of a directory can be compared quickly. Every file is a leaf whose hash depends on the file's content. Every directory is an internal node whose hash is computed from its children, and a directory can have any number of children rather than exactly two as in a binary Merkle tree.

Implement the class below in Python, working against the real file system. No test harness is provided: write your own tests for all three methods. The interview lasts 60 minutes, and the three methods and the tests are all expected to be written and working within it.

class MerkleTree:
    def build(self, path):
        """Build the tree for the directory at `path` and return it."""

    def get_hash(self, path, tree):
        """Return the hash of the file or directory at `path` inside `tree`."""

    def diff(self, tree_a, tree_b):
        """Compare two trees and report what differs between them."""

Constraints and Clarifications

  • The tree is built by reading an actual directory from disk, not an in-memory description of one.
  • Directory nodes are N-ary: a directory may hold any number of files and subdirectories.
  • The tree returned by build is the value passed to get_hash and diff , so its representation is your choice.

Clarifying Questions Guidance

  • Is the path given to get_hash absolute, or relative to the directory the tree was built from?
  • What should diff return: a flat list of changed paths, or paths labeled as added, removed and modified? When a whole directory is added, should diff report the directory once or every file inside it?
  • Should an entry's name contribute to the hashes, so that renaming a file changes its parent directory's hash?
  • How should empty directories, symbolic links, hidden files and unreadable files be treated?
  • Does only file content matter, or also metadata such as permissions and modification times?
  • Which hash function should be used, and in what form should hashes be returned?
  • What should get_hash do for a path that is not in the tree?

Part 1 — Build the tree

Implement build(path): walk the directory, create a leaf for every file and an internal node for every directory, and compute every node's hash from the bottom up.

What This Part Should Cover Guidance

  • A node structure that holds the name, the kind of entry, the hash and the children
  • Deterministic directory hashes that do not depend on listing order
  • What goes into each hash (content, names, kind of entry) and why
  • Reading large files without loading them into memory at once

Part 2 — Look up a hash

Implement get_hash(path, tree): return the hash stored for the file or directory at path.

What This Part Should Cover Guidance

  • Resolving the path into components relative to the tree's root
  • Behavior for the root itself and for paths that are not in the tree
  • Lookup cost in terms of the path's depth

Part 3 — Diff two trees

Implement diff(tree_a, tree_b): report the files and directories that were added, removed or changed between the two trees.

What This Part Should Cover Guidance

  • Skipping identical subtrees without visiting them
  • Matching children by name and classifying each difference
  • A path that is a file in one tree and a directory in the other
  • Deterministic output order, and cost in terms of how much actually changed

Part 4 — Test it

Write tests showing that all three methods work.

What This Part Should Cover Guidance

  • Temporary-directory fixtures that are always cleaned up
  • Identical trees, a single edited file, added and removed entries, nested changes, and empty directories
  • Assertions on which hashes change and which stay the same

What a Strong Answer Covers Guidance

  • The Merkle property: editing one file changes exactly the hashes on the path from that file to the root
  • A clean split between walking the disk, hashing, and comparing trees
  • Complexity of build , get_hash and diff , stated and justified
  • Complete, working code and passing tests within the time limit

Follow-up Questions Guidance

  • One file changes after the tree is built. How do you update the tree without rebuilding all of it?
  • The two directories live on different machines. How would you use the hashes to find the differences while sending as little data as possible?
  • The directory holds millions of files. What would you change in build to make it fast?
  • How would you detect that a file was moved or renamed, rather than reporting a deletion and an addition?
Loading comments...