KognitaKognita.

Blog

Engineering and Product Have Different Definitions of Done. Nobody Resolves It.

9 min read

The engineer closed the ticket. Code reviewed, tests passing, deployed to production. Forty minutes later, the product owner reopened it with two comments: the loading state uses the old spinner, and it does not handle the case where the user has no data. QA added a third: the mobile layout breaks at 375px. The engineer thought they were done. By their definition, they were.

This scenario is not a failure of the engineer or the product owner. It is a failure of the shared understanding that was supposed to exist before the work started. The Definition of Done is an agile concept with a clear purpose: align the team on what "complete" means so that surprises at review are not surprises. In practice, most teams have a DoD document in Confluence that was last updated six months ago and is read by roughly nobody before a ticket is picked up.

The real definition of done is whatever the person reviewing the ticket decides at the moment they look at it. Engineers apply one standard. Product owners apply another. QA applies a third. All three are legitimate. None of them is the same. And because the reconciliation happens after the work is done rather than before it starts, the cost is paid in reopened tickets, stalled sprints, and the slow erosion of trust between roles that all believe they are doing their job correctly — because they are.

The three definitions of done that live in the same team

Engineering's definition of done is anchored in technical correctness. The code does what it was written to do. The tests pass. The PR is reviewed by someone who understands the implementation. It deploys without incident. This is a rigorous and valid definition. It says nothing about whether the behavior matches the product intent, whether it works across all the environments QA cares about, or whether it handles edge cases that were not written into the acceptance criteria.

Product's definition of done is anchored in behavioral specification. The feature does what the spec described. The UI matches the Figma. The user flow works end to end. This is also valid. It says nothing about how the behavior is implemented, whether the implementation will hold up under load, or whether there are system states that the spec never described because the spec was written without full knowledge of how the current system behaves.

QA's definition of done is anchored in coverage. The feature was tested across the required environments and browsers. The edge cases were exercised. Regression tests pass. This is also valid. It says nothing about whether the test cases covered the right scenarios — which depends entirely on whether QA had complete system knowledge when the test plan was written. A test suite that does not know about a second code path that reaches the same UI state will pass every time, and will miss the bug every time.

One feature. Three definitions of done. Three different gaps.
The same feature. Three definitions of done. Three different outcomes.

  Feature: "Show empty state when a user has no saved items."

  Engineering definition of done:
  -> Component renders without errors when items array is empty.
  -> Unit tests pass for empty array input.
  -> PR reviewed, merged, deployed to production.
  -> Done.

  What engineering missed:
  -> The empty state copy says "No items yet." — identical to the
     error state copy. Users cannot distinguish a working empty
     state from a broken load.
  -> The empty state does not render on mobile at 375px width.
     The container collapses to zero height.

  Product definition of done:
  -> The feature behaves as described in the spec.
  -> Loading state is visually distinct and uses the new spinner.
  -> Empty state matches the Figma design.
  -> Done.

  What product missed:
  -> The spec did not define what "no saved items" means for a user
     who has items but has filtered them all out. The filter state
     and the empty state now use the same component with different
     copy — but the copy is managed in two separate places in the
     codebase. One was updated. The other was not.

  QA definition of done:
  -> Tested on Chrome, Firefox, Safari desktop.
  -> Tested on iOS Safari, Android Chrome.
  -> Empty state renders correctly on all tested environments.
  -> Done.

  What QA missed:
  -> QA tested with a user account that had previously had items.
     A brand-new user account with zero history triggers a different
     code path that was not covered by any test case. That path
     throws an unhandled error in production.

None of these gaps are the result of carelessness. They are the result of each role working from an incomplete picture of the system. Engineering knew the code it wrote. Product knew the spec it described. QA knew the test scenarios it planned. None of them had a complete view of what the system actually does in every relevant state — which is the only foundation on which a shared definition of done can stand.

Why the gap surfaces at the worst time

If DoD misalignment were a planning problem, it would surface during backlog refinement or sprint planning. It would be caught before the work started, corrected in the ticket, and built correctly the first time. Instead, it surfaces at review, at demo, or at release — the three moments when the cost of a correction is highest.

At review, the product owner reopens the ticket. The engineer has already moved on to the next story. Re-engagement means context-switching back, finding the relevant part of the codebase, understanding the gap, making the change, getting a new review, and redeploying. Depending on the change, this is anywhere from two hours to two days of unplanned work — work that was not in the sprint estimate, was not in the sprint capacity, and now either delays another story or extends the sprint.

At demo, the stakeholder sees the wrong behavior in front of an audience. This is not a technical problem anymore; it is a trust problem. The team said it was done. It is not done by the definition that matters to the person watching the screen. The gap between engineering's done and stakeholder's done becomes visible at the exact moment when visibility is most damaging.

At release, the gap surfaces as a production issue. A user in a state the team did not test encounters behavior that engineering considers correct (the code does what it was written to do) but that produces a broken experience. The follow-up ticket is now urgent, unplanned, and pulling engineering away from sprint work. This compounds into the next sprint as the kind of unplanned debt that erodes traceability between what was decided and what was built.

What definition of done discussions usually miss

Most DoD conversations focus on process and criteria: "we need acceptance criteria to include mobile behavior," "QA needs to sign off before a ticket closes," "product review is required for all user-facing changes." These are reasonable controls. They make the definition more complete. They do not address the root cause.

The root cause is that done is defined against an incomplete description of the system. Acceptance criteria describe intended behavior. They do not describe current system behavior, which may differ from the intent in ways that matter enormously for what "done" needs to include. A ticket that says "show empty state when user has no items" is written against an assumed system state. If the system has two code paths that reach the empty items condition — one for a user with zero history and one for a user whose items were deleted — the ticket's acceptance criteria need to cover both. But nobody writing the ticket knew about the second path, because that knowledge lives in the codebase and is not surfaced during planning.

This is why DoD conversations produce process improvements that help at the margins but do not eliminate the reopened ticket problem. The process improvements make it more likely that the person reviewing the ticket catches the gap at review rather than at production. They do not make it less likely that the gap exists in the first place. The gap exists because the ticket was written without complete system knowledge — and that problem is solved before the ticket is written, not after it is reviewed.

How queryable codebase context helps define done before the ticket exists

Five questions to align definition of done before writing the ticket
Five questions that should be answered before a ticket is written:

  1. What does the current system do in this scenario right now?
     -> Not what the spec describes. What the running code actually does.
     -> "When a user has no saved items, the system currently returns
        an empty array and the component renders nothing — no empty state,
        no error, just blank space."

  2. Are there multiple code paths that reach this state?
     -> "There are two paths: a user with zero items ever, and a user
        whose items have been deleted. They call different service methods
        and return differently shaped responses."

  3. What does each role expect to happen in this scenario?
     -> Engineering: consistent with the data contract.
     -> Product: matches the Figma empty state design.
     -> QA: testable across environments, including the zero-history path.

  4. Which of those expectations conflict, and where?
     -> The zero-history path is not in the Figma spec.
     -> The deleted-items path uses a different response shape.
     -> The ticket as written covers one path, not both.

  5. What does done look like for all three roles, stated explicitly?
     -> "Done means: both code paths render the correct empty state,
        the copy is managed in one place, the component handles 375px,
        and both paths are covered by test cases."

  If you cannot answer these before the ticket is written, you will
  answer them after the ticket is reopened.

The five questions in the block above are not new questions. Good product owners and scrum masters have always asked some version of them. The problem is that answering them currently requires routing through an engineer — pulling someone out of their work to explain the current system behavior, which code paths exist, and what the data shapes look like. That routing is slow, inconsistent, and rarely happens for lower-priority tickets. The tickets get written on assumptions instead of system truth.

When the codebase is queryable in plain language, those questions become self-serve. A product owner preparing a ticket can ask "what does the system currently do when a user has no saved items?" before writing a single acceptance criterion. The answer changes the ticket. The ticket written against system truth is the ticket that produces less reopening, less demo surprise, and less production incident.

This is a different framing than "improve the DoD process." It is: make the system's current behavior available to the people who define what done means, before they define it. The alignment problem is fundamentally an information problem. When everyone in the conversation — engineering, product, QA writing the test plan — can see what the system actually does in the relevant states, the definition of done can be grounded in reality rather than in assumptions about it.

Kognita's Jira and codebase integration for DoD alignment

What Kognita returns when asked about current system behavior — the query that prevents the reopened ticket
Query: "What does the current system do when a user has no saved items?"

  Kognita returns:

  -> SavedItemsService.getUserItems() returns an empty array []
     when the user has no items. This is handled in SavedItemsController,
     which passes the empty array directly to the frontend.

  -> The SavedItemsList component checks for items.length === 0 and
     currently renders null — no empty state, no fallback UI.
     This is the behavior in production today.

  -> There is a separate code path for deleted items: when items have
     been soft-deleted, SavedItemsService returns { items: [], deleted: true }.
     The controller does not distinguish this from a genuine empty state.
     Both cases reach the same null render.

  -> There is an EmptyStateComponent in /components/ui/EmptyState.tsx
     that was built for the search results page. It has not been
     connected to SavedItemsList.

  -> Jira ticket FEAT-2201 ("Add empty state to saved items") was
     written before the deleted-items code path was added in PLAT-1904.
     The ticket description does not reference the deleted state.

  This is what causes the reopened ticket. The ticket was written against
  an incomplete picture of what the system does. Kognita makes that
  picture available before the ticket exists.

The answer in the block above is not available anywhere in the team's current workflow. It is not in Jira, because Jira describes intended behavior, not current behavior. It is not in the README, because READMEs describe the initial design, not the cumulative state. It is not in Figma, because Figma describes the target, not the existing implementation. It lives in the codebase, and it is currently accessible only to engineers who know where to look.

Kognita indexes your codebase continuously and connects it to your Jira tickets. For DoD alignment, this means a product owner or scrum master can ask a plain-language question about current system behavior before writing the ticket, and get an answer that names the code paths, the existing components, the relevant prior tickets, and the current production behavior. That answer changes what the acceptance criteria say. It changes what QA plans to test. It changes the definition of done from "what we assumed" to "what the system actually requires."

The Jira integration is specifically valuable for tracing historical context. When the query result surfaces that FEAT-2201 was written before PLAT-1904 added the deleted-items path, the product owner knows the ticket is out of date before they open it. The definition of done that gets written covers both paths. The engineer who picks up the ticket knows about both paths from the start. QA plans test cases for both paths. Nobody is surprised at review, demo, or release, because the surprise was absorbed at ticket creation instead.

Nothing in this workflow requires technical background. The interface is conversational. The product owner asks about system behavior in plain language. The answer comes back in plain language, grounded in the indexed codebase and connected to Jira history. No one's laptop needs configuration. No repo access is required. The whole team — engineering, product, QA, scrum master — works from the same system picture, because the platform manages the indexing and the access in one place.

Final take

Improving the Definition of Done document does not solve the DoD problem. The problem is not that the criteria are poorly written. The problem is that the criteria are written without full knowledge of what the system currently does — which means the definition of done is built on assumptions that only become visible when someone with a different definition looks at the same ticket after the work is complete.

Done means different things to engineering, product, and QA because each role has access to a different slice of system truth. The fix is not a better process for reconciling those definitions after the work is done. It is making the full system picture available to all three roles before the ticket is written — so the definition of done is grounded in what the system actually does, not in what each role assumes it does. That is what queryable codebase context provides. That is where the reopened ticket problem is solved.