Microsoft Frontend Developer (Mid-Level) Interview Preparation Guide
Microsoft's frontend developer interview process for mid-level candidates consists of an initial recruiter screening followed by technical phone screens and multiple onsite rounds. The process evaluates coding proficiency, system design thinking, React/JavaScript expertise, algorithmic problem-solving, and cultural alignment. Mid-level candidates are expected to demonstrate ownership of projects, strong fundamentals, and the ability to discuss architectural trade-offs.
Interview Rounds
Recruiter Screening
What to Expect
Initial conversation with a Microsoft recruiter to assess background, motivation, and fit. This round covers your resume, experience with relevant technologies, understanding of the role, and career goals. The recruiter verifies your availability, salary expectations, and visa sponsorship needs if applicable. This is your opportunity to clarify what the role entails and understand Microsoft's team structure.
Tips & Advice
Be enthusiastic about web technologies and Microsoft's products. Clearly articulate your experience with React, JavaScript, and responsive design. Prepare 2-3 concrete examples of projects you've built that demonstrate frontend skills. Ask thoughtful questions about the team, product, and tech stack. Show genuine interest in learning and growth at Microsoft.
Focus Topics
Motivation and Fit
Articulate why you're interested in Microsoft, what excites you about the role, and how this opportunity aligns with your career goals.
Practice Interview
Study Questions
Technical Stack Familiarity
Demonstrate hands-on experience with React, JavaScript, HTML5, CSS, responsive design, and browser development tools. Mention any experience with REST APIs, testing frameworks, or state management libraries.
Practice Interview
Study Questions
Professional Background and Experience
Overview of your 2-5 years of frontend development experience, key projects, and measurable impact. Be ready to discuss technical decisions you made and results achieved.
Practice Interview
Study Questions
Technical Phone Screen - React and Component Design
What to Expect
First technical interview conducted over video/phone with a senior frontend engineer. You'll be asked to build a simple React component or UI feature with well-defined requirements. The focus is on your ability to think through component architecture, state management, and writing clean, maintainable code. You'll share your screen and code in real-time using a collaborative editor or CodePen-style environment. This round typically includes follow-up questions about your approach, potential optimizations, and edge cases.
Tips & Advice
Clarify requirements before coding. Think out loud as you structure your solution—discuss component hierarchy, props vs. state, and potential performance considerations. Write clean, readable code with proper naming conventions. Be prepared to refactor or add features on the fly. Explain your reasoning for choices like using useState vs. useContext. Ask for feedback and be open to suggestions. Common tasks include building a to-do list, autocomplete search, image carousel, or form with validation.
Focus Topics
Code Quality and Maintainability
Write self-documenting code with meaningful variable names, proper indentation, and logical structure. Discuss potential edge cases and error handling. Show awareness of performance (e.g., unnecessary re-renders).
Practice Interview
Study Questions
CSS Styling and Responsive Design
Write CSS-in-JS or traditional CSS for React components. Implement responsive layouts using flexbox/grid. Understand CSS specificity and how to scope styles to components.
Practice Interview
Study Questions
DOM Manipulation and Event Handling
Handle events in React (onClick, onChange, form submissions), understand event delegation, and work with refs when necessary. Know event propagation and how to prevent default behavior.
Practice Interview
Study Questions
State Management in React
Manage local component state with useState, handle side effects with useEffect, and know when to use Context API or external libraries like Redux. Understand the differences and trade-offs.
Practice Interview
Study Questions
React Component Architecture and Composition
Design functional components with proper separation of concerns, reusability, and testability. Understand when to use custom hooks, context, or prop drilling. For mid-level, demonstrate awareness of performance implications.
Practice Interview
Study Questions
Technical Phone Screen - Algorithms and Problem-Solving
What to Expect
Second technical interview focusing on algorithmic problem-solving and core data structures using JavaScript. You'll receive a LeetCode-style coding problem (typically Easy to Medium difficulty) and solve it in a collaborative editor. The interviewer evaluates your problem-solving approach, code correctness, time/space complexity awareness, and ability to optimize. For mid-level candidates, you're expected to arrive at working solutions efficiently and discuss trade-offs without requiring major hints.
Tips & Advice
Start by clarifying the problem and discussing your approach before coding. Mention time and space complexity. Write clean code incrementally and test with examples. If you get stuck, explain your thinking and ask for hints rather than staying silent. Common problem types include array/string manipulation, sorting, searching, linked lists, and basic tree problems. Practice on LeetCode; focus on understanding the solution pattern rather than memorizing.
Focus Topics
Problem-Solving Approach and Communication
Think out loud, ask clarifying questions, discuss your approach before coding, explain your logic, and handle edge cases. Show structured thinking.
Practice Interview
Study Questions
JavaScript Fundamentals for Algorithms
Work with arrays, objects, loops, conditionals, and sorting. Know built-in methods (map, filter, reduce, slice, split, etc.) and when to use them. Understand variable scope and closures in the context of problem-solving.
Practice Interview
Study Questions
Array and String Manipulation
Solve problems involving array operations (searching, sorting, two-pointer), string transformations, and substring/subsequence patterns. Understand when to use different approaches.
Practice Interview
Study Questions
Time and Space Complexity Analysis
Analyze algorithm efficiency using Big O notation. Discuss trade-offs between time and space. For mid-level, optimize from brute force to efficient solutions.
Practice Interview
Study Questions
Onsite Round 1 - Advanced React and UI Component Implementation
What to Expect
An in-depth technical round (onsite or virtual onsite) where you build a more complex React component or mini-application with multiple requirements and edge cases. This might involve creating a reusable UI component (like a dropdown, modal, or data table) with attention to accessibility, performance, and user experience. You'll be evaluated on architectural decisions, handling of complex state, and how you approach testing and edge cases. The interviewer may ask you to extend functionality or refactor code based on new requirements.
Tips & Advice
Start by understanding all requirements, including accessibility (ARIA attributes, keyboard navigation). Sketch out your component structure before coding. Use proper TypeScript/JSDoc types if offered. Consider error states, loading states, and edge cases. Write testable code. If asked to add a feature, refactor gracefully. Discuss performance optimizations (memoization, lazy loading). Be prepared to talk about how this component would integrate into a larger application. Show your design sense—components should be intuitive and follow platform conventions.
Focus Topics
API Integration and Data Fetching
Fetch data from REST APIs, handle loading/error states, implement error boundaries, and manage side effects. Know when to use useEffect vs. other patterns.
Practice Interview
Study Questions
Testing React Components
Write unit tests using testing libraries like React Testing Library. Test user interactions, rendering logic, and edge cases. Understand the difference between unit and integration tests.
Practice Interview
Study Questions
Form Handling and Validation
Build forms with validation logic, error handling, and user feedback. Handle field focus, blur, and submission. Manage form state efficiently.
Practice Interview
Study Questions
Performance Optimization Techniques
Identify and fix performance issues: unnecessary re-renders, inefficient state updates, large bundle sizes. Use React DevTools profiler. Discuss code splitting and lazy loading.
Practice Interview
Study Questions
Advanced React Patterns and Hooks
Master custom hooks, useReducer, useCallback, useMemo, and useContext. Understand when to optimize with memoization and when it's premature. Know the rules of hooks.
Practice Interview
Study Questions
Accessibility Standards and Implementation
Implement semantic HTML, ARIA attributes, keyboard navigation, focus management, and screen reader support. Know WCAG guidelines and how to test accessibility.
Practice Interview
Study Questions
Onsite Round 2 - Frontend System Design and Architecture
What to Expect
A design-focused technical round where you architect a complex frontend system or feature at a high level. You might be asked to design how you'd build a specific Microsoft feature (e.g., 'Design the emoji autocomplete feature in Microsoft Teams' or 'How would you build a collaborative document editor UI?'). The interviewer evaluates your ability to break down problems, discuss trade-offs, consider scalability and performance, and think about component architecture, state management, caching, and API design. You're expected to ask clarifying questions, propose multiple approaches, and justify your choices.
Tips & Advice
Ask clarifying questions first: What are the key requirements? How many users? What's the performance requirement? Start with a high-level architecture diagram or description. Discuss trade-offs openly (e.g., client-side caching vs. server-side). Consider edge cases, error handling, and offline behavior. For mid-level, you should show awareness of real-world constraints. Discuss how the frontend would integrate with backend services. Be open to feedback and willing to pivot if the interviewer introduces new constraints.
Focus Topics
Error Handling and Resilience
Design how the system handles network failures, API errors, timeouts, and edge cases. Implement graceful degradation and user-friendly error messages.
Practice Interview
Study Questions
API Design and Data Flow
Design how the frontend would request data from the backend. Discuss pagination, filtering, sorting, real-time updates, and error handling. Consider API efficiency and reducing network requests.
Practice Interview
Study Questions
Frontend Architecture and Scalability
Design component hierarchies, folder structures, and module organization for large applications. Discuss how to keep components maintainable as the codebase grows. Consider separation of concerns and modularity.
Practice Interview
Study Questions
State Management Strategy
Design state management for complex applications: where to keep state (component, context, external store), how to avoid prop drilling, and how to sync state across components. Compare approaches.
Practice Interview
Study Questions
Performance and Caching Strategies
Discuss caching (client-side caching, HTTP caching, service workers), code splitting, lazy loading, and pagination. Design for performance from the start. Consider memory constraints.
Practice Interview
Study Questions
Onsite Round 3 - Behavioral and Hiring Manager Round
What to Expect
Final round with a hiring manager or senior engineer focusing on behavioral assessment, culture fit, and team collaboration. This round explores your work style, communication, how you handle conflicts or setbacks, your approach to mentoring or helping junior developers, and your growth mindset. The interviewer assesses whether you work well in a collaborative environment, take ownership of problems, and align with Microsoft's values (innovation, customer focus, growth mindset). You'll also have the opportunity to ask questions about the team, role, and career growth at Microsoft.
Tips & Advice
Prepare specific STAR method stories that demonstrate collaboration, conflict resolution, learning from failure, and impact. Talk about a time you mentored someone, owned a difficult project, or advocated for a better technical decision. Emphasize growth mindset and continuous learning. Be authentic and genuine. Ask thoughtful questions about the team's technical challenges, growth opportunities, and how success is measured. Show interest in how your work impacts Microsoft's customers. Discuss how you'd contribute to your team beyond just coding—code reviews, documentation, pairing with junior developers, etc.
Focus Topics
Growth Mindset and Learning
Discuss how you stay current with frontend trends, handle technologies you don't know, approach learning challenges, and get feedback. Share your learning plan.
Practice Interview
Study Questions
Mentoring and Knowledge Sharing
Share examples of helping junior developers, code reviews you've conducted, knowledge you've shared, or initiatives you've led. Show you invest in your team's growth.
Practice Interview
Study Questions
Handling Ambiguity and Pressure
Share a story where requirements weren't clear, priorities shifted, or you faced a tight deadline. Describe your approach, how you communicated, and the outcome.
Practice Interview
Study Questions
Ownership and Accountability
Share examples of projects where you took end-to-end ownership, made key technical decisions, and drove results. Discuss how you handle setbacks and learn from failures.
Practice Interview
Study Questions
Collaboration and Communication
Describe how you work with designers, backend developers, product managers, and other frontend engineers. Share examples of resolving disagreements, giving feedback, and explaining technical concepts to non-technical stakeholders.
Practice Interview
Study Questions
Frequently Asked Frontend Developer Interview Questions
As a senior/frontend lead you must define and drive API integration standards across multiple teams. Outline your approach to integration governance: standards you would set (schema, error formats, versioning), how you'd enforce them (CI checks, codegen, SDKs), how to onboard teams, and which metrics you'd track to measure adoption and integration quality.
Sample Answer
Approach summary
I’d create a pragmatic, developer-friendly API governance program that balances consistency with team autonomy, focused on frontend needs (predictable payloads, small responses, stable contracts).
Standards I’d set
- Schema: OpenAPI v3 required, strict request/response schemas, nullable rules, example payloads, pagination cursor patterns.
- Error formats: unified error envelope (code, message, details, traceId, userFacing boolean).
- Versioning: semantic versioning on major breaking changes; prefer non-breaking additive releases and deprecation headers (Sunset, Deprecation-Date).
- Performance: size limits, caching headers, and idempotency where applicable.
- Security/headers: auth schemes, CORS, rate-limit headers.
Enforcement
- CI checks: validate OpenAPI, contract tests, schema linting, payload size tests.
- Codegen & SDKs: generate TypeScript clients and typed models used by frontend repositories to ensure typesafety.
- Gateways: API gateway policy checks (headers, auth).
- Review process: lightweight API design review board for major changes.
Onboarding
- Starter repo with generated SDK, example React integration, and Storybook mocks.
- Workshops, docs portal, and Slack channel; template PRs and migration playbooks.
Metrics
- Adoption: % services with OpenAPI + generated SDK.
- Quality: contract test pass rate, production 4xx/5xx per integration, response size and latency percentiles.
- Stability: number of breaking changes per quarter and mean time to migrate.
This keeps frontend developer teams productive while ensuring reliable cross-team contracts.
When you set out to learn something new, how do you decide where to learn it from? And how quickly do you notice when the source you picked is not working for you? Tell me about a time you abandoned one partway through.
Sample Answer
Direct answer
I match the source to what I actually need: a quick conceptual grasp, a deep applied skill, and a decision-grade understanding each call for a different kind of source, and before committing real time I check the source's credibility, currency, and depth rather than assuming a polished one is automatically a good one.
Structured elaboration
Matching source to goal: an overview article is fine for a quick conceptual grasp, but a deep applied skill usually needs hands-on exercises with feedback, and a decision I have to get right needs the primary or authoritative source (the actual specification or documentation) over a summary of it, because summaries drift from what the thing actually does.
Judging credibility, currency, and depth upfront: checking when it was written or last updated, whether it matches the current version of whatever it's teaching, and whether it has exercises or just explanation, before investing real time.
When focused practice against feedback beats open-ended exploration, and when it doesn't: repeated, deliberate practice against concrete feedback is better once I know roughly what I'm aiming for; open-ended exploration is better earlier, when I don't yet know enough to know what to practice.
Sequencing reading and building: I interleave them rather than doing all of one before the other, since building surfaces exactly which parts of the reading I didn't actually understand.
Cost and time as real constraints: I weigh a resource's price and the time it demands against how urgent the need is, not just its reputation.
Early warning signs a source is wrong: it's too shallow for what I need, it's clearly outdated, it targets the wrong version or stack, or it has no exercises at all. Once I see one of those, I drop it rather than finishing it out of sunk-cost momentum. I also treat a knowledgeable colleague as a resource with its own selection criteria, specifically someone close to the actual system in question, not just the most senior person available.
Worked example
I started with a broad video course to get oriented on a tool, and within the first session realized it was built for an older version with several behaviors that had since changed. I cross-checked one specific claim it made against the current official documentation, and the documentation contradicted it. I dropped the course immediately rather than finishing it out of momentum, and switched to the current primary documentation paired with hands-on exercises for the applied depth I actually needed.
Trade-offs and pitfalls
The common failure here is over-investing in a resource because it's polished or well-produced, without checking whether it actually holds up against a quick spot-check on the primary source. The other is judging a resource purely by its reputation rather than by whether its specifics still match the current reality of what you're trying to learn.
You've concluded that a legacy or troubled system needs to be retired or fundamentally rebuilt, but leadership isn't yet convinced it's worth the cost. Build the case and the plan: quantify the risk or cost of doing nothing, propose a phased approach with milestones, staffing, and a fallback option, and define the KPIs you'd use to know the effort succeeded and ownership can be handed off.
Sample Answer
Direct answer
Win this argument by translating "it's old and risky" into a number leadership already cares about, the cost or risk of doing nothing, then pair it with a phased plan that has a real fallback option, so the ask isn't all-or-nothing, and key performance indicators (KPIs, the specific measures used to judge success) that let leadership know later, objectively, whether the bet paid off.
Structured elaboration
- Quantify the cost of inaction: estimate current cost (maintenance hours, licensing, incident time) and risk exposure, as an honest range, not a falsely precise number.
- Phased plan: stages that each deliver something independently useful, so leadership sees value before everything is done.
- Milestones: a concrete checkpoint per phase with an explicit go/no-go decision.
- Staffing: name the team and time commitment, and where it comes from, since being unstaffed is the most common reason these efforts stall.
- Fallback: an explicit plan B if the full rebuild doesn't get funded or stalls partway.
- KPIs and handoff: two or three measurable success criteria plus a named owner once migrated, so "done" has a clear definition.
Worked example
A legacy reporting system costs the team an estimated 15 engineer-hours a week in firefighting, about 780 hours a year, and caused 4 data-accuracy incidents last year at roughly two days each to trace and fix. Cost of inaction: 780 hours a year in maintenance alone, before incident time or the risk of a fifth incident hitting an externally-reported number. Phased plan: Phase 1 (6 weeks) migrates the 3 highest-traffic reports as a parallel run, comparing output for parity; Phase 2 (8 weeks) migrates the remaining 12 once parity is proven; Phase 3 (2 weeks) decommissions the old system. Go/no-go at the end of Phase 1: pause if the new pipeline doesn't match within an agreed tolerance. Staffing: 1 dedicated engineer plus 20% of a second, for 16 weeks. Fallback: if the second engineer isn't funded, Phase 1 alone still cuts an estimated 60% of firefighting time, since those 3 reports account for most of it. KPIs: firefighting hours under 5 a week (from 15) and zero data-accuracy incidents in the following two quarters, reviewed at a 90-day check where ownership formally transfers.
Trade-offs and pitfalls
A suspiciously exact dollar figure instead of an honest range invites justified pushback. A phased plan with no independent value per phase means leadership sees nothing until it's all done, a hard sell. And no named owner after cutover lets the new system quietly become whoever built it's permanent side project.
Implement a reusable debounce utility in JavaScript with options for leading and trailing invocation and a cancel method. Ensure it correctly handles rapid successive calls, retaining 'this' context, and returns the result of the debounced function when appropriate. Provide an ES6 implementation and explain edge cases (e.g., cancel during pending leading call).
Sample Answer
Direct answer
A correct debounce utility needs to track not just a pending timer but how many calls occurred within the current window, because leading-edge and trailing-edge invocation interact: a single call in a leading+trailing window must fire once, not twice, while multiple calls in that same window correctly fire twice (once on the leading edge with the first call's arguments, once on the trailing edge with the last call's arguments). cancel() can only suppress a pending trailing call; it cannot undo a leading call that has already executed synchronously.
Structured elaboration
- Leading vs. trailing semantics: leading invokes immediately on the first call of a new window; trailing invokes once the window elapses with no further calls. Both can be enabled together, which is where most naive implementations get the single-call case wrong (see below).
- The double-invoke trap: a naive implementation that unconditionally fires the trailing call whenever
trailingis true will invoke twice for a window containing exactly one call under{leading: true, trailing: true}, once from the leading edge and once from the trailing edge, for the same logical call. The fix is tracking a per-window call count and suppressing the trailing invocation specifically when the window had exactly one call AND that call already fired on the leading edge. - Retaining
thiscontext: use a regularfunctionexpression for the returned debounced wrapper (not an arrow function) and call the wrapped function withfn.apply(thisArg, args), capturingthisfrom the call site of the debounced wrapper itself, not from wheredebounce()was originally invoked. - Cancel during a pending leading call: if
leading: trueand a call has already fired synchronously, the side effect has already happened, cancel cannot retroactively undo it. Whatcancel()legitimately does is clear the pending timer so the TRAILING call that would otherwise fire later never happens. This distinction (already-happened vs. still-pending) is the actual edge case named in the question, and a test must assert on it precisely: the leading call's effect persists after cancel, only the trailing call is suppressed. - Return value: the debounced function should return whatever the underlying function returned on its most recent actual invocation, since a caller synchronously reading the return value on a leading call needs that value immediately, not after the debounce window closes.
Worked example (executed, ES6/ECMAScript 2015)
function debounce(fn, wait, { leading = false, trailing = true } = {}) {
let timer = null, lastArgs = null, lastThis = null, result, callCountInWindow = 0;
function invoke(args, thisArg) { result = fn.apply(thisArg, args); return result; }
function debounced(...args) {
const isNewWindow = timer === null;
lastArgs = args; lastThis = this; callCountInWindow += 1;
if (isNewWindow && leading) invoke(args, this);
if (timer !== null) clearTimeout(timer);
timer = setTimeout(() => {
const shouldTrailingInvoke = trailing && !(leading && callCountInWindow === 1);
if (shouldTrailingInvoke) invoke(lastArgs, lastThis);
timer = null; callCountInWindow = 0;
}, wait);
return result;
}
debounced.cancel = function () {
if (timer !== null) { clearTimeout(timer); timer = null; callCountInWindow = 0; }
};
return debounced;
}
Run against seven scenarios with real timers (30ms wait, real setTimeout, no mocked clock):
scenario1_trailing_only: ["c"]
scenario2_leading_and_trailing_multi_call: ["a","c"]
scenario3_leading_and_trailing_single_call: ["only"]
scenario4_this_context: fn ran without throwing, retained call succeeded
scenario5_leading_return_value: 10
scenario6_cancel_during_pending_leading: {"callsRightAfterLeadingEdge":["first"],"callsAfterCancelAndWait":["first"]}
scenario7_cancel_noop: no throw, safe
Scenario 2 confirms the multi-call case correctly fires twice (["a","c"], first and last), scenario 3 confirms the single-call case correctly fires exactly once (not ["only","only"]), and scenario 6 confirms the cancel-during-pending-leading-call edge case precisely: first is recorded once (the leading call already ran) and stays at exactly ["first"] after cancel and the wait elapses, meaning the trailing call for second was successfully suppressed and the already-fired leading call was correctly left alone.
Trade-offs and pitfalls
The most common wrong turn is the double-invoke bug described above, which passes any test that only checks "trailing mode alone" and "leading mode alone" separately and never tests the combination on a single-call window. A second pitfall is using an arrow function for the debounced wrapper, which permanently binds this to the enclosing lexical scope at definition time and silently breaks any usage as an object method. A third is assuming cancel() should reset callCountInWindow retroactively affect a leading call that already fired; it should not, and a test asserting the leading call's side effect persists after cancel is the one that actually catches an implementation that got this wrong.
Legal sign-off is going to take three weeks, but the team wants to ship in one. How do you manage that timeline without steamrolling legal's concerns?
Sample Answer
Direct answer
Treat "legal needs three weeks but the team wants one week" as a scope problem, not a speed problem. Split the release into what can ship without new legal review and what genuinely needs sign-off, then give legal a narrow, well-defined ask for the second piece instead of asking them to review everything faster. The team ships on time, and the risky piece launches on its own review-driven schedule.
Structured elaboration
Find out what is actually blocking legal
"Legal sign-off" is rarely one undivided review. Ask legal directly which specific elements are new or unreviewed, and which are unchanged from something already approved. Most releases are a mix, and the review clock usually belongs to a small fraction of the surface area.
Split the release along that line
Everything that reuses already-approved language, patterns, or flows ships in the one-week window. Anything net-new that legal has not seen goes behind a feature flag (a toggle that keeps new code hidden from users until you're ready to turn it on) and ships later, once sign-off lands, decoupled from the original deadline.
Reduce legal's per-item cost, do not just ask for speed
A vague "please review this flow" invites a slow, open-ended read. A redlined diff (a side-by-side markup showing exactly which words changed from the last approved version, like tracked changes) against previously-approved language, with a one-paragraph explanation of what changed and why, is something legal can turn around fast because the review surface is small and explicit.
Keep everyone honest about the split
Do not quietly ship around legal's concern and call it done. Tell legal what you are shipping now, what is gated, and why you drew the line there, and let them confirm or push back on the boundary itself, not just react to a missed deadline.
Worked example
A signup redesign is due in one week. It includes a new consent checkbox asking users to opt into sharing data with a third-party analytics partner, and the copy for that checkbox has never been reviewed (legal quotes three weeks because it touches data-sharing language that needs a compliance read). Everything else in the redesign, the new layout and the reworked field order, is unchanged from an already-approved pattern used elsewhere in the product.
The split: ship the redesign now using the existing, already-approved consent copy and opt-in behavior unchanged. Put the new third-party-sharing consent language and checkbox behind a flag, off by default. Send legal a one-page diff: exactly the new sentence, what data it covers, and why it is being added, instead of the whole signup flow. The redesign ships in the one-week window. The new consent copy ships later, whenever legal actually signs off, on its own timeline, without ever having blocked the rest of the release.
Trade-offs and pitfalls
A flag-gated split adds real overhead: someone has to remember to remove the flag, and a half-shipped feature can linger longer than planned if nobody owns closing the loop. It also only works when the risky piece is genuinely separable. If the new element is load-bearing, meaning the whole flow depends on it, forcing a split creates a worse product than waiting.
The biggest pitfall is doing the split unilaterally and only telling legal afterward. That reads as shipping around the reviewer even when the intent was reasonable, and it burns the relationship needed for the next time this happens. The senior move is proposing the boundary and getting legal's explicit agreement on it before the ship date, not after.
When should you write a comment versus refactor the code so it explains itself? Given a trivial restating comment like // increment i by 1 above i += 1, explain whether it should be removed, and give one example each of a comment that legitimately belongs (explains WHY) and one that's a smell (explains WHAT).
Sample Answer
Direct answer. Comment when the code can't express WHY (a business rule, a workaround, a non-obvious trade-off); refactor instead of commenting when the comment only restates WHAT the code already says -- a comment that duplicates the code is guaranteed to drift out of sync with it eventually.
The trivial case
# increment i by 1
i += 1
This comment is pure noise: it tells you nothing i += 1 doesn't already say faster to read. Delete it; if i needs a better name to convey intent (e.g., retry_count += 1), fix the name instead of commenting around it.
A comment that legitimately belongs (explains WHY)
# Stripe requires idempotency keys to be reused for retries within 24h,
# otherwise it treats a retry as a new charge. See INC-4021.
idempotency_key = order_id # intentionally NOT time-based
No amount of renaming makes 'why we chose this specific value, tied to an external API's undocumented-until-we-got-burned behavior' obvious from the code alone -- this is exactly the kind of context a comment should preserve, ideally with a link to the incident/ticket for anyone who wants the full story.
A comment that's a smell (explains WHAT, redundant with the code)
# loop through all users
for user in users:
The code already says this as clearly as English could; the comment adds a second thing that has to be kept in sync every time the loop changes, for zero reader benefit.
A simple test to apply
Ask: 'if I deleted this comment, would a competent reader lose information, or just lose a restatement?' If deleting it loses nothing, delete it. If deleting it loses the REASON something non-obvious is true, keep it (and consider whether the reason belongs in a commit message / ticket link too, for permanence).
Trade-offs and pitfalls
- Comments that explain why are still at risk of going stale if the underlying reason changes (the external API behavior gets fixed) but nobody removes the now-obsolete comment -- treat comments as code that also needs maintenance, not a write-once artifact.
- Don't over-correct into a 'no comments ever' culture; some domains (financial regulations, security-sensitive code, deliberately non-obvious performance tricks) genuinely need WHY documented, and a codebase that bans comments entirely just pushes that knowledge into people's heads (or nowhere), which is worse.
- A comment that says 'TODO: fix this properly' with no ticket link or date is close to noise too -- if it's worth flagging, it's worth tracking somewhere more durable than an inline string that nobody searches for.
You are given an array of n+1 integers where each value is between 1 and n (inclusive). Prove and implement an algorithm to find a duplicate value in O(n) time and O(1) extra space without modifying the array. (Hint: use cycle detection/floyd's algorithm treating indices as pointers.)
Sample Answer
Direct answer
Treat each value in the array as a pointer: from index i, "follow" nums[i] to land on index nums[i]. Because there are n+1 values all in the range [1, n], at least two different indices must point to the same value (pigeonhole), which means this functional graph has a cycle, and the duplicate value is exactly the entry point of that cycle. Floyd's tortoise-and-hare cycle detection finds that entry point in O(n) time and O(1) extra space, without modifying the array at all, which is exactly what the question asks for.
Approach (Floyd's cycle detection)
- Start both
slowandfastatnums[0], i.e. one step into the implicit linked structure (index 0 always has an outgoing "pointer," but nothing points back to it, so it can't be part of the cycle itself, only the tail leading into it). - Advance
slowone step (slow = nums[slow]) andfasttwo steps (fast = nums[nums[fast]]) each iteration until they meet; a meeting point is guaranteed to exist since the structure has a cycle (standard tortoise-and-hare argument). - Reset a second pointer to index 0, then advance it and
slowone step at a time together; the index where they meet is the cycle's entry point, which is the duplicate value.
Complexity
Time: O(n) (each phase does at most O(n) steps). Space: O(1) extra; nums itself is never modified.
Edge cases
- Exactly one duplicate value, appearing exactly twice: this is the assumed input shape and the algorithm handles it directly.
- The duplicate value equal to
nitself (the largest allowed value): handled the same way, since indexing is 0-based but values start at 1, sonums[i]is always a valid index regardless of which value 1..n is duplicated.
def find_duplicate_floyd(nums):
slow = nums[0]
fast = nums[nums[0]]
while slow != fast:
slow = nums[slow]
fast = nums[nums[fast]]
slow2 = 0
while slow2 != slow:
slow2 = nums[slow2]
slow = nums[slow]
return slow
data = [1, 3, 4, 2, 2]
original = list(data)
print(find_duplicate_floyd(data), data == original)
Output:
2 True
The duplicate is correctly identified as 2, and data == original confirms the array was never mutated during the search.
Alternative technique: index-marking
A second valid approach exploits the same "values are indices" fact differently: walk the array once, and for each value, negate the entry at the index that value points to (abs(value) - 1). If you ever land on an index whose entry is already negative, that index (converted back to 1-based) is the duplicate, because it means two different positions "pointed" to it. This is also O(n) time and O(1) additional space, but unlike Floyd's approach, it works by temporarily mutating nums in place (each visited value's target slot gets negated), so if the caller needs nums to remain externally unmodified while the function runs (not just restored by the time it returns), Floyd's version is the safer default.
def find_duplicate_marking(nums):
duplicate = None
for x in nums:
idx = abs(x) - 1
if nums[idx] < 0:
duplicate = idx + 1
break
nums[idx] = -nums[idx]
for i in range(len(nums)):
nums[i] = abs(nums[i])
return duplicate
data2 = [1, 3, 4, 2, 2]
print(find_duplicate_marking(data2), data2)
Output:
2 [1, 3, 4, 2, 2]
Both techniques agree on the duplicate (2), and the marking approach restores the array to its original values by the time it returns, even though it mutated it during the scan.
Trade-offs and pitfalls
- "Does not modify the array" has two readings, and the question's phrasing ("without modifying the array") most naturally means Floyd's guarantee: never mutated, at any point, including during execution. The marking approach only satisfies a weaker version ("unmodified once the function returns"), which is a meaningful difference if another thread could read
numsconcurrently while this function runs, or if the function could throw partway through and leave the array in its negated state. - A frequent proof gap: candidates often reach for cycle detection without first establishing why a cycle must exist here. The argument is exactly pigeonhole: n+1 values drawn from a range of only n possible values guarantees at least one repeat, and because every value is a valid index (never 0, since the range is [1, n] not [0, n-1]), the "value points to index" structure is well-defined for every position, forcing at least one node in the sequence to be revisited, i.e. a cycle.
- A common bug in the marking approach: forgetting the final restoration pass, which silently corrupts the caller's array (still functionally finds the right duplicate, but violates the "don't modify the array" requirement in a way that's easy to overlook if you only test the return value).
You have a form where a custom clickable icon sits inside the form but should not submit it when clicked. Explain when to use event.preventDefault() versus event.stopPropagation() and provide a short code example that prevents form submission for that icon only while allowing other form submissions to proceed normally.
Sample Answer
Answer (Frontend Developer perspective)
When to use which
- event.preventDefault(): stops the browser’s default action (e.g., submitting a form, following a link). Use this when you want to cancel the default behavior of the element clicked.
- event.stopPropagation(): stops the event from bubbling up to ancestor elements. Use this when clicking should not trigger parent handlers (but default action may still occur unless prevented).
Goal: clicking the icon should NOT submit the form but other submit buttons should work normally — use preventDefault on the icon click. If form or parent has click handlers that you also want to avoid, add stopPropagation.
Example
// Icon inside form
const icon = document.querySelector('.no-submit-icon');
icon.addEventListener('click', function(event) {
// prevent the browser from treating this click as a form submit
event.preventDefault();
// also stop bubbling if parent click handlers might submit
event.stopPropagation();
// perform icon-specific action
console.log('Icon clicked: opened menu');
});
This prevents submission only when the icon is clicked; normal submit buttons remain unchanged.
What fields does a good JSON error response need so both a machine caller and a human developer can act on a failure? Design that shape for a REST API consumed by mobile and web clients: an error code, a user-facing message, a developer-facing trace or correlation ID, and how you would map these to HTTP status codes. Provide one example error JSON a client could parse programmatically.
Sample Answer
Four things, at minimum: a machine-parsable error CODE (a stable string a client can branch logic on), a human-readable MESSAGE (for logging and debugging, never for client branching logic), a correlation or trace ID (so a developer can find this exact request in server-side logs), and a documented mapping from error codes to HTTP status codes so the status line and the body agree on what actually happened.
Why each field earns its place
Error code, not just a status code. HTTP status codes are coarse (a 400 covers many different validation failures); a specific, stable string like insufficient_inventory lets a client write real logic ("if this specific error, show the user a restock message and offer alternatives") instead of parsing a human-readable sentence, which is fragile and can change wording without warning.
A message meant for humans, not machines. The message field exists for logs, support tickets, and developer debugging; a client should never regex-match against it to decide behavior, because message wording is exactly the kind of thing that changes without being treated as a breaking change.
A correlation ID. When a client reports "I got an error," the correlation ID is what lets an engineer find the exact request in server-side logs in seconds instead of guessing based on approximate timestamps.
A stable status-code mapping. insufficient_inventory should map to the same HTTP status every time (409 Conflict is a reasonable choice here, since it reflects a state conflict rather than malformed input), documented so both server and client code agree on the mapping rather than each side guessing.
Worked example
{
"error": {
"code": "insufficient_inventory",
"message": "Only 3 units of sku_1001 are available.",
"correlation_id": "req_8f2c9a1e",
"retryable": false
}
}
This example is 145 bytes as compact JSON. A client parses this programmatically by checking error.code === "insufficient_inventory" (stable, documented) to decide its own behavior (offer the customer 3 units instead of the requested 5), logs error.correlation_id for later debugging if the customer files a support ticket, and displays a localized version of its OWN copy for that error code to the end user rather than showing error.message directly (which is written for a developer audience, in one language, and not meant for end-user display).
Trade-offs and pitfalls
The most common mistake is putting only a human-readable message in the error body and expecting clients to parse it for meaning; the moment that message's wording changes (even a small rewording meant purely to be clearer for humans), any client parsing it silently breaks. A second common mistake: reusing the SAME error code for conceptually different failures because they happen to produce the same HTTP status, which forces clients back to string-matching the message anyway to tell them apart, defeating the entire purpose of having a code field.
What basic keyboard interactions should every interactive component support? Include examples for buttons, links, custom controls (e.g., dropdowns), and explain why visible focus indicators and logical tab order are important for keyboard-only users.
Sample Answer
Overview — keyboard basics every interactive component must support
- Tab and Shift+Tab to move focus into and out of components
- Enter/Space to activate or toggle controls
- Arrow keys for list navigation where applicable
- Esc to close overlays/modals/dropdowns
- Home/End/PageUp/PageDown for long lists or grids
Examples
- Buttons: respond to Enter and Space; visible :focus state; pressing Space should trigger click behavior.
- Links: activate with Enter; open context menus with keyboard shortcuts if applicable.
- Custom dropdowns/comboboxes: Tab enters, ArrowDown/ArrowUp moves between options, Enter selects, Esc closes, Home/End jump to first/last option; manage aria-expanded/aria-activedescendant.
- Custom widgets (tabs/accordions): use Arrow keys for switching, Enter/Space to toggle, maintain roving tabindex for list-like navigation.
Why visible focus indicators matter
- They show which element receives keyboard input; without them keyboard-only users can’t orient or operate the UI. Always keep clear contrast and size for focus outlines or custom rings.
Why logical tab order matters
- Tab order should follow visual and reading order so users predictably arrive at controls. Avoid skipping interactive elements or trapping focus; implement focus management for overlays to return focus on close.
As a frontend developer I implement these behaviors with semantic HTML where possible, add appropriate ARIA only when necessary, and write keyboard handlers that mirror native control behavior.
Want to create your own tailored preparation guide using our deep research?
Get Started for FreeInterview-Ready Courses
Visual-first, interactive, structured learning paths
Browse Frontend Developer jobs
AI-enriched listings across hundreds of company career pages
Explore Jobs