CodeNex
An AI builder that turns a prompt into a running React application, on a microservice backend that gives every project its own live preview environment.
- backend services split by scaling profile behind one gateway
- 3backend services split by scaling profile behind one gateway
- isolated preview environment, addressed by its own subdomain
- 1 per projectisolated preview environment, addressed by its own subdomain
- public product with streaming generation and running previews
- Livepublic product with streaming generation and running previews
Generating code is the easy half. The hard half is everything around it: where the files live, how a user sees the result running, how concurrent builds stay isolated, and how streaming keeps the interface alive while a model works. I built CodeNex as a distributed system rather than a wrapper — a Spring Cloud backend, streamed generation, and per-project preview environments served on their own subdomains.
The problem
A prompt-to-app product only feels real when the user can click into a running application, not read a code listing. That requires solving problems a single service cannot: long-running generation that must stream rather than block, generated files that need durable storage separate from the container that produced them, previews that must be isolated per project, and the caching and quota accounting that keeps any of it affordable under concurrency.
Constraints
- Generation takes far longer than a request-response cycle tolerates, so progress has to stream or the interface looks broken.
- Every project's preview must be isolated — one user's generated app cannot reach another's files or environment.
- Generated artifacts outlive the process that created them, so they cannot live on the generating container's disk.
- Model calls cost money per token, so quotas and caching are product requirements, not optimizations.
Decisions
What I chose, why, and what I turned down to get there.
Split the backend by responsibility, behind one gateway
Account, workspace, and intelligence became separate services behind a Spring Cloud gateway, with routing by path prefix and configuration pulled from a central config service. Generation is bursty and expensive while account operations are cheap and constant — coupling them means scaling the wrong thing, and a model-provider failure taking down login.
Considered and rejected
- A single monolithic backend — faster to build, and it forces generation load and auth traffic to scale together
A preview environment per project, on its own subdomain
Generated apps are served from a dedicated previews namespace, matched by subdomain through the ingress and resolved to a dynamic preview by a proxy service. Subdomain isolation gives each project a real origin, which means browser security boundaries do the isolation work rather than application-level path checks that are easy to get subtly wrong.
Considered and rejected
- Serving previews from a shared path on the main domain — one origin for every user's generated code, so cookies and storage are shared by default
Object storage for generated files, not container disk
Files are pushed to object storage and watched into the preview environment, so the artifact survives the generating pod and a preview can be rebuilt without re-running the model. Treating generated code as durable state rather than process output is what makes workspaces resumable.
Stream tokens over server-sent events
Generation streams to the browser as it happens instead of resolving one long request. The user sees work in progress, which changes the product from a loading spinner into something that feels alive — and it removes the timeout ceiling a buffered response would impose.
An event bus between generation and everything downstream
Generation publishes events that other services consume, with a cache layer in front of read paths. Usage accounting, workspace updates, and preview refreshes are all reactions to generation rather than steps inside it, so a slow consumer cannot stall the generation itself.
The shape of the system
Traffic enters through an ingress that splits two ways: API calls to the gateway service and preview traffic to a proxy service that resolves project subdomains. Behind the gateway, account, workspace, and intelligence services each own their deployment and pull configuration centrally. The data layer carries a relational store for durable state, an event bus between services, a cache in front of hot reads, and object storage for generated files, which sync into the previews namespace.
- Preview resolution is a subdomain match rather than a lookup in application code, so adding a project needs no routing change.
- Configuration is fetched by services at runtime rather than baked into images, so an environment change does not require a rebuild.
What this was for
CodeNex is the project where I owned every layer at once — backend architecture, the streaming path, Kubernetes deployment, storage, authentication, quotas, and subscription billing. The value of building it was learning where a distributed system's real cost sits, which is almost never in the interesting part: it is in isolation boundaries, durable state, and what happens on the second concurrent request.
Stack
- Java
- Spring Boot
- Spring AI
- Spring Cloud Gateway
- React
- TypeScript
- Kubernetes
- PostgreSQL
- Kafka
- Redis
- MinIO
- Stripe
What was mine
Designed and built solo — backend services, streaming architecture, preview infrastructure, storage, authentication, quotas, and billing foundations.