Illustration of small colorful creatures working on a desk in front of a glowing monitor, one reading a map, one with a magnifying glass, one with a hammer, one sweeping up.

Nine Months with My Minions

Rahmatur Ramadhan

by Rahmatur Ramadhan

19 Sep 2026 - 9 min read

Tech
Agents
AI
Developer Experience
Workflow

In my 2025 retrospective, I wrote that I wanted to learn backend this year. I did, but not in the way I expected: much of it came from reading and reviewing code I hadn't written myself.

This is a post about the first nine months of 2026. It's not an "AI will replace developers" post, and it's not an "AI is just hype" post either. This is what happened when I started building with AI agents on the projects I worked on.

How It Started

Honestly, I didn't start this on my own.

In January, Pavenow gave the engineering team AI subscriptions and encouraged us to actually use them. And it wasn't a "here's a login, figure it out yourself" situation. We also got sessions on how to use the tools properly: what they're good at, how to give them context, and where they fall apart. Looking back, that second part mattered more than the subscription did.

Before that, I used AI the way most people do. A chat tab open on the second monitor, paste in a function, ask why it broke, paste the answer back. Basically a faster Stack Overflow with worse sourcing.

It worked fine for small things. It fell apart as soon as the question needed context. "Why is this state not updating" is impossible to answer without the four other files that touch that state, and I wasn't going to paste four files into a chat box every time.

So the bottleneck was never the model. It was me, carrying context back and forth by hand.

The shift wasn't that AI got smarter. It was that AI got access to my repo.

The Minions

The project where this finally clicked was our mobile app at Pavenow, a Flutter codebase. It's large enough that no single prompt can hold all of it, but its consistent MVVM and GetX structure gives an agent patterns to follow.

I started calling the agents minions. The setup has a planning role and a group of specialists, each with a narrow job. I still decide what problem matters and own the final call.

The planner breaks a request into tasks and chooses which specialists to involve. The crew looks like this:

  • The builder. Implements a scoped feature using the patterns already in the app.
  • The reviewer. Checks the change for correctness, project conventions, security, and edge cases.
  • The tester. Adds or updates tests for the behavior being changed.
  • The refactorer. Looks for duplication and cleans up code where it helps.
  • The debugger. Investigates errors and unexpected behavior to find their cause.
  • The architect. Evaluates module structure, dependencies, and data flow.

Not every task needs every specialist, and they don't always work in a fixed sequence. For example, an architecture review can happen alongside other work, while refactoring may need to happen before implementation and tests after it.

The role names aren't magic. What helps is giving each task a clear scope and handoff, then checking the result instead of assuming the next role will catch everything.

What Worked

Repeated module setup got cheaper. A feature in the mobile app follows a familiar MVVM and GetX shape: controllers for logic, views for UI, and bindings for dependency injection. Features that use APIs also have recurring models, remote data sources, and repositories. Once the existing pattern is clear, an agent can take on more of that scaffolding, leaving me to focus on whether the behavior and boundaries make sense.

A codebase reconnaissance note for one of my side projects might look like this. The example is reconstructed, but it refers to the real Kotoba code, so you can check whether the patterns it describes are there:

reconnaissance note: "let people retry only the questions they got wrong"
Files that matter:
  types.ts                      QuizResult already carries questions + userAnswers
  utils/quizUtils.ts            isQuizResult guards the router state, reuse it
  components/ResultsScreen.tsx  where the retry button goes
  components/QuizScreen.tsx     builds the pool in a useMemo, needs a pool override
  utils/progress.ts             tracks completion, check before writing a retry run

Patterns to follow:
  - router state is typed as any, so it goes through isQuizResult first
  - quizLength null means "all questions", do not coerce it to a number
  - shuffleArray returns a copy, the pool is never mutated in place

Not touching:
  data/lessons.ts. The questions already exist, this is a filter.

I use the same setup on side projects, which is also why I can show you this one.

Migrations stopped eating a whole day. In January last year I upgraded our Flutter SDK by hand, 3.22 to 3.27. Ten hours, from picking it up to handing it over.

This January I did the same job again, 3.32 to 3.38 plus Dart 3.10. Bigger upgrade, more breaking changes. The commit landed thirty five minutes after I started.

I write more tests now. Which is a little embarrassing to admit, because it means I wasn't writing enough before. The cost of writing a test dropped low enough that the excuse disappeared.

What Broke

This is the part most posts skip.

Wrong answers look exactly like right ones. When a minion is wrong, it's wrong fluently. Right style, right structure, right conventions, and no obvious seam where the mistake is.

The one that taught me this is small and a little embarrassing, because it happened on this blog. I asked for a diagram to go with a post, got one back, read the change, committed it. It never appeared on the page. The file had been saved to static/posts/, and this site quietly prefixes /images onto any image path that doesn't already start with it, so the browser was looking for the file one folder away from where it actually sat.

Everything in that change was correct. Valid SVG, correct markdown, clean commit message. The only thing wrong was a directory name, and there is no way to see that in a diff. I found it by opening the page and staring at an empty space where a picture should have been.

That one was invisible in the diff. The other kind is fully visible and still gets through. Here is one from Kotoba, where an agent offered to tidy up how the quiz screen builds its question pool:

git
   const shuffled = shuffleArray(questionPool);
-  if (quizLength === null || quizLength <= 0 || quizLength >= shuffled.length) {
-    return shuffled;
-  }
-  return shuffled.slice(0, quizLength);
+  return shuffled.slice(0, quizLength ?? shuffled.length);

Four lines become one, the types still check, and every test still passed, because nothing covered that path. But ?? only catches null and undefined, not 0, and 0 was the value meaning "give me everything". So the tidier version hands back an empty quiz. Nothing about the diff looks wrong, which is exactly the problem.

Review became the bottleneck. I removed the writing constraint and found the real constraint sitting underneath it. Go back to that Flutter upgrade: thirty five minutes to write, then a week sitting in review. The manual one a year earlier took ten hours to write and then sat in review and QA for nearly ten days. The writing collapsed. The waiting barely moved.

I can generate a 600 line diff in ten minutes and it still takes an hour to read properly. Anyone claiming a 10x speedup is measuring the wrong half of the job.

Approval is not understanding. Sometimes I return to a file I approved and have to reconstruct why it works. That is a reminder to keep reviewing until I can explain the change, rather than treating approval as a substitute for understanding.

From My Own Setup to a Shared One

In June, about five months into all this, the first commit landed on a shared engineering toolkit, and that's when the setup changed shape again.

Until then, the agent setup and conventions lived in individual projects. The shared toolkit gave the team a versioned place to keep and review them.

It brings together role definitions, repeatable engineering workflows, and writing rules in a package the team can reuse. The materials are plain text and versioned with the code, so people can read them and propose changes through the usual review process.

Three things changed once it was shared:

  1. The working agreements became visible. People can read the conventions instead of relying on a conversation to learn them.
  2. Workflows became reusable. The team can start from shared definitions rather than rebuilding each process from scratch.
  3. Changes became reviewable. Updates to shared guidance can be discussed and reviewed like other code.

The trade-off is that shared guidance changes through review rather than being edited ad hoc. That adds a step, but it also makes changes visible to the people who rely on them.

Rules I Actually Follow Now

After nine months, these are the ones that survived:

  • Never let an agent decide what the problem is. It's very good at solving a well specified problem and very bad at telling you which problem matters. That part hasn't moved at all.
  • Keep the scope clear. Break work into small tasks and define what done looks like before implementation starts.
  • Separate implementation from review. A reviewer should be able to question the diff, not defend the decisions that produced it.
  • Review in context, then run the relevant checks. A code diff won't reveal every broken path; sometimes you have to open the page and look.
  • If I can't explain the change, it doesn't merge. Not "the agent said it works". I have to be able to defend it in review as if I wrote it, because as far as the team is concerned, I did.

Where This Leaves Me

On the newer Flutter upgrade, the commit landed thirty-five minutes after I started; a year earlier, a comparable upgrade took ten hours from start to handoff. Review and QA still took days in both cases. That gap is the point: faster implementation does not remove the need to decide whether a change belongs in the codebase. Knowing the project well enough to question a plausible-looking answer still matters.

I'll write a follow up at the end of the year, probably in the retrospective format, and we'll see how much of this I still believe in December.

Powered by SvelteKit. Icons by Iconoir.