02Blog

The missing documentation

Notes on what we keep learning. Almost all of it comes from a specific problem that cost us more than we expected.

All
  1. ProductoLa web de un pintor solo tiene que hacer una cosa: que le llamenHice la web y la app de trabajo de Mamu, un pintor de Valencia, en cuatro días sueltos. Lo que más mejoró la web fue borrar las páginas de precios y de por qué elegirnos.
  2. IACómo construimos Zenbu, un buscador de anime que busca por lo que cuentasCatorce mil animes, un vector por serie y un LLM que reordena al final. Lo que más nos costó no fue la búsqueda semántica: fueron las secuelas con sinopsis de una línea.
  3. ArchitectureI've migrated auth providers four timesFour managed providers in a year and a half, and the same destination every time: my own middleware. What breaks in each transition, and what I check now before writing a line of auth.
  4. OperationsStripe has broken my build five times and now it's on the calendarBumping the Stripe API version isn't an incident. It's recurring maintenance, with fairly predictable collateral damage, and you should budget for it.
  5. ProductCredits, balance, spend limitsI've built usage billing the same way in five products: start with credits, end up with a balance in cents and per-key limits. Now I know which stage to start at.
  6. DatabasesWhy I removed RLS from Postgres both times I used itRow Level Security looks like the correct default. With Drizzle, server actions and my own auth, the permission logic already lives somewhere else, and keeping both hurts.
  7. JudgementI never start from scratch: what I always keep and what I always cutTen projects on the same boilerplate. The first commit of each one looks more like the last commit of the previous project than like a new project, and the second commit almost always deletes something.
  8. JudgementThe first thing I remove from any template is StripeI start almost every project by deleting the checkout, the portal and the webhook. It's not absent-mindedness: it's separating the infrastructure I'll always need from the part that isn't due yet.
  9. ArchitectureThe same concurrency bug has shown up in three different projectsA counter that adds up under load loses increments, and one that subtracts can charge twice for the same thing. When the counter is money, that isn't a detail you fix later.
  10. AIEvery new model breaks something differentSix model migrations in a year and a half. None of them was changing a string, though they all started out looking like it.
  11. DatabasesI rewrote the same contracts model more than ten timesA complicated domain model doesn't come out right the first time. You converge on it by rewriting it with the real data in front of you, and that includes reverting decisions that looked correct.
  12. DatabasesMy first data schemas almost never survive the first monthThe day 1 schema is a hypothesis about a domain you don't understand yet. Accepting that changes how you write it and how much it hurts to change it.
  13. SecurityWhy I do guest access with a session token and not with codesA six-digit code looks like the simple way to let someone in without an account. It is, until you have to revoke it, and then you find out you don't know who you're kicking out.
  14. OperationsWhy "it works on my machine" tells me nothing anymoreThe gap between your machine and production isn't bad luck: it's four concrete categories that fail every time, and a green build can't see any of them.
  15. EngineeringThe test that said green and liedSix SCRIPT ERROR lines inside a test that printed ALL TESTS OK and exited with code 0. How you find out a green is false, and what I check now before trusting one.
  16. PerformanceAdmin lists are the first thing to fall overThe internal panel gets written without limits because at first there are four rows. It's exactly the place that blows up once the product grows.
  17. SecurityWhat I learned auditing my own code as if it were someone else'sAn empty ACL that returned the whole corpus, a CSS injection without a single line of JavaScript, and an API key that still worked after its owner was closed down. All in one day, all in code I wrote myself.
  18. ArchitectureWhy I require login in apps that don't need itWith no identity there's no quota, no spend limit, no way to cut off whoever abuses it. Signup friction is cheap compared to what not having it costs.
  19. JudgementWhat always ends up happening when I build something by handI've built editors by hand four times. Three ended up rewritten with a library. The fourth didn't make it past two days.
  20. FrontendWhen a global store earns its placeGlobal state isn't justified by convenience. It's justified when a middle component receives props it never uses, only to pass them down.
  21. JudgementFive migrations from ESLint to Biome: the decision is already madeWhen you do the same migration in five projects without stopping to think about it, it stops being a preference and becomes judgement. What breaks along the way and what you lose.
  22. JudgementDocument before you code: my rule for integrations I don't controlIn an integration with someone else's API, the first four commits touch no code. Writing the document first forces you to measure the limits that later blow up in production.