'The code works on my machine' and 'this is actually done' are two different claims. The gap between them is exactly where a task gets marked complete and then bounces back a week later.
2 min read
A task can compile, run correctly for the happy path, and still not be done — no tests, no error handling for an obvious failure case, a hardcoded value that only worked because of the specific data on your machine. "It works" and "it's done" sound similar but make different claims, and treating them as the same is how a task gets marked complete, merged, and then reopened a week later as a bug.
Teams that avoid this ambiguity write down, once, what "done" means for any normal piece of work — not per-task, but as a standing checklist applied to every task by default:
A task is "done" when:
- The code does what the requirement actually asked (see the requirements
lesson) — not just the happy path
- Tests exist for the new behavior, and the full suite still passes
- Error/edge cases have been considered, not just the expected input
- The code has been reviewed and review feedback addressed
- Documentation (a comment, a README, an API doc) is updated if the change
affects how someone else would use or understand the system
- It's actually deployed/merged, not sitting finished-but-unmerged on a branch
This list isn't universal — a team's actual definition of done should match its own real risks (a team shipping to production multiple times a day cares about different things than one shipping monthly) — but having one written down and applied consistently is the part that matters, more than its exact contents.
A task that's 90% done in the sense of "the interesting part works" but still needs tests, edge-case handling, and review often takes as long for that last 10% as the first 90% did — a pattern experienced engineers eventually stop being surprised by. Reporting a task as "done" when it's actually at that 90% mark isn't dishonest so much as measuring a different, less useful thing than what "done" is supposed to mean to everyone else relying on that status.
A shared, explicit definition of done removes an entire category of disagreement — "I thought this was ready" vs. "there's no tests and it crashes on empty input" stops being a values disagreement and becomes a simple checklist check. It also protects the person who did the work: a clear, met checklist is a much stronger answer to "why isn't this actually finished" than a vague sense that it should have been enough.
Check your understanding
A quick comprehension check — not tracked, not graded, just for you.
1. Why are 'it works' and 'it's done' treated as different claims in this lesson?
2. What matters more, according to this lesson: a team's exact definition-of-done checklist, or having one at all?
3. Why can the 'last 10%' of a task (tests, edge cases, review) take as long as the first 90%?
Software Engineering Fundamentals