The Microsoft SDE-2 loop has a reputation for being approachable, and in some ways it is: the coding difficulty sits mostly at LeetCode Medium, the behavioral questions are predictable if you know what Microsoft actually cares about, and the process runs on a well-worn track. What candidates underestimate is how tightly behavioral signal is woven into every single round. You won't get a standalone "tell me about yourself" session and then a coding round where nobody checks your interpersonal instincts. At Microsoft, the two happen in the same room, in the same hour.
This page covers the SDE-2 interview process as it runs in 2026, targeting Level 61 and Level 62 positions. The questions below are drawn from verified candidate write-ups posted on Glassdoor, LeetCode Discuss, Blind, and firsthand accounts published in 2023 through 2025. I've noted where something is level-specific or team-dependent. Microsoft's org is large enough that the exact mix of rounds shifts by team, but the core structure is consistent.
Easy questions
14Iterative: keep three pointers, prev, curr, and next. At each step, save curr.next, point curr.next back at prev, then advance prev and curr by one. Recursive: reverse the rest of the list first, then fix the two-node link at the tail of the recursion. Both run O(n) time; the iterative version uses O(1) space, the recursive version uses O(n) call-stack space.
This is a warm-up in Microsoft phone screens more than onsite rounds, and interviewers move fast if you nail it in under two minutes. The real signal comes from the follow-up: reverse only a sub-range of the list, or reverse it in groups of k. Candidates who extend the same pointer-juggling logic to the harder variant without re-deriving it from scratch score noticeably better than those who treat each variant as a brand new problem.
def reverse_list(head):
prev = None
curr = head
while curr:
nxt = curr.next
curr.next = prev
prev = curr
curr = nxt
return prevFloyd's cycle detection, the "tortoise and hare." Move a slow pointer one step and a fast pointer two steps at a time. If they meet, a cycle exists. To find where the cycle begins, reset one pointer to the head and advance both pointers one step at a time; they meet exactly at the cycle's start node.
LeetCode #141 (detect) and #142 (find the start) both sit in the Microsoft-tagged set, and 2024 Blind write-ups mention this as a common opener before a harder tree or graph problem in the same round. Interviewers sometimes skip straight to "how would you do this with O(1) extra space," which rules out the hash-set approach and forces Floyd's method.
def has_cycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return FalseEvery company asks some version of this. Microsoft's version carries more weight than most because the interviewers can usually tell within one sentence whether you've researched the team's actual work. "I want to work on Azure" is not an answer. "I want to work on the Azure Service Bus reliability team because I read the 2024 engineering post about their exactly-once delivery work and I've been solving a similar problem at my current company" is an answer.
If you don't know which team you're interviewing with, ask your recruiter before the loop. It's a completely reasonable question and the recruiter will tell you. Then read the team's public blog posts, GitHub repos, or recent conference talks. Microsoft's engineering teams publish more publicly than most people realize.
Be specific about what you cut, what you deferred, and what the cost of those cuts turned out to be in retrospect. The best answers include an honest assessment of whether the trade-off was the right call: "We deferred the error handling to hit the deadline. It turned out that was fine for the initial release but we paid for it three months later when a new edge case hit production. If I were doing it again, I'd have pushed for one more week to add the error handling."
Microsoft interviewers don't expect you to have made perfect calls. They expect you to have reflected on your calls and learned from them. An answer where every trade-off worked out exactly as planned is less credible than one where something went wrong and you can say precisely what you'd do differently.
SDE-2 at Microsoft spans two internal levels: Level 61 and Level 62. Level 61 is the entry point for SDE-2 and typically requires 2 to 5 years of industry experience. Level 62 is the senior end of SDE-2, typically 4 to 6 years of experience, and carries higher compensation and a slightly harder interview bar, especially for system design. Some candidates apply for one level and get offered the other based on how the loop goes. Total compensation at L61 averages around $197K; at L62, around $207K, both lower than equivalent levels at Google or Meta.
Most candidates report 6 to 10 weeks from application to verbal offer. Internal referrals can compress the timeline to 4 to 5 weeks. The online assessment or phone screen usually happens within 2 weeks of application. The virtual onsite loop is typically 2 to 4 weeks after that. After the loop, debrief and offer turnaround take 1 to 2 weeks, though it can stretch to 3 to 4 weeks if an AA round needs to be scheduled separately.
The AA round (As Appropriate) is an additional interview conducted by a senior engineer or principal within the same org. Getting an AA invite is generally a positive signal, it typically means your loop performance was strong enough to warrant a closer look at senior level. The content varies by interviewer: some conduct a coding question plus behavioral, others focus entirely on a systems discussion or on "selling Microsoft" to a candidate they want to hire. A short AA round with a very senior interviewer is not a bad sign. Treat it like another loop round and prepare accordingly.
Yes, for Level 61 and Level 62 positions. System design is not standard at Level 59 or 60 (new grad SDE and early SDE-1). At SDE-2, expect one dedicated design round of 45 to 60 minutes covering high-level distributed system design. Some loops also include a low-level design (LLD) round focused on object-oriented design and class hierarchy, particularly for roles on platform or API teams. Ask your recruiter before the loop whether both HLD and LLD rounds are included.
More important than most candidates expect. Microsoft evaluates behavioral signals in every round, not just a standalone behavioral interview. The "growth mindset" framework, a company-wide cultural initiative that has been active since 2014 under Satya Nadella, is explicitly used in interview debriefs. Candidates who perform well technically but signal "know-it-all" behavior in their stories, specifically by describing conflicts where they were fully right and others were wrong, have failed loops despite strong coding performance. Have 4 to 5 STAR stories ready with genuine failure examples and genuine learning outcomes.
Microsoft accepts any major language for coding rounds. Python, Java, C#, C++, and JavaScript are all reported from recent loops. C# gets a mild reception given Microsoft's product ecosystem, but it's not required and offers no scoring advantage. The collaborative code editor in Teams supports all major languages. If your strongest language is Python, use Python. Interviewers at Microsoft are looking at your problem-solving approach, not your language loyalty. The one exception: if you're interviewing for a role explicitly in the .NET or C# ecosystem, knowing the language idioms will matter for the coding quality discussion.
Typically 2 to 3 dedicated coding rounds inside the virtual onsite loop, plus whatever coding already happened during the OA or phone screen stage. A hiring-manager round often folds in one more coding question alongside behavioral topics, which some candidates count separately and some don't, which is why the "4 to 6 rounds total" figure shows up in several write-ups. The exact split depends on whether a dedicated system design round is scheduled (L61 and above) and whether an AA round gets added.
No, not on its own. The algorithmic difficulty at Microsoft SDE-2 sits at LeetCode Medium, which most candidates who've done 150 to 200 problems can handle technically. What LeetCode practice alone doesn't build is the habit of narrating your reasoning out loud before writing code, which Microsoft interviewers weight heavily and note negatively when it's missing. Practicing the same problems while explaining your approach to another person, or recording yourself, closes that gap faster than doing more problems in silence.
Not necessarily. Some teams and some levels skip the AA round entirely as a matter of process, independent of how the loop went. It's more accurate to say that getting an AA invite is a positive signal, your performance was strong enough that someone wanted a closer look at senior-level judgment, than that skipping it is a negative one. If you're unsure, it's reasonable to ask your recruiter directly whether an AA round is part of your specific loop before assuming its absence means anything.
A process is an independent unit of execution with its own memory space, its own set of file handles, and its own address range. The operating system isolates one process from another, so if one process crashes it doesn't take down another one. Starting a process is relatively expensive because the OS has to allocate a new address space and set up page tables from scratch.
A thread is a unit of execution that lives inside a process and shares that process's memory, open file descriptors, and heap with every other thread in the same process. Threads within a process can read and write the same variables directly, which makes communication fast but also introduces race conditions if you're not careful with locks or other synchronization. Creating a thread is much cheaper than creating a process because the OS only needs to set up a new stack and register set, not a whole new address space.
The practical tradeoff comes up constantly in system design and in day to day engineering. If you want strong isolation, say running untrusted plugin code or separate services, you reach for processes even though context switching between them costs more. If you need many workers cooperating on the same in-memory data structure, like a thread pool processing requests against a shared cache, threads are the right tool, but you own the responsibility of protecting shared state with mutexes, atomics, or higher level constructs.
Medium questions
24Use a hash map for O(1) key lookups combined with a doubly linked list to track access order. The most recently used entry sits at the list head; the least recently used sits at the tail. On a get, move the node to the head. On a put that exceeds capacity, evict the tail node and remove its key from the map.
This specific problem shows up in multiple independent Microsoft SDE-2 reports from 2024, including at least one account where Round 2 was described as "implement LRU cache, then extend it to support multiple eviction policies." The eviction policy extension is a natural follow-up: how would you swap LRU for LFU or FIFO without rewriting the entire cache? The answer uses a strategy pattern with an abstracted eviction interface.
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity: int):
self.cap = capacity
self.cache = OrderedDict()
def get(self, key: int) -> int:
if key not in self.cache:
return -1
self.cache.move_to_end(key)
return self.cache[key]
def put(self, key: int, value: int) -> None:
if key in self.cache:
self.cache.move_to_end(key)
self.cache[key] = value
if len(self.cache) > self.cap:
self.cache.popitem(last=False)Two approaches: sort and index (O(n log n), trivial to implement), or a min-heap of size k (O(n log k), better for large n with small k). For the heap approach, iterate through the array, push each element, and pop when the heap exceeds size k. At the end, the heap's minimum is the k-th largest.
Reported as a phone screen question. Follow-ups probe the space complexity trade-off and whether you've heard of Quickselect (O(n) average, O(n^2) worst). Microsoft interviewers don't always require Quickselect, but knowing it and explaining when you'd choose it over the heap approach demonstrates the kind of reasoning they want to see.
import heapq
def find_kth_largest(nums, k):
# min-heap of size k
heap = []
for n in nums:
heapq.heappush(heap, n)
if len(heap) > k:
heapq.heappop(heap)
return heap[0]Use a min-heap seeded with the head node from each list. At each step, pop the minimum node, append it to the result list, and push that node's next pointer into the heap (if non-null). Total time complexity O(N log k) where N is the total number of nodes and k is the number of lists.
Reported from a 2024 onsite. The key correctness trap is what you push into the heap: you need to push the node itself (or a tuple of (value, node) with a tiebreaker) rather than just the value, because you need the pointer to the next node in that list. Python's heapq doesn't handle ties on non-comparable objects without an explicit comparator or index tiebreaker.
import heapq
from typing import Optional
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def merge_k_lists(lists):
heap = []
for i, node in enumerate(lists):
if node:
heapq.heappush(heap, (node.val, i, node))
dummy = ListNode(0)
curr = dummy
while heap:
val, i, node = heapq.heappop(heap)
curr.next = node
curr = curr.next
if node.next:
heapq.heappush(heap, (node.next.val, i, node.next))
return dummy.nextSliding window with a set or hash map tracking the current window's characters. Expand the right pointer on each step. When a duplicate character enters the window, advance the left pointer until the duplicate is removed. Track the maximum window length throughout.
Reported from a 2022 technical round at Microsoft (India), and appears repeatedly in Microsoft-tagged LeetCode problems. It's a warm-up question in some loops and the main question in others. The follow-up often asks you to return the actual substring rather than just the length, and then asks how you'd handle Unicode multi-byte characters.
For each word, compute a canonical key (sorted characters, or a 26-length character count tuple) and use it to bucket words into a hash map. Sorting each word costs O(k log k) for a word of length k; the count-tuple approach is O(k) per word and faster for longer strings. Return the map's values as the grouped lists.
LeetCode #49, Microsoft-tagged, reported repeatedly as a phone-screen question in 2024 and 2025 GeeksforGeeks candidate write-ups. The follow-up worth preparing for: what if the input has hundreds of millions of words and doesn't fit on one machine? That turns a 10-minute coding question into a short distributed-systems tangent (shard by hash of the key, reduce locally, merge), and interviewers tend to like watching candidates make that jump unprompted.
Modified binary search. At each step, at least one half of the array (left of mid or right of mid) is guaranteed to be properly sorted. Check which half is sorted, then check whether the target falls inside that half's range. If it does, search there; if not, search the other half. This keeps the O(log n) guarantee without ever fully sorting the rotated array.
LeetCode #33, one of the most frequently cited Microsoft-tagged problems across Glassdoor and LeetCode Discuss write-ups from 2023 through 2025. The follow-up almost always adds duplicates to the array, which breaks the "at least one half is sorted" invariant; the fix is a linear-time fallback when nums[left] equals nums[mid] equals nums[right].
def search(nums, target):
lo, hi = 0, len(nums) - 1
while lo <= hi:
mid = (lo + hi) // 2
if nums[mid] == target:
return mid
if nums[lo] <= nums[mid]:
if nums[lo] <= target < nums[mid]:
hi = mid - 1
else:
lo = mid + 1
else:
if nums[mid] < target <= nums[hi]:
lo = mid + 1
else:
hi = mid - 1
return -1Two passes, no extra matrix. First, transpose the matrix (swap element [i][j] with [j][i] across the diagonal). Second, reverse each row. The combination produces a clockwise 90-degree rotation using O(1) extra space. Doing it in a single pass with four-way cell swapping, layer by layer, is the harder but more elegant version some interviewers ask for as a follow-up.
LeetCode #48, reported in Microsoft phone screens and in at least one 2025 onsite account on GeeksforGeeks. The trap most candidates fall into: reversing columns instead of rows, or transposing after reversing instead of before, which rotates the matrix the wrong direction. Walking through a 3x3 example on paper before touching the keyboard catches this early.
BFS with a direction flag that alternates each level. Use a deque to collect node values for the current level. On even levels, append left to right. On odd levels, append right to left (or collect left-to-right and reverse before adding to the result). Either approach works; the deque-based approach without an explicit reverse is slightly more efficient.
Reported in a 2024 Microsoft SDE-2 hiring manager round alongside a multithreading question. Binary tree BFS variants appear in Microsoft loops at a very high frequency across all levels. If you've been through a Microsoft loop and didn't get at least one tree question, you're in the minority.
from collections import deque
from typing import List, Optional
def zigzag_level_order(root) -> List[List[int]]:
if not root:
return []
result = []
queue = deque([root])
left_to_right = True
while queue:
level_size = len(queue)
level = deque()
for _ in range(level_size):
node = queue.popleft()
if left_to_right:
level.append(node.val)
else:
level.appendleft(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(list(level))
left_to_right = not left_to_right
return resultBuild an adjacency list and an in-degree array. Push all nodes with in-degree 0 into a queue. Process each node, decrement the in-degree of its neighbors, and push any neighbor whose in-degree reaches zero. If the result list has length equal to the number of courses, return it. Otherwise a cycle exists and completion is impossible.
LeetCode #210. Shows up in the Microsoft-tagged problem set and reported from multiple SDE-2 onsite rounds. The follow-up is almost always: "How does the answer change if you need to detect which specific courses form the cycle?" The answer: run a DFS with a coloring scheme (white/gray/black) and record the back edge that closes the cycle.
Count the number of ways n friends can either stay single or pair up with exactly one other friend. If the n-th person stays single: ways(n-1). If the n-th person pairs with someone: (n-1) choices for the partner, then ways(n-2) arrangements for the rest. Recurrence: dp[n] = dp[n-1] + (n-1) * dp[n-2], with base cases dp[1] = 1 and dp[2] = 2.
Reported directly from a 2024 Microsoft SDE-2 OA (GeeksforGeeks candidate writeup for L62). It presents as a combinatorics problem but the recurrence structure is classic DP. Candidates who try to build it bottom-up from scratch in-interview often find it faster to first write the recursive version and then memoize, then optimize to the iterative form if time allows.
Sort meetings by start time. Use a min-heap tracking the end times of currently active meetings. For each meeting, if its start time is greater than or equal to the heap's minimum (the earliest-ending active meeting), pop that meeting (it's done) before pushing the new end time. The heap's maximum size over all iterations is the answer.
A variation of this was reported in a 2025 Microsoft SDE-2 DSA round ("single DSA question with a followup," per a LeetCode Discuss post from that period). The follow-up usually asks you to return not just the count of rooms but also the actual assignment of meetings to rooms, which requires tracking which room each meeting is assigned to alongside the end times.
Recursive post-order approach. At each node, recurse into the left and right subtrees looking for the two target nodes. If both subtrees return a non-null result, the current node is the LCA. If only one side returns non-null, propagate that result upward. The base case: if the current node matches either target, return it immediately.
LeetCode #236, Microsoft-tagged, and one of the most frequently reported tree questions across 2024-2025 Glassdoor write-ups, often paired with a request to also handle the case where one or both nodes don't exist in the tree at all. That variant needs a small change: track whether each target was actually found during the traversal, and only trust the LCA result if both were.
def lowest_common_ancestor(root, p, q):
if not root or root == p or root == q:
return root
left = lowest_common_ancestor(root.left, p, q)
right = lowest_common_ancestor(root.right, p, q)
if left and right:
return root
return left or rightTreat the grid as an implicit graph where each land cell connects to its up, down, left, and right neighbors. Scan every cell; whenever you hit an unvisited land cell, run a BFS or DFS to mark every connected land cell as visited, and increment the island count once per call. Time complexity is O(rows x cols), since every cell is visited at most twice: once in the scan, once in a flood fill.
LeetCode #200, reported from Microsoft SDE-2 phone screens more often than onsite rounds, according to multiple 2024 GeeksforGeeks and LeetCode Discuss accounts. The standard follow-up asks for the number of distinct island shapes rather than just the count, which requires normalizing each island's coordinates relative to its top-left cell before comparing shapes with a set.
def num_islands(grid):
if not grid:
return 0
rows, cols = len(grid), len(grid[0])
visited = set()
def bfs(r, c):
stack = [(r, c)]
visited.add((r, c))
while stack:
cr, cc = stack.pop()
for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)):
nr, nc = cr + dr, cc + dc
if (0 <= nr < rows and 0 <= nc < cols
and grid[nr][nc] == "1" and (nr, nc) not in visited):
visited.add((nr, nc))
stack.append((nr, nc))
count = 0
for r in range(rows):
for c in range(cols):
if grid[r][c] == "1" and (r, c) not in visited:
bfs(r, c)
count += 1
return countThe O(n^2) version is the one most candidates reach first: dp[i] holds the length of the longest increasing subsequence ending at index i, and dp[i] equals the max of dp[j] + 1 for every j less than i where nums[j] is less than nums[i]. The O(n log n) version replaces the DP array with a "tails" array tracking the smallest tail value for an increasing subsequence of each length, using binary search to find where each new element belongs.
Reported in Microsoft SDE-2 rounds as both a standalone question and a warm-up before a harder DP problem in the same session, per multiple 2024 and 2025 candidate accounts. Microsoft interviewers generally accept the O(n^2) solution as a complete pass if you can explain it cleanly, but naming the O(n log n) improvement, and roughly how patience sorting works, is what gets noted as exceeding expectations in the debrief.
Three threads, each responsible for one modulo class (n%3 == 0, n%3 == 1, n%3 == 2). Use three semaphores initialized to 1, 0, and 0 respectively. Each thread: wait on its semaphore, print its number, signal the next thread's semaphore. Cycle the signaling so thread 0 signals thread 1, thread 1 signals thread 2, thread 2 signals thread 0.
Reported verbatim from a 2024 Microsoft SDE-2 hiring manager round alongside the zigzag tree traversal. Microsoft's hiring manager rounds are the most likely to include a concurrency or multithreading question, more so than the peer engineering rounds. If you're not comfortable with semaphore semantics, the mutex-plus-condition-variable alternative is also acceptable and sometimes easier to reason about under pressure.
import java.util.concurrent.Semaphore;
public class ThreeThreadsPrint {
static Semaphore[] sems = {
new Semaphore(1),
new Semaphore(0),
new Semaphore(0)
};
public static void main(String[] args) throws InterruptedException {
for (int id = 0; id < 3; id++) {
final int threadId = id;
new Thread(() -> {
try {
for (int n = threadId + 1; n <= 100; n += 3) {
sems[threadId].acquire();
System.out.println(n);
sems[(threadId + 1) % 3].release();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
}
}Two counting semaphores (or condition variables) track the queue's empty and full slots, plus a mutex protecting the underlying buffer. A producer waits on the "empty slots" semaphore before inserting, then signals the "full slots" semaphore. A consumer does the reverse. The mutex ensures only one thread touches the buffer's head and tail pointers at a time, regardless of how many producer or consumer threads are running.
This maps to LeetCode 1188 (Design Bounded Blocking Queue, a premium concurrency problem) and is reported as a Microsoft hiring-manager or AA-round question when the team works on infrastructure or messaging systems. The follow-up worth rehearsing: what changes with multiple producers and multiple consumers instead of one of each? The semaphore-plus-mutex structure doesn't change, but naming the risk of thread starvation under unfair scheduling is the detail that separates a good answer from a great one.
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Semaphore;
public class BoundedBlockingQueue {
private final Queue<Integer> queue = new LinkedList<>();
private final Semaphore emptySlots;
private final Semaphore fullSlots = new Semaphore(0);
private final Object mutex = new Object();
public BoundedBlockingQueue(int capacity) {
emptySlots = new Semaphore(capacity);
}
public void enqueue(int element) throws InterruptedException {
emptySlots.acquire();
synchronized (mutex) {
queue.offer(element);
}
fullSlots.release();
}
public int dequeue() throws InterruptedException {
fullSlots.acquire();
int value;
synchronized (mutex) {
value = queue.poll();
}
emptySlots.release();
return value;
}
}The growth mindset question in its purest form. The failure needs to be real and specific: a project that shipped broken, an architectural decision that had to be reversed, a performance gap your manager called out. Generic failures ("I sometimes work too hard") fail immediately.
What separates strong from weak answers is what comes after the failure. Microsoft interviewers want to hear a specific behavior change, not a general lesson. "I learned to communicate more" is weak. "I started sending a weekly written update to stakeholders after the incident, and here's what that actually changed" is the level of specificity that lands.
Name the technology and the timeline. Describe how you ramped: documentation, internal colleagues, experiments, reading source code. Name what you got wrong first. The structure should show a systematic approach to unfamiliar territory, not "I Googled it and figured it out."
This question is particularly common in rounds where the team uses a tech stack that differs from your background. Microsoft interviewers will sometimes use your answer to probe whether you'd ramp well on Azure services if you come from an AWS background, or on C# if your experience is primarily in Python or Java. Have an example ready that shows you've done exactly this kind of cross-technology ramp before.
The learn-it-all test in disguise. Pick a situation where you had a conviction, received contradicting evidence, and updated. Situations where you initially resisted the feedback before updating are better stories than ones where you immediately agreed, because they show you can hold a position while also being genuinely open.
The follow-up is almost always: "How do you distinguish between good reasons to change your mind versus social pressure?" The expected answer involves naming the type of evidence that moved you (data, a user complaint, a code review showing a bug in your reasoning) versus the type that wouldn't (someone senior just saying you're wrong without substantiation).
Microsoft interviewers listen for two things: whether you took partial ownership of the conflict (even if the other person was mostly wrong), and whether the resolution involved structural change or just one conversation. "We talked it out and it was fine" is the minimum passing bar. "We talked it out, identified the underlying ambiguity in our team's ownership model, and proposed a change to how we document that" is the level that stands out.
Avoid stories where you were fully right and the other person was fully wrong. That outcome may have been true, but telling it that way reads as a lack of self-awareness to a Microsoft interviewer, specifically to one who's been trained to look for growth mindset signals.
Microsoft is large. Cross-team influence without direct authority is a constant in SDE-2 daily work. Interviewers want to see that you can align stakeholders through shared goals and data rather than escalation. Strong answers describe the actual mechanism: a shared doc that framed the problem from the partner team's perspective, a proof-of-concept that de-risked the proposal, a metric that made the case numerically.
The failure mode here is answers that lean heavily on "I escalated to my manager." That's sometimes necessary, but if it's your primary tool for influence, it signals that you haven't yet built the interpersonal effectiveness Microsoft expects at SDE-2.
Microsoft interviewers are checking whether you can push back on a decision without either caving immediately or digging in past the point of usefulness. Name the actual disagreement, the case you made, and how it resolved, including if it resolved with your manager's original call standing. A story where you were overruled and then executed the decision well anyway is often a stronger signal than one where you got your way, since it shows you can disagree and still commit.
The weak version of this answer avoids naming a real disagreement ("I always try to see both sides") or ends with you going around your manager to their boss. Both read poorly. The strong version names the specific point of contention, quotes roughly what was said, and is honest about whether, in hindsight, your original position was actually right.
Name the decision, the information you were missing, and the deadline pressure that forced you to act anyway. Microsoft interviewers want to hear your actual reasoning process for filling the gap: did you make a reversible bet and set a checkpoint to revisit it, did you consult someone with more context, did you ship the smallest version that let you gather real data quickly? Any of these is a reasonable answer; "I went with my gut" is not.
This question shows up often in AA rounds specifically, more so than in the standard hiring-manager slot, per multiple candidate accounts describing their AA interviewer as a senior engineer probing for judgment under ambiguity rather than running a coding-heavy conversation. The follow-up is almost always "what would you have done differently with more time," and a credible answer here matters more than the original decision having been right.
Name the actual feedback, word for word if you can remember it, and your honest first reaction, defensiveness included. Then describe the specific thing you changed afterward, not a general resolution to "be better." Microsoft's growth mindset framework treats this question as one of the clearest behavioral signals in the whole loop, since it directly tests the learn-it-all versus know-it-all distinction from Nadella's framing.
Candidates who claim they've never been surprised by feedback, or who reframe every piece of critical feedback as something they'd already privately decided to fix, tend to read as coached rather than candid. An answer with a beat of genuine discomfort before the resolution is the one interviewers remember afterward, and it's reported often enough as an AA-round staple that it's worth having two separate stories ready rather than one.
Hard questions
5Recursively handle groups of three digits. For each group, handle hundreds, tens, and ones separately. Maintain a list of thousands-group labels ("", "Thousand", "Million", "Billion"). The edge cases are where candidates slip: zero, numbers exactly at group boundaries (1000, 1000000), negative numbers if the problem includes them.
Reported from a December 2024 Microsoft onsite as a hard-difficulty coding question. Interviewers who give this problem are testing whether you can decompose a parsing problem systematically under time pressure, not whether you've memorized the solution. Writing the recursive structure on a whiteboard before coding is the right opening move.
For each bar, the water it holds equals the minimum of the tallest bar to its left and the tallest bar to its right, minus its own height. A brute-force solution recomputes both maxes for every index (O(n^2)). The two-pointer solution tracks a running left-max and right-max and moves whichever pointer sits behind the smaller of the two, reaching O(n) time and O(1) space without precomputing arrays.
LeetCode #42, one of the harder problems in the Microsoft-tagged set and reported from at least one Level 62 onsite loop. Candidates who only know the O(n) space version (precomputed left-max and right-max arrays) usually pass, but interviewers will ask if you can do it without the extra arrays, and reaching the two-pointer insight live, rather than from memory, is what separates a strong pass from a borderline one.
Two heaps: a max-heap holding the lower half of values, a min-heap holding the upper half. On each insertion, determine which heap to add to (based on current median), then rebalance if the size difference exceeds one. The median is either the top of the larger heap or the average of both tops if they're equal size.
LeetCode #295. Reported in verified Microsoft interviews. The question tests whether you can design a data structure for an online problem (streaming input, query at any point) rather than a batch problem. The interviewer's first follow-up is usually: "What if we only need an approximate median and our input is bounded in range?" Answer: a bucket array over the value range works and is O(1) per operation.
Five philosophers sit around a table with five forks, one between each pair, and each needs both adjacent forks to eat. The naive solution, where every philosopher picks up their left fork and then their right, deadlocks the moment all five grab their left fork at once and wait forever for the right. The standard fix: break the symmetry. Have one philosopher (or every even-numbered one) pick up the right fork first instead of the left, or add a waiter or semaphore that only allows four philosophers to attempt eating at a time.
LeetCode #1226, a premium concurrency problem, reported as a Microsoft AA-round question specifically for candidates on teams that touch OS-level or driver code. Interviewers care less about which fix you name and more about whether you can articulate why the naive version deadlocks, a circular wait on a shared resource, before jumping to a solution. That diagnostic step is where weaker candidates lose points even when they know the fix by memory.
Core components: metadata service (tracks file versions, sync state per device), blob storage for file chunks (block-level deduplication to avoid resending unchanged chunks), conflict detection and resolution (last-write-wins vs. branch-and-merge, OneDrive uses last-write-wins for most file types). Delta sync: client sends a hash of its current version; server compares and returns only changed blocks. Websocket or long-poll channel for real-time change notifications from the server to clients.
Reported as a real Microsoft system design question category (confirmed by multiple prep guides and a 2024 candidate post on EngineeringBolt). The interviewer's depth probes tend to focus on conflict resolution ("what happens when two devices edit the same file offline?") and on how you'd handle very large files without loading them entirely into memory on upload. Chunked multipart upload with pre-signed Azure Blob Storage URLs is the standard answer.
Real-time scenario questions
9Double-checked locking with a volatile instance variable. The outer null check avoids synchronization on every call after the instance is created. The inner null check inside the synchronized block prevents a race condition where two threads both pass the outer check and then queue up for the lock. The volatile keyword prevents CPU instruction reordering from exposing a partially constructed object.
Reported alongside the three-threads question in the same hiring manager round. A cleaner Java alternative is the initialization-on-demand holder idiom (a private static inner class holding the instance), which relies on class-loading guarantees and doesn't require explicit synchronization or volatile. Either is acceptable; knowing both and being able to explain the trade-off is what distinguishes a strong answer.
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}Model the directory tree as a recursive data structure. Each node is either a file (contains content, has a parent) or a directory (contains a map from name to child node). Core operations: create file or directory (traverse path, insert node at target), read file (traverse to node, return content), list directory (return keys of the directory's children map), rename (detach node from current parent, reattach under new name), search (BFS or DFS from a given path, match by name or content).
Reported from a 2024 Microsoft SDE-2 onsite Round 2 (DEV Community writeup from candidate "Alex R"). The multi-level hashmap implementation was described as the candidate's approach. The "distributed environment" follow-up asks how you'd extend this to a distributed file system: the answer involves separating metadata (namespace) from data (chunks) into separate services, which is how HDFS and GFS are structured.
class FileSystem:
def __init__(self):
self.root = {"__type": "dir", "__children": {}}
def _traverse(self, path):
parts = [p for p in path.split("/") if p]
node = self.root
for part in parts:
node = node["__children"][part]
return node
def mkdir(self, path):
parts = [p for p in path.split("/") if p]
node = self.root
for part in parts:
if part not in node["__children"]:
node["__children"][part] = {"__type": "dir", "__children": {}}
node = node["__children"][part]
def create_file(self, path, content=""):
parts = [p for p in path.split("/") if p]
dir_node = self._traverse("/" + "/".join(parts[:-1])) if len(parts) > 1 else self.root
dir_node["__children"][parts[-1]] = {"__type": "file", "__content": content}
def read_file(self, path):
node = self._traverse(path)
return node["__content"]
def ls(self, path):
node = self._traverse(path) if path != "/" else self.root
return list(node["__children"].keys())Generation: create a cryptographically random 6-digit code, store it in a fast key-value store (Redis with a TTL of 5 to 10 minutes) keyed by the user identifier. On verification, look up the key, compare the submitted code, then immediately delete the key to prevent reuse (delete-on-read is critical). Uniqueness across concurrent generation requests: the write is a Redis SET with NX (only write if key doesn't exist), or accept overwrite and update the TTL.
Rate limiting is the most important follow-up: without it, an attacker can enumerate the 6-digit space (1M combinations) faster than the TTL expires. Standard mitigations: limit to 3 to 5 attempts per user per TTL window, add exponential backoff on failure, and consider a 30-second reuse window before allowing re-generation. Reported from a 2024 Microsoft SDE-2 hiring manager round (GeeksforGeeks candidate account).
Core entities: Song, Album, Artist, Playlist, User. Song has audio data (stored as blob URL, not in the DB), metadata (title, duration, artist ID, album ID). Playlist is a many-to-many join between User and Song with an ordering field. Playback state: current song, position in seconds, shuffle seed, repeat mode. Key design patterns: Factory for creating different player states, Strategy for shuffle algorithms, Observer for updating recently-played and recommendation signals when a song completes.
Reported as a low-level design round question at Microsoft SDE-2 (L61, Hyderabad, 2025). The factory design pattern, immutability, and abstraction were specifically noted as the focus. Microsoft's LLD rounds care about clean OOP design and whether your class hierarchy makes future extension cheap, not about producing a perfect schema on the first pass.
Model the core entities as classes: ParkingLot (holds a collection of Levels), Level (holds Spots of different sizes), Spot (has a type such as compact, large, or handicapped, and a status of free or occupied), and Vehicle (has a size that must match a compatible spot type). A ParkingSpotAssigner interface lets you swap assignment strategies, nearest-available versus load-balanced across levels, without touching the rest of the class hierarchy, the same Strategy pattern that shows up in the music-streaming LLD question above.
This is one of the most frequently reported low-level design questions across Microsoft's platform and Windows-adjacent teams, cited in multiple 2024-2025 candidate write-ups as a 45-minute LLD round on its own rather than a warm-up. The usual follow-up: add hourly-rate billing with different rates per vehicle type, then add a reservation system that holds a spot before the vehicle arrives. Both extensions should slot into the existing class design without forcing a rewrite of ParkingLot or Level.
Core classes: Elevator (current floor, direction, a queue of requested floors), ElevatorController (owns multiple Elevator instances, decides which elevator answers each new request), and Request (source floor, destination floor, direction). The interesting design decision sits inside the controller's dispatch algorithm: the simplest version assigns the nearest idle elevator; a more realistic version scores every elevator by direction match and distance and picks the lowest-cost one, closer to how SCAN and LOOK disk-scheduling algorithms work.
Reported as a Microsoft low-level design round question, usually for candidates on teams closer to Windows, devices, or Surface hardware integration. Interviewers push on the same two follow-ups almost every time: what happens when two elevators tie for a request (tie-break deterministically, don't just pick the first one found), and how do you avoid starving requests at the extreme top or bottom floor when the building gets busy in the middle.
Token bucket is the default answer: each client gets a bucket that refills at a fixed rate up to a max capacity, and each request consumes one token; if the bucket is empty, the request gets rejected or queued. Sliding-window log and sliding-window counter are the two alternatives worth naming, with sliding-window counter as the practical middle ground between token bucket's burstiness and a full log's memory cost. At Microsoft's Azure-flavored scale, the bucket state usually lives in a fast distributed store like Redis, with a Lua script or transaction ensuring the check-and-decrement happens atomically across concurrent requests.
Rate limiting comes up both as a standalone design question and as a follow-up tacked onto the OTP-service question earlier on this page, which is itself about preventing brute-force enumeration. Multiple 2024-2025 Microsoft SDE-2 accounts describe it as a design-round topic when the interviewer works on an API gateway or developer-platform team. The sharpest follow-up: how do you rate-limit fairly across a fleet of gateway instances without a single global counter becoming the bottleneck? The answer involves either a centralized counter with a short TTL or approximate counting with periodic reconciliation.
import time
class TokenBucket:
def __init__(self, capacity, refill_rate):
self.capacity = capacity
self.tokens = capacity
self.refill_rate = refill_rate # tokens per second
self.last_refill = time.time()
def allow_request(self):
now = time.time()
elapsed = now - self.last_refill
self.tokens = min(self.capacity, self.tokens + elapsed * self.refill_rate)
self.last_refill = now
if self.tokens >= 1:
self.tokens -= 1
return True
return FalseStart with the interface: read/write with TTL support, eviction policy (LRU default, configurable), and consistency requirements. Partition the key space using consistent hashing so nodes can be added or removed without a full rehash. Replication: each partition has a primary and N replicas; writes go to the primary and are asynchronously replicated, which means stale reads are possible. Discuss the CAP trade-off explicitly: Redis prioritizes availability over strong consistency (AP), which is the right call for a cache that's not the system of record.
This exact scenario (implement LRU cache, extend for distribution) was reported in a 2024 Microsoft SDE-2 Round 3 coding and design round. The interviewer asked candidates to address concurrent updates, the CAP theorem, and data consistency. At the SDE-2 level, the expected design doesn't need to be a complete production spec, but you should be able to reason through: what happens when a node goes down? What happens when you add a node mid-traffic?
Message ingestion: HTTP write to a message service, which writes to a durable message queue (Azure Service Bus or equivalent). Delivery to recipients: WebSocket connections from clients to a connection gateway; gateway subscribes to the user's message channel and pushes on receipt. Message ordering: assign monotonic sequence numbers at write time per channel. Presence (online/away/offline): heartbeat from client every 30 seconds, TTL-expiring record in a fast store (Redis), read on channel open.
Read receipts and typing indicators are common follow-up design elements. Read receipts require a per-user per-message state record, which at Teams scale (hundreds of millions of users, billions of messages) means you need a schema that doesn't fan out to all senders on every read. The pattern: batch read receipts and flush asynchronously every 5 to 10 seconds rather than acknowledging immediately on every message open.
Candidates who run Microsoft SDE-2 practice sessions through LastRoundAI show a consistent failure pattern that doesn't show up in prep guides. The failure isn't usually on the hard LeetCode question or the system design diagram. It shows up in the behavioral layer that's embedded in every technical round.
Microsoft interviewers ask "Why did you make that choice?" not to confirm your architecture is correct but to test whether you can describe a trade-off without sounding defensive. Candidates who treat every design question as having one right answer, and who argue for their first approach rather than exploring alternatives out loud, score poorly on the behavioral dimensions even when their technical answer is solid.
The second pattern we see: candidates who prepare for behavioral questions in isolation from technical preparation. Microsoft's "tell me about a time you failed" questions frequently end with "and what would you do differently?" and the strongest answers reference a technical approach the candidate would change. Behavioral and technical preparation at Microsoft are not separate workstreams. They should be the same workstream.

