Eleven PRs in one afternoon — the day the bot became a teammate
Portfolio·

Eleven PRs in one afternoon — the day the bot became a teammate

On Wednesday I opened the Portfolio repository and the pull request queue looked like an auction: eleven dependabot PRs stacked up, each with the same polite request — “bump X from A to B”. In projects I maintained in the past, that queue meant a lost afternoon: reading changelogs by hand, running the build, crossing fingers. This time the process was different, and the difference wasn’t luck.

What came in the batch

Package From To
vercel 58.9.0 59.11.7
vitest 4.1.10 5.0.0
framer-motion 13.0.0 13.2.0
mercadopago 3.3.0 3.6.0
eslint-config-next 16.3.0 16.3.4
@testing-library/user-event 14.6.3 14.6.7
@testing-library/jest-dom 6.9.1 7.0.1
@testing-library/react 16.3.2 16.3.3
@vitejs/plugin-react 6.0.5 6.1.1
netlify-cli 27.1.1 27.5.0
actions/setup-python v6 v7

Most of the batch is what I call invisible polishing: deploy CLIs, lint configs, testing bindings. None of these ship in the bundle the user downloads. The real risk concentrates in two spots: vitest, which jumped a major version (4 to 5), and framer-motion, which controls the animation of everything that moves on the home page.

The major that didn’t hurt

A test runner major is usually the scariest PR in the queue — it’s the package that decides whether CI stays green. Vitest 4.1.10 to 5.0.0 came in as PR #77 and passed without a single change to the test code. That’s not the dependabot’s merit, it’s the merit of technical debt I never took on: the project uses no experimental runner APIs, no obscure plugins, no exotic configuration. The rule I learned maintaining this repository: the duller your testing dependencies, the cheaper the major.

The framer-motion snapshot false alarm

Framer-motion 13.2.0 (PR #78) came at the end of the queue and became the last merge of the day. And in the same week, a visual regression test run failed with a 6% diff against a 5% budget. First-hour instinct: “the update broke something”.

Except the same test, run again against an identical build with frozen baselines, passed without a single pixel of difference. The 6% diff was animation timing — a frame captured in a different phase of the transition — not a visual regression. Framer-motion is the most alive package on the home page: it animates entrance, hover and scroll, and any test that photographs the page mid-transition is photographing time, not pixels.

The process that holds the batch

The flow that makes this day possible is simple to describe and expensive to build:

  1. Dependabot opens the PR with a changelog and release link
  2. CI runs the entire suite on the PR branch — unit and E2E tests
  3. Green = merge; red = the PR waits and the investigation happens in isolation
  4. After the batch, an integration commit ties everything into the project’s history

Step 3 is what turns a queue of 11 PRs into routine instead of a gamble. CI is what reads the changelog for me — it doesn’t summarize features, but it answers the only question that matters: does it still work?

PR type What decides What can hurt
CLI patch/minor green CI nothing, in practice
Test runner major the whole suite broken config, removed API
UI/animation lib CI + VRT snapshot flake, timing

The lesson of the day

The counterintuitive part of keeping dependencies up to date isn’t technical — it’s trust. Trust that CI covers what the changelog doesn’t tell you, and the discipline of never merging a PR the CI hasn’t seen. Eleven PRs in a single afternoon isn’t robot productivity: it’s the result of a project where the safe path is also the fast path.

The batch closed with a green suite and an empty queue. Next round, dependabot will open more — and the queue, again, will be routine.

~/lifelog — bash
$cat about.txt
╔══════════════════════════════════════╗
║  Samuel Medeiros                    ║
║  Senior Software Engineer           ║
║  Stack: Python · TypeScript · Rust  ║
║  Projetos: Arachne, Dogwalk,        ║
║            Capivara, TatuEngine      ║
╚══════════════════════════════════════╝
      
$