<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alina Chyzh</title>
    <description>The latest articles on DEV Community by Alina Chyzh (@alina_chyzh_).</description>
    <link>https://dev.to/alina_chyzh_</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3657274%2Fc6706abd-2139-4d67-b470-86cd13c731f8.png</url>
      <title>DEV Community: Alina Chyzh</title>
      <link>https://dev.to/alina_chyzh_</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alina_chyzh_"/>
    <language>en</language>
    <item>
      <title>Jira Cycle Time vs Lead Time: What They Actually Measure</title>
      <dc:creator>Alina Chyzh</dc:creator>
      <pubDate>Fri, 03 Jul 2026 07:28:23 +0000</pubDate>
      <link>https://dev.to/alina_chyzh_/jira-cycle-time-vs-lead-time-what-they-actually-measure-41ig</link>
      <guid>https://dev.to/alina_chyzh_/jira-cycle-time-vs-lead-time-what-they-actually-measure-41ig</guid>
      <description>&lt;p&gt;"Cycle time" and "lead time" get used interchangeably in Agile retrospectives. They're not the same thing and using the wrong one to diagnose a delivery problem sends you in the wrong direction.&lt;/p&gt;

&lt;p&gt;The practical difference, how to pull each from Jira, and when each one is actually worth tracking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lead time:&lt;/strong&gt; total elapsed time from when a request enters your system to when it ships. In Jira: from issue creation to resolution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cycle time:&lt;/strong&gt; time from when work actually starts to when it's done. In Jira: from the first transition to "In Progress" to "Done".&lt;/p&gt;

&lt;p&gt;Everything that happens before work starts: backlog sitting time, prioritization, sprint planning counts in lead time but not cycle time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Issue created ──────────────────────────────── Issue done
     │                    │                        │
     └──────── Lead time ─────────────────────────┘
                          │                        │
                          └──── Cycle time ────────┘
                        (work started)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why the distinction matters
&lt;/h2&gt;

&lt;p&gt;They answer different questions.&lt;/p&gt;

&lt;p&gt;Cycle time answers: how fast does your team execute once work begins? If your average is 3 days and it jumps to 7, something in your delivery process changed, PRs sitting in review longer, issues scoped too large, a recurring blocker type.&lt;/p&gt;

&lt;p&gt;Lead time answers: what's the full wait from request to delivery, from the outside? A team can have tight cycle time (fast once they start) but a 3-week backlog before anything gets picked up. Customers experience lead time, not cycle time. Promising a delivery window without knowing your lead time is promising something you can't verify.&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting these from Jira
&lt;/h2&gt;

&lt;p&gt;Neither metric is surfaced cleanly by default.&lt;/p&gt;

&lt;h3&gt;
  
  
  Control Chart (closest native option)
&lt;/h3&gt;

&lt;p&gt;Available under Reports on Kanban boards. It plots time-in-progress per issue, an approximation of cycle time if your "In Progress" column maps accurately to when work starts.&lt;/p&gt;

&lt;p&gt;The catch: it measures time in board columns, not workflow statuses. If your board doesn't map 1:1 to your actual statuses, the numbers are blurred.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jira project → Reports → Control Chart&lt;/strong&gt; (Kanban boards only).&lt;/p&gt;

&lt;h3&gt;
  
  
  JQL for lead time
&lt;/h3&gt;

&lt;p&gt;Jira doesn't expose &lt;code&gt;timeInStatus&lt;/code&gt; as a JQL field, but lead time is straightforward once you export to CSV:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MYPROJECT&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Done&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;resolved&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;resolved&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the export: &lt;code&gt;resolutiondate&lt;/code&gt; minus &lt;code&gt;created&lt;/code&gt; = lead time. You can do this in a spreadsheet in about five minutes.&lt;/p&gt;

&lt;h3&gt;
  
  
  REST API for cycle time
&lt;/h3&gt;

&lt;p&gt;Cycle time requires the status change history, which doesn't appear in the standard CSV export. You need the changelog:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/rest/api/3/issue/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;issueKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?expand=changelog`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Basic &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;btoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;changelog&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inProgressTransition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;changelog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;histories&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created&lt;/span&gt; &lt;span class="p"&gt;})))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;In Progress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doneTransition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;changelog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;histories&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created&lt;/span&gt; &lt;span class="p"&gt;})))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Done&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inProgressTransition&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;doneTransition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cycleTimeDays&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doneTransition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inProgressTransition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Cycle time: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cycleTimeDays&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; days`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing to watch: this finds the first transition to In Progress. If an issue bounced back from Done to In Progress and back again (which happens) you'll want to decide whether you want total active time or just first-to-last.&lt;/p&gt;




&lt;h2&gt;
  
  
  The number most teams ignore
&lt;/h2&gt;

&lt;p&gt;The gap between lead time and cycle time is waiting time and that's usually where the actual problem is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Average lead time:   15 days
Average cycle time:  4 days
─────────────────────────────
Waiting time:        11 days
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If 11 of your 15 days are pre-work queue time, optimizing your development process won't move the number customers see. The problem is upstream, backlog grooming, prioritization, how long issues sit before anyone picks them up.&lt;/p&gt;

&lt;p&gt;Teams that only measure cycle time tend to optimize the wrong thing. Execution looks fine on their dashboards. Meanwhile the backlog is 6 weeks deep.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to use which
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cycle time&lt;/strong&gt;: use this for internal process work. Did the new PR review policy make things faster or slower? Are bugs taking longer than features? Is there a particular engineer whose issues have much longer cycle times (sign of blocked work, not slow work)?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lead time&lt;/strong&gt;: use this for anything external. Setting delivery expectations with clients, tracking whether SLA commitments are realistic, understanding the actual experience of someone who filed a request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Both together&lt;/strong&gt;: use this when diagnosing a slowdown. Lead time up, cycle time flat: the problem is pre-development. Both up: it's in execution. Lead time flat, cycle time up: execution is slower but the backlog is moving faster, which might mean better prioritization is masking a process issue.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Jira still won't tell you
&lt;/h2&gt;

&lt;p&gt;Even with cycle time and lead time figured out, there's a gap: time per workflow status how long an issue sat specifically in In Review vs. In QA vs. Waiting for Deployment.&lt;/p&gt;

&lt;p&gt;An issue with a 6-day cycle time might have spent 1 day in development, 4 days waiting for code review, and 1 day in QA. Without the per-status breakdown, you'd target the wrong stage.&lt;/p&gt;

&lt;p&gt;Jira Cloud doesn't surface this natively. The Control Chart shows you the aggregate; the status-level view requires the changelog API (same approach as cycle time above, but aggregated per status) or a reporting tool that does the work for you. The &lt;a href="https://grandiasolutions.com/jira-time-tracking-reports/" rel="noopener noreferrer"&gt;Jira time tracking reports guide&lt;/a&gt; covers what's available natively, what needs JQL workarounds, and where the gaps are if you want a fuller picture of where time actually goes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Cycle time&lt;/th&gt;
&lt;th&gt;Lead time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Starts at&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Work begins (In Progress)&lt;/td&gt;
&lt;td&gt;Issue created&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ends at&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Issue done&lt;/td&gt;
&lt;td&gt;Issue done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Answers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Is execution fast?&lt;/td&gt;
&lt;td&gt;What does the customer wait?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Native in Jira&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Approximated (Control Chart)&lt;/td&gt;
&lt;td&gt;Not directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Internal process improvement&lt;/td&gt;
&lt;td&gt;Stakeholder commitments, SLAs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The JQL query and API snippet above cover the manual path. If you want this without writing extraction code, most reporting add-ons for Jira handle the aggregation, but knowing what the numbers actually count is still on you.&lt;/p&gt;

&lt;p&gt;One last thing worth saying: most teams I've talked to who "track cycle time" are actually tracking something closer to calendar days in the sprint, not time from first status transition. That's not cycle time. It's a rough proxy that includes weekends, holidays, and all the time the issue sat assigned but untouched. Worth auditing how your tooling actually calculates it before drawing conclusions from the numbers.&lt;/p&gt;

</description>
      <category>jira</category>
      <category>agile</category>
      <category>projectmanagement</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to Query Jira Worklog Data with JQL And Where JQL Falls Short</title>
      <dc:creator>Alina Chyzh</dc:creator>
      <pubDate>Tue, 28 Apr 2026 13:32:47 +0000</pubDate>
      <link>https://dev.to/alina_chyzh_/how-to-query-jira-worklog-data-with-jql-and-where-jql-falls-short-4dba</link>
      <guid>https://dev.to/alina_chyzh_/how-to-query-jira-worklog-data-with-jql-and-where-jql-falls-short-4dba</guid>
      <description>&lt;p&gt;Jira stores every worklog your team ever submits. Time spent, date, author, comment — it's all in there. The frustrating part is that most of this data is surprisingly hard to surface through the UI.&lt;/p&gt;

&lt;p&gt;If you've ever tried to answer "how much time did the team log last week, grouped by user?" using native Jira tools, you've probably ended up in one of these places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An Issue Navigator export that gives you issue-level time, not user-level totals&lt;/li&gt;
&lt;li&gt;A spreadsheet with &lt;code&gt;=SUMIF&lt;/code&gt; formulas pulling from a CSV dump&lt;/li&gt;
&lt;li&gt;A dashboard gadget that kind of shows what you need but not quite&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post covers what JQL can and can't do with worklog data, some queries worth having in your toolkit, and where the Jira REST API becomes a better approach than JQL for anything more complex.&lt;/p&gt;




&lt;h2&gt;
  
  
  What JQL actually lets you filter on for worklogs
&lt;/h2&gt;

&lt;p&gt;JQL has four worklog-related fields you can use in queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;worklogAuthor&lt;/span&gt;
&lt;span class="n"&gt;worklogComment&lt;/span&gt;
&lt;span class="n"&gt;worklogDate&lt;/span&gt;
&lt;span class="n"&gt;worklogDate&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nv"&gt;"YYYY-MM-DD"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No &lt;code&gt;worklogHours&lt;/code&gt;, no &lt;code&gt;totalTimeSpent&lt;/code&gt; filter, no grouping by user. JQL can find &lt;em&gt;which issues&lt;/em&gt; a person logged time on, within a date range — but it can't sum that time, filter by amount, or aggregate across users.&lt;/p&gt;

&lt;p&gt;Here are the queries I find myself writing most often:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All issues where a specific user logged time this month:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;worklogAuthor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"user@company.com"&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;worklogDate&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;startOfMonth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;All issues where anyone on the team logged time this sprint:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;worklogAuthor&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;membersOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"team-name"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;worklogDate&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;sprint&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;openSprints&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Issues logged by the current user that are still open:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;worklogAuthor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;worklogDate&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;statusCategory&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;Done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Issues with worklogs containing a specific keyword (useful for billing codes):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;worklogComment&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="nv"&gt;"billable"&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;worklogDate&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nv"&gt;"2026-04-01"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of these return issue lists, not time summaries. To get total hours from the results, you export to CSV and sum the &lt;code&gt;Time Spent&lt;/code&gt; column yourself.&lt;/p&gt;




&lt;h2&gt;
  
  
  The gap: what JQL won't tell you
&lt;/h2&gt;

&lt;p&gt;A few things you'd expect to be queryable that aren't:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You can't filter by amount of time logged.&lt;/strong&gt; There's no &lt;code&gt;worklogHours &amp;gt; 2&lt;/code&gt; equivalent in JQL. You can find issues &lt;em&gt;where&lt;/em&gt; a user logged work, but you can't filter to only issues where they logged more than a threshold.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You can't group by user.&lt;/strong&gt; JQL returns a flat list of issues. If three people logged time on the same issue, you get that issue once — not three rows, one per author.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You can't query worklogs directly.&lt;/strong&gt; JQL queries issues, not worklog entries. If a user logged two separate entries on the same issue on different days, you can't distinguish those entries via JQL — the issue just shows the total &lt;code&gt;Time Spent&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;timespent&lt;/code&gt; is queryable but limited.&lt;/strong&gt; You can write &lt;code&gt;timespent &amp;gt; 2h&lt;/code&gt; to find issues where more than 2 hours have been logged total, but this is total across all users, not per-user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;timespent&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;7200&lt;/span&gt;  &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;JQL&lt;/span&gt;
&lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MYPROJ&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;timespent&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;7200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Where the REST API does what JQL can't
&lt;/h2&gt;

&lt;p&gt;If you need per-user time summaries, the Jira REST API is the right tool. Two endpoints are worth knowing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get worklogs for a specific issue:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /rest/api/3/issue/{issueIdOrKey}/worklog
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returns all worklog entries for that issue, including &lt;code&gt;author&lt;/code&gt;, &lt;code&gt;timeSpentSeconds&lt;/code&gt;, &lt;code&gt;started&lt;/code&gt; date, and &lt;code&gt;comment&lt;/code&gt;. This is the granular data JQL never surfaces.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Search issues and get their worklogs:&lt;/strong&gt;&lt;br&gt;
The practical pattern is two-step: use JQL via the search endpoint to get a list of issues, then fetch worklogs per issue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Step 1: get issues with worklogs in the date range&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/rest/api/3/search?jql=worklogAuthor="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userEmail&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" AND worklogDate&amp;gt;="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;startDate&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&amp;amp;fields=summary,timespent`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Basic &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;btoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;issues&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;searchResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Step 2: get worklogs for each issue&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worklogs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;issue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/rest/api/3/issue/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;issue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/worklog`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Basic &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;btoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Step 3: filter by author and date, sum time&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userWorklogs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;worklogs&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;worklogs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
    &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emailAddress&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;userEmail&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;started&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;startDate&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalSeconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;userWorklogs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeSpentSeconds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalHours&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;totalSeconds&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you what JQL can't: actual per-user time totals, filterable by date range, groupable however you need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One gotcha:&lt;/strong&gt; the &lt;code&gt;/worklog&lt;/code&gt; endpoint paginates at 20 entries by default. For issues with a lot of logged time, you'll need to handle the &lt;code&gt;startAt&lt;/code&gt; parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAllWorklogs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;issueKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;allWorklogs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;startAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/rest/api/3/issue/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;issueKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/worklog?startAt=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;startAt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;maxResults=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;maxResults&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;allWorklogs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;allWorklogs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;worklogs&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;startAt&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;maxResults&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;startAt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;maxResults&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;allWorklogs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The updated worklogs endpoint (often overlooked)
&lt;/h2&gt;

&lt;p&gt;There's a less-known endpoint that's useful for syncing or auditing worklog data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /rest/api/3/worklog/updated?since={timestamp}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returns all worklog IDs updated since a given Unix timestamp in milliseconds. Useful if you're building a sync process and don't want to re-fetch everything — just poll for updates since your last sync.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;since&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// last 7 days&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/rest/api/3/worklog/updated?since=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;since&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextPage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// values = array of { worklogId, updatedTime }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then fetch the actual worklog details in bulk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /rest/api/3/worklog/list
body: { "ids": [worklogId1, worklogId2, ...] }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is significantly more efficient than querying per-issue if you're maintaining any kind of time tracking integration.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to use what
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Find issues where someone logged time&lt;/td&gt;
&lt;td&gt;JQL with &lt;code&gt;worklogAuthor&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filter open issues by who's working on them&lt;/td&gt;
&lt;td&gt;JQL with &lt;code&gt;worklogAuthor&lt;/code&gt; + status filter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get total hours per user&lt;/td&gt;
&lt;td&gt;REST API &lt;code&gt;/issue/{key}/worklog&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build a timesheet view&lt;/td&gt;
&lt;td&gt;REST API, aggregate by &lt;code&gt;author&lt;/code&gt; + &lt;code&gt;started&lt;/code&gt; date&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sync worklog data incrementally&lt;/td&gt;
&lt;td&gt;REST API &lt;code&gt;/worklog/updated&lt;/code&gt; + &lt;code&gt;/worklog/list&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show time trends across sprints&lt;/td&gt;
&lt;td&gt;Reporting add-on (JQL and API both get complex fast)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For the last row — if you need ongoing time reporting inside Jira without building and maintaining your own API integration, &lt;a href="https://grandiasolutions.com/report-hub/" rel="noopener noreferrer"&gt;Report Hub&lt;/a&gt; handles the aggregation layer. It's a Forge app so the data stays inside Atlassian's infrastructure, which matters if you're in a regulated environment. The &lt;a href="https://grandiasolutions.com/jira-time-tracking-reports/" rel="noopener noreferrer"&gt;full breakdown of what's possible with Jira time tracking reports&lt;/a&gt; — including the JQL workarounds and their limits — is on the Grandia Solutions blog if you want more depth on the reporting side.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tags
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;#jira&lt;/code&gt; &lt;code&gt;#atlassian&lt;/code&gt; &lt;code&gt;#javascript&lt;/code&gt; &lt;code&gt;#productivity&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally written by the team at &lt;a href="https://grandiasolutions.com" rel="noopener noreferrer"&gt;Grandia Solutions&lt;/a&gt; — Atlassian Solution Partner specialising in Jira administration and Marketplace app development.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>jira</category>
      <category>jql</category>
    </item>
    <item>
      <title>Why Developers Hate Jira and How to Make It Dev-Friendly Again</title>
      <dc:creator>Alina Chyzh</dc:creator>
      <pubDate>Thu, 11 Dec 2025 14:21:03 +0000</pubDate>
      <link>https://dev.to/alina_chyzh_/why-developers-hate-jira-and-how-to-make-it-dev-friendly-again-67j</link>
      <guid>https://dev.to/alina_chyzh_/why-developers-hate-jira-and-how-to-make-it-dev-friendly-again-67j</guid>
      <description>&lt;p&gt;Most development teams have a love–hate relationship with Jira. On paper, it's a powerful work-management platform designed to bring structure and transparency. In reality, many developers experience it as a friction engine: slow interfaces, complicated workflows, noisy dashboards, too many fields, and processes that grow heavier with every sprint.&lt;/p&gt;

&lt;p&gt;Developers often joke that updating a task in Jira takes longer than fixing the bug inside it and for many teams, that’s uncomfortably close to the truth. The is how teams configure and use it.&lt;/p&gt;

&lt;p&gt;This article explains the real reasons developers become frustrated with Jira, what damage poorly designed workflows cause, and how to make Jira lighter, faster, and more supportive of engineering work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Real Reasons Developers Hate Jira&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;It becomes a management tool, not a developer tool&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Teams often turn Jira into a system for tracking, reporting, and controlling people rather than enabling engineering flow. Every status change becomes a process step. Every update becomes an audit entry and every sprint feels like a reporting exercise instead of collaboration. When Jira starts representing bureaucracy instead of value, developers disconnect from it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Too many fields, too many statuses&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A simple quick update often turns into a miniature epic: mandatory fields, dropdown lists with 40 options, tabs full of unused data. The more clicks a task requires, the less likely developers are to keep it updated. Over-engineered workflows slow teams down and create frustration long before any development work even starts.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Slow performance breaks deep-work cycles&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Jira can be sluggish, especially in large organizations. If every transition takes 3–5 seconds, and those transitions happen dozens of times per day, developers lose focus. Momentum disappears. Instead of supporting flow, Jira interrupts it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Reporting chaos undermines trust&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Teams accumulate dashboards the way projects accumulate technical debt. Different charts show the same metric with slightly different filters; multiple dashboards contradict one another, leaders reference graphs developers have never seen. Instead of clarity, reporting generates confusion and eventually nobody trusts the numbers.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Time tracking becomes a punishment, not a tool&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Time tracking isn't bad by itself. But when it becomes a micromanagement mechanism (log every minute,hours = productivity), morale drops fast. Developers stop updating tasks accurately because the system feels punitive, not helpful.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Too many boards and fragmented visibility&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A team might have a main board, a triage board, a PM board, a QA board, a roadmap board, and three experimental boards from old initiatives. Tasks get duplicated or lost. Developers stop knowing where work truly lives.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Notifications: either overwhelming or nonexistent&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Some developers receive 40+ notifications per day. Others receive none when something critical changes. Poor notification hygiene reinforces the perception that Jira adds noise rather than value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Poor Jira Experience Does to Teams&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A noisy Jira environment does more than annoy peopl, it slows teams down.&lt;br&gt;
Developers lose focus because they constantly switch mental contexts, cycle time increases because flow is interrupted, engineers avoid updating tasks because the system feels heavy, managers lose visibility because data becomes outdated or unreliable.&lt;/p&gt;

&lt;p&gt;Over time, frustration with the tool becomes frustration with the process itself and that leads to disengagement, friction, and burnout. When developers stop believing in the process, velocity suffers regardless of how many metrics you track.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jira Can Be Developer-Friendly&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Simplify workflows aggressively&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most teams only need a small set of core statuses: To Do → In Progress → In Review → Done. Anything beyond that should be justified, not assumed. Simplicity restores speed and reduces errors.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Reduce fields to the essentials&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If a field doesn’t influence planning, reporting, or workflow logic, remove it or make it optional. The fewer cognitive steps a task requires, the more consistently developers will keep it updated.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Automate the boring parts&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Jira Automation can eliminate dozens of manual actions: moving tasks when a PR is created, assigning reviewers, closing resolved issues, surfacing stale items. When Jira does the repetitive work, developers stay focused on engineering, not administration.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Build developer-centric dashboards, not management dashboards&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Useful metrics for developers include cycle time, review latency, throughput, and WIP. Unhelpful metrics include tasks completed per developer or one-dimensional time logs.&lt;/p&gt;

&lt;p&gt;Teams often introduce tools like &lt;a href="https://grandiasolutions.com/" rel="noopener noreferrer"&gt;Report Hub&lt;/a&gt; when they need clean, consolidated, developer-friendly dashboards without maintaining multiple native Jira gadgets. Such tools reduce reporting clutter instead of adding to it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One team, one board&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If everything is important, nothing is. A single, authoritative board per team reduces duplication and confusion while keeping sprint work visible and manageable.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Integrate Jira into the developer workflow&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A great Jira setup lets developers interact with tasks without leaving their environment.&lt;br&gt;
VSCode plugins, Git integrations, Slack updates and keyboard shortcuts reduce friction dramatically. The best Jira experience is one that minimizes the need to open Jira at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What a Healthy Jira Setup Looks Like&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here’s a typical “before vs after” transformation:&lt;/p&gt;

&lt;p&gt;Before&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;11 workflow statuses&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;20 required fields&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;multiple boards per team&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dashboards full of conflicting metrics&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;long load times&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;developers avoiding task updates&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;4–5 clear statuses&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;only critical fields&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;one board per team&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a small set of meaningful metrics&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;automation supporting transitions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;developers updating tasks effortlessly&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the right balance of simplicity and automation Jira stops being a burden and becomes a natural part of the development lifecycle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jira Isn’t the Problem — Overengineering Is&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Jira becomes painful when teams over-customize it, misunderstand metrics, or treat it as a surveillance tool rather than a collaboration platform. But when teams streamline workflows, reduce noise, automate tedious steps, and design dashboards around developer needs, Jira becomes fast, intuitive, and reliable.&lt;/p&gt;

&lt;p&gt;Fix the system and Jira becomes one of the most powerful allies an engineering team can have.&lt;/p&gt;

</description>
      <category>jira</category>
      <category>development</category>
      <category>productivity</category>
      <category>startup</category>
    </item>
  </channel>
</rss>
