Machine coding · Matching engines
Interviewer allocation
- Reported once
- SDE-2
- Strategy
- Registry
- 1 source
One candidate write-up. Treat it as a single data point.
Interviewers and interviewees each register for one of two interview types and list the time slots they prefer, and the system pairs them by type and slot. The code should be object oriented, easy to extend with a new interview type or a new slot, ready to demonstrate, and clear about errors such as a slot outside the allowed range.
Restated in our own words, not a quote from Flipkart or any page. The answer below is written for this site.
Clarify first
Pin the scope down before you name a single class.
Questions to ask: how are the two interview types identified, and could there be more later? Are slots fixed-length, and drawn from a fixed list? Does each person give one slot or several in order of preference? Can one interviewer take several interviews in different slots? Should pairing happen as people register or in a batch run? What should happen to someone who cannot be matched? Does it matter which of several free interviewers is chosen?
The report names two interview types, preferred timings, assignment by type and timing, follow-ups about adding a type or a slot, and a run with an out-of-range slot. Everything else below is assumption. Assumed scope: types and slots are configured as data and can be added at runtime. A slot is an hour number from an allowed set. An interviewer offers a set of slots, an interviewee lists slots in preference order. An interviewer takes at most one interview per slot and an interviewee gets at most one interview. Allocation is a batch call that walks interviewees in registration order and leaves unmatched people waiting for the next call. Out of scope: time zones, durations longer than a slot, notifications and rescheduling.
Entities and relationships
Who owns what, and who talks to whom.
- Scheduler is the service. It owns the set of interview types, the set of allowed slots, both registries and the chosen assignment strategy, and exposes
registerInterviewer,registerInterviewee,allocate,withdrawandunmatched. - Interviewer holds an id, a type, the set of slots offered and the set of slots already booked.
- Interviewee holds an id, a type, an ordered list of preferred slots and the interview they were given, if any.
- Interview is the plain result: interviewee, interviewer, type and slot.
- Assignment strategy is an interface with
pick(candidates), implemented by FirstFree and LeastLoaded. It chooses among interviewers who are equally valid.
Relationships: the scheduler has many interviewers and many interviewees, uses one strategy, and creates an Interview each time it pairs two people.
The design
The class map, the main flow and the patterns that earn their place.
- Registration checks the type and every slot against the configured sets.
- Interviewees are kept in registration order and are served in that order.
- Interviewers record the slots they offer and the slots already booked.
- When several interviewers are free, the strategy chooses one.
- The pairing is stored on the interviewee as an Interview.
Open on the whiteboardAll 2 diagrams on one tidy board, with a step-by-step walkthrough.
The design goal is that adding a type or a slot never edits the matching code. The scheduler holds both as data: a Set of type names and a Set of allowed slots. addType and addSlot are single calls, and matching only ever compares a type value and a slot value for equality. If a new type later needs behaviour of its own, such as a different duration or a panel, the Set becomes a registry of type objects, but the matching loop stays the same.
Validation sits at the boundary in one check method. It rejects a duplicate id, an unknown type, an empty slot list and any slot that is not in the allowed set, each with a message that names the problem, and nothing is stored when it throws. That covers the out-of-range slot case directly.
Which interviewer gets a given interviewee is the only policy that varies, so it is isolated behind the assignment strategy. FirstFree takes the earliest registered. LeastLoaded takes the one with the fewest booked slots, which spreads the work. This is the Strategy pattern, and it matters because fairness among interviewers is the first thing an interviewer will ask you to change.
allocatewalks interviewees in registration order and skips anyone who already has an interview, which makes the call repeatable.- For each preferred slot in order it asks
freeInterviewers(type, slot): interviewers of the same type who offer that slot and have not booked it. - If the list is empty it tries the next preferred slot. Otherwise the strategy picks one, the slot is added to that interviewer's booked set and an Interview is recorded on the interviewee.
- Someone with no free option stays unmatched, and
unmatchedlists them. withdrawremoves an interviewee and frees any slot they held, so the nextallocatecan offer it to someone waiting.
Cost is about interviewees times slots each times interviewers; indexing interviewers by type and slot would make each lookup constant time. The trade-off is that first come first served is greedy, not a maximum matching. If one interviewer offers slots 10 and 11, a first person who lists 10 and 11 takes 10, and a second person who lists only 10 is left out although both could be seated. Fixing that needs augmenting paths and means moving someone who may already have been told their slot, so I kept the greedy rule and would name the limit out loud. For concurrent callers, wrap allocate in a lock or book with a conditional write on the interviewer and slot.
The code
Runnable JavaScript, with the checks it passes.
class Interviewer {
constructor(id, type, slots) {
this.id = id;
this.type = type;
this.slots = new Set(slots);
this.booked = new Set();
}
}
class Interviewee {
constructor(id, type, slots) {
this.id = id;
this.type = type;
this.slots = slots;
this.interview = null;
}
}
class FirstFree {
pick(candidates) {
return candidates[0];
}
}
class LeastLoaded {
pick(candidates) {
return candidates.reduce((best, one) => (one.booked.size < best.booked.size ? one : best));
}
}
class Scheduler {
constructor(types, slots, strategy) {
this.types = new Set(types);
this.allowedSlots = new Set(slots);
this.strategy = strategy;
this.interviewers = new Map();
this.interviewees = new Map();
}
addType(name) {
this.types.add(name);
}
addSlot(slot) {
this.allowedSlots.add(slot);
}
check(registry, id, type, slots) {
if (registry.has(id)) throw new Error("duplicate id " + id);
if (!this.types.has(type)) throw new Error("unknown interview type " + type);
if (slots.length === 0) throw new Error("at least one slot is required");
for (const slot of slots) {
if (!this.allowedSlots.has(slot)) throw new Error("slot " + slot + " is not offered");
}
}
registerInterviewer(id, type, slots) {
this.check(this.interviewers, id, type, slots);
this.interviewers.set(id, new Interviewer(id, type, slots));
}
registerInterviewee(id, type, slots) {
this.check(this.interviewees, id, type, slots);
this.interviewees.set(id, new Interviewee(id, type, slots));
}
freeInterviewers(type, slot) {
return [...this.interviewers.values()].filter(
(one) => one.type === type && one.slots.has(slot) && !one.booked.has(slot)
);
}
allocate() {
const made = [];
for (const person of this.interviewees.values()) {
if (person.interview) continue;
for (const slot of person.slots) {
const free = this.freeInterviewers(person.type, slot);
if (free.length === 0) continue;
const interviewer = this.strategy.pick(free);
interviewer.booked.add(slot);
person.interview = { interviewee: person.id, interviewer: interviewer.id, type: person.type, slot };
made.push(person.interview);
break;
}
}
return made;
}
withdraw(id) {
const person = this.interviewees.get(id);
if (!person) return false;
if (person.interview) this.interviewers.get(person.interview.interviewer).booked.delete(person.interview.slot);
this.interviewees.delete(id);
return true;
}
unmatched() {
return [...this.interviewees.values()].filter((person) => !person.interview).map((person) => person.id);
}
}
Runs here in your browser, in a sandboxed worker. Nothing is sent anywhere.
Try your own input
Write JavaScript that calls the code above and prints with console.log. It runs after the solution.
Extend it
The change requests an interviewer throws next.
- How would you add an interview type that needs a panel of two interviewers, and where does that rule live so that the matching loop stays generic?
- How would you change allocation to maximise the number of pairs, and what do you do about people who were already told their slot?
- How would you move from hour numbers to real time ranges, so that a 90 minute interview blocks two neighbouring slots?
- How would you allocate as each person registers instead of in a batch, and what ordering guarantee do waiting people get?
- What should happen when an interviewer is removed after being booked, and who needs to be told?
Where candidates slip
The mistakes this problem punishes.
- Hard-coding the two types and the slot range as enums or if-else chains, so that the follow-up "add a new type" means editing the matching code.
- Skipping validation, throwing a bare generic exception, or validating after the person has already been stored, so a rejected slot still leaves a half-registered person.
- Matching on slot only and forgetting the type, or booking one interviewer twice in the same slot, or giving one interviewee two interviews.
- Making
allocatenon-repeatable, so a second call duplicates interviews or loses the first ones. - Reaching for an optimal matching algorithm before the basic flow, the demo and the edge cases all work. The brief stresses a demo that runs and handles edge cases.
Have a go before you read.
Interviews reward the sketch you make cold. Clarify out loud, draw it, then compare with the answer.
Now make it stick
Where this was reported
Public pages only. Companies do not confirm these, and they rotate their questions, so treat each link as one person's account or one list's tag.