Extract the contract
Recognizing a familiar pattern is helpful, but it can also make you skip details. Before reaching for a hash map, a queue, or a sliding window, say what the function receives and what it must return. Be precise about whether the answer is an index, a value, a count, or a new collection.
Separate guarantees from assumptions. Is the input sorted? Can values repeat? Can the collection be empty? Are you allowed to change it? For a graph, check whether edges are directed and whether every node is reachable. If the prompt leaves an important detail unclear, ask about it rather than quietly choosing a convenient interpretation.
Design examples that reveal mistakes
Work through an ordinary example by hand, then choose cases that challenge the contract. A one-element array can expose an off-by-one error. Repeated values can show whether you confused a value with its position. An input with no valid answer can reveal an undefined return path.
Write the expected result before running the implementation. Otherwise, a plausible-looking output can become its own explanation. You do not need a huge test list at this stage; you need a few examples whose results you can justify from the problem statement.
Use a simple baseline to guide optimization
Describe a straightforward solution first, even if it repeats work. Estimate how its time and memory grow with the input size. Then compare that cost with the constraints. This makes optimization a response to a concrete limit rather than a search for the cleverest technique you remember.
Look for the work the baseline repeats. Does it scan the same range again? Recompute information you could store? Explore states it has already visited? Use that observation to explain why a different data structure or traversal helps. A faster approach is easier to defend when you can name the work it avoids.
Explain the rule your algorithm preserves
Before writing the main loop, describe its invariant: the condition that remains true after each step. For example, a running minimum should represent the smallest value seen so far. A traversal's visited set should identify the nodes you have already discovered. State when that condition first holds and how each update preserves it.
Then implement the approach and return to your examples. When a test fails, check whether the contract was misunderstood, the invariant was broken, or the implementation differed from the plan. Keeping those questions separate helps you fix the actual mistake instead of replacing a sound approach with another guess.