KognitaKognita.

Blog

How to Write Jira Tickets That Engineering Can Actually Estimate

11 min read

The PM writes a ticket: "User can filter their order history by date range." Seems simple. Three-pointer, maybe. Engineering comes back: this touches the search service, requires a new database index, needs a UI component that does not exist yet, and the current API contract does not support date range filtering for that endpoint. It is a 13. The PM did not know any of that. Neither ticket was wrong — they just described the same thing at different layers. The PM described what the user should be able to do. Engineering described what the system would have to change to make that possible. The gap between those two descriptions is exactly where estimates explode.

This is not a process failure or a communication breakdown. It is a structural information problem. PMs write tickets from the user's perspective, which is their job. Engineers estimate from the system's perspective, which is theirs. For the two to meet, someone needs to bridge the gap before the ticket reaches grooming — ideally before it is written at all.

Why estimation mismatches happen

The estimation mismatch has a specific anatomy. A PM writes a ticket describing desired user behavior — the outcome they want. That description is accurate and complete from a product perspective. An engineer reads the same ticket and has to mentally translate it into a list of system changes: which services are involved, what the current state looks like, what API contracts need to change, what components exist or do not exist, what adjacent systems might be affected. That translation is where the scope lives.

The PM cannot do that translation without system knowledge. The engineer cannot skip it. So estimation becomes a process of the engineer surfacing scope the PM never intended to hide — and the conversation in grooming is not about the feature, it is about establishing the baseline system state that should have been established before the ticket was written.

This pattern repeats across every sprint for every team, at every level of PM seniority. It is not a skill problem. Senior PMs who understand their system deeply still write tickets that surprise engineering when the scope is fully mapped. The issue is that writing an accurate ticket requires access to system truth — and most PMs have no direct way to get it.

What makes a ticket estimable

The answer is not a template. Templates help with formatting but do not solve the underlying problem. A perfectly formatted ticket with a clear title, acceptance criteria in the "Given/When/Then" format, and tagged to the right epic still blows up in estimation if the PM did not know the API contract does not support the proposed behavior.

What makes a ticket estimable is information — specifically, the information engineers need to size the work without running a discovery session. There are five categories that matter most.

First: current system behavior. What does the system do today? A ticket that describes new behavior without describing the current behavior forces the engineer to establish the before-state themselves. "Users can filter order history by date range" describes the end state. "Currently, GET /orders returns paginated results sorted by created_at with no filter parameters" describes where the system starts. The engineer needs both.

Second: new behavior, precisely stated and testable. "Filter works correctly" is not a done criterion. "GET /orders accepts optional date_from and date_to ISO-8601 parameters; the order history UI displays a DateRangePicker; filtered results update on selection change" is. The precision enables an estimate because it defines the scope of what needs to be built and tested.

Third: whether similar patterns already exist. If a date picker component already exists in the codebase, the ticket should say so. If a similar filtering pattern exists elsewhere in the system that can be adapted, the ticket should reference it. This is not information most PMs have access to, which is exactly the problem — but it changes the estimate significantly when it is known in advance.

Fourth: likely services and integrations involved. A ticket for "add real-time inventory to the product detail page" has very different scope depending on whether the inventory service supports real-time queries or only batch updates. That information changes the estimate from 5 points to 21 points. It lives in the codebase, not in the ticket, and it should be verified before the ticket is committed.

Fifth: explicit boundaries. What is out of scope is as important as what is in scope. "This ticket does not include mobile layout changes, export of filtered results, or saved filter preferences" closes the scope on three conversations that would otherwise happen during grooming. Engineers stop estimating for things that are not in scope. PMs stop being surprised when those things are not in the next sprint's delivery.

The five types of under-specified tickets

Most estimation failures trace back to one of five recurring ticket patterns. They all share the same root cause — missing system context — but they manifest differently in grooming and create different kinds of mid-sprint surprises.

Five under-specified ticket types — with concrete examples and what each is missing
Five types of under-specified tickets — and what is missing from each:

  1. The outcome ticket
     Written: "User can filter their order history by date range."
     What's missing:
     -> What does the current order history view do? (no current state)
     -> Which service handles order queries? (no service context)
     -> Does the API contract support date range filtering? (no integration spec)
     -> Does a date picker component already exist in the UI? (no component check)
     Engineering receives: a 3-pointer. Discovers: new database index,
       API contract change, new UI component. Returns: 13.

  2. The requirements document masquerading as a ticket
     Written: 800 words of business context, user personas, and design goals.
       Done criteria: "User is satisfied with the experience."
     What's missing:
     -> A precise, testable definition of done
     -> Any boundary on what is and is not in scope
     -> Any reference to which parts of the system are involved
     Engineering receives: a story they cannot slice or size.

  3. The "small change" ticket
     Written: "Update the email template for password reset."
     What's missing:
     -> How many template variants exist (there are three)
     -> Whether the localization service is involved (it is)
     -> Whether the HTML/text versions both need updating (they do)
     -> Whether the template is managed in code or a third-party service
     Engineering discovers on day one: this is a 5-pointer, not a 1.

  4. The assumption ticket
     Written: "Add the new tiered pricing to the checkout flow."
     What's missing:
     -> Whether the PricingService changes are already complete (they are not)
     -> What API the checkout flow calls for pricing data
     -> Whether the frontend pricing display is a shared component
     Engineering discovers: blocked by an incomplete dependency not tracked in Jira.

  5. The stakeholder request passed through without PM review
     Written: "Sales wants real-time inventory visibility on product pages."
     What's missing:
     -> Any PM verification that this is technically feasible as described
     -> Whether the current inventory service supports real-time queries
     -> What "real-time" means in contract terms (polling? websocket? cache TTL?)
     Engineering discovers: the inventory service is a third-party API
       with a 15-minute cache. Real-time requires a new integration layer.

The outcome ticket is the most common. It describes what the user should be able to do and nothing else. Engineering has to establish the entire before-state, the system architecture, and the technical approach from scratch. The 13-pointer that looked like a 3 in the PM's mind was a 13 before the ticket was written — the PM just did not have the information to know that.

The "small change" ticket is the most insidious because it carries false confidence. Three variants of the email template, the localization service, the HTML and text version — none of that is visible from the product layer. The PM wrote what they knew. What they did not know was enough to triple the estimate.

The assumption ticket is the most likely to stall a sprint entirely. When a ticket depends on work that is not done — a pricing service change that has not shipped, an API update that is still in review — the ticket cannot start until the dependency is resolved. If that dependency is not surfaced until grooming or, worse, until implementation, the sprint absorbs the delay without warning.

What a well-specified ticket gives engineering

A ticket written with accurate system context changes what engineers can do before they start. They estimate without needing a 30-minute clarification call, because the ticket describes the current state, the new behavior, the services involved, and the scope boundaries with enough precision to size the work from the description alone.

They start without needing to re-read the spec or ask follow-up questions, because the ticket answers the questions that always come up: what does it look like today, what should it look like after, and what is explicitly not included in this ticket. These are not complex questions. They are the same questions every engineer asks before beginning implementation. A well-specified ticket answers them in advance.

They define done without guessing. Acceptance criteria that reference specific API endpoints, specific component names, and specific behaviors can be verified. "The user experience feels smooth" cannot. The specificity is not bureaucracy — it is the definition of what the engineer is trying to ship.

The cumulative effect is less grooming time, fewer mid-sprint surprises, and more accurate sprint commitments. Teams that write better tickets do not have fewer surprises because they are smarter. They have fewer surprises because the surprises were already surfaced before the sprint started.

The system context problem

Even PMs who understand this framework and want to write better tickets run into the same wall: they do not have access to system context. Writing "does a date picker component already exist?" as a pre-ticket checklist item is reasonable. Actually answering it is not straightforward without either asking an engineer or navigating a codebase — neither of which is a standard part of ticket writing.

This is where perfectly formatted tickets still fail. A PM who has read every article about writing better Jira tickets, who uses acceptance criteria, who lists out-of-scope items, who adds a description of the current behavior — still writes an 8-pointer that becomes a 21 because they did not know the search infrastructure is a third-party Algolia integration and not a service the team controls. "Add real-time search" to a PM means adding a search input. To engineering, it means either replacing Algolia with a custom solution, building a separate real-time search layer, or discovering that the feature as described is not feasible within the current architecture at the proposed timeline.

That gap is not a writing problem. It is an access problem. The PM needed to know about the Algolia dependency before writing the ticket. That information exists in the codebase. It was never surfaced.

What to check before writing a ticket

There are five specific questions that, when answered before writing, account for the majority of grooming-session surprises. None of them require deep technical knowledge to ask or interpret. They require system access — a way to query the codebase in plain language and get specific, grounded answers.

Does a similar feature or component already exist? Before designing a new capability, check whether it exists in some form. The DateRangePicker is in /components/ui. The batch order update method is already in AdminService. The export utility already handles three of four required formats. Finding these before writing saves discovery time in grooming and changes the estimate before it is committed.

Which services or data models would this change touch? A feature that sounds like a UI change often has service-layer implications. A data display change that requires a new sort order might require a new database index. A permission change that looks like a configuration update might flow through three services. Knowing the service map before writing the ticket means the acceptance criteria can reference the right boundaries.

Is there anything in progress that this overlaps with? Open Jira epics touching the same services, in-progress work modifying the same data models, a recently completed refactor that changed the API the new ticket assumes is still the same — all of these create conflict that is cheap to detect before planning and expensive to discover mid-sprint.

What does the current behavior look like? The before-state is as important as the after-state. "What does GET /orders return today?" tells the engineer where the implementation starts. "What does the checkout flow do when pricing is applied?" establishes the baseline before the new pricing change is scoped. Before-state answers live in the code, not in the PM's knowledge of the product, and they need to be verified rather than assumed.

Are there any known constraints in this area? Third-party API contract limitations, rate limits, legacy data model constraints, shared components with downstream dependencies — these are constraints that change what is possible within a sprint. A ticket that proposes real-time inventory display needs to account for whether the inventory service is a synchronous API or a batch job. A ticket that proposes modifying the billing service needs to account for whether the billing service has downstream dependencies that need to be updated simultaneously.

Pre-ticket checklist — answered before writing vs. discovered during grooming
Pre-ticket checklist — questions answered before writing vs. discovered during grooming:

  Questions answered before writing (with system access):
  -> Does a date-range filtering capability exist anywhere in the codebase?
     Answer: OrderQueryService has a date filter on admin exports.
     Can be extended. Not a net-new capability.
  -> Which services does the order history view call?
     Answer: OrderService → OrderQueryService → UserActivityLog.
     API contract does not expose date range parameters on the user-facing endpoint.
  -> Is there an existing date picker component?
     Answer: DateRangePicker exists in /components/ui. Used in reporting module.
  -> What does the current API contract look like for order history?
     Answer: GET /orders returns paginated results, sorted by created_at.
       No filter parameters supported.
  -> Is there anything in progress in this area?
     Answer: No open Jira tickets touching OrderQueryService.

  Questions discovered during grooming (without system access):
  -> "Wait, does the API even support date filtering?"
     Discovered 20 minutes into grooming. Requires engineer to check.
  -> "Do we have a date picker component?"
     Discovered when engineer asks during estimation.
  -> "Are we building this on the existing query infrastructure or new?"
     Full architectural discussion needed. Grooming session derailed.
  -> "What's the scope — just UI, or API changes too?"
     Unclear from the ticket. Back-of-napkin architecture session begins.

  Total time lost to grooming discovery: 45–90 minutes.
  Total time for pre-ticket system research with Kognita: 10–15 minutes.

Jira and codebase research before ticket writing

The pre-ticket checklist works when the PM has a way to answer the questions. Most PMs do not. They could ask an engineer — but that is exactly the interrupt they are trying to avoid by writing a better ticket. They could browse the codebase — but navigating a production codebase without development training is slow and error-prone. They could check documentation — but documentation is stale by the time it is relevant.

Kognita connects Jira work-in-progress context to a live semantic index of the codebase. For a PM writing a ticket, this means asking "does a date-range filtering capability exist anywhere in the codebase?" and getting a specific, grounded answer — before the ticket is written, before grooming starts, before any engineer's time is spent establishing what the PM could have learned in ten minutes.

The Jira integration is what makes the cross-cutting questions answerable. "Is there any open ticket that touches OrderQueryService?" draws on both the Jira work-in-progress data and the codebase's service map to surface conflicts that would otherwise stay invisible until grooming. "What does the current checkout flow look like, and are there any active epics that are modifying it?" takes two minutes to answer and prevents the assumption ticket failure mode entirely.

Nothing runs on the PM's laptop. No repo access is required. The PM asks in plain language. The answer comes from the actual indexed codebase — current, specific, and grounded in what the system actually does today, not what the last architecture diagram showed.

Same feature — written without vs. with system context
Same feature — unspecified ticket vs. system-grounded ticket:

  Unspecified ticket:
  Title: "User can filter order history by date range"
  Description: "Allow users to filter their order history by selecting a date range.
    This will help users find past orders more easily."
  Acceptance criteria: "Date range filter works correctly."
  Story points: 3 (PM estimate)

  After grooming (two days before sprint start):
  -> OrderService query endpoint needs new date range parameters
  -> GET /orders API contract change required
  -> New database index on orders.created_at (already present — existing admin query uses it)
  -> DateRangePicker component already exists in /components/ui
  -> API response pagination logic needs updating for filtered results
  -> Frontend OrderHistory component needs state for filter params
  Story points after discovery: 8

  System-grounded ticket (written after 10 minutes of pre-ticket research):
  Title: "Add date range filtering to user order history"
  Current behavior: GET /orders returns all user orders, paginated, no date filter.
  New behavior: GET /orders accepts optional date_from and date_to parameters.
    OrderHistory UI shows DateRangePicker. Results update on filter change.
  Services involved: OrderService, OrderQueryService.
  API contract change: yes — new optional query params on GET /orders.
  Existing component: DateRangePicker (/components/ui/DateRangePicker.tsx) — reuse.
  Database: orders.created_at index already exists per admin export query.
  Out of scope: export of filtered results, saved filters, mobile layout changes.
  Story points: 8 (accurate from the start — no mid-grooming discovery needed)

The system-grounded ticket in the second example is not longer or more bureaucratic. It is more specific. The specificity comes entirely from knowing, before writing, what the system currently does and what exists that can be reused. That knowledge took ten minutes to gather. It saved forty-five minutes in grooming and prevented a mid-sprint scope surprise.

Final take

The best Jira tickets are written by PMs who understand the current system state. That does not require learning to code. It requires access to accurate system answers before writing — not during grooming, not mid-sprint, before the ticket is committed.

Estimation mismatches are not a communication problem between PMs and engineers. They are an information access problem. The information engineers need to estimate accurately exists in the codebase. The PM does not have access to it. The grooming session becomes a discovery session because that is the only time the information gets surfaced. Move the discovery before the ticket, and the estimate in planning reflects the actual scope of the work — not the scope the PM imagined and not the scope engineering discovers on day two.