{"slug": "how-teppe-for-jira-works-under-the-hood", "title": "How Teppe for Jira works under the hood", "summary": "A developer built Teppe for Jira, a tool that renders tens of thousands of Jira issues in under 10ms by compiling a Rust layout core to WebAssembly and capping visible hierarchy levels at MAX_DEPTH + 2, so a single view renders at most 584 cells. The tool's AI grouping layer runs deterministic logic first — using existing Jira parent/child links, epics, components and labels — and only sends overflow buckets to an LLM in batches, costing about $0.24 per 1,000 LLM-resolved issues. Layout time measured ~1.4ms for a 1,000-issue project and ~1.8ms for a 10,000-issue one.", "body_md": "A few weeks ago I promised a technical write-up. Here it is.\n\nThe two questions I get most are: *how do you render tens of thousands of Jira issues in under 10ms?* And *how does the AI figure out how to group things?* They're related, and the answer to both starts with the same principle — do less.\n\nJira backlogs are large. A mature project might have 50,000 issues. Naively rendering 50,000 nodes in a browser canvas is slow, and no amount of JavaScript optimisation is going to change that.\n\nThe insight is that you never need to render all of them.\n\nThe Sierpiński carpet is a fractal — it's recursive by definition. At any given moment you're looking at one level of the hierarchy, with the levels above and below rendered at progressively lower fidelity. We cap this at `MAX_DEPTH + 2` levels visible at once. The result: no matter how large your backlog, a single view renders at most 584 cells. We measured layout time at ~1.4ms for a 1,000-issue project and ~1.8ms for a 10,000-issue one. It's essentially flat.\n\nThe geometry work — computing where every node goes, what size it should be, what fidelity level it gets — is pure arithmetic with tight loops. It's exactly the kind of work JavaScript is not well suited to, and exactly what Rust is.\n\nWe compile the layout core to WebAssembly. It runs in the browser, but it runs at near-native speed. The Rust core produces a flat buffer of `RenderedNode` structs — position, size, depth, status, fidelity — and the JavaScript layer is a dumb consumer of that buffer. It reads the numbers and draws rectangles. No logic lives in the rendering layer.\n\nThe canvas itself is four stacked layers: geometry, state (colours and fill), focus (the working level highlight), and presence (collaborator indicators). Only the layers that need to change repaint. A status update on one issue repaints the state layer. Navigation repaints the focus layer. The geometry layer repaints only on resize.\n\nThis separation is what keeps navigation instant. Drilling into a node is a WASM call and a canvas repaint, not a re-fetch or a DOM reconciliation.\n\nThe AI grouping was the part I was most nervous about — not technically, but economically. LLM calls cost money, and a Jira backlog with 5,000 issues could get expensive fast if you sent every issue to a model.\n\nThe answer was **deterministic first**.\n\nBefore we touch an LLM we build as much structure as we can from what Jira already knows: existing parent/child links, epics, components, fix versions, labels, issue type. For most backlogs this resolves the majority of issues at zero inference cost.\n\nOnly then do we look at what's left — the overflow buckets where a parent has more than 8 children, and the `Unsorted` pile of issues that don't fit anywhere. Those go to the model, **batched per bucket**, not per issue. One call covers an entire overflow group. The model receives the issue keys and summaries, and returns a named partition — at most 8 thematic groups with a one-line rationale for each.\n\nIf a returned group still has more than 8 members, we recurse. The constraint is enforced all the way down.\n\nWe measured this at around **$0.24 per 1,000 LLM-resolved issues**, which sounds small and is small — the cost is dominated by the fixed overhead of the prompt and tool schema per call, not by the number of issues. It scales with the number of overflow buckets, not the size of the backlog, which rewards the deterministic-first approach.\n\nOne more thing worth knowing: any group you rename, move or adjust is marked as yours. A rebuild will work around your edits, never overwrite them.\n\nThe combination — a WASM geometry core that never renders more than it needs to, and an AI layer that only runs where deterministic logic falls short — is what makes Teppe feel fast and cheap to operate at the same time.\n\nIf any of this is interesting and you want to go deeper, feel free to reach out.\n\nEmail: [info@teppe.io](mailto:info@teppe.io)\n\nLuke", "url": "https://wpnews.pro/news/how-teppe-for-jira-works-under-the-hood", "canonical_source": "https://dev.to/luke_jones_a844cfe3e576ea/how-teppe-for-jira-works-under-the-hood-4pmf", "published_at": "2026-09-23 15:16:56+00:00", "updated_at": "2026-09-23 15:59:01.562266+00:00", "lang": "en", "topics": ["ai-products", "ai-tools", "developer-tools", "ai-agents"], "entities": ["Teppe", "Jira", "Rust", "WebAssembly"], "alternates": {"html": "https://wpnews.pro/news/how-teppe-for-jira-works-under-the-hood", "markdown": "https://wpnews.pro/news/how-teppe-for-jira-works-under-the-hood.md", "text": "https://wpnews.pro/news/how-teppe-for-jira-works-under-the-hood.txt", "jsonld": "https://wpnews.pro/news/how-teppe-for-jira-works-under-the-hood.jsonld"}}