Building Shipstry: 640 Commits, 9 Days, One Launch
On March 3, 2026, I started with an empty folder. On March 11, 2026, Shipstry went live.
In between: 640 commits, countless cups of coffee, and a lot of lessons learned about building on the edge.
This is the story of how I built it, the technical decisions I made, and what I learned along the way.
The Name
Before writing a single line of code, I needed a name.
I spent an entire afternoon brainstorming with AI. I must have asked for hundreds of suggestions. The AI probably hated me by the end of it.
I wanted something that captured the essence of what makers do — we ship products. And I wanted it to feel like a registry, a place where products are officially recorded and discovered.
Ship + Registry = Shipstry
It sounded nautical, it felt right, and the .com was available. Done.
The nautical theme evolved into something more organic:
- Primary color: Olive Moss (#6B8A67)
- Accent: Warm Sand (#D4A574)
- Pricing tiers: Harbor, Voyage, Expedition, Admiral
- The logo: a geometric sailboat with twin sails
The Why
After launching several side projects over the years, I kept running into the same problem: Product Hunt is great, but it’s not built for indie makers anymore.
Big-budget launches dominate. Marketing teams game the algorithm. Great products from solo developers get buried in hours.
I wanted something different:
- A place that celebrates builders, not marketers
- Weekly cycles instead of daily chaos
- Quality over quantity
- Built by a maker, for makers
So I built Shipstry — “The Launch Registry.”
The Stack Decision
Before writing code, I spent time on stack selection. This is the most important decision you make at the start of a project — it will haunt you for months if you get it wrong.
I chose:
TanStack Start for the framework. It’s a full-stack React framework with file-based routing and excellent TypeScript support. The type safety is incredible — if you change a route, the compiler tells you everywhere that needs updating.
Cloudflare Workers for deployment. Edge computing means my users in Singapore, London, and New York all get the same fast experience. No cold starts, global distribution.
Cloudflare D1 for the database. It’s SQLite at the edge. Yes, SQLite — the same database that powers your phone, now running in 300+ locations worldwide. For a product like Shipstry, it’s perfect.
Cloudflare R2 for file storage. When users upload product logos and preview images, they go here. It’s S3-compatible but with zero egress fees, which means I don’t have to worry about surprise bandwidth bills.
Better Auth for authentication. Email/password plus Google OAuth, and it integrates natively with TanStack Start.
Stripe for payments, Resend for emails, Tailwind CSS v4 for styling, shadcn/ui for components.
The key insight: TanStack Start + Cloudflare is a powerful combination. You get React’s ecosystem with edge performance, and D1 gives you a real database with zero configuration.
The First Week
Day 1-2: Foundation
The first commits set up the entire foundation — TanStack Start with SSR, Cloudflare Workers adapter, Drizzle ORM, basic routing structure.
I also built the design system. I didn’t want another generic AI landing page with purple gradients. I created a custom “Olive Moss” palette — muted greens and warm grays that feel organic and calm.
By end of Day 2, I had a working dev server, a distinctive visual identity, and basic page layouts.
Day 3-4: Authentication
Authentication is always more complicated than you expect.
Better Auth needs to create its auth instance per-request, not as a singleton. In Cloudflare Workers, each request is isolated anyway, so this architecture actually works well. But figuring that out took a few hours of head-scratching.
I also designed the database schema upfront. The key decision: separating drafts from products.
Drafts have all nullable fields — users can save at any point in the submission flow and return later. Products have required fields — they only exist when fully submitted. This kept the data model clean and the code simple.
Day 4-5: The Submission Flow
The submission form is the heart of Shipstry. I wanted it to feel smooth, not overwhelming.
I built a progressive form with collapsible sections. Each section tracks its completion status. Users can save at any point, leave, and pick up where they left off days later.
For the product description, I integrated Milkdown — a plugin-driven Markdown editor with a custom toolbar. The tricky part was focus management: the toolbar kept stealing focus from the editor. I eventually fixed it by preventing default on mousedown for toolbar buttons.
Day 5: Pricing and Payments
I designed a nautical-themed pricing system:
- Harbor (Free): Basic submission, normal review
- Voyage ($9.9): Fast 24-hour review, same-week ship
- Expedition ($29): Featured on homepage, 7 days exposure
- Admiral ($59): 30 days featured, premium badge
Stripe integration was straightforward, but the webhook handler needed careful attention. D1 doesn’t support nested transactions, so I had to restructure the code to use sequential queries instead of wrapping everything in a transaction.
The AI Feature
Filling out product forms is tedious. Users paste a URL and then have to manually enter the name, tagline, description, logo, preview image…
So I built an AI-powered metadata fetcher.
When a user pastes their product URL, the system:
- Fetches the page and extracts Open Graph tags
- Sends the information to AI to generate an enhanced, compelling description
- Auto-fills all the form fields
The user can review and edit everything before submitting. It’s not about replacing human input — it’s about reducing friction.
Multi-Provider Failover
AI APIs are unreliable. They timeout, they rate limit, they have outages.
I built a failover system that tries multiple AI providers in priority order. If one fails, it automatically tries the next. The configuration is a simple JSON array in environment variables:
[
{"name": "openai", "priority": 1},
{"name": "claude", "priority": 2},
{"name": "gemini", "priority": 3}
]
If all providers fail, the form still works — users just fill it manually. Graceful degradation is key.
SSRF Protection
Allowing users to fetch arbitrary URLs is dangerous. You don’t want someone hitting your internal services through your server.
I implemented multiple layers of protection:
- Block private IP ranges (10.x, 172.x, 192.168.x)
- Block localhost
- Only allow HTTP and HTTPS protocols
- Rate limit: 5 requests per minute per user
The Community Features
Comments and Voting
Comments support nesting — users can reply to replies. I used soft deletes instead of hard deletes, so if a parent comment is removed, the threading structure stays intact.
For voting, I wanted instant feedback. Nobody wants to wait for a server round-trip to see their vote register.
I implemented optimistic updates: when you click vote, the UI updates immediately. The server request happens in the background. If it fails, the UI rolls back. This makes the app feel snappy and responsive.
Notifications
Users get notified for:
- Comments on their products
- Replies to their comments
- Award wins (weekly and monthly)
- Product status changes (approved, rejected)
For email delivery, I used Cloudflare’s waitUntil() function. This sends the response to the user immediately while the email sends in the background. The user doesn’t wait for the email to send.
The Final Days
Caching
To reduce database load, I built a caching layer using D1 itself as the cache store. Cached data has TTLs, and mutations trigger automatic cache invalidation.
This pattern dramatically reduced read load on the main tables during high-traffic periods.
Environment Configuration
I centralized all environment variables with validation. In development, the app validates that all required secrets exist and throws clear errors if something is missing. In production, I trust that Cloudflare has the secrets configured.
This caught several configuration mistakes during development that would have been painful to debug in production.
Launch
On March 11, 2026, Shipstry went live.
The final commits added a launch promo banner with a 50% discount code, and adjusted the ship week logic to allow immediate launches during the launch period.
What I Learned
TanStack Start is ready for production. The framework is stable, well-typed, and SSR works seamlessly with Cloudflare Workers.
D1 is good enough. SQLite at the edge sounds limiting, but for most applications, it’s perfect. Zero configuration, fast queries, generous free tier. The main gotcha is no nested transactions.
Edge functions change how you think. No global state, waitUntil() for background tasks, zero cold starts, environment access through imports rather than process.env.
AI integration is easier than expected. With the right abstraction — multi-provider failover and graceful degradation — you can build reliable AI features.
640 commits in 9 days. That’s roughly 71 commits per day. Each commit was small, focused, and reversible. The discipline of atomic commits saved me multiple times when I needed to roll back a bad decision.
What Happened After Launch
Shipstry has been live for two days.
In that time, I’ve been doing link building — submitting to directories, reaching out to communities, getting featured on various platforms.
The results? DR went from 0 to 14 in two days.
Try It
- Shipstry: shipstry.com
- Submit your product: shipstry.com/submit
- Launch promo: Use code
SHIPSTRY50for 50% off paid plans
Built with TanStack Start, Cloudflare Workers, D1, R2, and too much coffee.