iToverDose/Software· 6 MAY 2026 · 00:07

Solo dev’s Kotlin and Next.js AI directory lessons after 6 months

A solo founder built a multilingual AI tools directory using Kotlin, PostgreSQL, and Next.js. After six months of shipping, SEO blunders, and architectural regrets, here’s what really worked—and what almost broke everything.

DEV Community4 min read0 Comments

Building a niche tech directory from scratch is hard enough. Doing it solo, across two languages, with an SEO minefield and a backend stack that defies convention? That’s another story entirely.

I launched a bilingual AI tools directory—AI Explorer—after repeatedly hitting dead ends searching for French alternatives to mainstream AI services. Six months later, the platform indexes 1,424 tools across 37 categories, from chatbots and image generators to niche utilities in legal, real estate, and energy management. What started as a personal frustration became a fully functional directory built for indie hackers and professionals alike. But the journey came with unexpected challenges, technical regrets, and a few SEO scars that still sting.

Why Kotlin became the backbone of a solo AI project

When I talk about my tech stack, the first question is always the same: “Why Kotlin?”

Most developers expect Node, Go, or Python—especially in AI. But for me, the choice wasn’t about trends. It was about consistency and control. I’ve spent years writing Kotlin professionally, and I know its strengths: type safety, concurrency through coroutines, and a robust ecosystem that scales without the brittleness of some dynamic languages.

I prototyped parts of the backend in Node, but managing concurrent API calls to enrichment services, image processors, and database writes became a debugging nightmare. With Ktor and Kotlin coroutines, that complexity vanished. The code was cleaner. The errors were fewer. The JVM tuning was predictable.

// Sample Ktor route with coroutine handling
get("/api/tools") {
    val tools = async { toolRepository.findAll() }
    val metadata = async { categoryService.fetchMeta() }
    call.respond(tools.await() to metadata.await())
}

The infrastructure runs on PostgreSQL with Flyway migrations, hosted on an OVH VPS with 8 vCores and 24GB RAM. Total cost? About €24 per month. Could I run it cheaper? Probably. Could I move it to Vercel for €200? Sure. But I chose the path that lets me SSH in, tweak the JVM heap, and watch the GPU utilization climb when I finally migrate to self-hosted models.

That last part—self-hosting Mistral, Qwen, or Llama locally—is a dream I keep postponing. A Ryzen server with 64GB RAM and a 4090-class GPU would cost €200–400 monthly. The rational founder says “wait for revenue.” The hardware nerd says “do it anyway.” Most of the time, the founder wins. But the pull toward full autonomy is undeniable.

The Next.js App Router trap: speed vs. scalability

Next.js App Router is powerful—when it works. The trap? Letting it trick you into over-fetching.

In development, pages render fast. In production, every uncached request hits your backend for fresh data. Time to First Byte jumps. User experience suffers. I learned this the hard way.

My initial approach treated every page as a dynamic, server-rendered component. Beautiful in localhost. Painful in production.

It took two weekends of debugging to untangle the mess. I had to map out cache invalidation strategies based on page type:

  • Daily-changing pages (tool updates)
  • Weekly-changing pages (category overviews)
  • User-generated content (slow, rare updates)

Only then did I implement a hybrid model using revalidate, Incremental Static Regeneration (ISR), and Cloudflare edge caching. The docs made it sound simple. Reality? It was a mini-architectural project of its own.

Lesson learned: before writing a single component, spend half a day defining your data freshness rules. It saves weeks of firefighting.

How a single feature nearly killed my SEO—and what I did to fix it

Three weeks ago, I removed a major section of the site: individual pages for every LLM model imported from HuggingFace. At first glance, it made sense—more content, more keywords. In practice, it became 3,000 thin pages that diluted my domain’s authority and confused search engines.

I followed the textbook steps: sent 410 Gone responses, updated the sitemap, adjusted robots.txt. Then I waited.

What I didn’t expect? Google’s purge was faster than my recovery. My indexed pages dropped from 7,500 to 954 in two weeks. For days, I feared a penalty. Search Console became my midnight companion. It wasn’t a penalty—just a domain recalibration after removing 40% of its surface area.

The key insight: Google evaluates your entire domain, not just individual pages. When you delete a large chunk of content, it temporarily reduces your visibility until new, valuable content replaces it.

Patience and consistent content creation are your allies here. But the lesson stung. Next time, I’ll phase out thin pages gradually—or not at all.

What’s next: self-hosted models, deeper curation, and global reach

AI Explorer is still growing. The next milestone? Migrating from hosted AI APIs to fully self-hosted models. Running LLMs locally on a dedicated server with a proper GPU will eliminate rate limits, reduce costs long-term, and give users full control over their data.

But it’s a leap. The monthly hardware bill is real. The setup complexity is real. And the revenue curve hasn’t caught up yet.

In the meantime, the focus is on deepening curation, refining the comparator tool, and expanding the bilingual content to serve non-English speakers better. The goal has always been to build something useful—not just another AI directory, but a reliable map for those who need alternatives that speak their language.

The journey has been messy. The lessons have been hard. But the platform is here—and it’s only getting better.

AI summary

Tek başına Kotlin ve Next.js kullanarak çok dilli bir AI araçları dizini inşa etmek ne kadar zor? 6 aylık deneyimlerden çıkan mimari, SEO ve donanım dersleri.

Comments

00
LEAVE A COMMENT
ID #OPVJLR

0 / 1200 CHARACTERS

Human check

8 + 4 = ?

Will appear after editor review

Moderation · Spam protection active

No approved comments yet. Be first.