Distributed systems
RabbitMQ, async-first service boundaries, and CQRS where the read and write models have genuinely diverged.
Event sourcing treated as an operational primitive rather than a doctrine. Context as files, not a database of prompts. Twenty years of delivery across PHP, Python, and TypeScript.
I've built systems that are still running many years later.
Most of my work has been the unglamorous half of a platform: message queues that do not lose work, event logs somebody can actually replay at an inconvenient hour, and deployment paths that a small team can operate without a runbook. The architecture matters less than whether it holds up under load and whether the next person can reason about it.
On AI systems specifically, I have settled on context as files. Treating prompts, schemas, and reference material as versioned text in a repository is the most reliable thing I have found for keeping model behaviour reproducible, reviewable, and diffable. It fits the way the rest of the systems I build are reasoned about.
Every company that exists is the consequence of a series of events, so choosing events as the primitive and projecting database views from them is the right call early. It is the model that matches what the business actually is. When scaling accelerates, more systems, more teams, more infrastructure, the projections can be rebuilt to match and the organisation can follow. Starting from projections and trying to recover the history later is the expensive direction.
Depth across the stack, weighted towards the parts that fail under production load.
RabbitMQ, async-first service boundaries, and CQRS where the read and write models have genuinely diverged.
Events as the primitive, database views projected from them. A model that matches what a business actually is, so it scales without a re-architecture.
Prompts, schemas, and reference material versioned as repository text so model behaviour is diffable and reviewable.
Building them and integrating against other people's, which is where the versioning and error contracts get real.
SQL and NoSQL, plus the unglamorous work of wiring systems that were designed independently of each other.
Docker, Linux and Debian, deployment pipelines sized for a team that has other things to do.
The layout I keep in a repository when the AI system needs to stay reproducible.
Every file above is plain text under version control. A behaviour change is a diff, reviewed like any other change.
git+diffon a prompt is the same asdiffon code
The rules I hold myself to when nobody is reviewing the pull request.
Prefer designs where the bad case is a rejected message or a visible error over one where it is silent data corruption. Boring failures are ones you can page on at three in the morning and reason about without a debugger.
If a model consumes it, it is an artefact with an owner and a history. Context belongs in the repository next to the code that depends on it, reviewed in the same pull request, because that is the only place a diff is meaningful.
Abstractions earn their place by removing a decision. When a layer only forwards calls or wraps a type, it is cost without benefit, and it is usually a sign the boundary was drawn in the wrong place.
Moving a call into a queue changes the shape of the system, not just its latency. It means designing for retry, idempotency, and ordering up front, because those constraints do not arrive later for free.
Code is written once and read many times, usually under worse conditions. Optimise for the reader who is tired, in a hurry, and missing the context you had three months ago.
What I am currently taking on.
Fixed-scope or long-running engagement.
Greenfield systems taken to production.
Async architecture and event-driven design.
Describe the problem, the constraints, and roughly when you need it working. I reply to everything.
javier@buenoscoders.comI answer as soon as I can.