When the last PR merged on our per-user pricing release, I sat staring at my screen for a while. I had just redesigned and shipped our billing page, in our actual codebase, mostly on my own. That had never happened before. It was scary, empowering, mind-blowing, all at the same time.
This is how I got there. It worked, but I want to be honest about why. This isn't a "product managers should ship code now!" manifesto. It's one product manager’s experience, and the questions it left me with.
How I ended up in the code
Per-user pricing was a project months in the making. Arielle, our Interim CEO and head of marketing, had driven the initial spec on pricing and packaging. Peldi, our product designer and owner, wireframed the initial design in Balsamiq Cloud: three tiers, a new seat permission system, AI credits per tier. The plan was to offload most of the new billing flows to Stripe and keep our UI thin. Even at that stage, I'd flagged that I wasn't sure Stripe would cover everything, but we left it as something to figure out during build.

During build, the question came back: how would we handle gated features (SAML, data residency, billing admin) inside Stripe? Stefano, our Balsamiq Cloud architect, said we could route them through Stripe with some workarounds. The technical path existed. But going that route meant we couldn't properly communicate to users why some options weren't available to them. That was the part I couldn't get past. Owning the UI ourselves meant I could choose how to communicate the gating directly. It also meant more surface for us to own and more product decisions to work through.
Engineering split the work. Stefano took the backend: Stripe integration, plan migration, billing rules. The product engineering team took the in-product changes. The billing page existed as a first pass that Claude had generated from the pitch. Functional, but not designed.
When Stefano finished the backend, he flagged the billing page early. It made sense for someone else to take the refinement work. He said in Slack:
"What you see is what Claude came up with after reading the pitch. I think it supports all required use cases but it definitely needs some UX/UI work."
That message was the door opening. I'd been wanting to take the page on myself for a while. There were two things stopping me: a technical blocker (I couldn't run Cloud locally) and a cultural one (PMs writing serious implementation code wasn't something we'd done at Balsamiq before). When I raised both with Peldi in our 1-1, I also flagged that his design time was better spent on bigger problems than a billing page polish pass. He agreed: this would be a good thing for me to work on, and he'd review.
I was already slated to own QA. I'd managed Olio, our internal licensing system, for years, and I'd become the go-to person on subscription state and billing edge cases. So the QA part wasn't new. The new piece was that I would also design and implement.
There was one piece of infrastructure that made any of this possible. Engineering had given me what I needed to run Balsamiq Cloud locally. Stefano set up a single CLI command and made the first few steps super easy. Without that, none of what follows would have been an option.
What hands-on actually looked like
The AI assistance ran through every phase, not just the code.
Prototyping the design. Before any production code, I first built a prototype using Peldi's Balsamiq Cloud wireframes as my reference. It wasn't in our actual codebase yet, just a standalone prototype I could iterate on quickly. I used it to walk Stefano and our CX team through the design decisions and to collect feedback on the trickier ones: refund policy, billing admin gating, what to show in the trial pricing flow. The recap I wrote afterward became the implementation brief.

Writing the implementation plan. Once the prototype was approved, I wrote an implementation plan with Claude. Task by task, file by file. The plan was mostly for me, a way to make sure I'd covered my bases before diving in.
This was before our MCP server went live. If I’d had access to it, I would have asked Claude to read my project and create the build plan from my prototype.

Implementing in code. With local Balsamiq Cloud running, I opened the codebase with Claude Code as my build partner. The job was to take the implementation plan and put it into practice. The first round was the billing page UI, adapting Claude's initial pass to fit Stefano's backend hooks. The rest was polish: small visual tweaks, credit limit adjustments, copy changes. For each task, I wrote the spec, Claude Code proposed the change, I reviewed, sometimes pushed back, and committed.

Testing the same way. QA wasn't a separate phase done with a different toolkit. I wrote an AI-assisted QA plan, kept a running bug list across multiple passes, used an AI-generated probe template to test billing flows at scale, and wrote unit tests for the changes. The same partnership that wrote the implementation wrote the verification.

The work also surfaced gaps the pitch hadn't named explicitly. That's pretty much always the case: a pitch can take you far, but some claims only get proven or disproven once you're in the build. Legacy plans needed to retain the ability to switch between legacy tiers, and they also needed a path to migrate to per-user pricing. Canceled legacy plans could only resubscribe to per-user. These showed up during implementation, not during QA or post-launch. I surfaced each one in Slack or 1-1s while we were still in the build phase.

Working with engineers, not around them
The starting point wasn't blank, as Stefano had already implemented the backend and a basic version of the updated billing page with Claude. But it still needed a pass to answer questions such as:
- How should the plan-switching table behave when someone has a gated feature enabled?
- What's the right hover state for a Cancel link that shouldn't compete with the primary CTA?
- Should the trial pricing flow show the editor cap or not?
These were product calls, and I was the closest person to them.
After the main PR merged but before release, the work didn't stop. I opened additional PRs for things I found during QA prep, including one I caught that I considered a showstopper for the release: a path where a re-subscribe at lower capacity could allow free seats. I fixed it in code, opened the PR, and it was approved the next morning. Without me in the code, that fix would have moved at a different speed: a ticket filed, picked up, context shared, reviewed. We'd have caught it later than we did.

At one point I asked Stefano directly whether the division of labor was working for him. Whether it would have been easier to just do it himself. His answer reframed how I had been thinking about the whole thing:
"Coding comes easiest to the person who's closest to the domain or topic. In this case it's you, so this is the most efficient solution overall. I'm just serving as the safety net, to ensure Claude behaves... sometimes it veers off track [with respect to] our conventions."
His answer gave me confidence that things were working well. The way I see it now: when AI is in the loop, the work goes to whoever's closest to the product judgment. Engineering becomes the safety net, the convention-keeper, the one who makes sure Claude doesn't ship something weird. That's a different role from writing the implementation, but it's just as important.

Some specific moves that kept this from feeling like an imposition:
Engineering set the boundary at the start. Stefano naturally worked in his area of expertise, which is the backend, and I stepped in to refine the UI. As the work moved into the fixing phase, those lines blurred. I'd find something during testing, fix it in the codebase, send it for review. The clean handoff stopped being clean, and that was fine because trust had been built.
I asked before touching anything I wasn't sure about, especially early on, and I never deployed to production myself. PRs went through normal review, I specifically requested Stefano’s eyes, and I learned the team's conventions (squash-merge to keep history clean; reading existing patterns before proposing new ones).
The same tightening showed up on the design side. After the working call with Stefano and CX, our brand design lead sent me a Slack thread with issues she'd spotted on the billing page. In the old workflow, that feedback would have become tickets in engineering's backlog and shipped over the following days or weeks. Instead, I opened a PR with the fixes that afternoon. (Even shorter if our designer had written the fixes herself; same principle, different role.)
A lot has changed for PMs in the AI era. The past year proved that to me. The core craft of the job, though, has stayed the same. The cross-team glue is still the work, and so is the institutional knowledge that comes with it: knowing our users, knowing the edge cases, knowing the history, and advocating for the user when the code gets written. What changed for me was the cadence: with AI helping me write the implementation, I could close the gap between cross-functional feedback and a shipped change without losing the coordination that makes everything work.
The unexpected wins
A few effects I hadn't predicted.
The first was how much earlier I started catching bugs. Running the codebase locally meant I could run a scenario, watch what happened, change something, and run it again. The re-subscribe showstopper I mentioned earlier is the clearest case, but it wasn't the only one; the lag between thinking about an edge case and reproducing it shrank from days to probably minutes. As I worked through implementation, gaps in the pitch started surfacing that hadn't been visible before: the legacy migration path that wasn't specified, the legacy-to-per-user switch behavior, the cancel-and-resubscribe edge case. I flagged them in Slack while we were still building, and we agreed on the design before they became QA surfaces.
The business logic got clearer, too. Writing a spec when you'll be the one implementing it forces you to think through the rules differently. And the QA plan I wrote was tighter than my pre-code QA plans had been.
But the part that surprised me most was how the product manager job started to feel different: it felt like less translation, and more direct ownership. I wasn't writing a doc for someone else to interpret, I was making the call. What felt like magic to me was realizing how much I enjoyed it. I'd always considered myself someone who likes to do, not just direct, and being in the code let me act on that. Watching changes happen, catching small bugs and fixing them on the spot, there was a rhythm to it I hadn't experienced in PM work before.
The honest tradeoffs
The release itself went smoothly, although my personal hours stretched. Owning design, implementation, and QA in one person means I was doing work that would normally pass between roles. Some weeks I worked longer days than I would have otherwise.
It only worked because the conditions around me were right: engineering let me in, the team's culture didn't read product manager-in-code as a threat, and I had a manager who actively watched my hours and pushed me to stop when I crossed the line.
None of this generalizes. "Every product manager should ship code now" is the wrong takeaway. Your product process, access to the right tools/budget, and team culture have to enable it.
What I'm taking with me
I'm not going to claim this is the future of product management, but I will say it's a future I'm interested in living in.
I've been sitting with a question since this release shipped. Where else could the cycle be shorter? Where could the handoffs be replaced by closer collaboration with AI in the mix?
I don't have a clean answer yet, and I'm okay with that. It's not "everywhere," and that took me some time to sit with. Bounded work in a codebase I understand makes sense. New systems or critical infrastructure probably don't. The discipline part matters more than the tool, too.
The part I want to hold onto is the cadence. The same release, but a smaller loop around it: bugs caught the same day, design feedback shipped the same afternoon, edges of the pitch worked through during build instead of after.
Looking back
Scary, empowering, mind-blowing, all at the same time. That's how the release actually felt.
What surprised me most was finding out how much I enjoy being a builder. I'd always seen myself as a doer, but this was the first time the PM job let me act on that.
And because I was the one designing the billing page, the result was more customer-focused than it would have been otherwise. I'd been close to our customers through years of support, and I knew which edges they hit, which language confused them, what they looked at first. That knowledge went into the design directly, not translated through a doc for someone else to interpret. Arielle told me afterward that the work felt focused on the customer in a way it wouldn't have been otherwise.
That's the part I keep coming back to.