Why I Built Jafa - Taming Roblox Mono-Repos
10 August 2026 · 8 min read
Jafa came out of a specific problem. I was building a sports game on Roblox, and it didn't fit into a single place. There was a lobby where players gathered, and each game mode lived in its own separate place that players teleported into. Every one of those places ran its own code at match time, but they all leaned on the same underlying logic: player teaming, teleporting between places, match initialisation, and a dozen other systems that had no business being written more than once.
That left me pulling in two directions. Each place had to stand on its own, with its own code running independently. At the same time, I wanted to write the shared systems once and have every place reuse them, instead of copy-pasting teaming and teleport logic into four projects and then fighting to keep them in sync as the game changed.
The answer was obviously a mono-repo: one repository holding the shared code and every place built on top of it. Setting that up on Roblox, though, turned out to be a project in itself.
I wasn't the first person to hit this. Jafa is a spiritual successor to Gaffer, a Roblox mono-repo orchestrator built by my friend Eleanor. I'd worked with her on a large, gnarly Gaffer project, so I already knew the approach worked. A shared workspace with independent projects inside it was exactly the right shape for a multi-place game.
Using Gaffer day to day also showed me where it strained. It relied on a separate package-linking tool to hold things together, and the scaffolding side was fiddlier than I wanted, both spinning up a fresh workspace and adding new projects to it. When the whole point is to make starting a new place easy, a rough setup flow rather defeats the object.
Gaffer's retired now, and Eleanor points people towards Jafa, which is a genuine privilege. It also set my goal cleanly: keep the mono-repo model Gaffer got right, and make scaffolding a workspace and adding projects to it as close to a single command as I could get.
If you build on Roblox with external tooling, you know the ritual. Before a single line of gameplay code, you're in setup mode: install Rojo, wire up a default.project.json, work out how client, server, and shared map onto the data model, set up Wally, pin your tool versions, add a linter and a formatter, and get them all playing nicely together.
Once, that's a fun afternoon of yak-shaving. Across a lobby and every game-mode place in a single game, it stops being fun. I was copy-pasting config between projects, forgetting which version of which tool a given place expected, and ending up with subtly different folder layouts because I'd changed my mind about structure somewhere along the way. The shared code I wanted to centralise was drifting apart instead, scattered across places that were each set up a little differently.
None of this is the tools' fault. The Roblox open-source ecosystem is excellent. Rojo, Wally, Rokit, Selene, StyLua, Lune, Mantle, every one of them is well-built and does its job. The friction was the glue: stitching them together the same way every time, and holding that setup steady as the game grew from one place into several.
So I built Jafa to do the stitching.
The core idea is the mono-repo. Instead of a fresh repository with its own tooling per experience, you scaffold one workspace that holds the shared config, and every experience lives as a project inside it.
Creating a workspace is one command:
jafa init my-game
cd my-game
That does what used to eat an afternoon. It scaffolds the shared rokit.toml and a single wally/wally.toml, initialises a git repository, runs rokit install to pull down Rojo, Wally, StyLua, Lune, Mantle, Selene, and wally-package-types at pinned versions, installs the Wally packages, and drops in editor scaffolding like a .code-workspace and .vscode/ folder. It even creates a starter project, so you're not staring at an empty directory.
Adding a new experience to the workspace is another single command:
jafa new my-experience
This is the part that fixed my original headache. Every project Jafa creates gets the same structure, client, server, shared, and ui folders, each with a sensible internal layout, wired into a Rojo tree that maps them onto the Roblox data model. shared lands in ReplicatedStorage, server code in ServerScriptService, and so on. Every project also links back to the workspace's single shared wally/Packages directory rather than installing its own copy, so packages are shared across the whole repo instead of duplicated per place.
Back to the sports game, this is exactly the shape I needed. The lobby and each game-mode place are separate projects, built and deployed on their own, but they sit in one workspace, share one set of tools and packages, and follow one identical layout. The teaming, teleporting, and initialisation logic I never wanted to write twice lives in shared, and every place picks it up the same way. Consistency isn't something I enforce by hand anymore; it's just how the workspace is built. Jumping between places, I'm not relearning where anything lives.
The other daily friction was the loop of building a project, opening it in Studio, and starting a live-sync server. Three tools, three commands, in order, every time I sat down. Jafa folds that into one:
jafa dev my-experience
dev builds the project with Rojo, opens the .rbxl in Roblox Studio, and starts rojo serve so Studio syncs your changes live. If you'd rather run the steps yourself, jafa build, jafa open, and jafa serve are all there; dev just chains them for the common case.
One decision worth calling out: Jafa doesn't replace these tools, it drives them. Every command shells out to the real thing underneath. jafa build runs rojo build; jafa serve runs rojo serve; jafa deploy runs Mantle. I didn't want a walled garden, or to reinvent things the ecosystem already does better than I could. Jafa is the glue, not a replacement, so if you already know the Roblox toolchain, nothing it does is a mystery. It runs the same commands you would, in the same order, minus the remembering.
Two commands go further than day-to-day iteration, and both came straight out of my own workflow.
jafa deploy ships a project to Roblox through Mantle, which is infrastructure-as-code for a Roblox place. Jafa builds the project, generates or updates a per-environment mantle.yml, and runs the deploy. Each environment (dev, stg, prod) gets its own config and its own Mantle state, so a staging deploy can't clobber production. Dev and staging resources even get prefixed with [DEV] and [STG] automatically, so they're easy to tell apart, and a --all flag builds and deploys every project in the workspace at once.
jafa pull scratches a more specific itch: getting published place content like terrain, maps, and lighting back down into version-controlled files. Each project carries a small .jafa manifest mapping Roblox instance paths to files on disk, and jafa pull fetches the published place and serialises those instances into the project directory. The parts of an experience you build visually in Studio can then live in git alongside the code, instead of being trapped inside a .rbxl somewhere.
The honest test of a tool like this is whether it removes friction or just shuffles it around. For me, adding a new place, or a whole new experience, stopped being a decision. There's no setup cost to weigh up: jafa new, and I'm writing gameplay code seconds later in a structure identical to everything else I maintain.
That changes how I work. Small experiments I'd once have crammed into an existing project, because a fresh repo felt like too much ceremony, now get their own clean project in the workspace. The tooling stops taxing the act of trying things.
Jafa comes straight out of my background: years of self-taught Roblox development, a lot of collaborative and freelance work (the Gaffer project with Eleanor among it), and a stubborn preference for building my own tooling close to the metal rather than reaching for something heavier. It solves my problem first. But it isn't only my problem; anyone maintaining more than one Roblox experience hits the same wall.
If that's you, the docs cover installation and a quick start, and the whole thing is open source on GitHub. It's early and the roadmap is public, and I'd like to hear where it breaks for workflows that aren't mine.
8 min read
Published on Monday, 10 August 2026