Skip to content

Web Development#

Same App, 5x the Code: Anatomy of a Full-Stack Polyglot Tax

We built the exact same app twice. Once with FastAPI, SQLAlchemy, LangChain, React, TypeScript, Vite, and Bun - the state-of-the-art (SOTA) stack that a senior engineer would reach for today. Once in Jac. Same features, same UI, same AI-powered categorization, same persistence. Both QA-verified to behave identically.

The SOTA version is 11 files, 233 lines of application code. The Jac version is 1 file, 46 lines.

These are good tools. FastAPI is arguably the best Python API framework. SQLAlchemy is the most mature Python ORM. LangChain is the dominant LLM orchestration library. React and TypeScript need no introduction. We picked the best of each category on purpose - this isn't a comparison against straw men.

And yet, building with the best-in-class version of every tool still produces ~5x more application code across 11x more files than expressing the same idea in a language designed to handle the full stack. We're calling this the polyglot tax - the code you write not to solve your problem, but to make your tools talk to each other across language, runtime, and type-system boundaries. This article is a line-by-line anatomy of where that tax shows up and what it costs.

I Built a Postman for MCP Servers. Here's the Story.

It started in a Jaseci weekly sync. We were discussing the jac-mcp server when someone brought up the idea of an MCP playground — a place to test MCP servers the way Postman lets you test REST APIs. The idea stuck with me, but it didn't feel real until I ran into the problem myself.

I was connecting jac-mcp to GitHub Copilot, trying to understand how tools were being called and what they returned. The answer was: I had no idea. Copilot called tools based on whatever prompt and model it chose, and I was left guessing.

When I started building the jac-shadcn MCP server, I hit the same wall. Testing through an LLM client meant my results depended on the model and prompt, not the server itself. That's not testing. That's hoping.

Browser Automation Has a Data Model Problem. Walkers Fix It.

Every browser automation script you've written has the same hidden bug: it doesn't know what it's doing. It knows the steps. It doesn't know the structure.

You write page.goto(), then page.fill(), then page.click(). Each call succeeds or fails on its own. The script has no idea that it just navigated to a login page, that the text field it filled was a username input, or that the button it clicked submits a form. It's running commands against a stateless API. The moment the page structure changes, a new modal shows up, an element gets renamed, there's an extra redirect, the script breaks. Not because you got the logic wrong. There just wasn't any logic. Just steps.

This is where browser automation sits in 2026. Playwright, Selenium, Browser-use, Stagehand, they all give you good primitives. None of them give you a model for what a browser session actually is. State lives in local variables. Context evaporates between function calls. The relationship between a session, its pages, and their elements exists only in your head.

I built jac-browser to try to fix this. It's a browser automation library written entirely in Jac, 153 walkers that wrap Playwright. But the wrapper isn't really the point. The point is that browser sessions are graphs, and Jac gives you graphs as a first-class language construct. The result is a library where the data model is the automation framework.