Summary
I participated in a 1-1 coding interview with Incident.io, where the task involved extending an existing on-call pay calculator to incorporate special rates for specific periods like Christmas and New Year.
Full Experience
1-1 coding interview after the take home exercise. You have about 15 minutes to implement.
# On call pay calculator
## Background
At incident.io, we believe everyone should be on-call by default.
We acknowledge that presents an inconvenience and want to compensate people for
that - we pay a flat rate of £5/hour, pro-rated to the time you spend on-call.
Every month, we export our on-call data, and run a script to calculate how much
everyone should be paid. We then send this information to our accountants.
## The challenge
We've implemented the ability to pay users a flat rate based on a list of schedule
entries. Now we'd like you to help us with the following:
We'd like to start paying more for periods like Christmas and New Year.
This is already represented in our `pay_config.json` sample data, but we're not yet
using it anywhere.
* Pay periods will never overlap each other, but may overlap one or more schedule entries.
* Pay periods can start and end at any time (not just whole days, for example)
There is a test that is currently failing, which covers one possible scenario (although there
are a few more that you should consider). You can find it in `calculateAmountsOwed.test.ts`.
## Note on AI
We'd ask that you don't use an AI assistant for this part of the task, as
we'd like to see how you approach the problem and what decisions you make.
This includes auto-complete mode, as agents can get a bit eager and start
filling in entire blocks for you.
Input:
{
"entries": [
{
"user_id": "1",
"start_at": "2024-11-02T16:00:00Z",
"end_at": "2024-11-09T15:00:00Z"
},
...
]
}
Pay config:
{
"default_hourly_gbp": 500,
"periods": [
{
"name": "Christmas,
"start_at": "2024-12-23T00:00:00Z",
"end_at": "2024-12-26T00:00:00Z",
"hourly_gbp": 1000
},
{
"name": "New Year",
"start_at": "2024-12-31T00:00:00Z",
"end_at": "2025-01-02T00:00:00Z",
"hourly_gbp": 1500
}
]
}
The existing code:
export type PayPeriod = {
name: string
start_at: Date
end_at: Date
hourly_gbp: number
}
export type PayConfig = {
default_hourly_gbp: number
periods: PayPeriod[]
}
export type ScheduleEntry = {
user_id: string
start_at: Date
end_at: Date
}
export type AmountsOwed = { [key: string]: number }
// calculateAmountsOwed calculates how much each person is owed and logs the output to console
export const calculateAmountsOwed = (scheduleEntries: ScheduleEntry[], payConfig: PayConfig): void => {
const userTotals = calculateUserTotals(scheduleEntries, payConfig)
console.log(generateOutput(userTotals))
}
// calculateUserTotals calculates how much each person is owed by adding up all their shifts
export const calculateUserTotals = (
scheduleEntries: ScheduleEntry[],
payConfig: PayConfig
): AmountsOwed => {
let userTotals: AmountsOwed = {}
const amountPaidPerSecond = payPerSecond(payConfig.default_hourly_gbp)
for (const scheduleEntry of scheduleEntries) {
const scheduleEntryDurationSeconds =
(scheduleEntry.end_at.getTime() - scheduleEntry.start_at.getTime()) / 1000
const amountOwed = Math.round(
amountPaidPerSecond * scheduleEntryDurationSeconds
)
userTotals[scheduleEntry.user_id] ||= 0
userTotals[scheduleEntry.user_id] += amountOwed
}
return userTotals
}
// Given "pay per hour", convert the amount into "pay per second" to make
// calculations a bit easier. The result isn't rounded, which means we'll return
// fractions of a penny. That's OK, we'll round amounts later in the calculation.
const payPerSecond = (hourlyPay: number) => hourlyPay / 60 / 60
// generateOutput formats the amounts owed for each user into a string suitable for logging
// to the console or sending to an accountant.
const generateOutput = (userAmounts: AmountsOwed): string => {
let output = "Copy the below into a CSV file and send it to the accountants:\n"
for (let userID in userAmounts) {
let amount = userAmounts[userID]
output += `- User ${userID} is owed £${((amount * 1.0) / 100).toFixed(2)}\n`
}
return output
}
Interview Questions (1)
The task is to extend an existing on-call pay calculator to account for special pay periods like Christmas and New Year. Currently, the system pays a flat rate based on schedule entries. The goal is to modify the calculateUserTotals function to apply different hourly rates for time spent on-call during specified pay_config.json periods.
Background: Incident.io compensates individuals for being on-call at a flat rate of £5/hour, prorated. Monthly on-call data is processed to calculate payments.
The Challenge:
Implement the ability to pay more for periods like Christmas and New Year. The pay_config.json already contains this data but isn't being used.
- Pay periods will never overlap each other, but may overlap one or more schedule entries.
- Pay periods can start and end at any time (not just whole days).
- A failing test in
calculateAmountsOwed.test.tscovers one scenario that needs to be addressed.
Input Examples:
entries (Schedule Entries):
{
"entries": [
{
"user_id": "1",
"start_at": "2024-11-02T16:00:00Z",
"end_at": "2024-11-09T15:00:00Z"
},
...
]
}
pay_config:
{
"default_hourly_gbp": 500,
"periods": [
{
"name": "Christmas,
"start_at": "2024-12-23T00:00:00Z",
"end_at": "2024-12-26T00:00:00Z",
"hourly_gbp": 1000
},
{
"name": "New Year",
"start_at": "2024-12-31T00:00:00Z",
"end_at": "2025-01-02T00:00:00Z",
"hourly_gbp": 1500
}
]
}
Existing Code:
export type PayPeriod = {
name: string
start_at: Date
end_at: Date
hourly_gbp: number
}
export type PayConfig = {
default_hourly_gbp: number
periods: PayPeriod[]
}
export type ScheduleEntry = {
user_id: string
start_at: Date
end_at: Date
}
export type AmountsOwed = { [key: string]: number }
// calculateAmountsOwed calculates how much each person is owed and logs the output to console
export const calculateAmountsOwed = (scheduleEntries: ScheduleEntry[], payConfig: PayConfig): void => {
const userTotals = calculateUserTotals(scheduleEntries, payConfig)
console.log(generateOutput(userTotals))
}
// calculateUserTotals calculates how much each person is owed by adding up all their shifts
export const calculateUserTotals = (
scheduleEntries: ScheduleEntry[],
payConfig: PayConfig
): AmountsOwed => {
let userTotals: AmountsOwed = {}
const amountPaidPerSecond = payPerSecond(payConfig.default_hourly_gbp)
for (const scheduleEntry of scheduleEntries) {
const scheduleEntryDurationSeconds =
(scheduleEntry.end_at.getTime() - scheduleEntry.start_at.getTime()) / 1000
const amountOwed = Math.round(
amountPaidPerSecond * scheduleEntryDurationSeconds
)
userTotals[scheduleEntry.user_id] ||= 0
userTotals[scheduleEntry.user_id] += amountOwed
}
return userTotals
}
// Given "pay per hour", convert the amount into "pay per second" to make
// calculations a bit easier. The result isn't rounded, which means we'll return
// fractions of a penny. That's OK, we'll round amounts later in the calculation.
const payPerSecond = (hourlyPay: number) => hourlyPay / 60 / 60
// generateOutput formats the amounts owed for each user into a string suitable for logging
// to the console or sending to an accountant.
const generateOutput = (userAmounts: AmountsOwed): string => {
let output = "Copy the below into a CSV file and send it to the accountants:\n"
for (let userID in userAmounts) {
let amount = userAmounts[userID]
output += `- User ${userID} is owed £${((amount * 1.0) / 100).toFixed(2)}\n`
}
return output
}
Note on AI: The problem explicitly states that AI assistants should not be used.
Summary
I participated in a take-home exercise for Incident.io, which challenged me to develop a script for managing on-call schedules, including detecting overlaps and gaps, and flattening schedule and override entries into a single timeline.
Full Experience
Take home exercise at Incident.io.
On-Call scheduling take-home task
We expect this task to take you about an hour to complete.
Please feel free to use AI coding tools such as Cursor or Claude to complete the challenge, just as you would in a real-world scenario. However, the expectation is that you will fully understand the code you submit, and could comfortably explain it to a colleague or extend it.
Additionally, you can use any libraries that you choose.
Background
At incident.io, we believe everyone should be on-call by default.
It's also important that you can get 'cover' for parts of your on-call shift, for example if you want to go for a swim or you've had a bad night and need to catch up on sleep.
This is a script we use to figure out who was actually on-call when, based on a list of schedule entries (the original plan) and overrides (when someone else temporarily took the pager).
The Challenge
We'd like you to 'fill in the blanks' in the script, so that when someone runs the script it will load the data from input.json and then:
- Check that the schedule entries do not overlap with each other
- Check that there are no 'gaps' in the schedule (i.e. no time when no one was scheduled to be on-call).
- Check that the override entries do not overlap with each other
- Flatten the schedule entries and overrides into a single list of 'entries', that represents who was actually on-call at any given time.
Output
The script should either:
- Exit with an error if there are any gaps in the schedule, or overlapping overrides
- Write an
output.jsonfile in the data directory that contains the flattened list of entries.
Assumptions
You can assume that:
- The input data structure is valid (JSON is well-formed, dates are in ISO 8601 format, etc.).
- There will be no more than 100 schedule entries or override entries.
- All dates are in UTC.
- Overrides will not extend beyond the start or end of the schedule.
For the purposes of this task, you do not need to consider performance optimisations: we're much more interested in the structure, correctness and clarity of your code.
Understanding the on-call schedule
An example schedule export can be found in input.json.
There are two lists of entries in the data:
schedule_entries: This is a list of periods when a user is scheduled to be on-call. There should be no gaps in this list, meaning that one shift starts as the one before it ends.override_entries: This is a list of periods when a user temporarily takes over the pager from another user. These can overlap with the schedule entries, and should be compensated instead of the original user.
The input:
{
"schedule_entries": [
{
"user_id": "1",
"start_at": "2024-11-02T16:00:00Z",
"end_at": "2024-11-09T16:00:00Z"
},
...
],
"override_entries": [
{
"user_id": "3",
"start_at": "2024-11-09T15:00:00Z",
"end_at": "2024-11-09T20:00:00Z"
},
...
]
}
Interview Questions (1)
On-Call scheduling take-home task
We expect this task to take you about an hour to complete.
Please feel free to use AI coding tools such as Cursor or Claude to complete the challenge, just as you would in a real-world scenario. However, the expectation is that you will fully understand the code you submit, and could comfortably explain it to a colleague or extend it.
Additionally, you can use any libraries that you choose.
Background
At incident.io, we believe everyone should be on-call by default.
It's also important that you can get 'cover' for parts of your on-call shift, for example if you want to go for a swim or you've had a bad night and need to catch up on sleep.
This is a script we use to figure out who was actually on-call when, based on a list of schedule entries (the original plan) and overrides (when someone else temporarily took the pager).
The Challenge
We'd like you to 'fill in the blanks' in the script, so that when someone runs the script it will load the data from input.json and then:
- Check that the schedule entries do not overlap with each other
- Check that there are no 'gaps' in the schedule (i.e. no time when no one was scheduled to be on-call).
- Check that the override entries do not overlap with each other
- Flatten the schedule entries and overrides into a single list of 'entries', that represents who was actually on-call at any given time.
Output
The script should either:
- Exit with an error if there are any gaps in the schedule, or overlapping overrides
- Write an
output.jsonfile in the data directory that contains the flattened list of entries.
Assumptions
You can assume that:
- The input data structure is valid (JSON is well-formed, dates are in ISO 8601 format, etc.).
- There will be no more than 100 schedule entries or override entries.
- All dates are in UTC.
- Overrides will not extend beyond the start or end of the schedule.
For the purposes of this task, you do not need to consider performance optimisations: we're much more interested in the structure, correctness and clarity of your code.
Understanding the on-call schedule
An example schedule export can be found in input.json.
There are two lists of entries in the data:
schedule_entries: This is a list of periods when a user is scheduled to be on-call. There should be no gaps in this list, meaning that one shift starts as the one before it ends.override_entries: This is a list of periods when a user temporarily takes over the pager from another user. These can overlap with the schedule entries, and should be compensated instead of the original user.
The input:
{
"schedule_entries": [
{
"user_id": "1",
"start_at": "2024-11-02T16:00:00Z",
"end_at": "2024-11-09T16:00:00Z"
},
...
],
"override_entries": [
{
"user_id": "3",
"start_at": "2024-11-09T15:00:00Z",
"end_at": "2024-11-09T20:00:00Z"
},
...
]
}