Course contents

LLD problem — File System

Sign in to run this lab and save your progress

Learn

Before you start

File System is the definitive Composite pattern design problem. If module 3.10 clicked, this lesson writes itself.

The Composite signal, unmistakable

File and Directory both need size(). Directory's size is the sum of its children's sizes. If ANY child is a File, use its size. If ANY child is a Directory, RECURSE.

This is exactly the Composite payoff: size() on the ROOT walks the whole tree without caring what's a leaf and what's a container. sum(c.size() for c in children) is one line — no isinstance needed.

Your design.md Patterns section should name Composite AND say why the isinstance-free recursion is the win.

The signal check

Ask: "would my Directory code contain isinstance(child, File) or isinstance(child, Directory)?"

If yes → you're branching on kind, which is exactly the smell Composite removes. Fix: unify under a Node interface where both types answer the same questions (size, walk, ls).

If no → Composite is doing its job.

Path parsing

Paths look complex but they're mechanical:

  • Absolute (starts with /) — strip leading /, split on /, drop empty strings.
  • /[] = root.
  • /foo/bar["foo", "bar"].
  • foo/bar (no leading /) → invalid, raise.

Walk from root: for each component, go into the child Directory. If missing OR the current node is a File, raise.

The rules

Three matter:

  1. No orphaned children. Every Node lives in exactly one parent's children dict. delete() removes it from that dict; no other references exist.
  2. Root can't be deleted. Enforced by the check at the top of delete().
  3. Every non-root path has exactly one parent. Enforced by the mkdir/create validation — you can only add a node inside an existing parent Directory.

Each has a clear enforcement point. Name them.

Where this shows up in real code

  • Every operating system's file system (obviously)
  • Version control systems (git tracks trees this way)
  • Cloud object stores' "folder" abstraction
  • IDE project trees
  • JSON documents (value / array / object all walked uniformly)
  • DOM (Element / TextNode both walked)

All the same shape. Composite is one of the most-used patterns in production code.

Interviewer follow-ups

  • "How would you add symlinks?" — new Node subclass that forwards operations to its target. Careful about cycles.
  • "How would you make this thread-safe?" — a lock per directory, or one big lock. Complex tradeoffs.
  • "How would you handle 1M files?" — memory concern. Move to lazy-load from disk. Different problem — you've been building an in-memory FS.

Your task

In-memory file system with directories and files, supporting path-based operations. Composite pattern signal is central.

The problem

A FileSystem() with a root directory /.

Operations:

  • mkdir(path) — create a directory. Missing intermediate dirs are NOT auto-created (raise ValueError).
  • create(path, content="") — create a file. Parent dir must exist. Fails if a node already exists at path.
  • write(path, content) — overwrite file contents. Fails if path is not a file.
  • read(path) — return file contents. Fails if not a file.
  • ls(path) — list entries at path. If path is a file, returns [path.split("/")[-1]]. If directory, returns sorted list of immediate child names.
  • size(path) — for file: len(content). For directory: sum of sizes of all descendants.
  • delete(path) — remove file OR entire directory subtree. Fails if path is root or doesn't exist.

Paths:

  • Absolute, use / separator.
  • / is the root directory.
  • /foo/bar — normal absolute path.
  • Empty path or non-absolute paths raise ValueError.

Requirement-gathering questions

  1. Symlinks / hardlinks? — Not for MVP.
  2. Permissions? — Not for MVP.
  3. Case sensitivity? — Case-sensitive.
  4. File size limits? — None.
  5. Auto-create parent dirs on mkdir? — No, must exist.

Design goals

  • size(path) — recursive over descendants uniformly for files and directories.
  • delete(path) — must clean up the entire subtree.
  • Invariants: no orphaned children; root can't be deleted; every non-root path has exactly one parent.

Your task

This problem is textbook Composite (module 3.10). Your design.md's Patterns section should name it and defend the choice.

Public API:

from main import FileSystem

fs = FileSystem()
fs.mkdir("/docs")
fs.create("/docs/intro.md", "# Welcome")
fs.write("/docs/intro.md", "# Updated")
fs.read("/docs/intro.md")           # "# Updated"
fs.ls("/docs")                      # ["intro.md"]
fs.ls("/")                          # ["docs"]
fs.size("/docs/intro.md")           # 9
fs.size("/docs")                    # 9 (sum of all files)
fs.delete("/docs")
fs.ls("/")                          # []

Click Submit to run the tests.

Starter files

Preview only — open the lab to edit and run.

files_dir
10-file-system-starter