DEV Community

insidewhy
insidewhy

Posted on • Edited on

Suggested Jira setup for software engineering projects

I've been refining the way I deal with software development workflows for more than 20 years now. I've worked in different positions of seniority, with team/organisation sizes ranging from minuscule to huge, and at various stages of the software development life cycle. This article represents the current state of evolution of the Jira configuration I use.

Team-managed vs company-managed projects

I advise choosing company-managed, team-managed projects offer much less customisability, in particular the scrum/kanban board layout is extremely limited and cannot be customised to the degree shown in the configuration suggested by this article.

Custom fields

The following fields are not part of Jira by default but can be useful for tracking important information. These fields don't create any additional maintenance burden as they are updated automatically by the workflow suggestion shown later in the article.

  • Developer: The Jira user associated with the person who developed the ticket.
  • Tester: The Jira user associated with the person who tested the ticket.
  • Start Time: The date and time the ticket started being actively worked on (post refinement).
  • Development Complete Time: The date and time the ticket was transitioned from In Review to Ready for QA or Done.
  • End Time: The date and time the ticket was closed. There is already the resolution field which includes the resolutiondate component, but for some reason Jira does not show the resolution date anywhere in the UI and it can be very useful to see.

Workflow

Image description

Statuses

Draft

The initial state of any ticket to show it hasn't been refined. After a refinement session:

  • Discard or Mark as duplicate sets the status to Done with a corresponding Resolution.
  • Finish refinement sets the status to To do.

To do

The ticket is ready to work on unless there is are tickets related to it with an is blocked by association that are not yet Done. Generally issues with a status of To do won't have an Assignee but a person can set this if they want to claim the ticket to show that they would like to be the one to develop it in the future.

  • Redraft reverts the status to Draft in case something was missed or life changed.
  • Start updates the status to In Progress and:
    • Assign the ticket to the user who performed the transition.
    • Set the Developer custom field to the assignee.
    • Set the Start time custom field to the current date and time.

In Progress

  • Pause transitions the ticket back to Todo.
  • Request review transitions the ticket to In Review.

In Review

  • Merge code and skip testing can be useful for setting a ticket to Done when it doesn't require any testing. This is usually not a good idea but an escape hatch when life doesn't make sense. This transition will:
    • Perform all the actions that the Pass testing transition does
    • Sets the Tester to the Developer field to indicate that the developer tested their own ticket (which is generally bad practice but can be useful in limited circumstances).
    • Set the End Time and the Development Complete Time custom fields to the current date and time.
  • Merge code sets the status to Ready for review and will:
    • Clear the Assignee field. The Developer custom field will still be there for use later.
    • Set the Development Complete Time custom field to the current date and time.

Ready for QA

The ticket is ready for QA and won't have an Assignee, but a person may set the Assignee to claim the ticket to show that they would like to be the one to test the ticket in the future.

  • Start testing updates the status to In testing and:
    • Set the Assignee to the user who performed the transition.
    • Set the Tester custom field to the assignee.
  • Request change sets the status back to To do in case something was noticed before testing started, when this happens a comment should be left in the ticket indicating what should change. This will:
    • Assign the issue to the user in the Developer field.

In testing

The ticket is actively being tested.

  • Request change sets the status back to To do, if this is done the tester should leave a comment in the ticket to say what bugs were found or what changes should be made.
  • Pass testing updates the status to Done with a Completed resolution and:
    • Updates the Assignee field so it's the same as the Developer custom field. It's useful to quickly associated a closed ticket with the person who developed it and the Tester custom field is still there for deep diving.
    • Sets the End time custom field to the current date and time.

Done

The ticket is most likely done with forever but may be reopened in case something comes up at a later date. The Reopen transition will:

  • Clear the Assignee.
  • Clear the End time custom field.
  • Clear the Development Complete Time custom field.
  • Clear the Resolution which also has the effect of clearing the resolutiondate. Resetting the resolutiondate is particularly useful for ordering kanban/scrum board columns other than Done sensibly, how to do this will be shown later.

Automations

Jira automations can be used to prevent naughty things:

Do not assign QA issues to developer

Image description

A developer shouldn't test their own code, any attempts to assign a ticket in Ready for QA or In testing statuses to the user in the Developer custom field will revert the assignation.

Prevent Developer transitioning issue to "In Test"

Image description

A person shouldn't transition an issue to In testing when they were the one who developed it, any attempts to do this will be reverted. Here the Edit issue fields advanced option has the following content:

{
  "fields": {
    "Tester": null
  }
}
Enter fullscreen mode Exit fullscreen mode

The board

The following filter creates a nice order for the kanban/scrum board:

project = YOURPROJECT
and (
  status != Done
  or resolution not in (Duplicate, "Cannot Reproduce", "Won't Do")
)
order by resolutiondate desc, fixVersion asc, priority desc
Enter fullscreen mode Exit fullscreen mode
  • Don't show issues which were discarded.
  • Order the tickets in the Done column by the resolution date in descending order (i.e. most recently resolved tickets at the top). resolutiondate won't be set for tickets without a status of Done (as enforced by the workflow) so this order by clause won't have any effect for tickets in other columns.
  • For columns other than Done tickets will be shown in order of their fixVersion (i.e. tickets for the upcoming release will be shown at the top of the board), and within each release, tickets will be ordered by priority.

Releases

Assigning tickets to releases is a great way to keep track of progress of a particular release and for ordering the scrum/kanban board. Note that the releases list should be in descending date order (i.e. the most recent release should be at the top of the list and the oldest release should be at the bottom of the list). Releases can be dragged and dropped to reorder the list.

Filters

The following filters can be useful:

Open issues sorted by release then priority

project = MYPROJECT
and type != Epic and status != Done
order by fixVersion ASC, priority desc
Enter fullscreen mode Exit fullscreen mode

Closed issues ordered by resolution date

project = MYPROJECT
and resolution not in (Duplicate, "Cannot Reproduce", "Won't Do")
order by resolutiondate
Enter fullscreen mode Exit fullscreen mode

Issues in progress

project = MYPROJCT
and type != Epic
and status in ("In progress",  "In review", "In testing")
order by fixVersion ASC, priority desc
Enter fullscreen mode Exit fullscreen mode

Issues for next release

project = MYPROJECT
and fixVersion = earliestUnreleasedVersion()
and (
  status != Done
  or resolution not in (Duplicate, "Cannot Reproduce", "Won't Do")
)
order by resolutiondate desc, fixVersion asc, priority desc
Enter fullscreen mode Exit fullscreen mode

Issues to be tested

project = MYPROJECT
and type != Epic and status = "Ready for QA"
order by fixVersion ASC, priority desc
Enter fullscreen mode Exit fullscreen mode

Github

If the project shortcode in Jira is PJ then issue identifiers will look like PJ-1, PJ-42 etc. When creating a it's useful to name the branch after the Jira ticket with an optional prefix to show the type of work e.g.

  • fix/PJ-1: A fix relating to the issue PJ-1.
  • chore/PJ-2: A task relating to the issue PJ-2.
  • feature/PJ-3: A feature/story relating to the issue PJ-3.
  • PJ-4: If you don't care to mark the task type in the branch name.

Assuming this convention has been used (or that the PR title begins with PJ-42:) a github action can be used to:

  • Set the pull request title to the Jira ticket title.
  • Add a text block to the pull request description with a link to the Jira ticket (this block can be updated when the Jira content changes as updating text blocks is a supported feature of insidewhy/actions-body-fields).

To do this the following file can be created within the github repository at a file location such as .github/workflows/jira-linker.yml:

name: jira-linker

on:
  pull_request:
    types:
      - opened
      - reopened

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

jobs:
  action-jira-linker:
    runs-on: ubuntu-latest

    permissions:
      pull-requests: write
      issues: write

    steps:
      - name: determine associated issue id
        id: get-ticket-id
        run: |
          ticket_match='grep -Eqx [A-Z]+-[0-9]+'

          # first try to get the ticket id from the title
          title="${{github.event.pull_request.title}}"
          ticket_id=${title%%:*}
          if ! echo $ticket_id | $ticket_match; then
            # otherwise try to get the ticket id from the branch
            branch=${{ github.head_ref || github.ref_name }}
            ticket_id=${branch#*/}
            ticket_id=${ticket_id%%_*}
            if ! echo $ticket_id | $ticket_match; then
              exit 0
            fi
          fi
          echo "ticket-id=$ticket_id" >> $GITHUB_OUTPUT

      - uses: insidewhy/action-get-jira-issue@v1
        id: jira
        if: steps.get-ticket-id.outputs.ticket-id
        with:
          user: ${{ secrets.JIRA_USER }}
          token: ${{ secrets.JIRA_TOKEN }}
          base-url: ${{ secrets.JIRA_BASE_URL }}
          ticket-id: ${{ steps.get-ticket-id.outputs.ticket-id }}

      - uses: insidewhy/action-body-fields@v1
        if: steps.get-ticket-id.outputs.ticket-id
        with:
          prepend: true
          title: '${{ steps.get-ticket-id.outputs.ticket-id }}: ${{ steps.jira.outputs.summary }}'
          header: '## Status'
          fields: |
            Jira: [${{ steps.get-ticket-id.outputs.ticket-id }}: ${{ steps.jira.outputs.summary }}](${{ steps.jira.outputs.link }})
Enter fullscreen mode Exit fullscreen mode

For this to work a Jira token must be created, then information about this token must be set via github secrets:

  • JIRA_USER: The user that created the Jira token.
  • JIRA_TOKEN: The token itself.
  • JIRA_BASE_URL: The URL of the Jira instance e.g. https://my-kitten-project.atlassian.net

Top comments (0)