InterviewDB Question

Build Architect: Design a Dependency-Aware Build System

Question Details

Problem

Design a build system that compiles targets in dependency order, using cached results where possible. Each target has a list of source files and dependencies on other targets. A target needs rebuilding if any source file or dependency has changed since the last build.

python
class BuildSystem:
    def add_target(
        self,
        name: str,
        sources: list[str],
        deps: list[str],
        build_fn: callable
    ) -> None:
    def build(self, target: str) -> str:

**returns** build artifact path
    def invalidate(self, source_file: str) -> None:  # mark file as changed

Example

build.add_target("libfoo", sources=["foo.c"], deps=[], build_fn=compile_c)
build.add_target("app",    sources=["main.c"], deps=["libfoo"], build_fn=link)

build.build("app")   # compiles libfoo, then app
build.build("app")   # fully cached: nothing recompiles
build.invalidate("foo.c")
build.build("app")   # recompiles libfoo AND app (transitive invalidation)

Follow-ups

  1. How do you detect circular dependencies?
  2. Which targets can be built in parallel? How do you schedule them?
  3. How would you implement content-based caching (hash of inputs) vs. timestamp-based?

Full Details

Problem

Design a build system that compiles targets in dependency order, using cached results where possible. Each target has a list of source files and dependencies on other targets. A target needs rebuilding if any source file or dependency has changed since the last build.

python
class BuildSystem:
    def add_target(
        self,
        name: str,
        sources: list[str],
        deps: list[str],
        build_fn: callable
    ) -> None:
    def build(self, target: str) -> str:

**returns** build artifact path
    def invalidate(self, source_file: str) -> None:  # mark file as changed

Example

build.add_target("libfoo", sources=["foo.c"], deps=[], build_fn=compile_c)
build.add_target("app",    sources=["main.c"], deps=["libfoo"], build_fn=link)

build.build("app")   # compiles libfoo, then app
build.build("app")   # fully cached: nothing recompiles
build.invalidate("foo.c")
build.build("app")   # recompiles libfoo AND app (transitive invalidation)

Follow-ups

  1. How do you detect circular dependencies?
  2. Which targets can be built in parallel? How do you schedule them?
  3. How would you implement content-based caching (hash of inputs) vs. timestamp-based?
Free preview. Unlock all questions →

Topics

Coding Onsite Phone