← Citadel Interview Insights

Citadel·Software Engineer·Technical Phone Screen·Senior

Senior
Jul 2026

Summary

Citadel software engineer interview that went deep on JavaScript internals and real-time systems. The questions felt less like trivia and more like they wanted to see if you actually understood the mechanics, not just the syntax.

Questions Asked (5)

Q1

What are JavaScript's data types, and which values evaluate to false in a boolean context?

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

Felt easy until I second-guessed myself on empty string vs zero vs null vs undefined.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clearly categorizing JavaScript's data types into primitives and objects, then explain the concept of falsy values with concrete examples. Emphasize that understanding these fundamentals is crucial for writing robust code and avoiding common pitfalls.

Pro tip: Mention that falsy values are a common source of bugs in conditionals and that using strict equality (===) or explicit checks can prevent unexpected behavior. Also, note that while NaN is falsy, it is not equal to itself, which is a subtle but important detail.

1. Define Data Types

List JavaScript's primitive data types (string, number, bigint, boolean, undefined, symbol, null) and the object type. Clarify that objects include arrays, functions, and dates.

2. Explain Falsy Values

Enumerate the eight falsy values: false, 0, -0, 0n, '', null, undefined, and NaN. Mention that all other values are truthy, including empty arrays and objects.

3. Provide Examples

Give code examples showing how these values behave in boolean contexts, such as if statements or logical operators. Highlight common pitfalls like using 'if (x)' when x could be 0 or ''.

4. Discuss Implications

Explain why this matters in real-world coding, such as avoiding bugs in conditionals and understanding type coercion. Mention best practices like using strict equality or explicit checks.

Key Points to Mention

  • Primitive types: string, number, bigint, boolean, undefined, symbol, null
  • Object type includes arrays, functions, and other objects
  • Falsy values: false, 0, -0, 0n, '', null, undefined, NaN
  • All other values are truthy, including empty arrays and objects
  • NaN is falsy but not equal to itself
  • Use strict equality or explicit checks to avoid coercion pitfalls

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

For Promises, what do then, catch, and finally handlers return? What happens to the returned Promise if an error is thrown inside the executor or inside any handler, including inside an async function?

Technical Trade-offsAPI & Integrations
Author's notes

This is where it got interesting.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by explaining that then, catch, and finally each return a new Promise, enabling chaining. Then describe error propagation: errors thrown in the executor reject the promise, errors in handlers reject the returned promise, and async functions return rejected promises. Emphasize that finally passes through the original value or error unless it throws.

Pro tip: Mention that unhandled rejections can crash Node.js processes and that attaching a catch at the end of a chain is crucial for robustness. Also, note that finally does not receive the value or error, so it cannot alter the outcome unless it throws.

1. Explain return values of then, catch, finally

State that each method returns a new Promise, which resolves with the handler's return value or rejects if the handler throws.

2. Describe error handling in executor

If the executor function throws synchronously, the promise is rejected with that error.

3. Describe error handling in handlers

If a then or catch handler throws, the promise returned by that handler is rejected with the thrown error.

4. Explain async function error behavior

An async function always returns a Promise; if it throws, the returned promise is rejected with the thrown error.

5. Discuss finally's special behavior

finally does not receive the value or error and passes through the original resolution or rejection unless it throws or returns a rejected promise.

Key Points to Mention

  • then, catch, and finally each return a new Promise, allowing chaining.
  • Errors thrown in the executor synchronously reject the promise.
  • Errors thrown in then/catch handlers reject the promise returned by that handler.
  • Async functions return a Promise that rejects if the function throws.
  • finally passes through the original value or error unless it throws.
  • Unhandled rejections can lead to uncaught exceptions and should be handled with catch.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

Compare plain objects and Map in JavaScript. How do they differ in terms of key types, iteration order, prototype properties, and performance?

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

Talked through key types first since that's the clearest difference, then iteration order.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Structure your answer by comparing plain objects and Map across the four specified dimensions: key types, iteration order, prototype properties, and performance. For each dimension, state the key differences clearly and provide concrete examples or use cases. Conclude with a brief summary of when to use each data structure.

Pro tip: Emphasize that Map is optimized for frequent additions and removals and preserves insertion order, making it ideal for scenarios requiring ordered iteration or non-string keys. Mention that plain objects are more suitable for simple data storage with string keys and when JSON serialization is needed.

1. Key Types

Explain that plain objects only allow string and symbol keys, while Map allows any value as a key, including objects and functions.

2. Iteration Order

Describe that Map maintains insertion order for iteration, whereas plain objects have complex ordering rules (integer-like keys first, then strings in insertion order, then symbols).

3. Prototype Properties

Highlight that plain objects inherit properties from Object.prototype, which can cause conflicts, while Map does not have this issue as it doesn't have a prototype chain for keys.

4. Performance

Discuss that Map is generally more performant for frequent additions and deletions, while plain objects may be faster for simple lookups and are more memory-efficient for small, static data.

5. Use Cases

Summarize when to use each: Map for dynamic collections with non-string keys or frequent updates, plain objects for simple data storage and JSON compatibility.

Key Points to Mention

  • Plain objects only support string and symbol keys; Map supports any type of key.
  • Map preserves insertion order; plain objects have special ordering for integer-like keys.
  • Plain objects inherit from Object.prototype, which can lead to prototype pollution; Map is safe from this.
  • Map is optimized for frequent additions and removals; plain objects are optimized for fast property access.
  • Map has a size property; plain objects require manual size calculation.
  • Map is iterable by default; plain objects require Object.keys/values/entries for iteration.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q4

What is a WeakMap, and how does it differ from Map in terms of what keys are allowed and how garbage collection works?

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

Knew this one cold.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by defining WeakMap as a collection of key-value pairs where keys must be objects and are weakly referenced, then contrast with Map which allows any key type and holds strong references. Explain how weak references enable garbage collection of keys when no other references exist, and discuss the implications for iteration and memory management.

Pro tip: Mention that WeakMap is not enumerable and has no size property, which prevents memory leaks but limits its use to private data or metadata storage. Also, note that WeakMap keys are not enumerable, so you can't list them, which is a key trade-off.

1. Define WeakMap and Map

Provide a clear definition of both data structures, emphasizing that WeakMap holds weak references to keys while Map holds strong references.

2. Compare key types

Explain that WeakMap keys must be objects (or non-registered symbols in some environments), whereas Map keys can be any type, including primitives.

3. Explain garbage collection behavior

Describe how WeakMap allows keys to be garbage collected when no other references exist, preventing memory leaks, while Map keeps keys alive as long as the Map exists.

4. Discuss API differences and use cases

Highlight that WeakMap is not iterable, has no size property, and lacks methods like keys(), values(), entries(), and forEach(), making it suitable for private data or caching, while Map is fully enumerable and general-purpose.

5. Summarize trade-offs

Conclude by summarizing the trade-offs: WeakMap offers memory efficiency and privacy but limited functionality, while Map provides full control and enumeration at the cost of potential memory retention.

Key Points to Mention

  • WeakMap keys must be objects; Map keys can be any type.
  • WeakMap holds weak references, allowing garbage collection of keys; Map holds strong references.
  • WeakMap is not enumerable and has no size property; Map is enumerable and has size.
  • WeakMap is useful for associating metadata with objects without preventing their collection.
  • Map is suitable for general key-value storage where keys need to be retained.
  • Garbage collection in WeakMap is non-deterministic and depends on the JavaScript engine.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q5

For WebSockets, does a single connection guarantee in-order and reliable message delivery? When might ordering break down, and how would you design a system to handle ordering and idempotency if needed?

System DesignTechnical Trade-offsAPI & Integrations
Author's notes

Probably the best question of the session.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying that WebSockets run over TCP, which guarantees in-order and reliable delivery per connection, but ordering can break across reconnections, multiple connections, or application-level retries. Then discuss scenarios where ordering breaks down and propose a design using sequence numbers, acknowledgments, and idempotent message handling to ensure correctness.

Pro tip: Emphasize that ordering and idempotency are application-level concerns, not transport-level guarantees, and that designing for them requires explicit mechanisms like sequence numbers and deduplication keys. This shows you understand the difference between transport and application semantics.

1. Clarify TCP guarantees

Explain that WebSockets use TCP, which ensures reliable, in-order delivery of bytes within a single connection. However, message boundaries are not preserved, and ordering is only guaranteed for data sent on that connection.

2. Identify ordering breakdown scenarios

Discuss when ordering can break: reconnections (new TCP connection may have different latency), multiple connections from the same client, load balancing across servers, or application-level retries that duplicate or reorder messages.

3. Design for ordering

Propose using monotonically increasing sequence numbers per message stream, with the receiver buffering out-of-order messages and delivering them in order. Alternatively, use a central sequencer or a message queue that preserves order.

4. Design for idempotency

Suggest attaching unique message IDs (e.g., UUIDs) and having the receiver deduplicate based on these IDs. For state-changing operations, use idempotent APIs or store processed IDs to avoid double-processing.

5. Consider trade-offs and edge cases

Discuss trade-offs: buffering increases memory and latency, deduplication requires storage, and strict ordering may limit scalability. Mention handling of message loss, reconnection logic, and the CAP theorem implications.

Key Points to Mention

  • TCP guarantees in-order, reliable byte stream delivery per connection, but WebSocket messages are not inherently ordered across connections or reconnections.
  • Ordering can break due to reconnections, multiple connections, load balancing, or application-level retries.
  • Sequence numbers and acknowledgments can enforce ordering at the application level.
  • Idempotency can be achieved with unique message IDs and deduplication on the receiver side.
  • Trade-offs include increased latency, memory usage, and complexity versus strict ordering guarantees.
  • Consider using a message broker (e.g., Kafka) that provides ordering and exactly-once semantics if needed.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.