Code Crunch Worldwide · Problem-Solving Method
Five steps for working any coding problem out loud — in an interview, in a review, or alone at 2 a.m.
Free to use, teach, translate, and remix. No sign-up, no paywall.
Most people do not fail a technical interview because they cannot code. They fail because the first ninety seconds go badly: they start typing before they understand the problem, go quiet for eleven minutes, produce something that works on the example and nothing else, and never say why they chose it.
A method fixes that by giving you somewhere to put your attention when you are nervous. You are never staring at a blank screen wondering what to do next — you are on a step, and each step has a job. It also makes you legible. An interviewer is not grading your silence; they are grading what they can hear.
FRAME is the method we teach in every Code Crunch Worldwide course. It is five steps, it fits on an index card, and it works the same way whether you are in an interview loop, opening a pull request, or debugging something at work.
| Step | What you do | |
|---|---|---|
| F | Frame | Restate the problem, define inputs and outputs, ask clarifying questions. |
| R | Research constraints | Identify limits, edge cases, and what makes the problem difficult. |
| A | Assess options | Describe a simple approach, then compare better approaches and their tradeoffs. |
| M | Make the solution | Write clean, incremental code while explaining key decisions. |
| E | Examine | Walk through tests, edge cases, complexity, and possible improvements. |
The order matters. Every step is cheaper than the one after it — a wrong assumption caught in F costs a sentence; caught in M it costs the whole solution.
Say the problem back in your own words. Not the words on the screen — yours. If you cannot restate it, you do not have it yet, and everything built on top will be built on a guess.
The failure mode: starting to type during this step. You are buying certainty here for the price of thirty seconds. It is the best trade in the whole interview.
Find out what the problem is actually made of: how big the input gets, which cases break the obvious approach, and where the difficulty is hiding. This is the step people skip, and it is the step that decides whether your solution survives the follow-up.
n = 100 and n = 10_000_000 are different problems.The failure mode: treating edge cases as a testing concern to handle later. They are a design concern. An edge case discovered in E usually means rewriting M.
Start with the approach you are certain of, even if it is slow. Say its cost. Then look for a better one and say what it buys and what it costs. Choosing between approaches out loud is the single most valuable thing you do in an interview, because it is the part of your thinking nobody can see in the finished code.
Never skip straight to the clever solution, even when you recognise the problem. The brute force is your safety net — if the clever one collapses under time pressure, you still have something working, and you have already said out loud that you knew the difference.
The failure mode: silently picking the optimal approach because you have seen the problem before. To the person watching, that is indistinguishable from memorisation.
Now write it. Build it in pieces, keep it readable, and narrate the decisions — not the syntax. Nobody needs to hear "now I write a for loop." They need to hear why the loop stops where it stops.
left, right, seen, best_so_far — never a, b, tmp.The failure mode: writing the whole thing in one breath and then debugging it in one breath. Incremental beats heroic. Working code at minute thirty beats elegant code at minute fifty.
Run your own code by hand before anyone asks you to. Trace a real input, line by line, out loud. This is where you find your own bug — which is worth far more than the interviewer finding it.
The failure mode: "I think that works." Either you traced it or you did not. Tracing it takes ninety seconds and is the difference between a hire and a maybe.
The problem: given a list of ticket prices sorted from cheapest to most expensive, and a budget, find two different tickets whose prices add up to exactly the budget. Here is what the whole five steps sound like.
"So I am given a list of integers that is already sorted ascending, and a target integer.
I return the two prices that sum to the target — or the two positions, whichever you prefer.
Can the same ticket be used twice? No. And if no pair works, what do you want back — None?
Good. Can the list be empty? Yes, so that is a case I will handle."
"How long can the list get? Up to a million — so anything quadratic is out. Prices are positive integers, and duplicates are possible, so two tickets can have the same price as long as they are different tickets. The edge cases I care about: empty list, one element, no pair that sums to the target, and a pair made of two equal prices. The thing that makes this hard is that the naive approach checks every pair, and at a million elements that is a trillion checks."
"The simple approach is two nested loops over every pair — O(n²) time, O(1) space. Correct, but too slow at a million.
Better: one pass with a set of prices I have already seen, checking for target - price.
That is O(n) time and O(n) space, and it works whether or not the list is sorted.
But the list is sorted, and I am not using that. Two pointers, one at each end: if the sum is too big, move the right pointer down; too small, move the left pointer up. O(n) time and O(1) space — it beats the set on memory and uses the structure I was given. I will write that one."
"Empty and single-element lists cannot contain a pair, so those return early. Then the two pointers, with the invariant that the answer, if it exists, is always somewhere between them."
def find_pair(prices: list[int], budget: int) -> tuple[int, int] | None:
"""Return two prices from the sorted list `prices` summing to `budget`.
Returns None when no such pair exists.
"""
if len(prices) < 2:
return None
left, right = 0, len(prices) - 1
# Invariant: if a valid pair exists, both of its members are
# still inside the window prices[left..right].
while left < right:
total = prices[left] + prices[right]
if total == budget:
return prices[left], prices[right]
if total < budget:
left += 1 # need more, and left is the smallest available
else:
right -= 1 # need less, and right is the largest available
return None
"Trace [2, 4, 7, 11] with a budget of 13. left=0, right=3: 2+11=13 — returns
(2, 11) immediately. Now a case that has to move: budget 9. 2+11=13, too big, right
moves to 2. 2+7=9 — returns (2, 7).
Edge cases from R: empty list returns None at the guard. One element,
same. No valid pair — say [2, 4] with budget 100 — the pointers meet and it returns
None. Two equal prices, [5, 5] with budget 10, returns (5, 5),
which is correct because those are two different tickets.
Complexity: each iteration moves exactly one pointer inward, so the window shrinks by one every time — at most n iterations, O(n) time, O(1) space. If I had more time I would return the indices rather than the values, since a caller almost certainly needs to know which tickets. And before shipping I would want to know whether the input is guaranteed sorted or merely usually sorted — the whole approach rests on that."
Copy this into a file next to every problem you solve. Doing it in writing is what makes it automatic out loud — after fifteen or twenty of these, the steps stop being a checklist and start being how you think.
# <problem name>
## F — Frame
Restated:
Input:
Output:
No answer means:
Questions I asked:
## R — Research constraints
Size of input:
Edge cases:
What makes this hard:
Limits to respect:
## A — Assess options
Simple approach: time / space:
Better approach: time / space:
Trade being made:
Chosen, because:
## M — Make the solution
Invariant:
Key decisions:
<code>
## E — Examine
Trace of a normal input:
Edge cases re-run:
Complexity, and why:
Improvement with more time:
| Minutes | Step | If you are over |
|---|---|---|
| 0–4 | Frame | You are asking questions that would not change your code. Move on. |
| 4–9 | Research constraints | List the edge cases, do not solve them yet. |
| 9–15 | Assess options | Commit to the approach you can finish, not the one you admire. |
| 15–35 | Make the solution | Ship the brute force and say you would optimise next. |
| 35–45 | Examine | Never skip this. Cut the improvement talk, keep the trace. |
These are guides, not rules. The one that matters: leave time for E. An untraced solution and a wrong solution look identical from the other side of the table.
FRAME was written for interview prep, but the steps are not about interviews. They are about making your reasoning visible, and that has a use anywhere someone else has to trust your work.
FRAME is original to Code Crunch Worldwide. We wrote it for our own courses because we wanted five steps that stay the same across every subject we teach, in language a first-year student can use on day one and a working engineer does not find condescending.
Other problem-solving frameworks exist and some of them are good. We teach this one, everywhere, so that a learner moving from one Code Crunch course to another never has to relearn how to think.
Use it. Teach it. Translate it. Put it on a slide for your club. Attribution is appreciated and never required.
Our interview-prep course is fifteen weeks of drills, challenges and mock interviews built around FRAME — free, open source, and public on GitHub, with a published answer for every problem it assigns.
Course material is free to learn from, for anyone, anywhere.