Airbnb Frontend Engineer Interview Preparation Guide - Mid Level (2-5 Years)
Airbnb's Frontend Engineer interview process consists of a recruiter screening, an online technical assessment featuring algorithmic problems, and a comprehensive virtual onsite known as the 'Engineering Loop.' The Engineering Loop comprises four distinct rounds evaluating coding proficiency, system design thinking, code review competency, and cultural alignment. For mid-level candidates, expect 4-6 weeks from initial application to offer, with emphasis on practical frontend problem-solving, architectural thinking, and user-centric design considerations. The process focuses on evaluating your ability to build scalable, performant, and accessible user interfaces while demonstrating clear communication and Airbnb's core values.
Interview Rounds
Recruiter Screening
What to Expect
Initial conversation with a recruiting coordinator or hiring manager to assess your background, motivation for joining Airbnb, and general fit. This is typically a 30-45 minute call conducted over phone or video. The recruiter will walk through your resume, understand your career progression, and discuss your interest in the Frontend Engineer role. They will also provide an overview of the interview process, timeline, and answer any logistical questions. While not a technical interview, your communication clarity and enthusiasm for the role matter significantly.
Tips & Advice
Be specific about why you're interested in Airbnb—reference their platform, design philosophy, or specific features you admire. Show genuine enthusiasm about the role and the company's mission. Prepare a concise 2-minute summary of your professional journey and key achievements. Ask thoughtful questions about the team, the product, and the interview process. Research the recruiting team and hiring manager beforehand if possible. Be authentic and let your personality show while maintaining professionalism. Confirm technical requirements for upcoming rounds (such as your development environment setup for coding assessments).
Focus Topics
Technical Environment and Setup Readiness
Confirmation of your development environment, familiarity with required tools, and readiness for coding assessments.
Practice Interview
Study Questions
Clarifying Role and Expectations
Understanding the team structure, the specific product area or focus, and what success looks like in the first 3-6 months.
Practice Interview
Study Questions
Professional Background and Career Narrative
Your journey as a frontend engineer, key projects, growth areas, and career progression.
Practice Interview
Study Questions
Motivation and Company Fit
Why Airbnb specifically, what resonates about their mission ('Belong Anywhere'), and alignment with their culture and values.
Practice Interview
Study Questions
Online Technical Assessment
What to Expect
A timed online coding assessment conducted on a platform like HackerRank, typically featuring 2-3 algorithmic problems to be solved within 90-120 minutes. This round evaluates your core computer science fundamentals, problem-solving approach, and ability to implement solutions efficiently in JavaScript. You'll need to demonstrate understanding of data structures (arrays, linked lists, trees, graphs), common algorithms (DFS, BFS, dynamic programming), and clean, optimized code. The assessment serves as a filter to ensure candidates move forward with strong algorithmic foundations. This is often asynchronous, allowing you to complete it within a specified timeframe.
Tips & Advice
Start by reading all problem statements carefully; many mistakes stem from misunderstanding requirements. Spend 5-10 minutes planning your approach before coding. Write pseudocode first, then implement. Test your solution with provided examples and consider edge cases (empty inputs, single elements, negative numbers, etc.). Optimize for time and space complexity once your solution works correctly. Clean, readable code matters—use meaningful variable names and add comments for complex logic. Practice similar problems on LeetCode medium-level tagged problems beforehand. Time management is crucial; if stuck on a problem after 20-30 minutes, move to the next one and return if time permits. Submit working solutions even if not optimally efficient rather than incomplete solutions.
Focus Topics
Code Quality and Clean Implementation
Writing readable, well-structured code with meaningful variable names, comments for complex logic, and consistent formatting.
Practice Interview
Study Questions
Time and Space Complexity Analysis
Ability to analyze Big O complexity for your solutions, recognize bottlenecks, and optimize accordingly.
Practice Interview
Study Questions
Edge Cases and Error Handling
Identifying and testing edge cases (null inputs, empty collections, boundary values, negative numbers), writing defensive code.
Practice Interview
Study Questions
JavaScript Data Structures and Fundamentals
Deep understanding of arrays, objects, linked lists, stacks, queues, trees (binary, BST), graphs, heaps, and hash tables. Proficiency in implementing and manipulating these structures.
Practice Interview
Study Questions
Core Algorithms and Problem-Solving Techniques
Mastery of DFS, BFS, recursion, dynamic programming, sorting algorithms, searching algorithms, and pattern recognition for categorizing problems.
Practice Interview
Study Questions
Coding/Algorithm Round (Onsite - Engineering Loop)
What to Expect
The first of four onsite rounds comprising Airbnb's 'Engineering Loop.' This 60-90 minute round focuses on practical JavaScript coding problems, often with a frontend twist. You'll be asked to implement UI components or solve problems that simulate real Airbnb use cases (e.g., autocomplete functionality, star rating widget, event listener systems). Unlike the online assessment which emphasizes pure algorithms, this round evaluates your ability to write production-quality frontend code, handle DOM manipulation, work with asynchronous patterns, and consider user experience. Interviewers assess not just correctness but also your approach to component design, reusability, and code organization.
Tips & Advice
Clarify requirements with the interviewer before coding; understand whether you're building a reusable component or a one-off solution. Start with a clear structure: define inputs, outputs, and edge cases. Discuss your approach verbally before diving into code. Write modular, reusable code even for simple problems—this demonstrates mid-level thinking. Consider accessibility and user experience in your implementation (keyboard navigation, ARIA labels, responsive behavior). For async code, discuss handling race conditions and cleanup. Test your solution mentally with various inputs. Explain your design decisions as you code. If you get stuck, ask for clarification rather than making assumptions. Practice implementing common UI patterns like autocomplete, dropdown menus, and form validation beforehand.
Focus Topics
Form Handling and Data Validation
Input sanitization, form submission, validation logic, error handling, state persistence for form components.
Practice Interview
Study Questions
Reusable Component Design
Creating self-contained, configurable components that can be used in multiple contexts. Consider initialization, state management, and API design for components.
Practice Interview
Study Questions
Async Programming and State Management
Handling asynchronous operations (API calls, timers), managing component state, preventing memory leaks, handling race conditions in async flows.
Practice Interview
Study Questions
Accessibility and User Experience Fundamentals
Semantic HTML, keyboard navigation, screen reader support, ARIA labels, focus management, ensuring components work for all users.
Practice Interview
Study Questions
JavaScript Fundamentals for Frontend
Mastery of ES6+ features, closures, this binding, prototypes, async/await, Promises, callbacks, event handling, and DOM APIs.
Practice Interview
Study Questions
DOM Manipulation and Event Handling
Event delegation, adding/removing event listeners, traversing and manipulating DOM elements, managing event propagation and default behaviors.
Practice Interview
Study Questions
System Design Round (Onsite - Engineering Loop)
What to Expect
The second onsite round focusing on architecture and design at scale. In this 60-75 minute round, you'll be asked to architect a frontend system or application—examples include designing a property search page, a recommendation engine UI, or a chat interface. For mid-level engineers, this evaluates your understanding of rendering strategies (Server-Side Rendering vs. Client-Side Rendering vs. Hybrid), state management patterns, performance optimization, scalability, and user-centric design decisions. You'll discuss trade-offs between different approaches, justify your architectural choices, and address concerns like latency, scalability, and user experience. The interviewer looks for pragmatic decision-making and ability to handle ambiguous requirements.
Tips & Advice
Start by asking clarifying questions about requirements, scale, and constraints. Clarify whether you're optimizing for performance, user experience, development velocity, or SEO. Draw diagrams or use pseudocode to communicate your architecture. For each major decision, explicitly state trade-offs (e.g., SSR provides faster initial load and SEO benefits but increases server load; CSR offers smoother client transitions but poor initial load). Discuss real technologies and frameworks relevant to your experience and Airbnb's stack (React, Next.js, state management libraries). Address performance considerations: lazy loading, code splitting, caching strategies, image optimization. Think about accessibility and mobile responsiveness from the start. Discuss monitoring, error handling, and user feedback mechanisms. Be prepared to pivot your design based on interviewer feedback—adaptability matters as much as the initial solution. Reference real-world Airbnb features and challenges if possible.
Focus Topics
Marketplace-Specific Considerations
Understanding Airbnb's domain: real-time search functionality, property filtering, recommendation algorithms, booking flows, handling dynamic inventory, and scale challenges.
Practice Interview
Study Questions
Responsive Design and Mobile-First Approach
Designing for multiple devices and screen sizes, mobile-specific optimizations, touch interactions, and ensuring consistent UX across platforms.
Practice Interview
Study Questions
Scalability and Handling High Load
Caching strategies, content delivery optimization, handling API rate limits, managing traffic spikes, pagination strategies for large datasets.
Practice Interview
Study Questions
Rendering Strategies: SSR vs. CSR vs. Hybrid
Deep understanding of Server-Side Rendering, Client-Side Rendering, and hybrid approaches. Trade-offs between SEO, initial load time, server load, and client-side interactivity.
Practice Interview
Study Questions
Performance Optimization and Web Vitals
Code splitting, lazy loading, image optimization, caching strategies, reducing layout thrashing, minimizing bundle size, and optimizing for Core Web Vitals (LCP, FID, CLS).
Practice Interview
Study Questions
Frontend State Management and Architecture
Approaches to managing application state (Redux, Context API, Zustand, etc.), data flow patterns, caching strategies, and keeping state predictable and scalable.
Practice Interview
Study Questions
Code Review Round (Onsite - Engineering Loop)
What to Expect
The third onsite round simulating a real code review scenario. In this 60-75 minute round, you'll be presented with an actual pull request or code snippet and asked to conduct a thorough code review. Examples include reviewing a star rating widget, a form component, or an API integration. This evaluates your ability to assess code quality, identify bugs or edge cases, evaluate testing adequacy, ensure accessibility compliance, and communicate feedback constructively. You'll discuss concerns around performance, maintainability, security, accessibility, testing coverage, and adherence to best practices. This round reveals your standards for production code and your collaborative communication style.
Tips & Advice
Read the code carefully before commenting. Start by understanding the intent and requirements. Structure your review by categories: functionality, code quality, testing, accessibility, performance, and security. Ask clarifying questions if the code's purpose or context is unclear. Provide specific, actionable feedback rather than vague concerns. Explain the 'why' behind each feedback item—how it impacts maintainability, performance, or user experience. Acknowledge good practices you observe. Prioritize critical issues (bugs, security, accessibility) over style preferences. Suggest concrete solutions or alternatives. Ask about test coverage—check whether critical paths and edge cases are tested. For UI components, verify accessibility compliance (ARIA labels, keyboard navigation, contrast ratios). Consider developer experience and maintainability. Be respectful and collaborative in tone—code review is a two-way learning conversation, not a critique.
Focus Topics
Edge Cases and Error Handling
Identifying unhandled edge cases, assessing error handling strategies, checking for defensive coding practices, and robustness against unexpected inputs.
Practice Interview
Study Questions
Form Handling and Data Validation
Reviewing input sanitization, validation logic, error handling, state management within forms, and ensuring robust form submission flows.
Practice Interview
Study Questions
Accessibility and Inclusive Design Review
Verifying semantic HTML, ARIA labels, keyboard navigation, screen reader support, color contrast ratios, and ensuring components meet WCAG standards.
Practice Interview
Study Questions
Performance and Optimization Patterns
Identifying performance bottlenecks, unnecessary re-renders, memory leaks, inefficient data structures, optimization opportunities, and trade-offs.
Practice Interview
Study Questions
Testing Coverage and Quality
Assessing whether test coverage is adequate, evaluating test quality, identifying gaps in testing, distinguishing between unit, integration, and end-to-end tests.
Practice Interview
Study Questions
Code Quality Standards and Best Practices
Evaluating code for readability, maintainability, consistency with coding standards, use of design patterns, and adherence to SOLID principles where applicable.
Practice Interview
Study Questions
Behavioral Round (Onsite - Engineering Loop)
What to Expect
The final onsite round assessing cultural fit, communication skills, teamwork, and alignment with Airbnb's values. In this 45-60 minute conversation, you'll discuss your past experiences, how you handle challenges, your approach to collaboration, and your philosophy around building products. Interviewers will ask about specific situations you've encountered—conflict resolution, learning from failure, supporting teammates—and expect thoughtful, reflective answers using the STAR method (Situation, Task, Action, Result). Beyond competency, this round evaluates whether you embody Airbnb's mission 'Belong Anywhere' and whether you'd be a positive influence on team culture.
Tips & Advice
Prepare 3-4 concrete examples covering: overcoming technical challenges, collaboration and mentorship, handling disagreement or feedback, and learning from failure. Use the STAR method: set up the Situation briefly, explain your specific Task, detail the Actions you took (emphasize your agency and decision-making), and quantify the Results. Practice articulating the 'why' behind your decisions. Connect your answers to Airbnb's values when relevant. Be authentic—interviewers recognize canned responses. Listen carefully to questions and answer directly without excessive rambling. Discuss your growth mindset and how you learn. Prepare questions about the team, product direction, and culture. Research Airbnb's mission thoroughly and explain what 'Belong Anywhere' means to you personally—this is a frequently asked question. Show genuine interest in the role and company. End on enthusiasm for the opportunity and team.
Focus Topics
Impact and User-Centric Thinking
Discussing how your work directly impacts users, times you advocated for user needs, and your approach to building features with users in mind.
Practice Interview
Study Questions
Learning from Failure and Handling Criticism
Examples of mistakes you've made, how you responded, what you learned, and how you've applied those lessons. Receptiveness to feedback and critique.
Practice Interview
Study Questions
Mentorship and Supporting Junior Engineers
Examples of mentoring, onboarding, or helping junior teammates. How you share knowledge and elevate team capability.
Practice Interview
Study Questions
Airbnb Values and 'Belong Anywhere' Philosophy
Understanding Airbnb's mission and demonstrating alignment. How your personal values connect to Airbnb's vision of inclusive travel and belonging.
Practice Interview
Study Questions
Collaboration and Cross-Functional Teamwork
Experiences working with designers, backend engineers, product managers, QA, and other disciplines. How you communicate, incorporate feedback, and contribute to team success.
Practice Interview
Study Questions
Technical Problem-Solving and Adaptability
Examples of overcoming technical challenges, learning new technologies quickly, adapting when initial approaches don't work, and demonstrating growth mindset.
Practice Interview
Study Questions
Frequently Asked Frontend Developer Interview Questions
Some problems are not phrased as 'search this array' but still have a monotonic answer space: for example, given job sizes and a fixed number of identical servers, find the minimum possible value of the maximum load per server; or, given positions along a line, place k markers to maximize the minimum distance between any two. Explain how you recognize that binary search applies here even though there is no sorted array to search.
Sample Answer
Direct answer
Binary search doesn't require a sorted array; it only requires a monotonic predicate over the answer space, meaning that as candidate answers are scanned in one direction, the predicate flips from false to true (or true to false) exactly once. Once a fast "is this candidate value feasible" check exists with that monotonic property, the search runs directly over the range of possible answers instead of over array indices.
Structured elaboration
Approach
- Recognizing the pattern: look for a question phrased as "find the minimum or maximum value such that some condition holds," where checking the condition for one fixed candidate value is easier than finding the optimal value directly. If increasing the candidate value can only make the condition easier (or only harder) to satisfy, never both, the condition is monotonic and binary search applies.
- Job/server example: minimize the maximum load assigned to any of k identical servers, given a list of job sizes. For a candidate max-load D, checking feasibility means greedily assigning jobs to a server until adding the next job would exceed D, at which point a new server starts; if the assignment finishes using k or fewer servers, D is feasible. Feasibility is monotonic in D: any D' greater than a feasible D is also feasible, since a looser cap can only make greedy assignment use the same number of servers or fewer.
- Marker-placement example: place k markers along a line to maximize the minimum distance between any two chosen markers. For a candidate minimum distance D, checking feasibility means greedily placing markers left to right, always taking the next position at least D away from the last placed marker; if that produces k or more markers, D is feasible. Feasibility is monotonic in the opposite direction here: any D' smaller than a feasible D is also feasible, since a smaller required gap can only let you place the same number of markers or more.
- The same recognize-the-monotonic-property instinct applies to the "find peak element" problem, where the array itself is not sorted at all, but comparing an element to one neighbor still tells you which half must contain a peak; "is there a peak here or to one side" is a property that binary search's halving can still exploit even though nothing about the array is globally ordered.
def max_min_load(job_sizes, num_servers):
"""
Minimum possible value of the maximum load on any server, splitting the
jobs (in given order) into num_servers contiguous groups.
"""
def feasible(max_load):
servers_used = 1
current = 0
for size in job_sizes:
if current + size > max_load:
servers_used += 1
current = size
if servers_used > num_servers:
return False
else:
current += size
return True
lo, hi = max(job_sizes), sum(job_sizes)
while lo < hi:
mid = (lo + hi) // 2
if feasible(mid):
hi = mid
else:
lo = mid + 1
return lo
def max_min_distance(positions, k):
"""
Place k markers among sorted positions to maximize the minimum distance
between any two adjacent chosen markers.
"""
positions = sorted(positions)
def can_place(min_dist):
count, last = 1, positions[0]
for p in positions[1:]:
if p - last >= min_dist:
count += 1
last = p
return count >= k
lo, hi = 0, positions[-1] - positions[0]
while lo < hi:
mid = (lo + hi + 1) // 2 # bias up: looking for the largest feasible D
if can_place(mid):
lo = mid
else:
hi = mid - 1
return lo
Key points
- The object binary search operates over is the candidate answer value, not an array index.
- Proving the greedy feasibility check is monotonic, and separately proving that the greedy check itself is correct, are two distinct things to justify in an interview.
- The search range's bounds come from the problem itself, for example
[max single job size, sum of all job sizes]for the load-balancing version.
Worked example
job_sizes = [7, 2, 5, 10, 8], num_servers = 2. Binary search bounds: lo = max(job_sizes) = 10, hi = sum(job_sizes) = 32. Checking feasible(18): greedily accumulate 7, 2, 5 (running total 14); adding 10 would make 24 > 18, so a second server starts with 10, then 8 is added (running total 18, exactly at the cap). The assignment finishes using exactly 2 servers, so 18 is feasible. Checking feasible(17): the same greedy walk starts a third server once it reaches 8, exceeding the 2-server budget, so 17 is infeasible. Binary search converges on the smallest feasible value: max_min_load([7, 2, 5, 10, 8], 2) prints 18.
positions = [1, 2, 4, 8, 9], k = 3: max_min_distance prints 3, matching a direct check that placing markers at 1, 4, 8 gives a minimum gap of 3, and no arrangement of 3 markers on these positions can do better than 3.
Trade-offs & pitfalls
Complexity
Time: O(nlogW), where n is the number of jobs or positions (each feasibility check is O(n)) and W is the width of the answer search range (sum of job sizes, or the max position minus the min position); logW is the number of binary-search iterations.
Space: O(1) extra beyond sorting, if sorting is needed (the marker-placement version needs the positions sorted first, a one-time O(nlogn) cost).
Edge cases
k <= 1for the marker-placement version: any single marker trivially satisfies any distance requirement, so the answer is unconstrained (commonly defined as 0 or unbounded, depending on the problem statement).num_servers >= number of jobs: every job gets its own server, and the answer is just the largest single job.- All positions or job sizes equal: the feasibility checks degenerate but still behave correctly under the same greedy logic.
The hardest part of this pattern is proving the greedy feasibility check is actually correct, not just monotonic, which usually requires an exchange argument (showing any optimal solution can be rearranged into the greedy one without making it worse); stating "binary search works because the answer space is monotonic" without also justifying the O(n) check itself is an incomplete answer. A second common mistake is getting the search bounds wrong, either missing a valid answer at the boundary or introducing an off-by-one that causes the loop to terminate one step early or loop forever, depending on which bound is biased toward on a tie.
Tell me about the first time in your career you actively asked someone for performance feedback. What was the context, who did you ask, what did you hear, and how did it change your later work or the way you asked for feedback going forward?
Sample Answer
Direct answer
A strong answer here is a specific, honest story, not a general statement about valuing feedback: name the situation, the person, the actual content of what was heard, including if it stung a little, and then show a concrete, lasting change in either the work or the habit of asking, not just a one-time fix.
Structured elaboration
- Context. Pick a moment early enough in your career that asking took some initiative or nerve, since that's what this question is really probing: not "have you ever gotten feedback," but "did you go get it rather than wait for it."
- Who you asked, and why them. Name a specific person and why you chose them, a manager, a senior peer, someone whose judgment you specifically trusted, since the choice itself shows some intentionality.
- What you heard. Be specific and honest, including if the feedback was uncomfortable; a vague or safely-positive answer here reads as either not remembering it or not having really asked for anything real.
- What changed, and how you ask now. This is the part that separates a strong answer from a shallow one. Point to something concrete that's different in how you work now, or specifically in how you ask for feedback since then, more often, more specifically, from a wider set of people, not just "I took it to heart."
Worked example
Early in a first cross-functional project, for example a junior analyst's first project working directly with a business team outside their usual reporting line, the candidate proactively asks a more senior colleague on the project, "was there anything about how I presented this that made it harder to land with the business team?" The senior colleague says the analysis was technically solid, but it was presented with all the caveats and methodology up front, so by the time the actual finding showed up, the audience had already tuned out. That's specific, and a little uncomfortable to hear. The concrete change: since then, the candidate leads with the finding and the recommendation first, and puts methodology in an appendix or offers it only if asked. They also developed the habit of asking that same specific "what made this harder to land" question after every stakeholder-facing piece of work, not just waiting for a scheduled review.
Trade-offs and pitfalls
Choosing an example where the feedback was trivially positive doesn't actually demonstrate coachability. Describing what you heard but not what changed leaves the interviewer to guess whether it actually stuck. Over-dramatizing the feedback as devastating can read as performative rather than genuine. And describing only a one-time fix to that specific piece of work, instead of a durable change to how you work or ask for feedback afterward, undersells the point of the story.
Explain the JavaScript event loop with emphasis on macrotasks vs microtasks. Given a code snippet that uses setTimeout, Promise.resolve().then, and async/await, explain the order of execution and the practical implications this has for updating the DOM after asynchronous operations triggered from event handlers.
Sample Answer
Explanation (high level)
The event loop continuously takes one macrotask (task) from the task queue, runs it to completion, then drains the microtask queue (all microtasks) before rendering and moving to the next macrotask. Microtasks run immediately after the currently running script or macrotask and before the browser may paint.
Macrotasks vs Microtasks
- Macrotasks (tasks): setTimeout, setInterval, I/O, UI events.
- Microtasks: Promise callbacks (.then/.catch/finally), queueMicrotask, async function continuations (the part after await).
Microtasks have priority: all queued microtasks finish before the next macrotask or paint.
Example and execution order
Code:
console.log('script start');
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise then 1'))
.then(() => console.log('promise then 2'));
(async function() {
console.log('async start');
await null;
console.log('async after await');
})();
console.log('script end');
Order:
- script start
- async start
- script end
- promise then 1
- promise then 2
- async after await
- timeout
Why: the main script is a macrotask. During it, await schedules the continuation as a microtask. After the script finishes, the microtask queue runs (all Promise thens and async continuations), then the event loop processes the next macrotask (the setTimeout callback).
Practical implications for DOM updates in event handlers
- If you update the DOM inside a Promise.then or after await, the DOM mutation occurs as a microtask—before paint—so multiple microtask mutations get batched into one paint (good for performance).
- If you use setTimeout(..., 0) to update the DOM, the update happens in a later macrotask and will occur after microtasks and an intervening paint, which can cause flicker or extra layout paints.
- To ensure DOM reads reflect the latest writes, do writes in microtasks and limit synchronous reads (avoid layout thrashing). If you need the browser to paint before continuing (e.g., show a loader), use requestAnimationFrame or setTimeout to defer to the next frame.
Best practices for frontend devs
- Prefer Promise/async microtasks for quick, batched DOM work inside event handlers.
- Use requestAnimationFrame for visual updates tied to paint.
- Avoid mixing many sync reads/writes across microtasks to prevent forced reflows.
- Use setTimeout when you intentionally want to yield to the browser (e.g., allow a paint before heavy work).
You must support both IE11 and modern evergreen browsers. Outline a CSS and responsive design strategy that provides graceful degradation or progressive enhancement. Discuss feature detection strategies (e.g., @supports), PostCSS/transpilation pipelines, polyfills, fallback layouts for missing features, and testing approaches for verifying both legacy and modern behaviors.
Sample Answer
Approach summary
Describe features-first for modern browsers (progressive enhancement) and provide simple, robust fallbacks for IE11 (graceful degradation). Use feature-detection (@supports / JS) + build-time transpilation (PostCSS/Babel) + polyfills and test across real/virtual legacy browsers.
Feature detection & CSS examples
- Use @supports to gate modern code (Grid, container queries):
/* use Grid when available */
@supports (display: grid) {
.layout { display: grid; grid-template-columns: 1fr 3fr; gap: 16px; }
}
/* fallback to flex for browsers without grid */
.layout { display: flex; flex-direction: row; }
- CSS variables fallback pattern:
:root { --brand: #0070f3; }
.button { background: var(--brand, #0070f3); } /* second arg is fallback */
Build & transpilation pipeline
- Use Browserslist to target evergreen + IE 11.
- PostCSS with postcss-preset-env (autoprefixer included) to polyfill newer CSS and add vendor prefixes.
- Example PostCSS config snippet:
module.exports = { plugins: [ require('postcss-preset-env')({ stage: 3, browsers: 'defaults, IE 11' }) ] }
- JavaScript: Babel with @babel/preset-env and core-js for polyfills; configure useBuiltIns: 'entry' or 'usage' per project.
Polyfills & JS feature detection
- Provide polyfills only where needed: core-js (Promise, Symbol), fetch polyfill, URLSearchParams if used.
- Use feature detection in JS (or Modernizr) to conditionally load polyfills:
if (!window.Promise) { import('core-js/features/promise'); }
Fallback layouts & UX
- Prefer mobile-first CSS; ensure layout degrades to a single-column for IE11 if Grid features fail.
- Avoid relying on complex interactions that break; provide non-JS accessible alternatives for critical features.
- For CSS gap in flex (unsupported in some legacy), use margin-based fallbacks.
Testing strategy
- Automated: set Browserslist targets in CI and run cross-browser test suites with BrowserStack / Sauce Labs for IE11 and latest Chrome/Firefox/Safari.
- Visual regression (Percy/Chromatic) to catch layout regressions in legacy viewports.
- Manual: smoke-test on a real Windows 7/10 VM or BrowserStack for keyboard navigation, forms, focus styles.
- Performance & audit: run Lighthouse for evergreen; ensure polyfills are conditionally loaded to avoid perf regressions for modern users.
Trade-offs & notes
- Shipping fewer polyfills => better modern perf; rely on feature-detection and targeted polyfills.
- Where parity is impossible or expensive (CSS Grid advanced alignment), accept visual degradation but keep functionality intact.
- Document browser support and known limitations in README so designers and PMs set expectations.
Explain, with examples, how cognitive biases such as confirmation bias, anchoring, and sunk-cost fallacy can hinder a debugging investigation. Describe concrete practices, such as pair debugging, rotating investigators, hypothesis logs, and clear acceptance criteria, that you have introduced on a team to mitigate these biases.
Sample Answer
Direct answer
Confirmation bias, anchoring, and sunk-cost fallacy each distort a root-cause investigation in a specific, predictable way: confirmation bias makes you notice evidence supporting your first guess and discount evidence against it; anchoring makes an early, possibly wrong hypothesis dominate the rest of the investigation even after better evidence emerges; and sunk-cost fallacy keeps you investigating a disproven lead because of time already invested in it, rather than switching based on current evidence. Concrete team practices (pair debugging, rotating investigators, hypothesis logs, explicit acceptance criteria) counter each of these by introducing structure that doesn't rely on individual willpower to overcome the bias.
Structured elaboration
Confirmation bias: once you suspect a cause, you unconsciously interpret ambiguous evidence as supporting it and are quicker to dismiss evidence that doesn't fit. In debugging, this looks like reading a log line as "consistent with my theory" when a more neutral read would call it inconclusive, or stopping the investigation the moment ANY supporting evidence appears rather than continuing to look for disconfirming evidence too.
- Mitigation: hypothesis logs. Writing down each hypothesis and what specific evidence would DISCONFIRM it, before looking for evidence, forces a falsifiable framing up front; when you later find evidence, you check it against the pre-written disconfirmation criteria rather than retroactively deciding it "counts" as support.
Anchoring: the first plausible explanation offered (by you, or by someone else on the call) tends to dominate the rest of the investigation's framing, even as better evidence appears, because everyone's mental model has already organized around it.
- Mitigation: rotating investigators / a fresh pair of eyes. Someone joining the investigation LATE, without the accumulated anchor, will naturally form hypotheses from the current evidence rather than the initial framing, and is often the person who notices the anchor was wrong; deliberately bringing in a fresh perspective partway through a long investigation is a structural way to interrupt this.
Sunk-cost fallacy: having spent two hours pursuing one lead makes it psychologically harder to abandon, even once evidence stops supporting it, because abandoning it "wastes" the time already spent (which is, of course, already spent either way, and not actually recoverable by continuing).
- Mitigation: explicit acceptance criteria and timeboxing set in advance. Deciding, BEFORE starting to investigate a specific hypothesis, what evidence would confirm it and how much time is reasonable to spend testing it, makes the "abandon or continue" decision a pre-committed rule rather than an in-the-moment judgment call that sunk cost can distort.
Pair debugging as a mitigation for all three simultaneously: a second person, thinking independently, is less likely to share the exact same anchor or the exact same sunk-cost attachment to a specific lead, and naturally provides a real-time check on confirmation bias by asking "does that evidence actually support that, or are we reading it generously?"
Worked example
A team investigating an intermittent failure anchors early on "it's probably the recent deploy" (a reasonable first guess, given the timing). Two hours in, with the deploy's code reviewed thoroughly and nothing found, sunk cost starts to argue for continuing to scrutinize that same deploy rather than considering it possibly unrelated. A hypothesis log, written at the start, had specified "if the failure recurs on a service instance that predates this deploy, that disconfirms the deploy hypothesis"; checking that specific, pre-committed criterion shows the failure DID recur on an older instance, cleanly disconfirming the deploy theory despite two hours of sunk investigation into it. A rotating fresh investigator, brought in specifically because the original two were stuck, asks a question neither anchored investigator had considered ("what else changed around that time besides the deploy?") and identifies an unrelated infrastructure change that actually explains the failure.
Trade-offs and pitfalls
These practices have a real cost (pairing takes two people's time instead of one; hypothesis logs take a few minutes to write that could otherwise go straight into investigating), and the trade-off is worth it specifically for investigations that are ALREADY taking a long time or where the cost of a wrong conclusion is high; for a five-minute, low-stakes bug, the overhead of formal hypothesis logging isn't proportionate to the risk these biases actually pose in that context.
Explain route-based code-splitting and lazy loading in React using dynamic import, React.lazy, and Suspense. Describe how to use error boundaries with lazy-loaded components, preload/prefetch strategies to improve perceived performance, and pitfalls when using SSR frameworks.
Sample Answer
Brief explanation
Route-based code-splitting defers loading route component code until needed, reducing initial bundle size and improving time-to-interactive. In React you use dynamic import + React.lazy for component splitting and Suspense to show a fallback while the chunk loads.
Example
// lazy route component
const Users = React.lazy(() => import('./Users'));
// usage in router
<Suspense fallback={<Spinner />}>
<Route path="/users" element={<Users />} />
</Suspense>
Error boundaries
React.lazy throws when loading fails — wrap lazy components in an error boundary to catch and render a recovery UI.
class ErrorBoundary extends React.Component {
state = { err: null };
static getDerivedStateFromError(err) { return { err }; }
render() {
if (this.state.err) return <RetryUI onRetry={() => this.setState({err:null})} />;
return this.props.children;
}
}
Use: <ErrorBoundary><Suspense>…</Suspense></ErrorBoundary>
Preload / prefetch strategies
- import(/* webpackPrefetch: true */ './BigComp') hints browser to fetch during idle time (low priority).
- import(/* webpackPreload: true */ './MainComp') loads with high priority.
- Use <link rel="prefetch"> or rel="preload" for critical chunks or likely next-route assets.
- Programmatic preloading on hover/focus: onMouseEnter={() => import('./Settings')} to improve perceived speed.
SSR pitfalls
- React.lazy + Suspense for code-splitting isn’t SSR-friendly — Suspense for data/hydration differs; can cause mismatches.
- Use SSR-aware libraries (e.g., @loadable/component or react-loadable) which provide server-side chunk extraction and preload support.
- Ensure critical chunks are inlined or preloaded to avoid flash-of-loading and poor SEO.
- Beware chunking too granularly (many small requests) and user bandwidth/HTTP/HTTP2 constraints.
Best practices
- Split by route and by large, rarely used UI.
- Keep chunk sizes reasonable and measure with bundle analyzer.
- Combine ErrorBoundary + Suspense + prefetch for robust UX.
Product wants the fastest possible improvement to page load time and gives you four weeks. You're weighing frontend work like lazy-loading and code-splitting against a backend API-aggregation effort. Which would you recommend starting with, and what would change your answer?
Sample Answer
Approach
Trace what actually blocks the page from being usable before picking a side, since page load time is a chain, and lazy-loading (deferring resources until needed) and code-splitting (breaking a large JavaScript bundle into smaller pieces loaded on demand) fix a different part of that chain than API aggregation (combining multiple backend calls into fewer round trips).
Worked estimate (illustrative numbers)
Say a trace shows the page currently loads in 4.5 seconds: about 1.5 seconds comes from a chain of 8 sequential API calls at roughly 180 milliseconds round trip each, about 2 seconds from downloading, parsing, and running a large, unsplit JavaScript bundle, and the remaining second from rendering and images.
Aggregating those 8 calls into 2 combined calls could plausibly cut that 1.5 seconds down to 400 to 500 milliseconds, roughly a 1 second win. Code-splitting the bundle so only what is needed for first paint loads up front could plausibly cut the 2 second JavaScript cost by half or more, roughly another 1 to 1.2 second win. In this profile the two options are close in raw impact, so the tie-breaker becomes speed and risk to build in four weeks. API aggregation usually touches fewer moving parts and can ship incrementally, one endpoint at a time, with fast, measurable wins, while a full code-splitting pass across a codebase, deciding what is route-critical versus deferred, tends to take longer to get right.
What would change the answer
If the trace showed one side clearly dominating, say 3 of the 4.5 seconds coming from the sequential API calls, I would start there since it has the higher ceiling. If most users are on a fast connection but a smaller, high-value segment is on mobile or slow networks where the JavaScript payload dominates their experience, that shifts the priority depending on who "fastest possible" is really meant to serve. And if the backend calls are shared with other teams' services, aggregating them carries wider blast radius and may take longer to get sign-off on than a frontend-only change, which pushes the four-week bet toward frontend first.
Implement a curry(fn) utility in JavaScript that returns a curried version of fn supporting partial application until the original arity is reached. Add support for a placeholder value curry._ (a unique sentinel) that can be used to skip arguments and fill them later. Ensure that when the curried function is finally invoked as a method, the calling this context is preserved for the original function invocation. Provide working code and a short explanation.
Sample Answer
Brief approach
- Build a curried wrapper that accumulates arguments (including placeholders) until the original function's arity (fn.length) is satisfied.
- Use a unique sentinel curry._ to mark skipped slots.
- When new args arrive, merge them into existing slots left-to-right, filling placeholders.
- Preserve calling this by using normal functions and calling fn.apply(this, finalArgs).
Code
// curry utility with placeholder support and this preservation
function curry(fn) {
const arity = fn.length;
const PLACEHOLDER = curry._;
function mergeArgs(prev, next) {
const merged = prev.slice();
let ni = 0;
for (let i = 0; i < merged.length && ni < next.length; i++) {
if (merged[i] === PLACEHOLDER) {
merged[i] = next[ni++];
}
}
// append remaining next args
while (ni < next.length) merged.push(next[ni++]);
return merged;
}
function curried(prevArgs) {
return function (...newArgs) {
const all = mergeArgs(prevArgs, newArgs);
const filledCount = all.filter(a => a !== PLACEHOLDER).length;
if (filledCount >= arity) {
const callArgs = all.slice(0, arity).map(a => a === PLACEHOLDER ? undefined : a);
return fn.apply(this, callArgs);
}
return curried(all);
};
}
return curried([]);
}
curry._ = Symbol('curry placeholder');
Why this works (short)
- Merges partials and placeholders predictably, fills left-to-right.
- Stops when non-placeholder count reaches fn.length, then invokes fn with the original this via apply.
- Uses a Symbol sentinel to avoid collisions with user data.
Describe a time you mentored someone from their first day through shipping their first piece of real work. How did you ramp them up?
Sample Answer
Direct answer
Ramping someone from day one to their first shipped work is a deliberate sequence, not a single onboarding checklist: assess what they actually already know, give them small real tasks with tight review loops before a full feature, gradually widen the scope of ownership, and define upfront what "shipped" and "done" mean so the finish line is unambiguous. The plan should look different depending on who's arriving, not just be a fixed template applied to everyone.
Structured elaboration
The default arc
- First few days: orient and assess. Don't assume a blank slate; find out what they already know so you're not re-teaching things or, worse, skipping things they actually need.
- Early tasks: small, real, low-blast-radius work with fast, close review. The goal here is confidence and calibration to the team's standards, not speed.
- Middle stretch: progressively larger scope with more independence, review shifting from "check everything" to "check the risky parts."
- First real shipped piece: something end-to-end they own, with you available but not doing it alongside them, and a clear definition of "done" agreed before they start, so success isn't a moving target.
Adapting the plan to who's actually arriving
This is where a generic checklist breaks down, and it's the part that separates a senior answer:
- A contractor under least-privilege or compliance constraints: access is scoped down from day one, so the plan has to work around what they legitimately can't see or touch, and documentation often needs to be more explicit since they can't casually ask around as easily as a full-time hire embedded in the org.
- A career-changer from an adjacent discipline (a backend engineer moving into data engineering, a research scientist moving into production ML): they're not a blank slate, they have real transferable skills. The plan should explicitly identify what carries over and target ramp-up specifically at the actual new-domain gaps, not restart from zero the way you would for someone with no relevant background.
- A cohort of remote interns rather than one hire: 1:1 pairing time doesn't scale to a group. The plan shifts toward a shared structured curriculum, peer learning between the interns, and scheduled office hours, with 1:1 time reserved for the things that genuinely need it.
- A remote hire versus a senior IC joining: a remote hire needs more of everything written down explicitly, since the informal hallway learning that fills gaps for an in-person hire doesn't happen by accident. A senior IC's gap is usually organizational context and relationships, not raw skill, so their plan should be lighter on procedural scaffolding and heavier on introductions, context on how decisions get made, and where the landmines are.
Worked example
Situation
I mentored someone joining as an individual contributor with solid general skills but no exposure to our specific stack or codebase, with a goal of them shipping one real, complete piece of work within their first several weeks.
Action
Week one was mostly orientation and a short assessment task to see where they actually stood, not a generic reading list. From there, I gave them a small real bug fix with a tight review loop so they got fast, specific feedback on our conventions early, before those habits calcified the wrong way. Over the following weeks the scope widened: a small self-contained feature with me reviewing closely, then a larger piece with me available but stepping back from line-by-line review, focusing instead on the riskiest parts of the design.
Result
They shipped a real, complete piece of work end-to-end within the target window, with a review pass that looked much closer to how we review any other team member's work by that point, which was the actual signal of readiness, not just that the calendar had passed.
Trade-offs & pitfalls
- Treating every new hire's plan as the same template. A junior mentor runs the same onboarding for a contractor, a career-changer, an intern cohort, and a senior IC. A senior mentor adapts the shape of the plan to who's actually arriving, because the actual gap being closed is different in each case.
- Under-scoping early tasks out of excessive caution, or over-scoping out of impatience. Both undermine the confidence-building purpose of the early stretch: too small and it's condescending or boring; too large too soon and the first review becomes overwhelming and demoralizing.
- Not defining "done" up front. Ambiguity about what counts as finished either causes needless rework or lets something ship that isn't actually ready, and both erode trust in the mentoring relationship.
- Ignoring the constraints a nontraditional hire is actually operating under. Applying a full-access, in-person, junior-IC plan to a least-privilege contractor or a remote hire sets them up to fail on logistics that have nothing to do with their actual skill.
A layout looks correct in Chrome DevTools responsive mode, but on a real iPhone the header overlaps content when the browser chrome changes height and a sticky footer jumps unexpectedly. How would you debug and fix this class of responsive bugs?
Sample Answer
How I would debug it
I would reproduce the bug on a physical iPhone first, because Chrome responsive mode does not fully match Safari's viewport behavior. One note on terminology before going further: "browser chrome" below means the address bar, tabs, and other on-screen browser UI that can expand or collapse, not the Chrome browser itself; the two meanings of "chrome" are unrelated. Then I would inspect computed heights, scroll containers, and any element using 100vh, position: sticky, or position: fixed.
Likely causes
100vhon iOS can behave differently when the browser chrome expands or collapses- A sticky or fixed footer may ignore safe-area insets
- A parent container may have overflow rules that interfere with sticky positioning
Fix strategy
- Replace
100vhwith100dvh(dynamic viewport height: unlike the static100vh, it updates automatically as the browser chrome shows or hides) when I need the visible viewport height - Use
env(safe-area-inset-bottom)(a safe area is the part of the screen not covered by a device's notch, home indicator, or rounded corners) for footer spacing on notch devices - Avoid tying layout math to the old static viewport height if the browser UI changes size
- Test with the address bar visible and hidden to catch jumpy behavior
Example fallback
If a browser does not handle dynamic viewport units consistently, I may use a small JavaScript fallback to set an app height variable from window.innerHeight. The key is to keep the layout stable across scroll and chrome transitions, then verify on Safari, not just in DevTools.
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