dhaga.blog

dhaga. blog · Engineering

Engineering

Deep dives on the hardest problems we solved building Dhaga — rendering a 21k-node graph in the browser, keeping an AI product's unit economics alive, isolating tenants on serverless Postgres. Executive summary up top, full engineering detail below.

Don't build sharing with row-level security

Tenant isolation is one indexable equality on every table, and that uniformity is the whole value. Widening it with an OR so colleagues can share records gives away every column on the row, costs you the index on your hottest predicate, and ships a change no feature flag can hold back. Publish a projection instead.

engineeringpostgresrlssecurity

One token cannot be both

Light mode was audited to WCAG AA and the palette itself turned out to be the bug. On a light ground a colour's contrast as a fill and its contrast as text multiply to a fixed constant — ours is 16.78 — so no single value can clear 4.5:1 at both jobs. Why every accent in the system is now a pair.

engineeringdesign-systemsaccessibilitycss

The matcher that meant something else

Duplicate detection collapsed unrelated people into one contact. The helper it called was not buggy — it was a community-tag suggester that groups by surname on purpose, and it is still shipping unchanged. Why the types could never have caught this, and why making the shared helper smarter was the wrong fix.

engineeringcorrectnessapi-designwar-story

The model that could not read a card

Business-card scans were coming back confident, well-formed and wrong. The obvious fix was more pixels. The same 408px image read perfectly on a stronger model — so the fix was the model, and upscaling would have cost more to change nothing.

engineeringaillmvision

The summariser didn't fabricate. It overclaimed.

We rebuilt our public build timeline by summarising 847 commits with language models, then audited every line against git and the live code. The audit turned up no invented features — and a worse failure underneath: sentences that were true when written, and a code path described as shipped that no user could reach.

engineeringaillmdocumentation

One message, two notes

The rule that should have stopped it — one message goes in exactly one place — existed only as a sentence in the prompt. Nothing in code checked it. On the fix we rejected, why a Zod refinement was the wrong lever, and why we repair a bad plan instead of failing it.

engineeringaillmzod

The LLM didn't truncate. It edited.

A capture came back four fifths shorter than what was sent, and nothing reported it. It wasn't a token limit and it wasn't summarisation — the model was filtering to fit the schema. Why a structured-output schema is a filter as much as a contract.

engineeringaillmstructured-outputs

The fan-out that killed the search

Six keyword sources under one Promise.all, each awaiting its own scoped tenant connection, against a pool of three. Search returned HTTP 500 with a single user on it. Why Promise.all is a concurrency multiplier, not a performance tool, when every read checks out a connection.

engineeringpostgresperformancemulti-tenant

How we build Dhaga with Claude Code

A checked-in rulebook the AI must obey, hooks that gate every single edit, and a persistent memory that survives across sessions. Our AI-assisted engineering workflow — the practices that make an agent a reliable contributor instead of a fast intern who forgets everything.

engineeringaiclaude-codedeveloper-workflow

Isolating tenants on serverless Postgres

Row-level security is easy to turn on and easy to get catastrophically wrong. How a connection pooler almost leaked one customer's data into another's in Dhaga Cloud, and the transaction-scoped design that makes it impossible on either pooling mode.

engineeringpostgressecuritymulti-tenancy

The bill is the model, not the servers

In an AI product, infrastructure is a rounding error and inference is the P&L. How we found the real cost driver in Dhaga, and the guardrails that keep a heavy user from costing us $7,200 a month.

engineeringaicostllm

The feature flag that didn't fire

Our documented escape hatch for a native dependency didn't work — the app crashed on deploy anyway. A short war story about how `import` runs before your code does, and why a feature flag can't gate a static import.

engineeringdebuggingdeploymentjavascript

A schema that heals itself on cold start

No migration tool, no migration files, and a 15-second sign-in. How a single content hash turned a full schema replay on every serverless cold start into a no-op — while keeping the zero-config, self-healing setup that makes a Dhaga instance trivial to stand up.

engineeringpostgresmigrationsself-hosting

Rendering a 21,000-node graph in the browser

63,000 edges, 60fps pans, and a laptop with integrated graphics — without a graph database, a server-side render farm, or a spinner that never ends. How Dhaga renders a whole knowledge graph client-side.

engineeringperformancedata-visualizationgraph