Full-stack developer from Brazil. I've been writing HTML, CSS and JavaScript for 10 years, and building with React and Node since 2019.
React and Next.js on the front, Node behind it. When a project gets big enough that one Express app stops making sense, I switch to NestJS. Most of what I build now is a typed API with Supabase or Postgres doing the heavy lifting, and lately a good chunk of it is RAG and LLM work.
The part I actually enjoy is owning the whole thing: the UI, the API, the schema, the deploy, and the tests that stop me breaking it three weeks later. I work test-first and spec-first, which sounds tedious right up until you have to change something six months later.
AI & LLM
Engineering
hover any icon for its name
Front end
Back end & data
Tooling & testing
A RAG app you can actually trust. Upload your documentation, ask it a question, and every sentence in the answer cites the exact paragraph it came from. If a claim can't point at a source, it doesn't get shown.
Retrieval is hybrid: pgvector cosine similarity and Postgres full-text search run side by side and get fused with Reciprocal Rank Fusion, so an exact function name is found as reliably as a paraphrase. On top of that sits a tool-calling agent with a step limit and an approval gate before anything irreversible, plus structured extraction validated with Zod.
NestJS API and background worker, Next.js 16 front end, Drizzle over Postgres, Redis and BullMQ, all in one typed monorepo. An MCP server is the next milestone, so Claude Code and Cursor can query it straight from the editor.
precinho-demo.mp4
Grocery prices in Brazil change depending on which branch serves your address. Same barcode, same week, two São Paulo chains: Coke was R$ 14,98 at one and R$ 10,49 at the other. That 30% gap is the whole reason it exists. It used to ask for your postcode; it covers one town now and says so on screen, because a second region would not fit in the 500 MB the free tier allows.
Matching the same product across chains is the hard
80B8
part, and where the AI
comes in. Barcodes handle most of it, but when a chain doesn't publish one I
use Claude as a judge to decide whether two product names are the same
thing. "Bolo de Brigadeiro" and "Bolo de Brigadeiro com Amêndoas" share every
word but one, and that word is the product, so the prompt is written against
the pairs a similarity score got wrong and the judge is told to answer false
whenever it isn't sure.
40,435 pairs went through that in one run, through the Message Batches API at half price. 11,686 came back same — and merging them taught the sharper lesson, because verdicts are pairwise and groups are transitive. A group of 17 held three different coffee lines; every link in the chain read as defensible alone. The negatives are what break it: a group is refused when any pair inside it was judged different.
Portuguese or English, routed so each language is its own indexable URL. Next.js 16 and React 19 on Supabase, nightly collector in GitHub Actions, clean architecture with 100% coverage on the domain layer. 725 tests.
Overtone.-.Demo.comprimido.mp4
Podcast analytics that turn an episode into countable, citable facts. Upload the finished file and find out which words a show cannot stop saying, how often each running joke actually came up, and how all of it moves across the whole series — with a timestamp behind every single number, so any count can be clicked and heard. Counting is deterministic SQL, never a language model, which is what makes the numbers worth arguing about.
Transcription runs on Whisper large-v3 — the full model rather than the distilled one, because on a real ninety-second slice of this show the turbo model rendered a plain, very common sentence as something nobody said, and the entire output of this product is counts of words. It is still fast enough that a ten-minute chunk comes back in a couple of seconds, which is what lets the work happen inside a normal web request with no queue and no worker to keep alive. What paces a full episode is the free allowance instead, spent in seconds of audio rather than requests.
Getting a two hour episode past a 4.5 MB serverless upload limit was the fun part: the browser decodes the audio, splits it where there's silence rather than mid-word, and uploads each piece straight to storage.
Each episode's language is worked out from the audio rather than declared — three half-minute windows sampled from the middle of the recording, majority wins. Never the opening, because the show starts on music with English lyrics, so anything listening to the first thirty seconds calls every episode English. Portuguese and English each get their own stop words, stemmer and dictionary, so a language there is no pack for is refused by name instead of counted as Portuguese and reported as if it were right.
It's live, and it costs nothing to run. Vercel, Supabase and a free transcription tier, no credit card anywhere. 1,630 tests.
Specs and tests come before the code, and I write down why I made a decision, because the person who has to understand it in six months is usually me. Across these three projects that's 2,355 tests, 49 architecture decision records, and a README on each one that says plainly what's finished and what isn't.