Articles
Pseudocode in a Coding Interview: When to Sketch and When to Write Runnable Code
Learn when to sketch pseudocode and when runnable interview code is required. Practice a contract-to-code example, edge cases, and submission checks.
Pseudocode in a coding interview is useful when it makes an algorithm easier to agree on and implement. It becomes a problem when it replaces the deliverable the interviewer or assessment actually requires. A clear sketch can explain your plan; it cannot demonstrate that your chosen language, function signature, state updates, and tests work together.
Official guidance: Microsoft's technical-interview page asks candidates to clarify ambiguities and plan before implementation, while explicitly requiring real code rather than pseudocode in its coding section. These expectations are compatible: discuss the approach, then implement it. Our recommendation: treat the sketch as a bridge with a clear exit condition, such as an agreed invariant and data structure.
Start with a concrete translation drill, such as Return the Ten Most Frequent Words: explain the tie-breaking rule, then implement and test that exact rule.
Can you use pseudocode in a coding interview?
The useful answer depends on what you are being asked to produce. A brief algorithm discussion and a final submitted program serve different purposes. Look at the invitation, task instructions, and environment before deciding how much abstraction is appropriate.
Official guidance: Amazon's SDE II preparation page also specifies syntactically correct code instead of pseudocode for its coding discussion. That is evidence about the published SDE II guidance, not a rule for every employer, role, or interview stage.
Official platform fact: HackerRank documents coding questions as code submissions evaluated with predefined test cases. Its coding-question documentation also describes configurable languages and function signatures. A prose algorithm cannot fulfill an executable coding task's contract simply because its idea is correct.
Evidence boundary: This article does not use candidate reports to estimate how often interviewers accept pseudocode. The following matrix and worked example are original preparation tools. They help interpret instructions; they do not override them.
| Format or instruction | Useful role for a sketch | Deliverable to verify |
|---|---|---|
| Online assessment with executable test cases | Private planning or permitted scratch work before implementation | Code in an allowed language that fits the supplied interface and submission workflow |
| Live coding round requiring runnable code | Explain the algorithm and invariant briefly before typing | Syntactically valid implementation, with tests or a reasoned trace in the available tool |
| Algorithm discussion explicitly asking for pseudocode | Describe control flow, data structures, and costs precisely | The requested pseudocode, including meaningful edge cases and a correctness argument |
| System-design discussion | Illustrate one operation when it clarifies a design choice | The requested architecture and contracts; executable code only if the task asks for it |
| Paper or whiteboard exercise | Follow the stated notation and level of detail | The specified answer format; the absence of a compiler alone does not establish permission |
A narrow clarification helps when the instruction is ambiguous: “Would you like runnable Python for this part, or an algorithm sketch first?” If real code is already required, move toward it instead of repeatedly renegotiating the format.
A good sketch exposes implementation decisions
Weak pseudocode hides the work inside phrases such as “find a matching value” or “handle edge cases.” Those phrases leave the interviewer unable to inspect which state exists, when it changes, or why the result is correct.
A useful sketch names the operations that matter. For a hash-map approach, explain what the keys and values represent, whether membership is checked before insertion, and what happens when a search fails. You can leave out language-specific punctuation while retaining algorithm-specific precision.
Consider these two plans:
Weak: Find two numbers that match the target and return them.
Precise: Scan left to right. For each value, look for its
complement in a map of earlier values to indices. If found,
return those two indices. Otherwise store the current value
and index. Return no result after the scan ends.
The second plan is still a sketch, but it has a state model and operation order. It is ready to translate because the next question is about language mechanics rather than an undisclosed algorithm.
Keep the transition visible: “The map contains only earlier positions, so a lookup cannot reuse the current item. I will implement that order now.” This is an original example of an explanation, not a quoted employer script.
Worked exercise: contract, invariant, pseudocode, Python
Given a list of integers and an integer target, return any pair of distinct, zero-based indices whose values add to the target. Return None if no pair exists. Duplicate values and negative values are allowed. Do not mutate the input.
“Any pair” matters. This contract does not require the lexicographically smallest pair, all pairs, or a particular order of discovery. A solution to one of those variants needs additional reasoning. Likewise, returning values instead of indices would solve a different task.
Start with an invariant: immediately before processing position i, every index stored in seen is smaller than i, and each stored index points to the value used as its key. A key may retain just one earlier occurrence because any valid pair is acceptable.
The pseudocode follows directly:
seen = empty map from integer value to earlier index
for each index i and value in nums:
complement = target - value
if complement is a key in seen:
return (seen[complement], i)
seen[value] = i
return None
Now translate it into runnable Python:
def find_pair(nums, target):
seen = {}
for i, value in enumerate(nums):
complement = target - value
if complement in seen:
return (seen[complement], i)
seen[value] = i
return None
Each line has a job. enumerate supplies positions as well as values. Membership tests map keys. The lookup precedes the assignment, and the final return handles exhaustion. The function leaves nums unchanged.
For correctness, a returned pair has the required sum by the complement calculation. Its indices are distinct because the stored index precedes the current one. If a valid pair exists at positions j < i, the first value has been stored when the scan reaches i; even if another occurrence replaced its index, that earlier occurrence has the same value and still forms a valid pair.
With average constant-time map operations and unit-cost integer arithmetic, the expected time is O(n) and the auxiliary space is O(n). Those assumptions belong in the explanation. The sketch's word “lookup” does not make every possible implementation constant time.
Trace the mutation order before optimizing
Use [3, 3] with target 6 to connect the invariant to actual state:
| Position | Current value | Map before lookup | Decision |
|---|---|---|---|
| 0 | 3 | Empty | Complement absent; store 3: 0 |
| 1 | 3 | 3: 0 | Complement present; return (0, 1) |
Two equal values are valid because they occupy different positions. Compare the single-element input [3]: the same target must produce None.
Now deliberately move seen[value] = i above the membership check. With [3], the map contains the current item before lookup and the function can return (0, 0). That counterexample pinpoints the defect. “Use a hash map” was insufficient; the mutation order carries the distinct-index guarantee.
This is a useful stopping point for pseudocode. The contract, invariant, and counterexample are established. Continuing to polish the sketch while avoiding implementation adds little evidence about whether you can finish the task.
Test properties as well as examples
Test cases should target a claim. Empty input checks exhaustion. A single matching value checks that an item cannot pair with itself. Duplicates check distinct positions; negatives check the complement arithmetic.
| Input and target | Expected property | Defect it can reveal |
|---|---|---|
[], 0 | None | Missing no-result path |
[3], 6 | None | Insert-before-lookup self-pairing |
[3, 3], 6 | Two distinct indices | Incorrect duplicate handling |
[-2, 7], 5 | A valid pair | Incorrect assumptions about positive values |
[0, 0], 0 | A valid pair | Confusing a zero value or index with absence |
[1, 2], 9 | None | Returning an invalid fallback pair |
Because the contract allows any pair, a general test should check validity rather than demand one arbitrary pair:
nums = [1, 4, 2, 3]
before = nums.copy()
pair = find_pair(nums, 5)
assert pair is not None
i, j = pair
assert 0 <= i < len(nums) and 0 <= j < len(nums)
assert i != j and nums[i] + nums[j] == 5
assert nums == before
For offline preparation, compare small inputs against a separate brute-force oracle that tries every distinct pair. If the oracle finds none, the implementation must return None; otherwise its output must satisfy the contract. This catches more than reproducing the optimized algorithm inside the test.
These checks establish evidence for this exercise's finite test set. They are not proof that a different assessment's hidden cases will pass. Change the tests when the real task changes its contract.
Handle syntax uncertainty without leaving placeholders
When you know the algorithm but hesitate over a library call, identify the uncertainty precisely. “I need to confirm how this map checks membership” is actionable. “The rest is straightforward” leaves an implementation gap.
Use the language you can implement reliably, subject to the task's allowed choices. During preparation, practice its map, set, queue, sorting, and iteration operations in executable snippets. In the interview, use documentation or other assistance only within the stated rules. A sketch is not permission to consult an unauthorized tool.
Read the supplied function signature before adapting the example. A grader may expect a list, an exception, or a sentinel instead of our tuple-or-None result. A function task may require a return value while a standalone program requires formatted output. Debug prints can also corrupt a required output stream.
Make the smallest correct adaptation. Do not replace a working input harness merely to make the submission resemble your practice notebook. The boundary between your function and the runner is part of the executable contract.
Move from a successful run to a completed submission
Official platform fact: HackerRank's candidate instructions for modifying submissions distinguish running edited code from submitting the changed answer. After a late correction, follow the actual submission control and verify the saved state. Do not infer completion solely from a passing local run.
Our preparation recommendation: reserve room for implementation, checks, and the final delivery action. There is no universal number of minutes for the sketch. Stop planning when you can explain the contract, central invariant, and chosen operations; continue planning if one of those remains unresolved.
Before declaring completion, check that no placeholder helper remains, the output fits the required interface, and the final saved version includes your latest fix. If time ends with incomplete code, explain the exact unfinished behavior and the tests still needed. That is an accurate handoff, not a substitute for the required program.
Practice the translation on different task shapes
| Question | Translation skill to practice |
|---|---|
| Return the Ten Most Frequent Words | Turn ranking and lexical tie-breaking into a precise sort key and deterministic tests |
| Implement a BFS web crawler | Specify queue, visited-state updates, duplicate handling, and hostname filtering |
| First-Match CIDR Firewall: Status of an IP Address or a Whole CIDR Block | Preserve rule order and explain why a whole-block answer needs stronger reasoning than one-address matching |
| Connect JSON Coordinates Through a Documented Drawing API | Translate geometry semantics and documented interface details into actual requests |
| Debug a Test-Driven C++ Project | Use executable test failures to locate the gap between intended and implemented behavior |
Start with Implement a BFS web crawler. State when a URL enters the visited set, implement that decision, and test repeated links. The goal is to make your explanation predict what your program actually does.