Writing
Why our AI runtime is a separate service
The Python AI runtime runs as its own service, one network hop from the backend, so it can be replaced without touching the business logic.
Our backend is a modular monolith on purpose — one deployable, no premature microservices. The one deliberate exception is the AI runtime, and the reason is not fashion, it is the language boundary: LangChain, LangGraph and the memory tooling we want live in Python, and the rest of the platform is TypeScript.
So `backend/src/ai` stays a thin TypeScript boundary — contracts, guardrails, the authorization scope the acting user already has — and never calls an LLM directly. The actual reasoning happens in `ai-service`, a separate FastAPI process reached over the network.
That split means the AI runtime can be rebuilt, swapped or extracted to its own repository without anyone touching a domain's business logic, and it means a domain never has to trust an AI response with more authority than the user who asked for it already had.
More from the story
- Release
We rebuilt this site around what people actually ask us
Six named services instead of four abstractions, six solutions framed as outcomes, our own products kept separate from client work, and a contact form that reaches a real system rather than an inbox.
See more - Work
Every enquiry now lands in WES, not an inbox
The contact form posts to a real backend domain with its own schema, rate limiting and audit trail — and shows up in the WES inbox with the context needed to answer it properly.
See more - Writing
Contracts before code
Every boundary in the platform validates its input against a schema that is also the source of truth for the type — so malformed data fails at the edge, not halfway down a call stack.
See more
Get in touch
Tell us what you are building.
The first conversation is about what you are trying to build. No pitch, no scope and no estimate until we understand it properly.