Skip to content

May 2026#

Where Does This Abstraction Belong? A Contributor's Guide to Extending Jac

Every programming language faces the same inevitable question as it grows: where should this new thing live?

In Jac, this question has real architectural teeth. We don't have a single flat namespace where everything gets dumped. We have a deliberate three-layer abstraction hierarchy, and every new capability we introduce must land in exactly the right layer — or the whole design philosophy starts to erode.

This post is a deep guide for contributors to the Jaseci codebase. If you're adding a new feature to Jac, this is the framework for deciding where it belongs.

Rebuilding Jac's TopologyIndex: from O(N) scans to type-keyed buckets

Jac runs a lot of graph queries. They show up everywhere in Object-Spatial code, from [root --> [?:User]] to multi-hop walks like [r ->:Authored:-> [?:Post] ->:Tagged:-> [?:Topic]]. The naïve way to answer them is to walk every edge from the origin, load the targets, and check the filters. The TopologyIndex was added in PR #5205 to skip that work for type-filtered queries -- store enough metadata on the root node to resolve the survivors without ever touching the database.

The original implementation worked, but it had two problems we couldn't ignore. It scanned a flat adjacency list on every query, so cost was independent of selectivity (a query that matched 1% of the graph cost the same as one that matched 99%). And it had a quiet correctness bug for inheritance hierarchies: parent-type queries silently returned the empty set instead of all subtype instances.

PR #5784 rebuilds the index around a type-keyed lookup map with MRO-aware fan-out. Same canonical wire format. Same API surface. But typed reads now scale with match count, parent-type queries do the right thing, and the planner knows when to skip the index entirely. This post walks through what changed, why, and what the measured numbers look like.