career 16 min read

Interview prep, senior edition: a system design checklist, one backtracking template and what loops grade on

My 2022 prep notes for senior and staff interviews: a system design checklist, one Python DFS for seven backtracking problems, rubrics and a STAR story bank.

Hello folks,

Back in 2016 I wrote the best resource to prepare for a software engineering interview, fresher edition. Six years later, the interviews look very different! Since mid-June I’ve been interviewing for senior and staff engineer roles, and my Obsidian vault has filled up with prep notes along the way.

So, this post is the senior edition. 🙌 It has my system design checklist, one Python DFS skeleton that covers seven backtracking problems, what the rounds are scored on (pieced together from what recruiters and hiring managers told me before the loops), and how I prepped the behavioural round with a STAR story bank. The stack I prepped from is at the end.

My system design checklist

I wrote this down on 23 June, early in the loops. It’s ten steps, and you’re meant to work through them in order.

1. Gather requirements (functional). What’s the use case? Who is the customer? Why is this needed?

2. Discuss constraints (non-functional). Any limitations, and what’s allowed versus what isn’t.

3. Do capacity estimation.

  • Traffic: read requests per second, write requests per second
  • Storage: what it takes to keep about 3 years’ worth of the main object
  • Bandwidth: bytes per second in and out
  • Cache: memory for the hot reads, using the 80-20 rule

4. Define the system APIs. REST style, mostly.

5. Draw the top-level diagram. Client, web servers, platform, database, worker services.

6. Discuss the database choice. Schema, SQL or NoSQL, and why.

7. Make it work for a single user first. Get to an MVP before anything else.

8. Discuss scaling. Find the bottlenecks and single points of failure, then reach for the usual tools: load balancers, caching, replication, message queues, async workers.

9. Observability.

10. Test and review your design. Treat this like the end of a coding round:

  • Walk through the system and check that every customer need is met
  • Check there’s an API for every customer ask
  • Walk through the failover scenarios, not just the happy path
  • Make sure you drew system boundaries, so each part is easy to explain

Step 10 is the dry run. You walk the design with a scenario the same way you’d walk code with a test case before you say “done”. Step 7 is the one my design feedback was about (more on that below).

For the whiteboard itself, my go-to tool is Excalidraw. It’s open source, and I use it for anything whiteboarding: system design, control flow, wireframes. I drew my board in it during a design round last November too.

One backtracking template, seven problems

Subsets, combinations, combination sum and permutations look like different questions, but trust me, they’re the same recursion! I didn’t come up with this. It’s from a LeetCode Discuss post I saved to my notes, which groups seven LeetCode problems that all use the same dfs(nums, path, res) shape:

    1. Subsets and 90. Subsets II
    1. Combinations
    1. Combination Sum and 40. Combination Sum II
    1. Permutations and 47. Permutations II

Here’s that shape boiled down to one skeleton:

def solve(nums):
    res = []
    nums.sort()                          # only needed when there are duplicates

    def dfs(choices, path):
        if is_complete(path):            # what counts as an answer
            res.append(path)
            return                       # subsets skip this and record every path
        for i in range(len(choices)):
            if i > 0 and choices[i] == choices[i - 1]:
                continue                 # skip duplicates at the same depth
            dfs(remaining(choices, i), path + [choices[i]])

    dfs(nums, [])
    return res

Each problem on that list is this function with a couple of knobs turned:

Problem When do you record path? What do you pass down?
Subsets Always, on entry choices[i+1:]
Combinations (k) When len(path) == k choices[i+1:]
Combination Sum (reuse allowed) When the target hits 0 choices[i:]
Combination Sum II (use once) When the target hits 0 choices[i+1:]
Permutations When choices is empty choices[:i] + choices[i+1:]

Duplicates are the last knob, and they get the same treatment every time: sort first, then skip choices[i] when it equals choices[i-1]. That one check is the whole difference between Subsets and Subsets II, and between Permutations and Permutations II. Combination Sum II needs it as well, on top of passing choices[i+1:] so each number is used once. The skip only fires for i > 0, so you still take the first copy of a value at each depth. You just never start a second branch from an identical value at the same level, and that second branch is where duplicate answers come from.

So Subsets II is:

def subsets_with_dup(nums):
    res = []
    nums.sort()

    def dfs(choices, path):
        res.append(path)
        for i in range(len(choices)):
            if i > 0 and choices[i] == choices[i - 1]:
                continue
            dfs(choices[i + 1:], path + [choices[i]])

    dfs(nums, [])
    return res

And Permutations II is the same thing with a different “pass down” and a different “record” rule:

def permute_unique(nums):
    res = []
    nums.sort()

    def dfs(choices, path):
        if not choices:
            res.append(path)
            return
        for i in range(len(choices)):
            if i > 0 and choices[i] == choices[i - 1]:
                continue
            dfs(choices[:i] + choices[i + 1:], path + [choices[i]])

    dfs(nums, [])
    return res

For the sum problems, sorting gives you one more thing. Once choices[i] is bigger than the remaining target, every value after it is too, so you can break instead of continue and drop the rest of that branch.

One caveat: all the slicing (choices[i+1:], path + [x]) copies lists on every call. That’s fine for readability, and it’s hard to get wrong under pressure. If the interviewer asks about space, the answer is a start index plus a single path list that you append to and pop from. And when you use the template in a round, say out loud which knob you’re turning, so the interviewer sees that you recognised the pattern.

What the rounds grade on

Before several of the loops, a recruiter or hiring manager walked me through the rounds and what each one is scored on, and I took notes on those calls. The rubrics vary by company, but they overlap a lot. Here’s the combined version, with no company attached.

Coding rounds

Usually two of them, 45 to 60 minutes each, in any language. At senior levels, solving the problem is only part of the score. Here’s what they listed:

  • Fundamentals, and the right data structure for the job
  • Error checking, done proactively rather than when asked
  • Test cases and corner cases
  • Testable code: dependency injection instead of static, hard-coded links
  • Production-ready code: clean, well structured and idiomatic for the language
  • Alternatives: a second approach when a follow-up changes the constraints

Then there’s efficacy, a word that showed up in more than one of these calls. It has three parts:

  • Speed: how quickly you reach a working solution (ideally under 25 minutes, in one loop’s guidance)
  • Accuracy: getting it right in one go, with fewer iterations
  • Independence: needing as little help from the interviewer as possible

Next to independence, my note just says “AVOID”, as in avoid needing hints. 😅 The social side came up too: keep the interviewer engaged the whole time, leave no room for confusion, and always ask clarifying questions.

API rounds

In one loop, both coding rounds were closer to real work:

  • API implementation (about 75 minutes): you get functional requirements and an API skeleton, and you implement the service. It has to actually work by the end, so have a local environment set up beforehand.
  • API usage (about 60 minutes): you call a web API to solve a real-world problem. Practise making HTTP requests with query parameters and parsing JSON responses in your language, so the plumbing doesn’t eat your time.

Both still check testing and error handling.

System design

Sixty minutes, open-ended, with a block diagram on a virtual whiteboard. The rubrics I heard were pretty consistent:

  • Requirements: functional and non-functional, then scope it down. You’re expected to explore the scope on your own.
  • Risks and trade-offs: call them out without being prompted. Find the failure-prone areas and address them before anyone points at them.
  • Detail: at senior and staff level, a complete design that covers every component of the distributed system, plus a deep dive into at least two of them (performance, optimisation, scale).
  • Change: how the design adapts when new requirements land mid-round.
  • Communication and efficacy: clarity of thought, managing the time, and asking the interviewer what level of detail they want. Minimal input from the interviewer, and don’t fluster.

One longer rubric also listed the areas a design gets checked against, which works as a second checklist:

  • User experience: ease of use, abstraction, latency and reliability as the user sees them
  • Scale and performance: data volume, queries and compute, latency of operations, and the hardware (compute, storage, network)
  • Reliability: outages, durability, local and global redundancy for a DC outage, leader election, CAP, single points of failure
  • Privacy and security: misuse of private data, encryption, DDoS, authentication and authorization, rate limiting
  • Maintenance: decomposition into subsystems, reusing existing components, rollout process, statefulness, single or multi-tenant, clean APIs
  • Cost, time and versioning: the cost of building it (hardware, developers), time constraints, and milestones

Holding my 10 steps up against these, the obvious gap is that my checklist has no explicit trade-offs step, and every rubric I heard had one. Testing, build and release (how you’d actually roll it out) came up more than once too.

The feedback I got on one design round was good design, but I could have made something smaller and simpler before growing it. Which is basically step 7 of my own checklist. lol.

Some loops also had a low-level design round: start with a simple design that works, then expect follow-ups on concurrency, multi-threading and parallelism. One recruiter told me that finishing the multi-threaded version is what turns a yes into a strong yes.

Project, hiring manager and values rounds

Some loops had a round that’s all about your own projects, in STAR format, and it digs in. Expect questions on:

  • A project that spanned multiple teams, technical and business
  • How the problem was identified, and who identified it
  • How you worked with the business side and drove the initiative
  • Risk analysis, architecture, infra and documentation
  • Team structure, allocating and delegating work, code and design reviews
  • Execution: estimation, planning, productionisation
  • How you measured whether it worked
  • Retrospective: what would you change?

After one round where I talked about a project in detail, I noted that I should cover more of it end to end: PRD, then the engineering design doc, milestones, sprints, testing, enhancements, release, then iterate. The architecture is just one stop on that list.

Hiring manager rounds cover similar ground from the people side (mentoring, initiatives you took, how you organise things). Values rounds put you through a handful of scenarios mapped to the company’s values. Across these rounds the same traits kept coming up: ownership, handling conflict, and humility with a learning attitude.

The behavioural round

The coding and design rounds have a well-worn prep path. Behavioural rounds don’t. There’s no LeetCode for “tell me about a time you disagreed with your manager”. 😅

Last November I picked my behavioural examples on the fly from Shuttl, Synaptic and other places I’d worked. This time I wrote them down. I prepped against two frameworks, Amazon’s Leadership Principles and Google’s “Googleyness”, with a note in Obsidian for each: “Amazon Leadership Principals” and “Googlyness Practise” (yes, I misspelt both). Together they come to about 3,300 words. Most of the stories stay in my notebook, because they’re from my time at Uber and full of internal project names. So you folks get their shape here, and I keep the details.

STAR, written down

STAR stands for Situation, Task, Action, Result. Everyone knows the acronym. What I did differently was write every answer in that shape before the interview, so I wasn’t structuring it live while someone watched me think.

Amazon publishes its Leadership Principles and asks candidates for a story against each one, so the list doubles as a checklist. My note covers 14 of them: Customer Obsession, Ownership, Invent and Simplify, Are Right A Lot, Hire and Develop the Best, Insist on the Highest Standards, Think Big, Bias for Action, Frugality, Learn and Be Curious, Earn Trust, Dive Deep, Have Backbone; Disagree and Commit, and Deliver Results.

Under each one I pasted the principle’s own description, then wrote Example 1 and Example 2. Some examples are a full paragraph. Some are a one-line pointer to a story I can tell in my sleep, and one of them literally trails off with “Yada yada.” I’m fine with that. A pointer is enough as long as I know which story I’m reaching for, and I wasn’t trying to memorise a script anyway.

The story bank

When I read the finished note back, I hadn’t written 28 different stories. A handful of them were doing most of the work. One platform project at my current job shows up under five principles, and the S.A.F.E. crunch at Shuttl shows up five times.

Lesson learnt! The efficient way to build a bank is the other way round. List your six to eight best stories first, map each one to every principle and question it can answer, and then look for the gaps.

Story What it can answer
S.A.F.E. at Shuttl, 2017 Frugality, Deliver Results, an innovative product, a time you persisted, an obstacle you overcame
A sound-based check-in at Shuttl, after QR, NFC, OTP and face detection Invent and Simplify, Are Right A Lot, an innovative product
Dogfooding features on the ride home (Shuttl) Customer Obsession
A platform project at my current job Invent and Simplify, Are Right A Lot, Think Big, Frugality, Deliver Results, diving deep into data, a poor performer
Pushing back on shipping a release that wasn’t ready Have Backbone, earning a group’s trust, the most difficult interaction
A solo motorcycle ride that went wrong A risk that failed

S.A.F.E. shows why one story can stretch that far. I’ve already written up the face recognition side of it. The pitch was to show Shuttl as the safest form of public transport, and in three weeks, for a media event, we built face detection, CCTV, a Shuttl media centre, an alcohol detector and an engine cut-off. That’s my frugality answer. The same project is my deliver-results answer (working till 4 AM, debugging memory buffer overflows) and my obstacle answer: the Pi didn’t have enough GPU buffer to sustain a good frame rate, and once we’d trained enough faces we were down to 3 to 4 FPS, with a demo coming up. I reached out to the people behind the tools I was using.

The customer obsession story is a small one. I’d roll out a feature during the day and dogfood it with riders on my way home at night, and the feedback I collected that way on usability and the overall experience was a lot more varied.

The “risk that failed” answer isn’t from work at all. At 21 I set off on a solo ride on my Pulsar 150 from Delhi to Dharamshala, about 500 km. Around 200 km in, at about 4 AM near some village, the clutch wire snapped, and I pushed the bike about 10 km to a gurudwara. The garage we towed it to the next day didn’t have the part, so they rigged up a temporary fix and sent me 30 km to the nearest town to get one. I cancelled the trip and went home, knowing I’d been badly underprepared. (The whole ride has its own post.) The Result is what makes it a STAR answer. I spent the next few weeks reading the manual, paid my mechanic to teach me the basics of the bike for a couple of hours, read up on the common problems, and two months later did another solo trip, to Bir Billing, this time carrying a spare clutch wire, a spare accelerator wire and extra fuel.

The practice questions

The Googleyness note is a question bank. These are the ones I practised, with my answers kept mostly as bullet points:

  • Tell me an area where you think you can improve.
  • Tell me about a time you received critical feedback.
  • Walk us through a time you helped a customer through a difficult process.
  • Give me an example of when you took a risk and it failed.
  • Tell me about a time you created an innovative product.
  • Tell me about a time you saw two business opportunities to improve ROI, and how you worked out they were connected.
  • How do you find the time to stay inspired, learn new things and innovate?
  • Tell me about a time you had to deal with a poor performer on your team.
  • Tell me about a time you could have stopped working but persisted.
  • Tell me about a time you proposed a new business.
  • How would you handle a busy situation where three people are waiting for help from you?
  • Tell me about a clever way you found to save the company money.
  • Tell me about a time you earned the trust of a group.
  • Tell me about a time you had to dive deep into data, and what you achieved.
  • Tell me about the most difficult interaction you’ve had at work.
  • Tell me about a time you overcame an obstacle and delivered results.

And these are the ones I actually got asked this month, in one behavioural round:

  • How do you tell a project is slipping? The follow-ups were about telling stakeholders, and how proactive you are about it.
  • What do you do when the quality of a release is low, or you have low confidence in it?
  • What do you look for in a candidate when you interview them?
  • Two teams are working on projects with a similar goal. What do you do?
  • How would you cut down on night meetings?

Answers that aren’t stories

Some questions want a process more than a story. Here are my general answers, and honestly they’re the part of the notes I’m happiest to share.

Three people waiting on you. First, meet all three quickly and write down each situation and the outcome they expect. Then stack-rank the problems by impact and severity, and estimate how long each will take to resolve. Then work out how it gets done: do it myself, or delegate it to someone on the team, deprioritising something else if I have to.

Low confidence in a release. Have a definition of done, and hold the release to it: coding, testing, logging, observability, load and performance testing, and on-call integration.

Two teams, similar goals. Set up a meeting with the tech leads and managers on both sides, find a middle ground based on the pros, cons and severity, and merge forces.

Night meetings. Modularise the work so each side can divide and conquer, default to async communication, keep the meetings you do need agenda-driven with a point of contact driving each one, and send minutes so everyone stays in the loop.

What I look for in a candidate. Communication, proactiveness, teamwork and collaboration, depth and breadth of knowledge, being open to feedback, and curiosity: asking questions to clear up the scope of the problem and the boundary conditions before jumping in.

Critical feedback. I have regular 1:1s with my manager and skip-level, and there’s a quarterly peer feedback cycle. I try to be proactive about checking myself against our core competencies, asking where I’m lagging and what I can do about it. I feel feedback is the only way to grow.

A poor performer. Talk to them one-on-one before assuming anything, including whether everything is okay outside work. The time I had to deal with this, a teammate was struggling quietly. It turned out they were stuck and didn’t want to stand out by asking questions. I became their onboarding buddy, and they ramped up well. My takeaway was to communicate well and play to people’s strengths.

Staying current. I’m a regular on Hacker News, Slashdot, Reddit and tech Twitter, I go to Python and Go meetups in Delhi and Bangalore (I’ve written about one of them, DevFest 2018), and I run a lot of personal projects in my homelab.

What I want to learn. I’ve been tunnel-visioned on platform teams, where most of the infrastructure is abstracted away for us. I want to learn what happens underneath: database internals, deployment, auto-scaling, reverse proxies.

One note to self for all of this: let the interviewer speak. That was one of the two lessons I wrote down after a design round last November, and it applies just as much when you walk in with a bank of ready stories. Give the short version and let them ask for more.

The prep stack

For coding:

  • Blind 75, a curated list I’d saved back in November 2021, as the starting point
  • Cracking the Coding Interview by Gayle Laakmann McDowell
  • LeetCode’s company-tagged questions
  • Introduction to Algorithms (CLRS) as the reference

For system design:

  • Grokking the System Design Interview
  • The System Design Primer
  • Designing Data-Intensive Applications (DDIA)
  • awesome-scalability, for real write-ups of how companies scaled
  • The System Design Interview channel on YouTube
  • A ByteByteGo chart of the algorithms to know before a system design interview (geohash, quadtree, consistent hashing, leaky and token bucket, trie, and so on)

For the behavioural round: Amazon’s Leadership Principles, the Googleyness question bank above, and my own story bank.

The topics on my “read again” list are the ones that are easy to forget and hard to rederive in a round: Kahn’s topological sort, Kadane’s algorithm, Huffman decoding, bitwise operations, lowest common ancestor, Dijkstra and A*, and permutations of a string (which is just the backtracking section again).

If you’re a fresher, the 2016 post still covers the basics. For senior roles though, the algorithms are only the start. Most of the rubric above is about speed, independence and calling out trade-offs before anyone asks, and a good chunk of the loop is you telling stories about your own work.

Wish me luck, folks! 🤞

Leave a comment

Keep reading