A senior dev once told me: "Every side project is born from spite."
He was right. π
The Origin Story Nobody Asked For
It started with a simple need: add link shortening to a side project. Should take 10 minutes, right?
Bitly: $348/year for API access. The free tier? 10 links. Per month. With interstitial ads.
Rebrandly: $69/month for decent API limits. Custom domains cost extra. "Contact sales" for anything useful.
TinyURL: Free, but the API felt like it was built in 2008. Because it was.
I did what any reasonable developer would do: I decided to build my own.
Narrator: This was not reasonable.
Month 1: "How Hard Can It Be?"
Famous last words.
A URL shortener is just a database lookup and a 301 redirect, right? I'll knock this out in a weekend.
Weekend 1: Set up Spring Boot, basic CRUD
Weekend 2: Add authentication
Weekend 3: "Just need analytics real quick"
Weekend 4-47: π
Turns out "just analytics" means:
- Tracking clicks without slowing down redirects
- Geo-location (hello, MaxMind database)
- Device detection
- Referrer parsing
- Bot filtering (so many bots)
- Time-series aggregation that doesn't melt your database
I learned more about HTTP headers in a month than in 10 years of web development.
The Features Nobody Told Me I'd Need
Here's what I thought a URL shortener needed:
- Shorten URLs
- Redirect users
- Done
Here's what users actually wanted:
"Can I password-protect a link?"
Sure, that's like 2 hours of work.
"Can I set expiration dates?"
Easy, add a timestamp check.
"Can I transfer ownership of a link to someone else?"
Uh... let me think about that one.
"Can I white-label the whole thing so my customers never see your brand?"
nervous laughter
"Can I create bio pages like Linktree?"
Wait, that's a whole different productβ
"Can I get webhooks when someone clicks?"
I... yeah, okay.
"Can I A/B test destinations?"
opens another beer
Each "simple" feature taught me something. Ownership transfer taught me about audit trails. White-labeling taught me about multi-tenancy nightmares. Bio pages taught me that scope creep is real and it will find you.
The Code I'm Not Proud Of
Let me be honest about the dark corners.
There's a method in my analytics service that's 200 lines long. I've refactored it three times. It's still 200 lines. Some problems are just... long.
// TODO: Refactor this
// TODO: Seriously, refactor this
// Added 6 months ago by: me
// Last modified: yesterday, also by me
There's a database migration that I wrote at 2 AM during an incident. It works. I will never touch it. It has comments like:
-- Don't ask why this is here
-- It fixes the thing
-- Future me: I'm sorry
There's a feature flag called ENABLE_LEGACY_REDIRECT_MODE that I added "temporarily" 8 months ago. It's still there. It will probably outlive me.
We all have these. If you say you don't, you're lying. π
What I Got Right (Eventually)
After a year of building, breaking, and rebuilding, some things actually work well:
API-First Architecture
I built the API before the dashboard. This sounds backwards, but it meant:
- Every feature works via API
- The dashboard is just another API consumer
- Developers can do everything programmatically
- No "premium API" tier nonsense
Honest Pricing
No "contact sales" for basic features. No surprise overages. No enterprise tax for small teams that just want a decent product.
| Plan | Price | What You Get |
|---|---|---|
| Free | $0 | 30 links/mo, API access |
| Pro | $16/mo | 500 links/mo, custom domains |
| Enterprise | $140/mo | Unlimited, teams, white-label |
That's it. No asterisks.
Actually Good Analytics
I spent way too much time on this. But now you can see:
- Real-time clicks
- Geographic breakdown (city-level)
- Device and browser stats
- Referrer analysis
- UTM parameter tracking
And it doesn't cost extra. Because charging extra for analytics on a URL shortener feels like charging extra for wheels on a car.
The Mistakes I Keep Making
Over-engineering: I built a distributed caching layer before I had 100 users. I now have... not enough users to need a distributed caching layer.
Under-communicating: I'd ship features without telling anyone. Turns out, if you build it, they won't come. They don't even know it exists.
Perfectionism: I delayed launching the white-label feature for 3 months because the admin UI "wasn't quite right." Nobody cared about the admin UI. They cared about whether it worked.
Comparing to competitors: Dub.co raised $2M. Bitly is worth billions. I'm a solo dev with a domain name and spite. Different games, different rules.
Why I'm Still Building This
Some days I wonder if I should've just paid Bitly's $348/year.
Then I see another API-first startup that can't find a URL shortener that fits their needs. Or an agency that wants white-labeling without selling their firstborn. Or a developer who just wants a simple, honest product.
And I remember why I started.
Not because the world needed another URL shortener. The world has plenty.
But because I needed one that didn't make me angry. And maybe you do too.
The Point (If There Is One)
Building a SaaS is not a good financial decision. The math rarely works out. You'll spend hundreds of hours on something that might make hundreds of dollars.
But you'll learn:
- How to build things that scale (or don't)
- How to ship when it's 80% done
- How to talk to users (scary but necessary)
- How to make hard decisions about what matters
And sometimes, you'll build something people actually want.
That's the hope, anyway.
Try It If You Want
jo4.io is live. It works. Mostly.
- API docs: jo4.io/docs
- GitHub: We're not open-source (yet), but the awesome-url-shortener list is: github.com/rathnasorg/awesome-url-shortener
If you're building something that needs link shortening, maybe give it a shot. If it sucks, tell me. I'll fix it or explain why I can't.
And if you're thinking about building your own SaaS out of spite?
Do it. It's a terrible idea. You'll learn so much. π
Top comments (0)