AI Code Generated Software Development:
Who Makes the Final Call?

What happens when AI code generated software can build a feature across your backend, frontend, payments, tests, and integrations?
You might expect a simple answer: the team ships faster.
But the more interesting question is what happens to engineering work when implementation becomes much easier.
A recent feature gives us a useful example. It was a prepaid gift system built into a Rails API. A customer could pay through Stripe, receive a unique gift code, and send it to someone else. The recipient could redeem it for premium access or queue the gift until their existing access expired.
The feature also included scheduled emails, PDF gift cards, admin tooling, mobile and web interfaces, analytics, CRM synchronization, and integration with an external entitlement service.
It was not a small feature. And AI code generated agents were fully involved heavily in building it.
The stack was doing a lot of work
The backend was built with Ruby on Rails 7.2, PostgreSQL, Stripe, Sidekiq, Prawn, Pundit and RSpec.
The system also integrated with RevenueCat for entitlements and Mailchimp for contact and subscription synchronization. On the development side, the team used Claude Code with repository-specific instructions and skills, Figma and Notion MCP integrations, and GitHub Copilot review feedback.
That combination matters.
A payment webhook is not just an API endpoint. It can create a record, trigger asynchronous work, generate a PDF, send an email, update analytics, and eventually grant access through another system.
The interesting engineering problems therefore live between technologies.
Stripe has its own lifecycle. Rails has its transaction and locking model. PostgreSQL has its constraints. Sidekiq has its job semantics. RevenueCat has its entitlement model. The mobile app has its own expectations for what a successful response looks like.
AI can work with each of these pieces.
The hard part is deciding how they should behave together.
The AI code generated could build the first version
The available evidence shows substantial AI code generated implementation.
One engineer authored 9 of the feature’s 20 PRs and 32 of its 76 gift-related commits. For that portion, the transcripts consistently show AI drafting code and tests.
That gives us evidence that at least roughly 40% of the work units were AI-drafted, while the total AI contribution across the entire feature remains unknown because the other engineer’s AI sessions weren’t available.
And the AI was not just autocomplete.
It generated backend services and controllers, redemption flows, entitlement logic, validation endpoints, admin interfaces, mobile screens, and tests.
It also investigated documentation, reviewed code, produced backend-to-frontend contracts, and explored alternative implementations.
So what did the engineers actually do?
A lot of deciding.
Payment is a good example
The AI initially designed the gift to exist before payment was confirmed. The engineer rejected it. Instead, the final implementation passed the gift information through Stripe metadata and created the gift only after payment was confirmed.
That sounds like a relatively small implementation detail. It isn’t. It changes the system’s definition of reality. A gift shouldn’t exist as a valid purchased gift just because someone started checkout. It should exist because payment happened.
The AI could implement either model. The engineer had to decide which model the business could trust.
Then came idempotency
Payment systems have another uncomfortable property: events can happen more than once. The implementation used PostgreSQL partial unique indexes and application-level handling to make payment fulfillment idempotent. Redemption also introduced locking, while read-only validation was deliberately kept lock-free.
These decisions look very technical but ultimately reflect system behavior under failure. The AI proposed refactors around locking, and the engineer rejected some of them. In one case, the engineer explicitly changed the approach to lock once at the appropriate point instead of checking repeatedly. For queued gifts, the engineer also decided that redemption should return already_redeemed rather than letting the frontend interpret the situation as a successful redemption.
The code was being generated quickly. Humans were still negotiating the semantics.
AI can find bugs too
This wasn’t a story about humans constantly correcting an incompetent machine.
The interaction went both ways.
AI identified a problem with premium-access gating and suggested using has_premium_access?.
It identified a race condition in the redemption flow.
It questioned assumptions around gift validity.
It also caught problems in the engineer’s merge resolution.
At the same time, the AI produced incorrect or misleading findings.
It incorrectly questioned whether scheduled workers were actually scheduled. It misunderstood a webhook issue before the engineer pushed it for further investigation. During an edit, it generated a delivered gift code again, which the engineer caught.
There were also cases where repository rules were applied literally, even though they didn’t reflect how the application actually worked.
That is a much more interesting model of AI code-generated development than “AI writes code, human reviews it.”

The device changed the architecture
One of the clearest examples came from the mobile experience.
The AI initially reasoned that full navigation screens made sense for the redemption flow. The engineer challenged that and moved toward modals.
Then the actual device provided new information.
Nested modals caused a freeze.
The architecture changed again, this time based on observed device behavior rather than reasoning inside the coding environment. The team moved back toward screens and redesigned part of the flow.
This is an important boundary for AI code generated development.
The model can reason about a mobile interface.
It cannot substitute for the device.
The engineer supplied evidence that existed only outside the code-generation loop: screenshots, production errors, device behavior, design details, and knowledge of how the existing product actually works.
The AI also wanted to build more
Another recurring pattern was over-engineering. The AI proposed additional services, abstractions, and refactors. Some were useful. Others were rejected or reverted because they did not fit the existing architecture. The engineer repeatedly cut unnecessary componentization and refactoring in the admin UI to keep it consistent with the existing promo-code screens.
A technically elegant abstraction is not automatically a good product decision. Sometimes the best architectural decision is to not introduce another abstraction. That is particularly relevant when AI makes implementation cheap. When writing another service class costs almost nothing, the constraint is no longer the cost of writing it. The constraint becomes whether the system should have one.
The API contract was also a human decision
The unified promo and gift-code validator is another good example.
The AI proposed a unified endpoint.
The engineers then made the important product and API decisions around it.
The endpoint returns a consistent 200 response containing validity, type, and machine-readable reason codes. Gift redemption requires authentication, while shared validation does not. The reason codes were designed as codes rather than English messages so the frontend could support internationalization later.
Those decisions are easy to underestimate.
A developer can ask an AI:
“Build a validation endpoint.”
The harder question is:
“What exactly should the frontend be allowed to assume when this endpoint responds?”
That question belongs to the system, not to the endpoint.
Fourteen days, twenty PRs, and a lot of decisions
The feature took about 14 working days, or 18 calendar days.
Around 20 PRs were involved, covering roughly 21 gift service classes, eight migrations, and approximately 248 test examples. The implementation was merged into the integration branch, although the available evidence does not show a production release.
The evidence supports at least ~42% of work units being AI-drafted, but it doesn’t provide a precise percentage for the entire feature.
The evidence clearly shows the nature of the interaction.
The AI generated substantial amounts of implementation.
Humans repeatedly changed the architecture, narrowed the scope, rejected approaches, supplied missing context, tested on real devices, and decided what the system should mean.
The new bottleneck may not be writing code
This is where AI code generated development gets interesting.
If generating another controller, service, test, component, or migration becomes dramatically cheaper, the scarce resource starts moving somewhere else.

They are questions of product knowledge, architecture, system behavior, risk, and context.
AI can participate in all of them. It can propose alternatives, find inconsistencies, challenge assumptions, and write the implementation.
But someone still has to decide which reality the software should implement.
And as implementation becomes increasingly elastic, that decision layer may become one of the most important engineering capabilities a team can develop.




