Companies don't just evaluate your code. They evaluate your thinking. This guide covers everything from round structure to the hidden rubric interviewers use — so you walk in knowing what's actually being measured.
- Round Structure & Format
- Question Bank by Level
- The Hidden Evaluation Rubric
- Ideal Strategy — Step by Step
- Code Structure Principles
- State Management Patterns
- Hooks Mastery Checklist
- Edge Case Thinking
- Performance Signals
- Accessibility — The Biggest Differentiator
- Common Bug Patterns to Avoid
- Must-Know Concepts Before You Interview
- Difficulty Trend & Market Reality
- What Separates the Top 5%
Most machine coding rounds run 60–120 minutes. The format is consistent across companies — understanding each phase lets you allocate time deliberately instead of guessing.
┌─────────────────────────────────────────────────────────────┐
│ TYPICAL ROUND FLOW │
├──────────┬──────────────────────────────────────────────────┤
│ ~5 min │ 1. Problem statement — read carefully │
│ ~10 min │ 2. Clarify requirements — ask smart questions │
│ ~10 min │ 3. Plan verbally — component tree, state, flow │
│ ~50 min │ 4. Implement core functionality │
│ ~15 min │ 5. Edge cases + polish │
│ ~10 min │ 6. Code review discussion with interviewer │
└──────────┴──────────────────────────────────────────────────┘
The biggest mistake candidates make: spending 80% of time on the happy path and leaving zero time for edge cases. Interviewers notice this immediately.
What companies are NOT testing: Pixel-perfect UI, fancy animations, or framework knowledge breadth.
What they ARE testing: Engineering thinking, architectural decisions, correctness under edge cases, code quality, and performance awareness.
These appear at startups and as warm-up rounds at product companies. They test that you understand React's core model correctly.
| Question | Core Concepts Tested |
|---|---|
| Todo app (CRUD + persistence) | Controlled inputs, list rendering, localStorage |
| Search with debounce | useEffect, useRef, race conditions, timing |
| Pagination component | Derived state, data slicing, boundary math |
| Modal with accessibility | Portal, focus trap, Escape key, aria-modal |
| Tabs component | Controlled vs uncontrolled, keyboard nav |
| Infinite scroll list | IntersectionObserver, async loading, deduplication |
| Form validation | Error state, touched state, schema validation |
These are the most common across product companies. They separate candidates who know hooks from candidates who understand when and why to use them.
| Question | Core Concepts Tested |
|---|---|
| Nested comments system | Recursive components, tree state, optimistic updates |
| Kanban board (drag & drop) | Complex event handling, multi-list state, HTML5 DnD |
| File explorer UI | Recursive tree, expand/collapse, path tracking |
| Data table — sort + filter | Derived state, useMemo, controlled columns |
| Chat UI with typing indicator | WebSocket/interval, cleanup, stale closure traps |
| Shopping cart with global state | Context or Zustand, derived totals, persistence |
These test whether you can design a subsystem, not just implement a feature. Expect to explain architectural tradeoffs during the review.
| Question | Core Concepts Tested |
|---|---|
| Mini Redux / store from scratch | Pub-sub, reducer pattern, subscriptions, React integration |
| Virtualized list | Windowing, DOM recycling, scroll math, accessibility |
| Rich text editor | ContentEditable, Selection API, command pattern |
| Google Docs-like live typing | Operational transforms vs CRDT concept, cursor sync |
| Real-time notifications panel | WebSocket lifecycle, read/unread state, batching |
| Offline-first app | Service Worker concept, sync queue, conflict resolution |
3. The Hidden Evaluation Rubric
Interviewers rarely show you this. Here is what they are actually scoring against.
┌─────────────────────────────────────────────────────────────┐
│ HIDDEN EVALUATION SCORECARD │
├────────────────────────┬────────────────────────────────────┤
│ Code Structure │ Separation of concerns, reusable │
│ │ components, clean folder layout │
├────────────────────────┼────────────────────────────────────┤
│ State Management │ Correct state location, no prop │
│ │ drilling, no derived state bugs │
├────────────────────────┼────────────────────────────────────┤
│ Hooks Mastery │ Right hook for right job, stable │
│ │ dependencies, custom hooks │
├────────────────────────┼────────────────────────────────────┤
│ Edge Case Thinking │ Empty, loading, error, race cond. │
│ │ rapid input, network failure │
├────────────────────────┼────────────────────────────────────┤
│ Performance Awareness │ Debounce, memo, key usage, lazy │
│ │ loading — mentioned OR implemented │
├────────────────────────┼────────────────────────────────────┤
│ Accessibility │ Keyboard nav, focus, aria — huge │
│ │ differentiator, almost no one does │
├────────────────────────┼────────────────────────────────────┤
│ Communication │ Explains decisions while coding. │
│ │ Justifies tradeoffs. Asks questions.│
└────────────────────────┴────────────────────────────────────┘
A common surprise: a partially complete solution with clean architecture scores higher than a complete solution with messy code. Interviewers can extrapolate from good structure. They cannot unsee bad architecture.
Before writing a single line, ask requirement questions. This signals product thinking and prevents wasted implementation time.
Always ask:
- What is the expected data size? (Affects virtualization decision)
- Is mobile responsiveness required?
- Is persistence needed (localStorage, API)?
- Is accessibility explicitly required?
- Should I use a specific state management approach, or is that my call?
- Are there any API mocks provided, or should I stub my own?
Interviewers score this highly. Most candidates skip it and dive into code. That alone differentiates you.
Spend 5–10 minutes talking through your plan. Draw the component tree. Narrate the state model.
"I'll break this into three components:
- TodoList (container, owns state)
- TodoItem (display + inline edit)
- AddTodo (controlled form)
State lives in TodoList — items array. I'll derive filtered items
with useMemo rather than storing a separate filtered array.
Persistence goes into a custom useTodos hook."
This serves two purposes: it catches architectural mistakes before you've committed to code, and it demonstrates staff-level communication.
Follow this strict ordering:
Phase 1: Minimal working feature
└── Core happy path only. No loading states, no errors yet.
Get something on screen that actually works.
Phase 2: Edge cases
└── Empty state, loading state, error state, rapid input,
API failure. These reveal engineering maturity.
Phase 3: Polish (only if time remains)
└── Loading spinner, keyboard support, aria attributes,
animations. Nice to have, not required to pass.
Never start with styling. It signals wrong priorities and burns time you need for architecture.
When the coding phase ends, be ready to:
- Explain why each major decision was made
- Identify what you would do differently with more time
- Proactively name the known limitations of your implementation
- Suggest how you would scale it (test coverage, API integration, performance)
The candidate who says "I'd add virtualization here if the list can grow past ~500 items — I didn't implement it given time constraints, but the hook boundary I drew makes it straightforward to add" impresses more than the one who silently waits.
Even in a 60-minute round, structure your files intentionally. Interviewers look at your folder layout before they read your code.
src/
components/
TodoItem/
TodoItem.jsx ← Component
TodoItem.css ← Scoped styles (or module)
index.js ← Re-export
hooks/
useTodos.js ← Custom hook for state logic
useDebounce.js ← Reusable utility hook
utils/
storage.js ← localStorage helpers
validators.js ← Pure validation functions
context/
TodoContext.jsx ← Context + Provider (if needed)
App.jsx
Keep three types of logic clearly separated:
UI logic — How things look and respond to events. Lives in components. Should be thin.
State logic — How data changes over time. Lives in custom hooks. Should be pure and testable.
Side effects — Persistence, API calls, subscriptions. Lives in hooks, isolated behind an interface.
// ❌ Mixed concerns — hard to test, hard to reuse
function TodoList() {
const [todos, setTodos] = useState(() =>
JSON.parse(localStorage.getItem('todos') || '[]')
)
// ...100 more lines of logic mixed with JSX
}
// ✅ Separated — the hook can be tested independently
function TodoList() {
const { todos, addTodo, removeTodo, toggleTodo } = useTodos()
return (/* pure JSX */)
}
function useTodos() {
const [todos, setTodos] = useState(() =>
JSON.parse(localStorage.getItem('todos') || '[]')
)
useEffect(() => {
localStorage.setItem('todos', JSON.stringify(todos))
}, [todos])
// return stable action handlers
}Design components with a clear interface. Ask: "Could this component work in a different feature without modification?"
// ❌ Too coupled to one use case
function TodoSearchBar({ onTodoSearch }) { ... }
// ✅ General-purpose, reusable
function SearchBar({ placeholder, onSearch, debounceMs = 300 }) { ... }Ask: Who needs this state?
Only this component?
└─► useState inside the component
This component + immediate children?
└─► useState in the parent, pass as props
Multiple distant components?
└─► Context (for low-churn state)
or Zustand/external store (for high-churn state)
Server data (API responses)?
└─► React Query / SWR — not useState + useEffect
Never store data you can compute from existing state. Derived state causes sync bugs.
// ❌ Storing derived state — completedCount can go stale
const [todos, setTodos] = useState([])
const [completedCount, setCompletedCount] = useState(0)
// ✅ Derive it — always accurate, no sync required
const [todos, setTodos] = useState([])
const completedCount = useMemo(
() => todos.filter(t => t.done).length,
[todos]
)If you find yourself passing props more than 2 levels deep, extract a context:
// ❌ Prop drilling — passing theme through 4 layers
<App theme={theme}>
<Layout theme={theme}>
<Sidebar theme={theme}>
<NavItem theme={theme} />
// ✅ Context — NavItem reads directly
const ThemeContext = createContext()
<ThemeContext.Provider value={theme}>
<App /> {/* no prop threading */}For mid-complexity state in interview projects, useReducer + Context is the right answer. It shows architectural thinking without requiring external libraries.
// store/todoReducer.js
const initialState = { todos: [], filter: 'all' }
function todoReducer(state, action) {
switch (action.type) {
case 'ADD_TODO':
return { ...state, todos: [...state.todos, action.payload] }
case 'TOGGLE_TODO':
return {
...state,
todos: state.todos.map(t =>
t.id === action.payload ? { ...t, done: !t.done } : t
)
}
case 'SET_FILTER':
return { ...state, filter: action.payload }
default:
return state
}
}
// context/TodoContext.jsx
const TodoContext = createContext()
export function TodoProvider({ children }) {
const [state, dispatch] = useReducer(todoReducer, initialState)
return (
<TodoContext.Provider value={{ state, dispatch }}>
{children}
</TodoContext.Provider>
)
}
export const useTodoStore = () => useContext(TodoContext)Interviewers watch not just whether you use hooks, but whether you use the right hook for the right problem.
Ask: Does this value changing need to trigger a re-render?
YES → useState
NO → useRef
// useRef for: timers, previous values, DOM nodes, interval IDs
const timerRef = useRef(null)
const prevValueRef = useRef(value)
const inputRef = useRef(null)
// useState for: anything the UI needs to reflect
const [count, setCount] = useState(0)
const [isOpen, setIsOpen] = useState(false)Only memoize expensive computations. The memoization itself has a cost.
// ❌ Useless — primitive comparison is already cheap
const doubled = useMemo(() => count * 2, [count])
// ✅ Worthwhile — filters/sorts over large arrays
const sortedUsers = useMemo(
() => [...users].sort((a, b) => a.name.localeCompare(b.name)),
[users]
)
// ✅ Required — prevents re-render of memoized child
const config = useMemo(() => ({ theme, locale }), [theme, locale])
<MemoizedChild config={config} />useCallback is not for performance of the function itself — it is for stabilizing the function reference so memoized children do not re-render unnecessarily.
// ❌ Pointless — this component is not memoized
function Parent() {
const handleClick = useCallback(() => doThing(), []) // wasted
return <button onClick={handleClick}>Click</button>
}
// ✅ Required — MemoizedChild uses React.memo, needs stable ref
function Parent() {
const handleClick = useCallback(() => doThing(), [])
return <MemoizedChild onClick={handleClick} />
}Any logic that involves hooks and could be reused or tested independently should become a custom hook. This is one of the strongest signals of seniority in a coding round.
// useDebounce — reusable across search, autocomplete, etc.
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value)
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay)
return () => clearTimeout(timer) // cleanup on every change
}, [value, delay])
return debouncedValue
}
// usePrevious — useful for animations, change detection
function usePrevious(value) {
const ref = useRef()
useEffect(() => { ref.current = value })
return ref.current
}
// useLocalStorage — persistence abstraction
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
try {
const item = localStorage.getItem(key)
return item ? JSON.parse(item) : initialValue
} catch { return initialValue }
})
const setStoredValue = useCallback((newValue) => {
setValue(newValue)
localStorage.setItem(key, JSON.stringify(newValue))
}, [key])
return [value, setStoredValue]
}// Rule 1: Include everything you read from component scope
useEffect(() => {
fetchUser(userId) // userId must be in deps
}, [userId]) // ✅
// Rule 2: Stable refs don't need to be in deps
useEffect(() => {
inputRef.current.focus() // refs are stable — exclude
}, []) // ✅
// Rule 3: Functions defined outside the effect need useCallback or inclusion
const fetchData = useCallback(() => { /* ... */ }, [id])
useEffect(() => { fetchData() }, [fetchData]) // ✅
// Rule 4: Empty array = run once on mount (not "no dependencies")
useEffect(() => {
const sub = subscribe()
return () => sub.unsubscribe()
}, []) // ✅ — intentional one-time setupThis is where most candidates lose marks. Interviewers note whether you spontaneously handle edge cases or only build the happy path.
Every async operation or data-dependent UI has 5 states:
1. IDLE → Nothing has happened yet (initial render)
2. LOADING → Waiting for data
3. SUCCESS → Data received, display it
4. EMPTY → Success, but no data to show
5. ERROR → Something went wrong
Model these explicitly, never implicitly.
// ❌ Only handles success — fragile
function UserList() {
const [users, setUsers] = useState([])
useEffect(() => {
fetch('/api/users').then(r => r.json()).then(setUsers)
}, [])
return users.map(u => <UserCard key={u.id} user={u} />)
}
// ✅ Handles all 5 states
function UserList() {
const [users, setUsers] = useState([])
const [status, setStatus] = useState('idle')
const [error, setError] = useState(null)
useEffect(() => {
setStatus('loading')
fetch('/api/users')
.then(r => r.json())
.then(data => {
setUsers(data)
setStatus('success')
})
.catch(err => {
setError(err.message)
setStatus('error')
})
}, [])
if (status === 'loading') return <Spinner />
if (status === 'error') return <ErrorMessage message={error} />
if (users.length === 0) return <EmptyState message="No users found" />
return users.map(u => <UserCard key={u.id} user={u} />)
}When a user triggers multiple fetches in quick succession (typing in a search box), responses can arrive out of order. The last response to resolve wins — but that may not be the most recent request.
// ❌ Race condition — slow request for 'ab' might resolve after 'abc'
useEffect(() => {
fetch(`/search?q=${query}`).then(r => r.json()).then(setResults)
}, [query])
// ✅ Cleanup flag pattern — ignores stale responses
useEffect(() => {
let cancelled = false
fetch(`/search?q=${query}`)
.then(r => r.json())
.then(data => {
if (!cancelled) setResults(data) // only set if still relevant
})
return () => { cancelled = true } // cancel on query change
}, [query])
// ✅ AbortController pattern — actually cancels the fetch
useEffect(() => {
const controller = new AbortController()
fetch(`/search?q=${query}`, { signal: controller.signal })
.then(r => r.json())
.then(setResults)
.catch(err => {
if (err.name !== 'AbortError') setError(err)
})
return () => controller.abort()
}, [query])// Always debounce search inputs — never fire on every keystroke
function SearchInput({ onSearch }) {
const [value, setValue] = useState('')
const debouncedValue = useDebounce(value, 300)
useEffect(() => {
if (debouncedValue) onSearch(debouncedValue)
}, [debouncedValue, onSearch])
return (
<input
value={value}
onChange={e => setValue(e.target.value)}
placeholder="Search..."
/>
)
}// ❌ Stale closure — count is captured at interval creation time
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1) // always adds to the initial value of count
}, 1000)
return () => clearInterval(id)
}, []) // count not in deps — stale!
// ✅ Functional update — uses current state, not captured value
useEffect(() => {
const id = setInterval(() => {
setCount(c => c + 1) // c is always current
}, 1000)
return () => clearInterval(id)
}, [])You do not need to implement all optimizations in a 60-minute round. But you must mention them when relevant, and explain the tradeoff. That is the signal interviewers listen for.
Debounce → Fire AFTER user stops. Use for: search, autocomplete, save on type.
Throttle → Fire AT MOST every N ms. Use for: scroll, resize, mousemove.
// Debounce implementation (common interview sub-question)
function debounce(fn, delay) {
let timer
return function(...args) {
clearTimeout(timer)
timer = setTimeout(() => fn.apply(this, args), delay)
}
}
// Throttle implementation
function throttle(fn, limit) {
let lastCall = 0
return function(...args) {
const now = Date.now()
if (now - lastCall >= limit) {
lastCall = now
return fn.apply(this, args)
}
}
}// React.memo — prevent re-render when parent re-renders
const UserCard = React.memo(function UserCard({ user }) {
return <div>{user.name}</div>
})
// Only apply when:
// 1. Component renders often due to parent updates
// 2. Component is expensive to render
// 3. Props don't change often
// Don't apply blindly — memo has overhead too// ✅ Good key — stable ID from data
{todos.map(todo => <TodoItem key={todo.id} todo={todo} />)}
// When to use index as key (the only acceptable case):
// - List is never reordered
// - List items have no persistent state
// - List is never filtered
{staticLabels.map((label, i) => <span key={i}>{label}</span>)}If data size could exceed ~100–200 items, mention virtualization proactively:
"For this implementation I'm rendering all items, which works fine up to a few hundred. If this list can grow to thousands, I'd swap in
react-window— the API is similar but only renders visible DOM nodes."
That sentence alone signals seniority.
This is mentioned last by most guides but should be near the top of your implementation list. Fewer than 10% of candidates implement even basic accessibility. Doing so is an instant strong signal.
A modal requires four accessibility behaviors: focus trap, Escape key close, aria attributes, and scroll lock.
function Modal({ isOpen, onClose, title, children }) {
const overlayRef = useRef(null)
const closeButtonRef = useRef(null)
// Focus the close button when modal opens
useEffect(() => {
if (isOpen) closeButtonRef.current?.focus()
}, [isOpen])
// Escape key closes modal
useEffect(() => {
if (!isOpen) return
const handler = (e) => { if (e.key === 'Escape') onClose() }
document.addEventListener('keydown', handler)
return () => document.removeEventListener('keydown', handler)
}, [isOpen, onClose])
// Focus trap — Tab cycles within modal only
const handleKeyDown = (e) => {
if (e.key !== 'Tab') return
const focusable = overlayRef.current.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
)
const first = focusable[0]
const last = focusable[focusable.length - 1]
if (e.shiftKey && document.activeElement === first) {
e.preventDefault()
last.focus()
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault()
first.focus()
}
}
if (!isOpen) return null
return createPortal(
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
ref={overlayRef}
onKeyDown={handleKeyDown}
style={{ /* overlay styles */ }}
>
<div>
<h2 id="modal-title">{title}</h2>
{children}
<button ref={closeButtonRef} onClick={onClose}>
Close
</button>
</div>
</div>,
document.body
)
}function TodoList({ todos }) {
const handleKeyDown = (e, id) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault()
toggleTodo(id)
}
if (e.key === 'Delete') deleteTodo(id)
}
return (
<ul role="list">
{todos.map(todo => (
<li
key={todo.id}
role="checkbox"
aria-checked={todo.done}
tabIndex={0}
onKeyDown={(e) => handleKeyDown(e, todo.id)}
onClick={() => toggleTodo(todo.id)}
>
{todo.text}
</li>
))}
</ul>
)
}| Component | Key ARIA Attributes |
|---|---|
| Modal | role="dialog", aria-modal="true", aria-labelledby |
| Button (icon only) | aria-label="Close" |
| Loading spinner | role="status", aria-live="polite", aria-label="Loading" |
| Search input | role="search", aria-label, aria-controls for results |
| Tabs | role="tablist", role="tab", aria-selected, aria-controls |
| Checkbox | role="checkbox", aria-checked |
| Alert/Error | role="alert", aria-live="assertive" |
These are the bugs interviewers plant or look for. Knowing them in advance prevents embarrassing moments.
// ❌ Infinite loop — data changes → effect runs → fetches → sets data → repeat
useEffect(() => {
fetch('/api/items')
.then(r => r.json())
.then(items => setData({ ...data, items })) // new object ref every time
}, [data]) // data in deps = triggers on every data change
// ✅ Specific dependency — only runs when id changes
useEffect(() => {
fetch(`/api/items/${id}`).then(r => r.json()).then(setItems)
}, [id])// ❌ Direct mutation — React cannot detect the change
const handleAdd = (item) => {
todos.push(item) // mutates existing array
setTodos(todos) // same reference — React bails out, no re-render
}
// ✅ New array — React detects the change
const handleAdd = (item) => {
setTodos(prev => [...prev, item])
}// ❌ Memory leak — subscription never removed when component unmounts
useEffect(() => {
const subscription = eventBus.subscribe('update', handleUpdate)
}, [])
// ✅ Cleanup function returned — runs on unmount
useEffect(() => {
const subscription = eventBus.subscribe('update', handleUpdate)
return () => subscription.unsubscribe()
}, [])// ❌ Shallow spread — nested object mutation survives
const updateUserCity = (city) => {
setUser({ ...user, address: { city } }) // drops country, zip, etc.
}
// ✅ Spread at every level
const updateUserCity = (city) => {
setUser(prev => ({
...prev,
address: { ...prev.address, city }
}))
}If you can answer these confidently, you can handle 90% of machine coding rounds.
Controlled vs Uncontrolled Components — Controlled components store form state in React state (you control every keystroke). Uncontrolled components let the DOM own the state and you read it via ref. Know when each is appropriate and the tradeoffs.
Reconciliation & Keys — React matches children by key during diffing. Without keys, it matches by position, which breaks on reorder. With stable keys, it reuses existing fiber nodes and DOM elements.
Closure Pitfalls — Event handlers and effects capture the value of state variables at the time they are created. If the state changes, the old closure has the old value. Fix with functional updates (setState(prev => ...)) or refs.
Stale State Bug Pattern — Reading state inside a setTimeout or setInterval gets the value at capture time, not current time. Use refs to read current state inside timers.
Rendering Lifecycle — Render (pure, can be interrupted) → Commit (DOM mutations) → Layout effects → Paint → Passive effects. Know what runs when and why.
Batching — React 18 batches all state updates by default, including those in Promises and setTimeout. Multiple setState calls in one event produce one re-render. Before React 18, only event handler updates were batched.
Concurrent Rendering Basics — React can pause low-priority renders to handle urgent work. startTransition marks an update as non-urgent. useDeferredValue defers the update of a derived value. Both prevent UI jank during heavy updates.
The bar has risen significantly and continues to rise.
| Year | Expected Level |
|---|---|
| 2022 | Medium — implement the feature correctly |
| 2023 | Medium–Hard — correct + performant |
| 2024 | Hard — correct + performant + architectural thinking |
| 2025+ | Hard + system thinking — tradeoffs, scale, team implications |
The practical implication: What a mid-level engineer was expected to know in 2022 is now the floor for any engineer asking for a senior title. The delta is system thinking, not additional syntax knowledge.
Companies now expect mid-level candidates to demonstrate knowledge that was previously only tested at senior level. If you are targeting senior compensation, you need to prepare at the staff level.
The top 5% of candidates share one consistent behavior: they talk while they code.
They do not just type. They narrate decisions as they make them, in real time.
While writing the state model:
"I'm putting this in the parent rather than the child because
the sibling component also needs to read it."
While adding useMemo:
"I'm memoizing this sort because it runs over the full list on
every render — without memo, every keystroke re-sorts."
While skipping an optimization:
"I'd normally debounce this, but since the dataset is local
and small, I'll note the omission and skip it to save time."
Interviewers are not just evaluating your code. They are evaluating your thinking. The only way to demonstrate thinking is to externalize it.
What this communicates:
- You understand why you are making each decision, not just how
- You are aware of the tradeoffs you are choosing to accept
- You would be easy to work with and code review with
- You operate like someone who designs systems, not just writes features
The candidate who says "why state lives here", "why memo is needed", "why ref instead of state" — and gets the answers right — gets the offer.
The machine coding round is not a test of how fast you type. It is a structured window into how you think. Prepare accordingly.