Ivan Habor

I Tried Atlassian's Teamwork Graph

10 min readUpdated September 1, 2026

A ticket landed this morning with three sentences of description: a financial ledger was writing one of its rows under the wrong account. No design doc, no linked context, just a hunch from whoever filed it. Most of my day, on tickets like this, isn’t spent writing the fix. It’s spent figuring out whether the hunch is right, and whether I’m the first person to notice.

This time I decided to run the whole investigation through Atlassian’s Teamwork Graph, which is sold as an enterprise context graph: it maps people, content and relationships across Jira, Confluence, code hosting and the rest. I reached it two ways. One is twg, a command line interface built for coding agents, which fronts semantic search, document search, and grep across every repository my org owns. The other is the part I actually wanted to test, a literal graph-traversal call: hand it one item, get back the things connected to it. I’d been half-trusting all of it for weeks. I wanted an honest answer, on real work, to the question the product’s own name asks. Is this actually a graph, or is that just what search tools call themselves now?

What I understood going in

What I’d picked up from a few weeks of light use was that Teamwork Graph gives me two very different ways to ask the same question. What else connects to this ticket?

One is search: semantic search across tickets and wikis (rovo search), full-text search across our internal docs (docs search), and grep across every repository in the org (search-code). The other is the traversal call, getTeamworkGraphContext, which takes one ticket, walks the relationships somebody actually declared (links, mentions, edits), and hands back a small graph of what is connected to it.

Search finds things by meaning. Traversal finds things by citation. I’d been treating them as the same capability wearing different clothes. Today is when I stopped.

The ledger row filed under the wrong account

Before I touched the code, two things needed answering. Was this a real, agreed-on bug, or was I about to misread a system I only partly understood? And was anyone quietly depending on the current, wrong behavior?

I ran rovo search for the ticket’s own language, expecting a handful of loosely related issues. What came back instead were two investigation pages, written for two other tickets, that independently described this exact defect while scoping unrelated work. Neither page linked back to my ticket. Nobody had connected them. The connection only existed in what the pages said, in words, not in how anything was filed. I hadn’t found corroborating evidence. I’d found two colleagues who’d already done half my investigation for me, months apart, without knowing about each other.

my ticket ticket B ticket C doc doc open questions
one rovo search call — five connected items, none linked from my ticket

Then, mostly out of stubbornness, I asked the traversal call the same question about the same ticket. Not search this time. The feature the product is named for.

my ticket editor viewer reporter ticket B? ticket C?
the same ticket, getTeamworkGraphContext — three people, no documents

Three edges, all people: who’d edited the ticket, who’d viewed it, who’d reported it. Nothing pointing at either document, because there was never a link to walk. The graph tool wasn’t wrong. It traversed exactly the graph that existed. That graph just wasn’t the one I needed.

How the ledger bug ended

The fix came to six lines: read the party’s own id when it’s there, and fall back to the id that was already being written when it isn’t, logging loudly when that happens so a missing upstream field can’t hide again. A fallback, not a migration.

The historical rows already filed under the wrong id I left alone, on purpose. Those two investigation pages were exactly what let me do that with a clear conscience. Whether to re-file history turned out to be an open question someone else already owned, not something to settle quietly inside a bugfix.

Not a clever change. A boring one, arrived at faster and with more confidence than usual.

The field that already had the wrong name

A second ticket, later the same day, asked for a currency conversion in a place it currently wasn’t happening. No design doc again. The risk with a ticket like that isn’t the code, it’s scoping it wrong before you’ve written a line.

A keyword search across our internal docs (docs search, not the graph) turned up a finance-authored reference page nobody had linked from the ticket. It defined a field with a name almost identical to the one I was about to introduce, computed differently, and the page already carried a note from finance flagging that the two numbers disagreed. I hadn’t just avoided a naming collision three weeks from now in review. I’d learned the collision already had a paper trail I wasn’t looped into. Renaming around it became step one of my plan instead of a surprise.

A search-code pass across every repo in the org, not just the one I was sitting in, confirmed the field genuinely hadn’t landed anywhere yet, which is what let me say the collision was still avoidable rather than something to untangle after the fact.

Whether that finance page counts as something “the graph” found is exactly the question this whole day kept asking me. Nothing pointed at it. No one had declared a relationship for anything to traverse. Meaning found it; citation never would have.

A third ticket the same day taught me something smaller. Our main issue-tracker integration lost its authentication mid-session, through no fault of twg, and I’d have expected to sit and wait. Instead twg turned out to also expose a thin authenticated REST client to the same tracker, so I posted my update through that. Same payload, same destination, a different pipe. Not a research win, but a useful thing to learn about a tool on the day its neighbour goes down. It is an integration layer as much as a research one, and I hadn’t priced that in going into today.

The habit I didn’t plan to form

By the third ticket I noticed I’d fallen into a rhythm without deciding to. Ask the search first, in plain language. Then ask the graph the exact same thing, about the exact same item, before moving on. Put the two answers next to each other. Believe whichever one told me something I didn’t already know.

Ask the search. Ask the graph the same thing. Believe whichever one surprised me.

I hadn’t set out to design a test. It just turned out to be the only honest way I could tell, on my own tickets, what the word “graph” in the product’s name was buying me. The one time I ran both against the same ticket, the gap was not close: search came back with five things I needed, and traversal came back with three names, correct and useless for the question I had. Everything else that helped me that day, the finance page included, was found by meaning too.

I’ll almost certainly do the same two-question thing to the next tool that arrives with a confident name. Today it was the difference between believing a product and knowing what it does.

Where it bit me

I lost a stretch of the afternoon guessing that confluence page get existed. It doesn’t. The command is confluence content get, and only --help told me so. I lost more time to search-code, which quietly parsed my query as a subcommand name because I’d left off the verb, then failed with an “unknown command” that pointed at nothing. And I’m glad I ran the traversal call once on a ticket I already understood, before I leaned on it for one I didn’t. It is the only reason I knew what “connected” meant to this tool before it mattered.

Small things, and every one of them a one-time cost.

Where I'd reach for it again tomorrow:

finding context nobody had linked
the literal graph traversal
searching code across every repo at once
how it felt to drive
as a spare pipe when the main one died

Would I use it again

Yes. Teamwork Graph stays in my toolbox, routed through search first. The most graph-shaped result I got all day came out of a search box, and the feature actually named “graph” returned three names. That’s not a small caveat for a product with that name. But it’s also the most useful thing I learned today, and I only learned it by asking the same question twice.