跳到正文
DEV Community· Emily Woods·· 3 小時前AI 評分57

你的下一場程式設計面試,可能會從一段並非由你編寫的程式碼開始

Your Next Coding Interview Might Start With Code You Didn’t Write

AI 導讀

Emily Woods提出一套程式設計面試除錯練習方法:沿單一請求追查狀態變化,根據觀察定位故障,再以後續讀取驗證修復。租車示例中,更新回應返回 60,但資料仍是 80;作者建議使用受權限限制的寫入操作修復,再另行讀取驗證,並測試其他用戶編輯及寫入失敗。文章亦列出 35 分鐘練習安排,並説明文中提到的租車除錯題屬於有人報告的面試練習,並非對下一場評核形式的保證。

正文

A practical debugging rehearsal: trace one request, isolate the mistake, and prove the repair without rewriting everything.

You prepare to build a solution from an empty editor. Then you open the interview project and discover that the solution already exists. It just does not work.

There are routes you did not name, a storage layer you did not choose, and a failing test whose description sounds almost embarrassingly simple. Somewhere inside this unfamiliar code, a change that appears to succeed disappears when the user refreshes.

I find that a more useful practice challenge than another evening spent recognizing patterns. You have to decide where to look before you can decide what to write.

The uncomfortable part is the urge to make yourself useful immediately. You spot an awkward function, start cleaning it up, and ten minutes later the code is prettier while the original failure remains unexplained.

For this kind of rehearsal, I prefer a stricter rule: every edit needs a specific observation that justifies it.

Start with the complaint, then draw one request

Imagine a small car-rental service. A user changes a daily price from 80 to 60. The update response contains 60. A fresh request returns 80.

This is an illustrative exercise, with invented data and a deliberately simplified repository interface. It is not a reconstruction of a company’s private source code.

Before opening every folder, write the contract: an authorized update should change the stored price, and a subsequent read should return that change. For this example, reads go straight to the same store and no other writer is active.

That last sentence matters. Without it, a stale replica, a cache, or a concurrent edit could produce a similar symptom for a different reason.

Now follow just one path:

Request → authenticated user → handler → repository → stored record → response.

The first useful question is whether the stored record changed. If it still says 80, investigate the write. If it says 60 but the read returns 80, investigate how the read found its answer.

You have already cut the search space without changing a line.

The response looks right because the response is the bug

Here is a small service function. Assume findOwned returns a detached copy of a record owned by the authenticated user, or null. Also assume changes has already been validated and contains only permitted fields.

async function editCar(repo, userId, carId, changes) {
  const car = await repo.findOwned(carId, userId);
  if (!car) return null;
  const updated = { ...car, ...changes };
  return updated;
}

Pause before reading further. Which operation changes the store?

There isn’t one. The function creates a new object and returns it. An HTTP handler can serialize that object into a perfectly convincing success response while the stored record remains untouched.

For our example, the response contains 60, the store still contains 80, and the next read correctly returns the old value. The read is exposing the missing write.

Three-panel debugging trace: a user requests a price change from 80 to 60. The response says 60 but storage remains 80, so a new GET returns 80. Changing a returned object does not save the record.

This is why I want an expected result written down before I inspect the implementation. A response that agrees with the input can feel like evidence even when it proves very little.

My diagnosis note would contain four lines:

  • Observed: the edit response contains 60; a fresh read contains 80.
  • Hypothesis: the service changes only the returned object.
  • Evidence: this path calls a read method and never calls a write method.
  • Repair to check: perform the scoped update, then return its stored result.

That note is short enough to explain aloud. It also makes the reasoning easy to challenge.

Make the smallest repair that restores the contract

In this sketch, the repository offers updateOwned. Its contract is explicit: match both the car ID and owner ID, apply only validated fields, and resolve with the saved record after a successful write. Return null when no owned record matches; reject on a storage failure.

async function editCar(repo, userId, carId, changes) {
  const saved = await repo.updateOwned(
    carId, userId, changes
  );
  return saved;
}

The shorter function is not the achievement. The achievement is that the repository now performs the operation the endpoint promises.

This is an interface sketch, not a drop-in database implementation. In the real project, inspect the actual update method: does it return the old record, the new record, or only an affected-row count? Can it report success when nothing matched?

The handler must translate those outcomes into the documented response. It must also handle a rejected write rather than announce success before the operation finishes.

JavaScript’s await behavior is useful to keep precise here: it waits for the supplied promise to settle. It cannot turn a missing write into a real one, and it cannot repair a repository that resolves before its underlying work finishes.

I also want the owner restriction inside the update operation. A prior lookup alone is not a reason to let the eventual write target any record with that ID.

Prove the repair through a separate read

A check that only inspects the edit response would have accepted our broken version. The stronger check follows the user’s next action.

  1. Seed car C1, owned by user A, with a daily price of 80.
  2. As A, change C1’s price to 60 and wait for the request to finish.
  3. Read C1 again through the application’s normal read path.
  4. Confirm that both the response and the stored record contain 60.

Then make the check harder in two specific ways. Attempt the edit as user B and confirm that no change occurs. Make the storage write fail and confirm that the endpoint does not return a successful saved record.

Use the project’s documented error contract for the status codes. There is no value in replacing its conventions with your favorite API style halfway through a repair.

One subtle trap belongs in the test setup too. If a fake repository returns the same object reference that it stores, an accidental mutation can appear to persist. A fake should reproduce the storage behavior that matters, including detached reads when that is how the real repository behaves.

For a disk-backed service that promises persistence across restarts, add that check separately. Reading twice from a shared in-memory object cannot establish crash durability.

A different symptom needs a different investigation

The useful habit is to locate the first point where actual state disagrees with the contract. The same habit works when the complaint changes.

A new record never appears. Inspect the store first. If the record is missing, follow creation and its completion. If it exists, compare the fields used by the list query with the fields creation actually saved. An owner field mismatch can hide a perfectly successful insert.

A list includes another user’s records. Trace the authenticated identity into the query. Filtering the browser display is insufficient; the server must enforce access. Check sibling fetch, edit, and delete paths against the same ownership rule.

A change disappears only sometimes. Write down the order of events. Two requests might read the same old state, make different changes, and overwrite each other. Adding await to each request does not serialize them against one another.

Decision map: read the stored record. If it has the old value, inspect a missing save, wrong match, or incomplete write. If it has the new value, inspect the read filter, cache, or environment. Find the first disagreement, repair it, and read again.

If the project stores everything in one JSON file, this deserves special attention. Node’s file-system documentation warns against overlapping writeFile calls to the same file without waiting. Even individually completed writes can lose updates when two read–modify–write sequences race. Coordination must cover the whole operation and every writer involved.

That is a follow-up to investigate when the evidence points there. It is not a reason to replace a small service with a new architecture before understanding the original bug.

Turn this into your next practice session

The Amazon-tagged car-rental debugging exercise describes an unfamiliar Node.js project with three complaints: records leaking between users, missing new records, and edits lost after refresh. The original source code is not included, so use it to rehearse diagnosis, then build or choose a small local service for hands-on repair. The listing is a reported interview exercise, not a promise about your next assessment.

For a 35-minute rehearsal, this is the structure I prefer. It is a practice plan, not a hiring score.

  • First 5 minutes: read the API contract, run the supplied checks, and map failures to the affected operations. Confirm that the service is using the intended environment.
  • Next 10 minutes: reproduce one failure with a tiny fixture. Trace only the relevant request and write down the first wrong value.
  • Next 10 minutes: make one justified repair. Check the original symptom and a neighboring behavior that the repair could damage.
  • Final 10 minutes: rerun the full supplied suite, inspect the change, and explain the cause, repair, evidence, and remaining uncertainty.

Keep your diagnosis before opening a solution. If you use PracHub Premium for guided review, its solution walkthroughs and AI Mentor can be the comparison step after your own attempt. The useful question is where your reasoning diverged, not how quickly you can recognize the explanation afterward.

During an actual assessment, follow its rules for assistants and external resources. During practice, an assistant can help locate a call path or propose a counterexample; you still need to explain why the repair follows from the evidence.

Leave yourself something better than a green screen

At the end, keep a small repair record:

Given this state → this request should cause this change → this observation proved the defect → this patch restored the behavior → these checks protect it.

If you cannot fill in the observation, you may have guessed correctly without understanding the failure. If you cannot name the checks, you have not shown which behavior the repair protects.

This is the part of interview preparation I want to make less mysterious. You do not need to understand an entire repository before becoming useful inside it. You need enough of the system to make one claim, find evidence for it, and avoid breaking the next request.

Before your next practice session, choose a small project and ask someone to plant one bug. Keep the expected behavior, hide the changed line, and explain the repair aloud when you find it.

Could you prove the fix if the success response were lying to you?

~ Emily

來源:DEV Community · dev.to