TypeScript
Everything I write is TypeScript. Inside the program the types hold. At the edge they are only a claim: an unparsed response is whatever the annotation says it is, and the compiler carries that claim all the way to production. So I parse at the boundary and keep the failure in the type.
Node
One thread, shared by everybody. I have watched a single handler do real work on the CPU while requests that needed nothing from it queued up behind. It is the first thing I check when a service slows down under load and the database looks fine.
Postgres
My default store, and I put rules in it the service cannot be trusted with. A check constraint keeps a wallet from going below zero. A unique key keeps the same capture from being written twice. The price is a migration every time a rule moves, and I have paid it and would pay it again.
Redis
Cache and lock. The reads are the easy part. The hard question is which data is allowed to be stale, and who gets to decide. I have inherited keys nobody could name an owner for, and they are all still there.
Queues
BullMQ on Redis, SQS on AWS, RabbitMQ when the routing earned it. A queue moves the failure somewhere no user is watching. That makes the retry policy and the dead-letter queue the real design work, and I have never got either right on the first pass.
AWS
EC2, S3, RDS, SQS, Lambda, CloudWatch. IAM is the part that costs me time. I write a policy narrow enough to be right, then somebody widens it to unblock a deploy on a Friday and it stays wide. Including me.
Docker
Compose on my machine, images in CI. Docker made “works on my machine” a smaller lie. The image is identical everywhere. The environment it reads at boot is not, and that is where the afternoon goes.
NestJS
Twenty-seven feature modules on one backend, and the container is why there are that many. Wiring is cheap, so every module reaches the database itself. By the time that is a problem, no single place owns a transaction. Putting the handle back in the signatures took thirty-four files.
Next.js and React
I have shipped and maintained both, and this site is one of them. The cost sits on the server-client boundary, which serialises every value that crosses it. The bug then arrives looking like a rendering problem, and it is usually the shape of the data I sent.
Payments
Stripe, and Fedapay for West African francs. Money is where a retry stops being free. The same webhook arrives twice, and the second one has to do nothing in a way I can prove. I once booked a cost of $0.0769 as 8c, and every total still added up, because a wrong number adds up with itself perfectly well.
LLMs and agents
OpenAI models, LangChain and LlamaIndex, retrieval over pgvector and Pinecone. The model fails in a way nothing else here does: it returns the right shape with the wrong answer and no sign that anything went wrong. So the work is the schema at the edge and the decision about what the model is allowed to see. Very little of it is the prompt.
Observability
Sentry for exceptions, Seq for structured logs. Logs start earning their storage on the day you can follow one request end to end. Before that you have a great deal of evidence and no way to rebuild what happened.
I have shipped Python, but not for long enough to claim it. I have watched Java and C# up close and never carried either one.
This is not a complete list. I stopped where I ran out of things I could defend.
The list
- TypeScript
- Node
- Postgres
- Redis
- Queues
- AWS
- Docker
- NestJS
- Next.js and React
- Payments
- LLMs and agents
- Observability