Analyzing the difference between 'code complete' and 'product complete' through the 3-week post-feature work (payment stability, security, legal docs, SEO, etc.) which accounted for 88% of the effort.
We Built an MVP in 3 Days
WICHI’s first version came out of the Jocoding Hackathon in just 3 days. Claude Code handled the backend API, and Lovable generated the frontend. Thanks to AI coding tools, the development cycle was completely different from what traditional timelines would suggest.
The 3-Day Tech Stack
| Layer | Technology | Role |
|---|---|---|
| Backend | FastAPI + Python | AI search engine queries, brand analysis, score calculation |
| Frontend | React + Vite (Lovable) | UI, dashboard, report views |
| DB | Supabase (PostgreSQL) | Users, analysis results, credit storage |
| Infra | Railway + Lovable hosting | Server deployment, frontend hosting |
| Auth | Supabase Auth | Sign-up, login, session management |
Features That Worked at Hackathon Submission
- Parallel queries to 3 AI search engines (ChatGPT, Perplexity, AI Overviews)
- Brand mention analysis and GEO Score calculation
- Analysis execution based on the 9-Bucket query framework
- User sign-up, login, and dashboard access
- Payment page (Lemon Squeezy integration)
- Analysis report generation and viewing
The code was done. We had a product with working core features.
But it was not ready to launch.
”It Works” and “It’s Launchable” Are Different Things
Only after the hackathon ended and we decided to commercialize did we realize there was a massive gap between a “working MVP” and a “product people can pay for.”
graph LR
A["Working MVP"] --> B["Launchable Product"]
subgraph GAP["What Was Missing"]
direction TB
C["Legal Notices"]
D["Payment Stability"]
E["Security Validation"]
F["Error Monitoring"]
G["SEO"]
H["Accessibility"]
end
A -.->|"3 days"| A
B -.->|"+3 weeks"| B
A -->|"Didn't know this gap existed"| GAP
GAP --> B
The defining characteristic of this gap was that the items were not technically difficult — we simply didn’t know they existed. Here’s the breakdown by category.
What Was Missing — By Category
1. Legal Notices — Privacy Policy, Terms of Service
We were collecting user emails without disclosing our data handling practices anywhere. We were accepting payments without any terms of service.
Specifically missing:
- Privacy Policy (
/privacy) — what data we collect, why, how long we keep it, whether we share it with third parties - Terms of Service (
/terms) — conditions of use, liability limitations, dispute resolution - Cookie consent banner — disclosure of third-party cookies from GA4, Supabase Auth, Lemon Squeezy, etc.
- Refund policy — conditions and procedures for refunds on a credit-based service
The moment you accept payments, legal notices are not optional. Both Korea’s Personal Information Protection Act and GDPR require disclosure before service usage. We learned this after launch.
2. Payment Stability — Webhooks, Idempotency, Transactions
The payment page existed, but the flow after payment was incomplete.
sequenceDiagram
participant User as User
participant App as WICHI
participant LS as Lemon Squeezy
participant DB as Database
User->>App: Payment request
App->>LS: Create checkout session
LS->>User: Redirect to payment page
User->>LS: Complete card payment
LS->>App: Send webhook
Note over App: Problems start here
App->>DB: Add credits
Note over App,DB: No idempotency → duplicate charges possible
Note over App,DB: No signature verification → forged requests possible
Note over App,DB: No retry logic on failure
Specifically missing:
- Webhook signature verification — no validation that incoming requests actually originated from Lemon Squeezy
- Idempotency handling — if the same webhook arrived twice, credits would be double-charged
- Payment failure handling — no user-facing flow for card declines or network errors
- Credit transaction safety — no recovery logic if the server errored during an analysis run after credits were deducted
Without webhook idempotency, double-charging actually happens. This is not theoretical — it occurred during testing.
3. Security Validation — Input Sanitization, Injection Defense
The frontend had basic input length limits. The backend accepted user input almost as-is.
Specifically missing:
- Backend input validation — no allowlist-based filtering. SQL or script injection was possible through the brand name input field
- Prompt injection defense — user input went directly into LLM prompts. Unintended instructions could be injected
- Rate limiting — any user could fire unlimited analysis requests
- CORS configuration — the API was callable from any domain
# Before: using input as-is
async def analyze(brand_name: str):
prompt = f"Analyze the brand '{brand_name}' in AI search results"
# brand_name could contain prompt injection
# After: allowlist + pattern filtering
async def analyze(brand_name: str):
if not re.match(r'^[a-zA-Z0-9가-힣\s\-\.]{1,100}$', brand_name):
raise ValidationError("Invalid brand name")
if contains_forbidden_patterns(brand_name):
raise ValidationError("Forbidden input detected")
prompt = f"Analyze the brand '{sanitize(brand_name)}' in AI search results"
With a free MVP, someone entering malicious input causes limited damage. The moment payments are involved, the incentive for abuse appears. Credit fraud, API abuse, and injection attacks translate directly into real costs.
4. Error Monitoring — Sentry, Alerting
There was zero monitoring. When an LLM API call failed during analysis, users saw a blank screen. And we had no idea it was happening.
Specifically missing:
- Error tracking service (Sentry, etc.) — no server/client error collection
- Uptime monitoring — no way to know if the server had gone down
- Alert channels — no Slack/email/Telegram notifications on errors
- User-facing error messages — no feedback like “An error occurred during analysis. Your credits will be restored”
We had no way to know errors existed until users reported them directly. That is not the structure of a paid service.
5. SEO — A Paid Service That Can’t Be Found
No robots.txt, no sitemap, no meta tags, no Open Graph images. We hadn’t even registered with search engines.
Specifically missing:
| Item | Status | Impact |
|---|---|---|
robots.txt | Missing | Crawlers don’t know which pages to index |
sitemap.xml | Missing | Can’t communicate page structure to search engines |
Meta tags (title, description) | Missing | No meaningful info in search results |
| OG images | Missing | Broken link cards on social sharing |
canonical URL | Missing | Potential duplicate page issues |
| Structured data (JSON-LD) | Missing | No Rich Snippet opportunities |
| Google Search Console registration | Not done | Can’t monitor indexing status |
For an early-stage SaaS with no ad budget, organic search is practically the only free acquisition channel. That channel was completely shut.
6. Everything Else — Small But Costly to Skip
- GA4 integration — no user behavior data collection, no way to measure conversion rates
- Error pages (404, 500) — showing default browser error pages
- Loading states — no loading indicator for analysis runs (which take 3-5 minutes)
- Email notifications — no email when analysis completes
- Mobile responsiveness — layout broken on some pages
Why We Didn’t Know
The Blind Spot of AI Coding Tools
AI coding tools are fast at “build this feature.” Auth systems, payment integrations, CRUD APIs — they generate high-quality code quickly for specific feature requests.
But they don’t answer “what did you forget before launch.” This is not a deficiency of the tools. AI coding tools are designed to convert specifications into code, not to validate the completeness of the specifications themselves.
graph TB
subgraph AI_TOOL["What AI Coding Tools Do Well"]
A1["Feature request → code generation"]
A2["Bug fix request → patch generation"]
A3["Refactoring request → code improvement"]
end
subgraph BUILDER["What the Builder Must Know"]
B1["What's missing for launch"]
B2["What's legally required"]
B3["What's a security risk"]
B4["What to prioritize for the business"]
end
AI_TOOL -.->|"This area is not covered"| BUILDER
The Structural Limitation of Hackathons
A hackathon operates under the constraint: “show something working in 3 days.” Within that constraint, legal notices, security validation, and SEO naturally get deprioritized. That makes sense — “does it have a privacy policy?” is not on the judging criteria.
The problem is that those priorities linger after the hackathon ends. “We’ll handle it later” becomes a loop, until right before launch when you realize “we can’t accept payments without this.”
Time Distribution — 15% Code, 85% Everything Else
For WICHI, the actual time distribution looked like this:
| Task Category | Time Spent | Ratio | Difficulty |
|---|---|---|---|
| Core feature code (AI analysis, scoring) | 3 days | 12% | High |
| Payment integration + webhook stabilization | 3 days | 12% | Medium |
| Security hardening (input validation, injection defense) | 2 days | 8% | Medium |
| i18n (Korean/English bilingual support) | 3 days | 12% | Medium |
| SEO (meta tags, sitemap, Search Console) | 1 day | 4% | Low |
| Legal documents (privacy policy, terms) | 1 day | 4% | Low |
| Monitoring (GA4, Sentry integration) | 1 day | 4% | Low |
| Frontend migration (Lovable to Vercel) | 1 day | 4% | Low |
| Sample system (demo without login) | 2 days | 8% | Medium |
| Testing, bug fixes, edge cases | 4 days | 16% | Medium |
| Documentation, README, deploy config | 2 days | 8% | Low |
| DNS, CORS, env vars, misc config | 2 days | 8% | Low |
| Total | ~25 days | 100% |
gantt
title WICHI: MVP → Launch Timeline
dateFormat YYYY-MM-DD
axisFormat %m/%d
section Hackathon (3 days)
Core feature code :done, h1, 2026-02-27, 3d
section Commercialization (~22 days)
Payment stabilization :done, c1, 2026-03-03, 3d
Security hardening :done, c2, 2026-03-05, 2d
i18n :done, c3, 2026-03-06, 3d
Frontend migration :done, c4, 2026-03-04, 1d
Sample system :done, c5, 2026-03-09, 2d
SEO + legal docs :done, c6, 2026-03-07, 2d
Monitoring integration :done, c7, 2026-03-10, 1d
Testing + bug fixes :done, c8, 2026-03-11, 4d
Docs + deploy config :done, c9, 2026-03-15, 2d
Misc config :done, c10, 2026-03-17, 2d
3 days for the code, roughly 22 days for everything else. By ratio, the code was 12% of the total work.
The interesting part is that among the remaining 88%, almost none of it was technically difficult. Most items were “things that take half a day once you know they need to be done.”
Three Lessons from This Experience
1. Code Complete ≠ Product Complete
The faster AI coding tools get, the stronger the illusion that “the code is done, so we’re almost there.” In reality, code accounts for 10-15% of the total, and the remaining 85-90% determines launch quality.
2. If You Don’t Know About It, You Won’t Do It
When someone asks “do you have a password reset flow?”, you immediately think “oh, I missed that.” But if nobody asks the question, you won’t know until the first customer forgets their password. If it doesn’t make it onto the task list, it doesn’t get done.
3. Checklists Are Knowledge Democratization
Experienced developers know these items as “things you obviously need to do.” But a first-time SaaS builder doesn’t. The most efficient way to bridge this knowledge gap isn’t coaching or mentoring — it’s a well-organized checklist.
The Beginning of a Checklist
While fixing each missing item, we started documenting them simultaneously. Items fixed, items still pending, items that could wait. Initially, it was just a bullet list on a single Notion page.
The list grew. Security, payments, legal notices, SEO, monitoring, accessibility, testing, CI/CD. Each category accumulated 20-40 items.
Some items were specific to WICHI, but the majority were things any SaaS builder should verify.
It wasn’t that we thought “it would be a waste to keep this to ourselves.” More precisely, it was: if someone had organized this for us beforehand, we wouldn’t have wasted 3 weeks.
How this memo grew into 534 items, was organized into 15 categories, became a CLI tool, and ultimately an open-source project — that’s the story of the next post.
Related Posts

Where the 534 Items Came From
Documenting how 534 SaaS launch checklist items were derived from 80 service analyses, 12 guidelines, and 5 real-world failures, including the P0-P3 priority logic.

Using Feature Flags to Fix CLI Score Accuracy — Solving the False-Pass Problem
Solving the false-pass problem in MMU's checklist by designing a condition marker system with 17 feature flags and improving score accuracy through 48 regression tests.

Why the Share Feature Comes First in PLG — Designing mmu share
Documenting the psychological mechanisms and viral loop strategies behind three sharing elements designed for MMU's PLG growth: score cards, badges, and custom checklist links.