A long time ago...
About 7 years ago, I was 8 years into my 13 year tenure at Atlassian and helped build their Jira integration for Visual Studio Code.
It worked, and we even streamlined a few things, but it was a bit messy and suffered from trying to be too many things in a single extension.
After leaving Atlassian, I kept using the extension and was getting increasingly frustrated with using it for everyday tasks.
All the while, I was constantly thinking about the potential of a focused Jira extension.
So about a year ago, I decided to rebuild it from scratch.
Not to add more features, but to make the Jira extension I actually wanted to use every day.
What I ended up building
The result is Code with Jira.
Nothing revolutionary, just Jira when I need it and staying out of the way when I don't.
I set out on this journey to handcraft a piece of software to the best of my ability and to create an extension I will use every day.
I didn't give myself a time limit.
I just started typing and didn't stop until I felt I had something useful and satisfying to work with that's not trying to replicate all of Jira.
It just focuses on the things that keep me in the editor as much as possible.
Why Jira integrations are harder than they look
The hardest part of building a Jira integration is dealing with the flexibility that made Jira popular in the first place.
Every Jira instance is different.
Custom fields everywhere, inconsistent workflows, permissions that don’t behave the way you expect.
Even fields that feel like system fields are often indistinguishable from third party custom fields.
So you’re not building against one system. You’re building against thousands of slightly different configurations of Jira.
On top of that, a lot of Jira’s own functionality relies on internal GraphQL APIs that aren’t available to call by third parties.
You can see them, sometimes even read their docs, and in some cases you're told to use them. But if you're outside of Atlassian, you don't actually have access.
Even simple things get complicated.
For example, adding things like versions, components, or labels to an issue in the Jira web interface lets you either select an existing value or type a new one and create it on the fly.
There’s no single API for that.
You have to find the correct APIs for the entity in question and stitch multiple calls together if you want your UI to behave the same way.
A lot of Jira works like this.
What went wrong the first time
When we built the original extension, we were essentially treated as a third-party since the extension runs outside of the Atlassian network.
This means we had to discover all of these little undocumented behaviors that Jira has and try to recreate them all while learning how to build VS Code extensions.
VS Code extensions were still relatively new, and there weren’t many examples of UI-heavy integrations.
We ended up building large React-based webviews with Material UI components and making Jira API calls from the UI through a websocket, back to VS Code and finally to Jira.
It worked, but it came with tradeoffs.
- the UI felt heavier than it should have
- it didn’t really match how VS Code extensions are supposed to behave
- and it was harder to maintain over time
Authentication was another major challenge.
For stand-alone apps like this, Jira requires OAuth2 with a callback.
That’s straightforward in a web app, but much trickier when you’re running inside an extension on each user's machine.
At the time, we handled this by spinning up a localhost server to receive the auth callback and managing tokens ourselves.
It worked… until it didn’t.
We rewrote the auth flow multiple times, and it was always one of the most fragile parts of the system.
To make things even more complex, we tried to build an "all-in-one" extension that integrated Jira cloud, Jira datacenter, Bitbucket pull requests and Bitbucket pipelines.
We thought we'd be able to share a lot of code and having a single codebase would make things easier.
Instead it made almost everything more complicated than it needed to be.
Over time we added features and improved things, but we never really got to a place where the experience felt seamless.
Eventually the team was reorganized and the project was handed off.
What I did differently the second time
When I wrote the extension the second time, I approached it differently.
First, I wanted to make a Jira specific extension and I chose to make it cloud only.
Next, I didn’t start with features.
I started with a simple goal: make the core workflows reliable enough that I would actually want to use it every day and sprinkle in some fun.
A few things I focused on:
Leaning into how VS Code works
Using tree views, commands, and the quick input system instead of rebuilding everything in React components, I tried to build something that felt at home within the IDE.
I still used webviews and components, but they were only to display values.
99% of input was done on the VS Code side, which was a lot cleaner and turned out to be more reusable than I thought.
Apart from the UI, VS Code had also added built in authentication APIs.
I wanted to take advantage of those APIs instead of managing all things auth related in my own code.
Instead of relying on a localhost server, I used a dedicated service for handling OAuth callbacks.
With all of this combined, I hoped to achieve a much smoother integrated experience into VS Code.
Handling Jira’s Quirks
Jira doesn’t give you a lot of metadata about view layouts or field behaviors that are "tribal knowledge", so I built a layer that adds layout hints and behavior on top of the raw API responses.
Some of this data does exist in Jira, but it's not accessible from a public API.
There were also other gaps that would force me back to my browser that I wanted to address.
Things like deleting issues, re-parenting, and properly supporting rich text, emojis, at-mentions, etc.
A lot of this may sound easy, heck even "standard" at this point, but with Jira, things aren't always as easy as they seem.
Some Extra Bits
Automatic time tracking.
When you start working on an issue, it starts a timer, and when you’re done it logs the time back to Jira.
A simple concept that turned out to be way more useful than I expected.
Comment Tags.
In the original extension, you could "tag" a comment with a keyword and it would display a link to create a Jira issue and drop the newly created key into the comment.
In the rewrite, I decided to give this feature a little more love.
I started by moving the create issue function into the code insights menu so the editor is no longer littered with create issue links. (there when you need it, out of sight when you don't).
Then I added a treeview to help find these tags and jump straight to the code they belong to.
If they have an issue key, you also get a few Jira specific functions to act on the issue right in the tree.
What I learned from doing this twice
The second time around, a few things became really obvious.
First off, it's way better to embrace the paradigms of the IDE and use the patterns that VS Code lays out.
It's amazing to me how second nature these things become.
Trying to fully replicate Jira is a losing battle.
A lot of Jira’s behavior isn’t captured anywhere.
You figure it out by using the UI, watching network calls, and stitching things together yourself.
It's better to put this logic in the IDE side rather than trying to build UI component variants to handle allthethings.There are also things you just can’t fully recreate.
Jira uses internal APIs for a lot of its functionality, and you don’t get access to those.
You can get close, but it’s never quite the same as being inside Jira.
That being said, creative unintended use of APIs and a little hacking on atlaskit components can yield some interesting results, though not for the faint of heart.
Closing thoughts
Rewriting this reminded me how much I love writing software.
There’s a lot of talk right now about automating everything and letting tools write the code for us.
Maybe that makes sense in some places, but for me, this was a reminder that writing software is fun.
It's frustrating, it's humbling at times, and most importantly it's a creative outlet.
As humans, I think it's worth holding onto the creative processes we're being told to give away, especially the ones that bring us joy.
Top comments (0)