Turning Interview Code into a Reusable Library or API
Company: Jane Street
Role: Software Engineer
Category: Software Engineering Fundamentals
Difficulty: medium
Interview Round: Technical Screen
You have just finished implementing a small game engine during a coding interview: a board data structure for a two-player piece-dropping game, a function that applies a move (inserting a piece at the bottom of a column and pushing that column's pieces up one row), and a routine that detects a winner (`k` consecutive pieces of one color in a row or column). The code works and passes the interviewer's test cases, but it was written under time pressure as a single script.
The interviewer now asks:
> "Suppose we wanted to turn this code into a library that other engineers depend on — or expose it behind an API. What would you change, and what would you add?"
Walk through the concrete steps you would take, and explain why each one matters.
```hint How to organize your answer
Answer in layers rather than as a grab-bag: (1) code-level polish (naming, decomposition, docstrings, type hints), (2) interface design (what callers can do and what stays hidden), (3) robustness (validation, errors, edge-case semantics), (4) delivery (tests, packaging, versioning) — and, for an API, (5) transport and state management.
```
```hint Think contract, not code
A library is a **contract**. Start from what you promise callers — the public functions/classes, their invariants, and the documented behavior of edge cases (e.g., both players winning on the same move) — then work backward to the implementation changes that protect that contract.
```
### Constraints & Assumptions
- The interview implementation is a few hundred lines of working code (e.g., Python), single-threaded, in-memory, and logically correct.
- "Library" means other engineers import and call your code in-process; "API" means a network service that other systems call.
- No specific scale target was given; treat correctness and usability as primary and performance as secondary.
### Clarifying Questions to Ask
- Who are the consumers — my own team, other internal teams, or external users? This determines documentation depth, stability guarantees, and how defensive the interface must be.
- Is the goal an importable library, a network API, or a library that a service will wrap later?
- Is the rule set frozen, or should the design leave room for variants (different `k`, diagonal wins, more than two players)?
- Does game state need to survive process restarts (persistence), or is in-memory state acceptable?
- Could multiple callers mutate the same game concurrently?
### What a Strong Answer Covers
- **Interface and encapsulation:** a deliberately small public surface with internals hidden, rather than exposing raw data structures.
- **Contract quality:** clear naming, docstrings, type annotations, and explicitly documented edge-case semantics (e.g., simultaneous winners, moves after the game has ended).
- **Robustness:** input validation and a coherent error strategy instead of silent misbehavior on bad input.
- **Verification and delivery:** tests that pin down the contract, plus packaging, versioning, and CI practices that make the code safe to depend on.
- **Library vs. API distinction:** recognizing that a network API adds transport design, state management, idempotency, and compatibility concerns beyond code cleanup.
### Follow-up Questions
- How would you evolve the library if a new rule variant (for example, diagonal wins or a third player) had to be added without breaking existing users?
- If this became a network API, would you make the service stateful or stateless? What are the trade-offs?
- Once other teams depend on the library, how would you roll out a breaking change to the public interface?
- If you only had time to write five tests before shipping, which behaviors would you pin down first?