Loading...
Loading...

The product is live. Paying customers. Real revenue. Nineteen days from first commit to first payment.
I'm going to walk through exactly what I built, how I built it, every major decision I made, and every place I screwed up. No retrospective polish. This is what actually happened.
I built a B2B tool that helps marketing teams audit and optimize their content portfolios. The core features:
This is not a toy. Real companies are using it. I have six paying customers from the first month, each paying between $99 and $299/month.
Tech stack:
Nineteen days is not impressive because it's fast for a solo developer. It's impressive because this product would have taken a 3-4 person team two to three months in 2022. I'm one person. The AI did roughly 70% of the code writing.
The AI didn't replace my judgment. It replaced my execution time. I still designed every feature, made every architectural decision, defined every data model, and reviewed every significant piece of code. What I didn't do: write 80% of the implementation code manually.
Here's the breakdown of my time across 19 days:
| Activity | Days |
|---|---|
| Product design and spec writing | 3 |
| Architecture planning and CLAUDE.md setup | 1 |
| AI-assisted implementation (core features) | 8 |
| Testing, debugging, and refinement | 3 |
| Deployment and infrastructure setup | 1 |
| Landing page, onboarding, and marketing copy | 2 |
| Customer conversations and adjustments | 1 |
The most important day was day one. Not coding. Designing.
Most developers want to start coding immediately. This is a mistake that AI makes more expensive, not less. If you spec a feature poorly and ask an AI to implement it, you get a well-executed version of the wrong thing. Reverting is expensive.
I spent three full days on product design before writing a line of code.
Day 1: Customer conversations. I talked to five people in my target market. Marketing managers at companies with 10-50 person teams. I asked about their content problems, not my solution. The most common pain: they had no systematic way to evaluate which content was working, which was cannibalistic (multiple pieces targeting the same keyword), and which topics they were completely missing.
Day 2: Feature specification. I wrote a detailed spec for each feature. Not technical specs. Product specs. "User can see a content gap report that shows topics their competitors cover that they don't, sorted by estimated traffic value." This level of specificity matters enormously when you're prompting AI to implement it.
Day 3: Architecture planning and CLAUDE.md. I designed the data model, the component structure, the API layer, and the agent workflows. Then I wrote a 1,200-word CLAUDE.md file that described all of this to the AI agent that would help me build it.
The CLAUDE.md was the highest-leverage thing I wrote. Here's the structure I used:
## Project Overview
[What this is, who uses it, why]
## Tech Stack
[Each technology with version and why we chose it]
## Data Model
[Every entity with fields and relationships]
## Component Architecture
[Directory structure with descriptions]
## Coding Conventions
[Specific, not generic: "use server actions for mutations, not API routes"]
## Testing Requirements
[What tests are required for each component type]
## AI Integration
[How the Claude API is used, prompt patterns, output validation]With this document, the AI agent understood the project as a whole, not just the individual file it was editing.
This is the part people get wrong when they think about AI-assisted development. They imagine something like: "type a request, get working code."
The reality is more like pair programming with a very fast, very knowledgeable developer who occasionally needs correction.
Every development session followed the same pattern:
Define the task precisely. Not "build the content analysis feature." "Build the ContentAnalysisEngine class with methods for: (1) ingesting a URL and extracting content, metadata, and semantic structure; (2) comparing content against a corpus of competitor URLs; (3) returning a structured ContentAnalysisResult type that includes gaps, duplicates, and recommendations."
Let the agent implement. I watched and noted issues but didn't interrupt for minor things.
Run the tests the agent wrote. If they pass, I review the code quality. If they fail, I work with the agent to fix them.
Review for architectural alignment. Does this fit the patterns established in CLAUDE.md? Does it introduce technical debt? Is the code readable?
Iterate. Usually two to three passes per major feature.
Convex's real-time reactive backend eliminated an enormous amount of code. Here's a concrete example.
The content analysis pipeline needed to:
In a traditional backend, this requires: a job queue, a worker process, WebSockets or polling, a results storage schema, and error handling for all of the above. That's probably 400-600 lines of infrastructure code.
In Convex, the same thing is:
// convex/analysis.ts
import { action, mutation, query } from "./_generated/server";
import { v } from "convex/values";
import Anthropic from "@anthropic-ai/sdk";
export const analyzeContent = action({
args: { url: v.string(), workspaceId: v.id("workspaces") },
handler: async (ctx, { url, workspaceId }) => {
// Create analysis record
const analysisId = await ctx.runMutation(api.analysis.createAnalysis, {
url,
workspaceId,
status: "processing",
});
try {
// Step 1: Content extraction
await ctx.runMutation(api.analysis.updateProgress, {
analysisId,
stage: "extracting",
progress: 0.1,
});
const content = await extractContent(url);
// Step 2: AI analysis
await ctx.runMutation(api.analysis.updateProgress, {
analysisId,
stage: "analyzing",
progress: 0.3,
});
const anthropic = new Anthropic();
const analysis = await anthropic.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 2000,
messages: [{
role: "user",
content: buildAnalysisPrompt(content),
}],
});
// Step 3: Store results
await ctx.runMutation(api.analysis.completeAnalysis, {
analysisId,
results: parseAnalysisResults(analysis),
status: "complete",
});
} catch (error) {
await ctx.runMutation(api.analysis.updateAnalysis, {
analysisId,
status: "failed",
error: String(error),
});
}
return analysisId;
},
});
// Real-time query - UI automatically re-renders when this changes
export const getAnalysis = query({
args: { analysisId: v.id("analyses") },
handler: async (ctx, { analysisId }) => {
return await ctx.db.get(analysisId);
},
});The UI then subscribes:
// components/AnalysisProgress.tsx
"use client";
import { useQuery } from "convex/react";
import { api } from "@/convex/_generated/api";
export function AnalysisProgress({ analysisId }: { analysisId: Id<"analyses"> }) {
// This automatically re-renders when the analysis updates - no polling
const analysis = useQuery(api.analysis.getAnalysis, { analysisId });
if (!analysis) return <Skeleton />;
return (
<div className="space-y-4">
<Progress value={analysis.progress * 100} />
<p className="text-muted-foreground text-sm">
{stageLabels[analysis.stage]}
</p>
{analysis.status === "complete" && (
<AnalysisResults results={analysis.results} />
)}
</div>
);
}That's the complete real-time analysis system. No WebSocket setup. No polling. Convex handles it. The AI agent wrote most of this from my spec.
Stripe billing felt intimidating before I did it. The docs are extensive. The edge cases are many. AI made it tractable.
Here's the implementation pattern I used for subscriptions:
// convex/billing.ts
import { action } from "./_generated/server";
import { v } from "convex/values";
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
// Create checkout session
export const createCheckoutSession = action({
args: {
priceId: v.string(),
workspaceId: v.id("workspaces"),
},
handler: async (ctx, { priceId, workspaceId }) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) throw new Error("Unauthenticated");
const workspace = await ctx.runQuery(api.workspaces.get, { workspaceId });
if (!workspace) throw new Error("Workspace not found");
const session = await stripe.checkout.sessions.create({
customer_email: identity.email,
line_items: [{ price: priceId, quantity: 1 }],
mode: "subscription",
success_url: `${process.env.APP_URL}/billing/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.APP_URL}/billing`,
metadata: {
workspaceId: workspaceId,
userId: identity.subject,
},
});
return session.url;
},
});
// Handle webhook events
// app/api/stripe/webhook/route.ts
export async function POST(req: Request) {
const body = await req.text();
const sig = req.headers.get("stripe-signature");
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
body,
sig!,
process.env.STRIPE_WEBHOOK_SECRET!
);
} catch (err) {
return Response.json({ error: "Invalid signature" }, { status: 400 });
}
switch (event.type) {
case "checkout.session.completed": {
const session = event.data.object;
await convex.mutation(api.billing.activateSubscription, {
workspaceId: session.metadata!.workspaceId as Id<"workspaces">,
stripeCustomerId: session.customer as string,
stripeSubscriptionId: session.subscription as string,
});
break;
}
case "customer.subscription.deleted": {
const subscription = event.data.object;
await convex.mutation(api.billing.cancelSubscription, {
stripeSubscriptionId: subscription.id,
});
break;
}
}
return Response.json({ received: true });
}Several things were different from what I expected.
AI was better at boilerplate than at business logic. Authentication flows, CRUD operations, component scaffolding, database schema, these came out nearly perfect on the first try. Complex business logic, like the algorithm for identifying content gaps by comparing semantic similarity across URL sets, required multiple iterations and careful review.
Testing was the multiplier. The AI wrote tests as it built features. This meant I caught problems in hours instead of days. When a refactor broke something, the tests told me immediately. The confidence from comprehensive test coverage let me move faster than I would have without it.
The spec time paid off. Every hour I spent on product spec in days 1-3 saved me two to three hours of rework later. Vague specs produced mediocre features. Specific specs produced features that worked.
Convex eliminated more code than I expected. I estimated Convex would save maybe 30% of backend code. The actual savings were closer to 60%. The reactivity system, the schema validation, the real-time subscriptions, together they replaced what would have been hundreds of lines of infrastructure code.
I ran the product against real content from five websites, including my own. Several things broke:
Fixes:
Each fix took a fraction of what it would have taken without AI. The Playwright integration was a morning's work. The parallel processing was an afternoon. The prompt improvement was an hour.
Vercel deployment was straightforward with one gotcha: environment variables.
I had 12 environment variables across development, staging, and production. Managing them manually is a recipe for errors. I set up a structured approach:
# .env.example - committed to version control
CONVEX_DEPLOYMENT=
NEXT_PUBLIC_CONVEX_URL=
CLERK_SECRET_KEY=
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=
STRIPE_SECRET_KEY=
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
STRIPE_WEBHOOK_SECRET=
ANTHROPIC_API_KEY=
APP_URL=I used Vercel's environment variable interface for production, and a local .env.local for development. The CI/CD pipeline checks for required variables at build time.
The landing page took two days. I wrote the copy myself because the positioning required genuine market understanding. The AI built the components from my designs. I used shadcn/ui for the core structure and added custom animations with Framer Motion.
I posted in three Slack communities where my target customers hung out. Not a sales pitch. A post explaining what I built, why I built it, and offering 60 days free for early feedback.
Nineteen people signed up. Six converted to paid after the trial. The median company size was 35 employees.
The most common piece of feedback: the gap analysis was the most valuable feature, not the audit feature I thought was the core product. This is exactly why you ship and talk to customers instead of building in secret.
| Metric | Value |
|---|---|
| Total development time | 19 days |
| Lines of code I wrote manually | ~800 |
| Lines of code AI generated | ~4,200 |
| Paying customers in month 1 | 6 |
| Monthly recurring revenue | $1,194 |
| Infrastructure costs | $180/month |
| Time to breakeven | Month 2 |
Would I do anything differently? Yes. I'd spend even more time on the product spec and less time on implementation details. The customers didn't care about my elegant Convex queries. They cared about whether the product solved their problem.
The technology mattered in that it let me ship fast. The speed mattered because I could iterate based on real feedback instead of building on assumptions. The customers are what make it real.

Convex eliminates most backend code. Pair it with AI and you're shipping real-time features that would have taken weeks in a few hours.

Vercel deploys are easy until they're not. Environment variables missing, build errors in prod only, domain weirdness. Here's how to get it right the first time.

Stripe handles payments. AI handles the complexity around them. Here's how to build a complete billing system with subscriptions, usage, and smart dunning.
Stop reading about AI and start building with it. Book a free discovery call and see how AI agents can accelerate your business.