# OOD Interview Drill: Design a File System Like a Pro

# OOD Interview Drill: Design a File System Like a Pro

<img src="https://bugfree-s3.s3.amazonaws.com/mermaid_diagrams/image_1770920162737.png" alt="File System Diagram" style="max-width:700px; width:100%; height:auto; display:block; margin:12px 0;" />

Designing a file system is a popular object-oriented design (OOD) interview question. The goal in an interview is not to build a production-grade distributed file system, but to demonstrate clear thinking: gather requirements, define clean abstractions, pick appropriate patterns, and discuss trade-offs.

Below is a concise, interview-ready approach you can follow.

## 1) Gather requirements (ask clarifying questions)

- Core operations: create/read/update/delete (CRUD) for files and directories
- Navigation: absolute/relative paths, listing directory contents
- Metadata: name, size, timestamps (creation, modification), type
- Permissions and access control (basic: read/write/execute)
- Concurrency: concurrent reads/writes, locking semantics
- Persistence vs in-memory: is the FS persisted to disk or ephemeral?
- Extra features (optional): observers/notifications, symbolic links, hard links, quotas

State assumptions up front (e.g., single-machine vs distributed, persistence required, permission model complexity).

## 2) Model the core classes

Use a small, clear set of classes to represent the file-system tree and operations.

- INode (abstract/base)
  - common metadata: name, creationDate, modifiedDate, permissions
  - operations: getName(), getMetadata()

- File (inherits INode)
  - fields: content (or pointer to blocks), size, fileType
  - operations: open(), read(), write(), delete(), truncate()

- Directory (inherits INode)
  - fields: map<string, INode> children
  - operations: addChild(), removeChild(), listChildren(), getChild(name)

- FileSystem (Singleton)
  - field: rootDirectory
  - operations: createFile(path), createDirectory(path), delete(path), move(src, dst), find(path)

- (Optional) Permission, PathResolver, Observer interfaces/classes

Example pseudocode (class skeleton):

```
class INode {
  String name;
  Date createdAt, modifiedAt;
  Permission permissions;
}

class File extends INode {
  byte[] content;   // or handle to storage blocks
  int size;
  read(offset, len);
  write(offset, data);
}

class Directory extends INode {
  Map<String, INode> children;
  addChild(name, node);
  removeChild(name);
  listChildren();
}

class FileSystem {
  private static FileSystem instance; // Singleton
  Directory root;
  find(path);
  createFile(path);
  createDirectory(path);
}
```

## 3) Design patterns to use

- Composite: represent files and directories as a tree where both implement a common INode interface. This simplifies recursive algorithms (e.g., delete, list).
- Singleton: if the system mandates exactly one instance of FileSystem in the process, use Singleton to expose the root and APIs.
- Observer: useful if other components need notifications on changes (e.g., watch()/inotify-like behavior).

## 4) Path resolution and core algorithms

- Path parsing: split by separators, resolve "." and "..", handle absolute vs relative paths.
- find(path): walk from root (or current dir) following components; at each Directory, look up child by name (map lookup, O(1) average).
- createFile(path): find parent directory, check name collisions, insert new File node.

Complexity: a path operation is O(depth) where depth is number of components.

## 5) Concurrency and consistency

- Concurrency model: decide optimistic vs pessimistic locking. For interviews, outline simple locks:
  - Per-directory locks for structural changes (add/remove child)
  - Per-file read-write locks (multiple concurrent readers, exclusive writer)
- Consider atomic operations for move/rename to prevent transient inconsistencies.

## 6) Persistence, storage, and scaling (high-level)

- For an in-memory interview toy FS, store content directly in memory or as byte arrays.
- For a persistent or large-scale FS, separate metadata and data:
  - Metadata server stores the tree and attributes (could be a DB or in-memory store persisted to disk)
  - Data nodes store file blocks (chunking, block allocation, replication)
- For distributed scaling, discuss components: metadata service, chunk servers, caching, replication, and consistency guarantees.

Note: deep implementation details (block allocation, journaling, crash recovery) are usually out of scope unless asked.

## 7) Permissions and security

- Represent permissions as a simple read/write/execute bitset or a richer ACL depending on requirements.
- Enforcement: check permissions at the FileSystem API layer before performing operations.

## 8) Extra features to mention (optional)

- Symbolic and hard links: how they affect path resolution and storage
- Watchers/notifications (Observer)
- Quotas and size enforcement per directory/user
- Caching strategies for hot files and directories

## 9) Example flow: create a file at /a/b/c.txt

1. find("/a/b") — walk root -> a -> b
2. lock directory b for write
3. check no child named c.txt
4. insert File node into b.children and set metadata
5. release lock

## 10) Interview tips

- Start by clarifying requirements and constraints.
- Sketch the high-level design and main classes before coding.
- Use familiar patterns (Composite, Singleton) and justify why.
- Discuss concurrency and persistence trade-offs.
- Implement a core method (e.g., find or createFile) and explain edge cases.

---

This structure shows you know how to: collect requirements, model clear abstractions, pick patterns, and reason about complexity and trade-offs — exactly what interviewers want to see.

Happy designing!
