I Built a SaaS Product Using Only Cursor and Claude. The Surprising Part Wasn’t the Code.
The era of small teams isn’t coming. It’s already here.
I Built a SaaS Product Using Only Cursor and Claude. The Surprising Part Wasn’t the Code.
The era of small teams isn’t coming. It’s already here.

I built a SaaS product using only Cursor and Claude. Here’s what actually happened, what broke, and why software engineering is changing.
Three years ago, this headline would have sounded ridiculous.
Today, it’s barely controversial.
I recently built a SaaS product using almost nothing except Cursor and Claude. No engineering team. No dedicated frontend developer. No backend specialist. No DevOps engineer.
Just me.
And here’s the part most people get wrong:
The difficult part wasn’t generating code.
The difficult part was making decisions.
AI removed perhaps 80% of the typing.
It removed almost none of the thinking.
That’s an important distinction.
Because while everyone is debating whether AI will replace developers, something far more interesting is happening:
AI is replacing the parts of software development that were never the bottleneck.
Software Development Was Never About Writing Code
Most developers secretly know this.
The code itself is rarely the hard part.
The hard part is deciding:
- What should exist
- What should not exist
- Which tradeoffs matter
- Which complexity is unavoidable
- Which complexity is self-inflicted
Before AI, these decisions were buried under weeks of implementation work.
Now they’re exposed.
Painfully exposed.
Cursor can generate a CRUD API in minutes.
Claude can design database schemas faster than most engineers can sketch them on a whiteboard.
But neither tool knows:
- What your customers actually need
- Which features generate support tickets
- Which architecture decisions become technical debt six months later
Those are still human problems.
The First Mistake AI Helped Me Make Faster
The SaaS product started small.
A fairly straightforward workflow:
Users submit requests.
Background jobs process them.
Results appear in a dashboard.
Simple.
Or so I thought.
Within hours Cursor had generated:
- FastAPI services
- PostgreSQL models
- Docker configuration
- Redis caching
- Authentication flows
The velocity felt magical.
Then I looked at the architecture.
Claude had essentially built what looked like a startup preparing for 50 million users.
I had zero users.
Classic engineer mistake.
Just executed faster.
The Architecture I Almost Shipped
The AI-generated version looked something like this:
API Gateway
|
----------------------------------
| | |
User Service Billing Service Job Service
| | |
----------------------------------
|
Kafka Cluster
|
----------------------------------
| | |
Email Worker Analytics Worker Audit Worker
Looks impressive.
Also completely unnecessary.
I wasn’t building Stripe.
I was building an MVP.
Every service boundary creates:
- deployments
- monitoring
- debugging overhead
- operational complexity
The architecture was solving problems I didn’t have.
What I Built Instead
A modular monolith.
The most underrated architecture pattern in software engineering.
FastAPI Application
|
---------------------------------------
| | | |
Users Billing Jobs Admin
| | | |
---------------------------------------
PostgreSQL
|
Redis
One deployment.
One database.
One repository.
Clear module boundaries.
Zero distributed systems headaches.
The product launched weeks faster because of this decision.
Not because of AI.
Because of judgment.
Lesson #1: AI Makes Good Decisions More Valuable
When implementation becomes cheap, architecture becomes expensive.
That’s the paradox.
A junior developer can now generate 5,000 lines of code before lunch.
A bad architectural decision can still cost six months.
Nothing changed there.
Idempotency Is More Important Than Ever
AI generated my first payment workflow.
It looked clean.
It was also broken.
Bad Implementation
@app.post("/payments")
async def create_payment(payment: PaymentRequest):
await db.execute(
"""
INSERT INTO payments(user_id, amount)
VALUES ($1, $2)
""",
payment.user_id,
payment.amount
)
return {"status": "success"}
Looks fine.
Until:
- network retries happen
- client requests duplicate
- browser refreshes
- mobile reconnections occur
Now customers get charged twice.
Not great.
Production Version
@app.post("/payments")
async def create_payment(
payment: PaymentRequest,
idempotency_key: str = Header(...)
):
existing = await db.fetchrow(
"""
SELECT *
FROM payments
WHERE idempotency_key = $1
""",
idempotency_key
)
if existing:
return existing
payment_record = await db.fetchrow(
"""
INSERT INTO payments(
user_id,
amount,
idempotency_key
)
VALUES ($1, $2, $3)
RETURNING *
""",
payment.user_id,
payment.amount,
idempotency_key
)
return payment_record
AI generated both versions.
Only one survives production.
Async Workflows Beat Synchronous Dreams
Every beginner SaaS architecture eventually reaches the same moment.
The request starts taking too long.
Users click refresh.
Everything breaks.
My original endpoint looked like this:
@app.post("/generate-report")
async def generate_report():
report = expensive_report_generation()
send_email(report)
upload_to_storage(report)
return report
Response time:
12–18 seconds.
Terrible.
The fix:
@app.post("/generate-report")
async def generate_report():
job_id = str(uuid.uuid4())
await redis.lpush(
"report_queue",
json.dumps({"job_id": job_id})
)
return {
"job_id": job_id,
"status": "processing"
}
Worker:
while True:
job = redis.brpop("report_queue")
process_report(job)
update_status(job["job_id"])
Now requests finish in milliseconds.
Users feel speed.
Infrastructure feels relief.
Everybody wins.
The Outbox Pattern Saved Me From Data Corruption
Distributed systems don’t fail dramatically.
They fail quietly.
Imagine:
create_invoice()
publish_event()
What happens if the database write succeeds but the event fails?
You now have inconsistent state.
The worst kind of bug.
Instead:
with db.transaction():
create_invoice()
insert_outbox_event(
event_type="invoice_created"
)
Background worker:
while True:
events = get_pending_events()
for event in events:
publish(event)
mark_processed(event.id)
This pattern has existed for years.
Yet AI-generated code rarely includes it by default.
Because most examples online don’t.
That’s a fascinating lesson.
AI inherits the internet’s average quality.
Not its best practices.
Observability Is the New Superpower
AI can generate code.
AI cannot explain a production outage at 3:17 AM.
For that you need visibility.
Every service should emit structured logs.
logger.info(
"payment_processed",
user_id=user.id,
payment_id=payment.id,
amount=payment.amount
)
Bad logs:
logger.info("success")
Successful what?
Nobody knows.
Especially future you.
Metrics matter too.
payment_counter.inc()
request_duration.observe(
processing_time
)
You can’t optimize what you can’t see.
And most performance problems aren’t where engineers think they are.
Redis Was Worth More Than Most Refactors
Developers love rewriting systems.
Users love fast applications.
Different priorities.
A simple cache reduced database load dramatically.
cached_user = await redis.get(
f"user:{user_id}"
)
if cached_user:
return json.loads(cached_user)
user = await get_user(user_id)
await redis.setex(
f"user:{user_id}",
300,
json.dumps(user)
)
return user
Five minutes of caching.
Weeks of scaling postponed.
Rate Limiting Is a Feature, Not a Security Layer
The first public launch brought bots.
Of course it did.
Simple protection:
from slowapi import Limiter
limiter = Limiter(
key_func=get_remote_address
)
@app.post("/api/search")
@limiter.limit("100/minute")
async def search():
...
One line.
Huge difference.
Sometimes engineering isn’t about sophistication.
It’s about restraint.
Why Teams Historically Overengineered Everything
This isn’t an AI problem.
It’s an industry problem.
For years, developers learned architecture from companies like:
- Netflix
- Uber
- Amazon
The problem?
They were solving problems at extraordinary scale.
Most startups aren’t.
Most SaaS products never will.
Yet teams copied:
- microservices
- event meshes
- service discovery
- distributed transactions
before finding product-market fit.
The result wasn’t scalability.
The result was meetings.
Lots of meetings.
Microservices vs Monolith: The Conversation AI Changed
AI made implementation cheap.
Which means coordination became the dominant cost.
A microservice architecture introduces:
Service A -> Service B -> Service C
Every arrow is:
- latency
- failure risk
- deployment complexity
- debugging effort
For small teams, modular monoliths often outperform distributed architectures.
Not because they’re fashionable.
Because they’re efficient.
Most scalability problems are coordination problems wearing a CPU costume.
When This Advice Fails
There are absolutely situations where complexity is justified.
Examples:
- Hundreds of engineers
- Independent deployment requirements
- Massive traffic volumes
- Strict compliance boundaries
- Multiple autonomous teams
At that point:
- Kafka makes sense
- Service boundaries matter
- Event-driven systems pay off
The mistake is adopting enterprise architecture before enterprise problems arrive.
What Smart Teams Are Actually Doing Today
The strongest engineering teams I’ve seen recently tend to follow a surprisingly similar stack:
Application
- FastAPI
- Next.js
- TypeScript
Database
- PostgreSQL
Cache
- Redis
Queue
- RabbitMQ or lightweight Kafka usage
Infrastructure
- Docker
- Managed cloud services
Monitoring
- OpenTelemetry
- Prometheus
- Grafana
Architecture
- Modular monolith first
- Extract services only when necessary
Notice what’s missing.
Complexity.
Not because complexity is bad.
Because unnecessary complexity is expensive.
The Unexpected Truth About Building With Cursor and Claude
People think AI changes software engineering because it writes code.
I think it changes software engineering because it exposes thinking.
The implementation bottleneck is collapsing.
The judgment bottleneck isn’t.
Cursor generated thousands of lines.
Claude generated architecture suggestions.
Both accelerated execution dramatically.
Neither replaced engineering.
If anything, they amplified it.
Because every decision now arrives faster.
Good ones and bad ones.
And that means the developers who thrive won’t be the fastest typists.
They’ll be the best decision makers.
The future isn’t developers versus AI.
It’s developers with leverage versus developers without it.
And leverage has never been the same thing as wisdom.
That’s still a human skill.
For now.
메타데이터
- post_id
- aa07e97d7bf2
- slug
- i-built-a-saas-product-using-only-cursor-and-claude-the-surprising-part-wasnt-the-code-aa07e97d7bf2
- url
- https://medium.com/@kaushalsinh73/i-built-a-saas-product-using-only-cursor-and-claude-the-surprising-part-wasnt-the-code-aa07e97d7bf2
- canonical_url
- https://medium.com/@kaushalsinh73/i-built-a-saas-product-using-only-cursor-and-claude-the-surprising-part-wasnt-the-code-aa07e97d7bf2
- author_url
- https://medium.com/@kaushalsinh73
- status
- ok
- fetched_at
- 2026-07-07 13:53:00