NOTE: In this moment I have created a new PLUGIN TO DO THIS.
At many teams use Kanban to organise their project work. JIRA together with theJIRA Agile plugin (formerly known as Greenhopper) allows you to configure and maintain an electronic Kanban board and even define WIP limits per column. However, if you’d like to have multi-column WIP limits (like a joint WIP limit for “DEV analysis”, “DEV ongoing”, “DEV in review” and “DEV done”) you’re screwed. This tutorial will show you how to overcome this limitation with the help of a little user script …
When we introduced Kanban to our team we started off with a haptic wall board. This board gave us a lot of flexibility and opportunity in order to visualize our work in progress. If tickets got blocked we used to put red stickies on them. We could draw lines for feature branches and put notes on tickets. (Legendary as well, our christmas decoration of the board with magnetic bells, stockings, candy and xmas presents.) At least once a day we gathered in front of the board and discussed the state of affairs and multiple times a day someone would go to the board and move a ticket. Especially when a ticket was moved to “QA done” a sudden “Yeah!” or applause went through the room as everybody knew that yet another cool thing (or bugfix) could be released to the public.However, a single haptic board has its downsides: People working from remote, like our product owner on a roadshow or devs in home office, basically had no clue what’s going on. Transparency into the work in progress — one of the essential things in Kanban — was basically lost for everybody who left the room. So, we mirrored our tickets from the board in JIRA as well. Over time, this kind of double bookkeeping got really annoying and we started to explore the capabilities of JIRA’s Greenhopper (or JIRA Agile as it is called today) plugin to switch to an electronic board entirely. While we managed to transfer most of the features of our haptic board to its electronic counterpart, we struggled with our multi-column WIP limits as JIRA wouldn’t support them natively.As DEV ongoing and QA ongoing didn’t feel fine grained enough, we had split our DEV column into DEV analysis, DEV ongoing, DEV in review (for mandatory code reviews) and DEV done and enforced a single joint WIP limit on all DEV columns. (Like if the DEV WIP limit was 4 this meant we didn’t care where in DEV the tickets were at, but the devs should not work on more than 4 tickets at the same time.) In JIRA Agile you can only set a per column WIP limit and spreading the joint WIP limit among all DEV columns doesn’t make sense. But not setting the WIP limit in JIRA means you’re losing the emergency colors if the WIP limit is violated. What now?User scripts to the rescue!With browser extensions like Tampermonkey (for Chrome) and Greasemonkey (for Firefox) it’s actually quite easy to customize web sites according to your preferences and needs by tweaking them with the help of some Javascript. So we tried to train JIRA the multi-column WIP limits on our own and came up with the following script:

The script scans your JIRA Agile Kanban board, calculates the combined WIP limits for all “column groups” (columns with the same start of the column name, like DEV in “DEV analysis” and “DEV ongoing” or QA in “QA ongoing” and “QA done”) and applies a yellow background to all columns of a group where the joint WIP limit is already reached and a red background to all columns of a group where the joint WIP limit is violated. The result will look similar to the example screenshot below (where the WIP limit is violated in the QA columns).

// ==UserScript==
// @name Jira Rapid Board combined limits
// @namespace Jira
// @include https://jira.****.com/secure/RapidBoard.jspa*
// @version 1
// @grant none
// ==/UserScript==

(function jiraRapidBoardCombinedLimits($) {

function refreshLimits() {
var groups = {};
var classesForColumns = {};
var $header = $('ul#ghx-column-headers');
console.log("find headers");
$header.find('li.ghx-column').each(function () {
min = 0;
var $column = $(this);
var groupKey = $column.children('h2').text().split(/ /)[0];
var min = parseInt($('.ghx-limits .ghx-busted-min', $column).text().replace("Min ", ""));
var max = parseInt($('.ghx-limits .ghx-busted-max', $column).text().replace("Max ", ""));
var current = parseInt($('.ghx-qty', $column).text());

if (!groups[groupKey]) {
groups[groupKey] = {
$columns: $(),
current: 0

var group = groups[groupKey];
group.$columns = group.$columns.add($column);
if (min) group.min = group.min ? group.min + min : min;
if (max) group.max = group.max ? group.max + max : max;
group.current = group.current + current;

for (var groupKey in groups) {
var group = groups[groupKey];

console.log("Limit group " + groupKey, group);

var $limit = $('

<div class="ghx-x-limit-combined"></div>

if (group.current && group.max) {
$limit.text('Combined: ' + group.current + ' of ' + group.max);

var addClass = null;
if (group.current == group.max) addClass = 'ghx-x-limit-warning';
if (group.current > group.max) addClass = 'ghx-x-limit-fail';

group.$columns.each(function () {
classesForColumns[$(this).data().id] = addClass;


console.log("Classes for columns: ", classesForColumns);
$('#ghx-pool .ghx-swimlane .ghx-column').each(function () {
var $cell = $(this);
group.$columns.removeClass("ghx-x-limit-warning ghx-x-limit-fail");

$(function () {
var css = ".ghx-column.ghx-x-limit-warning { background-color: yellow; } " +
".ghx-column.ghx-x-limit-fail { background-color: #ff6666; } " +
".ghx-x-limit-combined { position: absolute; bottom: 0; font-size: 11px; color: #707070 }";




var handlerActive = false;
$("#ghx-pool").live("DOMNodeInserted", function (event) {
var $this = $(;
console.log("jiraRapidBoardCombinedLimits: Caught " + event.type, $this);
if ($this.closest('#ghx-column-header-group').length > 0 && !handlerActive) {
handlerActive = true;
handlerActive = false;



Posted by:.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s