4 Rounds: Karat Screening, DSA, Component Design & System Design
Staff Software Engineer @ Walmart Global Tech
Mentored 100+ frontend developers through successful interviews
What is Karat? A third-party technical screening platform used by major tech companies to conduct remote coding interviews.
Medium to Hard difficulty JavaScript output tracing questions. Focus on understanding JavaScript semantics, async behavior, and closure concepts.
Difficulty: Medium
const arr = [1, 2, 3];
const funcs = [];
for (var i = 0; i < arr.length; i++) {
funcs.push(function() {
return i;
});
}
console.log(funcs[0]());
console.log(funcs[1]());
console.log(funcs[2]());
Output:
3
3
3
Explanation:
var is function-scoped, not block-scopedi = 3i variableifor (let i = 0; i < arr.length; i++) {
funcs.push(function() {
return i; // Each iteration creates a new binding
});
}
// Output: 0, 1, 2
Difficulty: Hard
console.log('1');
setTimeout(() => {
console.log('2');
Promise.resolve().then(() => console.log('3'));
}, 0);
Promise.resolve()
.then(() => {
console.log('4');
setTimeout(() => console.log('5'), 0);
})
.then(() => console.log('6'));
console.log('7');
Output:
1
7
4
6
2
3
5
Explanation (Event Loop Order):
Difficulty: Hard
const obj = {
name: 'Atlassian',
regularFunc: function() {
setTimeout(function() {
console.log(this.name);
}, 0);
},
arrowFunc: function() {
setTimeout(() => {
console.log(this.name);
}, 0);
}
};
obj.regularFunc();
obj.arrowFunc();
Output:
undefined
Atlassian
Explanation:
this is window, so this.name is undefinedthis from parent scope (the object), so this is objDifficulty: Medium-Hard
async function fetchData() {
try {
const result = await Promise.reject('Error!');
console.log('Success:', result);
} catch (e) {
console.log('Caught:', e);
}
}
fetchData().then(() => {
console.log('Done');
});
console.log('Start');
Output:
Start
Caught: Error!
Done
Closures trap you. Async puzzles you. Between you and this round is understanding how JavaScript actually works—not just what it does. Get clarity with us →
1 medium LeetCode problem with optimization focus.
Difficulty: Medium | Time: 40 minutes
s, find the length of the longest substring without repeating characters.
📋 Constraints:
s = "abcabcbb"
Step 1: window = "a", max = 1
Step 2: window = "ab", max = 2
Step 3: window = "abc", max = 3
Step 4: See 'a' again (duplicate!)
Shrink from left: remove 'a'
window = "bca", max = 3
Step 5: Shrink: remove 'b'
window = "cab"
Step 6: Shrink: remove 'c'
window = "ab"
Step 7: Add 'b', window = "abb"
Shrink: remove first 'b'
window = "bb"
Shrink: remove 'b'
window = "b", max = 3
🎯 Optimal Solution (O(n)):
function lengthOfLongestSubstring(s) {
const charMap = new Map();
let maxLen = 0;
let left = 0;
for (let right = 0; right < s.length; right++) {
const char = s[right];
if (charMap.has(char)) {
left = Math.max(left, charMap.get(char) + 1);
}
charMap.set(char, right);
maxLen = Math.max(maxLen, right - left + 1);
}
return maxLen;
}
// Test: lengthOfLongestSubstring("abcabcbb") → 3
Algorithms aren't formulas; they're perspectives. Seeing the pattern first, then coding it fast—that's the gap. Atlassian rewards sharp algorithmic thinking. Sharpen your edge →
Build an accessible, reusable Custom Select with keyboard navigation and filtering.
Time: 90 minutes
Try typing to search, use arrow keys to navigate, press Enter to select, Escape to close
Selected: None
isOpen - dropdown visibilitysearchTerm - filter texthighlightedIndex - keyboard focusselectedValue - chosen itemonClick - toggle dropdownonKeyDown - keyboard navonClickOutside - auto-closeonChange - search filteringimport React, { useState, useRef, useEffect } from 'react';
function CustomSelect({ options, onSelect, placeholder }) {
const [isOpen, setIsOpen] = useState(false);
const [searchTerm, setSearchTerm] = useState('');
const [highlightedIndex, setHighlightedIndex] = useState(0);
const [selectedValue, setSelectedValue] = useState(null);
const selectRef = useRef(null);
const dropdownRef = useRef(null);
// Filter options based on search term
const filteredOptions = options.filter(opt =>
opt.toLowerCase().includes(searchTerm.toLowerCase())
);
// Handle keyboard navigation
const handleKeyDown = (e) => {
if (!isOpen && e.key !== 'Enter') return;
switch (e.key) {
case 'ArrowDown':
e.preventDefault();
setHighlightedIndex(prev =>
Math.min(prev + 1, filteredOptions.length - 1)
);
if (!isOpen) setIsOpen(true);
break;
case 'ArrowUp':
e.preventDefault();
setHighlightedIndex(prev => Math.max(prev - 1, 0));
break;
case 'Enter':
e.preventDefault();
if (filteredOptions[highlightedIndex]) {
handleSelect(filteredOptions[highlightedIndex]);
} else if (!isOpen && options.length > 0) {
setIsOpen(true);
}
break;
case 'Escape':
setIsOpen(false);
setSearchTerm('');
break;
default:
break;
}
};
// Handle option selection
const handleSelect = (option) => {
setSelectedValue(option);
setIsOpen(false);
setSearchTerm('');
setHighlightedIndex(0);
onSelect?.(option);
};
// Close dropdown when clicking outside
useEffect(() => {
const handleClickOutside = (e) => {
if (selectRef.current && !selectRef.current.contains(e.target)) {
setIsOpen(false);
}
};
if (isOpen) {
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}
}, [isOpen]);
// Auto-focus search when dropdown opens
useEffect(() => {
if (isOpen && dropdownRef.current) {
dropdownRef.current.focus();
}
}, [isOpen]);
return (
<div ref={selectRef} className="select-wrapper">
{/* Trigger Button */}
<button
className="select-trigger"
onClick={() => setIsOpen(!isOpen)}
aria-haspopup="listbox"
aria-expanded={isOpen}
>
<span>{selectedValue || placeholder}</span>
<span className={`arrow ${isOpen ? 'open' : ''}`}>▼</span>
</button>
{/* Dropdown Menu */}
{isOpen && (
<div className="select-dropdown" role="listbox">
{/* Search Input */}
<div className="select-search">
<input
ref={dropdownRef}
type="text"
placeholder="Search options..."
value={searchTerm}
onChange={(e) => {
setSearchTerm(e.target.value);
setHighlightedIndex(0);
}}
onKeyDown={handleKeyDown}
aria-label="Search options"
/>
</div>
{/* Options List */}
<ul className="select-options">
{filteredOptions.length > 0 ? (
filteredOptions.map((option, idx) => (
<li
key={option}
className={`select-item ${
idx === highlightedIndex ? 'highlighted' : ''
} ${option === selectedValue ? 'selected' : ''}`}
onClick={() => handleSelect(option)}
onMouseEnter={() => setHighlightedIndex(idx)}
role="option"
aria-selected={option === selectedValue}
>
{option}
</li>
))
) : (
<li className="select-item" style={{ pointerEvents: 'none' }}>
No options found
</li>
)}
</ul>
</div>
)}
</div>
);
}
export default CustomSelect;
📊 Edge Cases to Handle:
aria-haspopup="listbox"aria-expanded={isOpen}aria-label on inputsrole="listbox"role="option"aria-selectedArrow Up/Down - NavigateEnter - Select itemEscape - CloseTab - Standard focusType to search - FilterMany can build. Few can build accessible, scalable, performant components that teams trust. This is where the craft shows. Master the craft →
Design a scalable issue board with efficient pagination, filtering, sorting, and real-time updates for millions of issues.
Time Allocation: 5 min (Clarify) → 15 min (Design) → 30 min (Implementation) → 15 min (Optimization) → 10 min (Q&A)
-- Issues Table
CREATE TABLE issues (
id BIGINT PRIMARY KEY,
project_id BIGINT NOT NULL,
key VARCHAR(50) UNIQUE,
title VARCHAR(255),
description TEXT,
status ENUM('OPEN', 'IN_PROGRESS', 'DONE', 'CLOSED'),
assignee_id BIGINT,
priority ENUM('CRITICAL', 'HIGH', 'MEDIUM', 'LOW'),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
parent_id BIGINT,
INDEX idx_project_status (project_id, status),
INDEX idx_project_created (project_id, created_at DESC),
INDEX idx_project_assignee (project_id, assignee_id),
INDEX idx_project_priority (project_id, priority),
INDEX idx_updated (updated_at DESC)
);
-- Efficient pagination query
SELECT * FROM issues
WHERE project_id = ?
AND status IN (?, ?, ?)
AND created_at > ?
ORDER BY created_at DESC
LIMIT 50;
import React, { useState, useCallback, useEffect } from 'react';
import { FixedSizeList as List } from 'react-window';
function IssueBoard({ projectId }) {
const [issues, setIssues] = useState([]);
const [cursor, setCursor] = useState(null);
const [hasMore, setHasMore] = useState(true);
const [filters, setFilters] = useState({
status: [],
assignee: null,
priority: []
});
const [sortBy, setSortBy] = useState({ field: 'created', order: 'desc' });
const [loading, setLoading] = useState(false);
// Fetch issues with filters and pagination
const fetchIssues = useCallback(async (newCursor = null) => {
setLoading(true);
try {
const response = await fetch('/api/issues', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
projectId,
filters,
sortBy,
cursor: newCursor,
limit: 50
})
});
const data = await response.json();
if (newCursor) {
setIssues(prev => [...prev, ...data.issues]);
} else {
setIssues(data.issues);
}
setCursor(data.nextCursor);
setHasMore(data.hasMore);
} catch (error) {
console.error('Failed to fetch issues:', error);
} finally {
setLoading(false);
}
}, [projectId, filters, sortBy]);
// Fetch initial issues on mount or filter change
useEffect(() => {
fetchIssues(null);
}, [filters, sortBy]);
// Handle infinite scroll
const handleLoadMore = () => {
if (hasMore && !loading) {
fetchIssues(cursor);
}
};
// Row renderer for virtual list
const Row = ({ index, style }) => {
const issue = issues[index];
return (
<div style={style} className="issue-row">
<span className={`status-badge ${issue.status.toLowerCase()}`}>
{issue.status}
</span>
<span className="issue-key">{issue.key}</span>
<span className="issue-title">{issue.title}</span>
<span className={`priority ${issue.priority.toLowerCase()}`}>
{issue.priority}
</span>
</div>
);
};
return (
<div className="issue-board">
{/* Filter Section */}
<div className="filter-section">
<StatusFilter
value={filters.status}
onChange={(status) => setFilters({...filters, status})}
/>
<PriorityFilter
value={filters.priority}
onChange={(priority) => setFilters({...filters, priority})}
/>
<AssigneeFilter
value={filters.assignee}
onChange={(assignee) => setFilters({...filters, assignee})}
/>
<SortDropdown
value={sortBy}
onChange={setSortBy}
/>
</div>
{/* Virtual List */}
<List
height={600}
itemCount={issues.length}
itemSize={60}
width="100%"
onItemsRendered={({ visibleStopIndex }) => {
if (visibleStopIndex === issues.length - 10) {
handleLoadMore();
}
}}
>
{Row}
</List>
{loading && <div className="spinner">Loading...</div>}
</div>
);
}
export default IssueBoard;
🔄 Phase 5: Real-time Updates with WebSocket
// WebSocket for real-time updates
class IssueWebSocketManager {
constructor(projectId) {
this.projectId = projectId;
this.ws = null;
this.listeners = [];
}
connect() {
this.ws = new WebSocket(`wss://api.jira.com/issues/${this.projectId}`);
this.ws.onopen = () => {
console.log('Connected to issue updates');
this.ws.send(JSON.stringify({
action: 'subscribe',
projectId: this.projectId
}));
};
this.ws.onmessage = (event) => {
const update = JSON.parse(event.data);
// Notify all listeners of the update
this.listeners.forEach(callback => {
callback(update);
});
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
// Implement exponential backoff retry
setTimeout(() => this.connect(), 3000);
};
}
on(callback) {
this.listeners.push(callback);
return () => {
this.listeners = this.listeners.filter(l => l !== callback);
};
}
disconnect() {
if (this.ws) this.ws.close();
}
}
⚡ Phase 6: Caching Strategy
| Cache Layer | TTL | Use Case |
|---|---|---|
| Client Memory | Session | Recently viewed issues, current filters |
| LocalStorage | 7 days | Filter preferences, sort settings |
| Redis (Server) | 5 minutes | Filter query results, user preferences |
| Database | N/A | Source of truth for all data |
| Metric | Target | How to Achieve |
|---|---|---|
| Initial Load | < 200ms | CDN + compression + caching |
| Pagination Load | < 150ms | Cursor pagination + indexes |
| Filter Change | < 300ms | Debouncing + Redis cache |
| WebSocket Update | < 1s | Direct connection + small payload |
| Search (100k results) | < 500ms | Elasticsearch or similar |
Between you and Atlassian's system design lies thinking at scale: pagination, caching, trade-offs. They'll ask the hard questions. Answer with depth →
From JavaScript fundamentals to system design mastery—this is the complete Atlassian interview roadmap. But knowing what to expect and being able to deliver under pressure are two different things.
Our cohort has helped a few developers crack interviews at Atlassian, Microsoft, Uber, and Amazon. They didn't just learn the concepts—they learned to think like an Atlassian engineer.
What our cohort members get:
Cohort 2 starts June 2026. Limited to 25 seats. Early birds get exclusive pricing.