Kineto

Agents made architecture flexible

0
Ignat TubylovIgnat TubylovEngineer at Kineto

Jul 30, 2026

We regretted building a microservice but figured we were stuck with it for years. Coding agents are making that intuition obsolete.

We regretted building a microservice but figured we were stuck with it for years. Any experienced engineer will tell you large refactorings are almost never worth it. Coding agents are making that intuition obsolete.

The background

We're building an AI agent for content creators. Last year, needing to track users' AI spending, we started designing what we called Wallet. The requirements grew along the way: flexible plan and credit subsystems, promocodes, freemium, A/B testing, metrics, archiving.

Eventually we settled on a microservice. In hindsight, we didn't need to.

In my experience, there are roughly four valid reasons to reach for a microservice. Not an exhaustive list, but a good rule of thumb:

  • Independent scaling. If your workloads have very different resource shapes — GPU-bound AI compute, CPU-bound HTTP servers, in-memory caches on standby — a monolith forces every replica to carry every requirement. Microservices let you tune each pool to what it actually needs and scale each one independently.

  • Incompatible language or framework dependencies. You need feature A from an old version of a library and feature B from a newer one, and the two can't coexist in the same runtime. Isolating one into its own process is the clean fix.

  • Infrastructure isolation. Some subsystems need harder boundaries — a separate database, a separate machine pool, separate secrets, tighter access controls — because their data is more sensitive than the rest. All engineers can read the general service's logs, but only a small group should read users' personal information. A microservice makes that boundary enforceable and slows down both intruders and honest mistakes.

  • Varying deployment cycles and team processes. In larger teams, responsibilities naturally divide between subteams, and sometimes their processes diverge enough that reconciling them becomes a hassle. One team ships once a month; another needs continuous deployment. One relies on dedicated QA; another on unit tests. Separate services let each team iterate without constant compromise.

Ours was the isolation case. The plan was to integrate with two payment providers: Stripe and an older legacy processor. If that older integration ever put us anywhere near actual card data, we wanted our PCI scope to be one small billing service, not the entire monolith. That was the bet.

Two things later dissolved it. First, we never shipped the older integration — the product went through Stripe from day one, and Stripe's hosted checkout means the card never touches our servers. The isolation the wallet was buying us was mostly theoretical to begin with, and once we committed to Stripe as the only provider it became fully theoretical.

Second, and more prosaic: during the migration itself the backend had to hold the Stripe keys anyway, to verify webhook signatures. Once both processes held the same secrets, the "if the monolith is compromised" story stopped protecting the wallet — an attacker who owned the monolith already had the payment credentials. The boundary was ceremony.

Modular monolith

Rejecting microservices doesn't mean rejecting good design. You can — and should — still split code into independent modules with clear architectural boundaries. You just don't need to deploy them separately.

We started thinking about merging the wallet back into the monolith almost immediately. Isolation meant two databases, more infrastructure complexity, more code complexity. Instead of a JOIN for a simple report, we had to build and test a batch API. Local setup got harder.

There was also this: agents work better in a monolith.

In a monolith the agent can grep from the HTTP handler through the service into the ledger write in one pass, and in a typed language a schema rename trips the compiler at every callsite. Across a service boundary the same rename compiles green on both sides and blows up at runtime. The type system — your strongest guardrail for LLM-assisted refactoring — disappears. Retrieval degrades the same way: scatter the code across repos and the agent starts hallucinating interfaces ↗ instead of reading them. A recent benchmark ↗ confirmed it measurably: agents scored higher on the same task in a modular monolith than in the equivalent set of microservices. That cost compounds — every prompt, forever.

The longer we waited, the more features shipped, and the more we thought: "Yes, we'd do it differently today, but changing it now isn't worth it."

That was a reasonable intuition. Migrating a database — especially one holding payment data — means carefully designing the merge, running it in test, rechecking for lost rows, wiring a rollback switch, and making sure it can run against real traffic without hurting users. It's a serious piece of engineering.

But the more we worked with agentic AI, the more confident we got in both its ability to produce quality code and our ability to control the failure modes.

What we thought would take months took days. In one working day we rewrote the microservice back into the monolith and implemented the migration, rollbacks, dry-runs, and per-stage safety mechanisms. On the second day we covered every stage with metrics, logs, and a dashboard. We tested each stage end-to-end, and the instrumentation gave us enough confidence that no data was being lost or duplicated.

The migration itself ran over the next three days, as smoothly as any migration can.

Before: Backend and Wallet run as two separate services with two databases connected by events, and Stripe webhooks go straight to the Wallet. After: the Wallet is a module inside the Backend process, one database, and Stripe webhooks go to the Backend.

Ephemeral architecture

Agents already gave us the idea of ephemeral interfaces — UIs that aren't coded in advance but generated on the fly, based on what the user needs at that moment.

Architecture is becoming ephemeral too. An architectural decision used to be a heavy one — a wrong call could put you on a path of suffering for years, sometimes decades. As the cost of major rewrites drops, it's not hard to imagine the very structure of software being recomposed on the fly to fit changing requirements.

Concretely, that starts to look like:

  • Services that come and go. Our wallet was a microservice, is now a module, and could be a service again if we ever ship the second payment provider we planned for. The next split won't be a quarter of design; it'll be a week of implementation with an agent doing most of the plumbing.

  • Module boundaries that drift. Where we draw internal lines is a function of what humans and agents are editing together right now. When those patterns shift, the lines should move — and now they can, cheaply.

  • Migration playbooks assembled on demand. The staged webhook-relay-then-cutover pattern we used to move the wallet back wasn't a bespoke invention. Any agent with access to the codebase can reproduce it. Bespoke migration design used to be most of the work; now most of the work is deciding you want to migrate.

None of this is a fully ephemeral system yet. But the direction is clear: the shape of software will look less like laying track and more like moving furniture — measured in days rather than quarters, and reversible when the requirements change.


Further reading

0
Ignat TubylovIgnat TubylovEngineer at Kineto

Jul 30, 2026

More articles

Copyright © 2026 Kineto