DEV Community

Nick
Nick

Posted on

Next.js Codebase from Hell: A Tale of AI-Generated Technical Debt

Today I want to share a fascinating story about what happens when AI code generation meets unstructured development practices. This isn't a hypothetical scenario - it's a real story about a senior engineer named (let's call her Sarah) who experienced what might be the most chaotic codebase of the AI era.

The Setup

Sarah joined PromptCorp (name changed) in early 2024. The company had raised an impressive Series A with the pitch of being "AI-first in everything we do." Little did Sarah know that "everything" included their entire development process.

The first red flag appeared during onboarding. The Git history was surprisingly shallow - everything before six months ago had been squashed into a single commit labeled "legacy-migration-do-not-touch." This should have been her cue to run.

The Architecture (or Lack Thereof)

The application was a Next.js project, but calling it that feels generous. It was more like a collection of independently generated React components that happened to live in the same repository. The entire codebase was maintained by product managers using Vercel v0.

This wasn't just a case of non-technical people writing code - this was non-technical people prompting an AI to generate entire features, then deploying them without review.

The Numbers Are Terrifying

Let me share some metrics that will haunt your dreams:

  • Total component count: 1,147
  • Average component size: 486 lines
  • Duplicate component ratio: 68%
  • Number of unique API endpoint implementations: 312
  • Same endpoints with slightly different implementations: 89
  • Number of different ways to fetch user data: 47

The duplication wasn't just similar components - they had literally regenerated the same components multiple times because they couldn't find the original versions in their own codebase. There were 47 different implementations of a user profile card, each prompted slightly differently.

The Development "Process"

The typical feature deployment went like this:

  1. PM identifies a need for a new feature
  2. PM prompts Vercel v0 to generate a component
  3. Component gets committed directly to main
  4. If it breaks something, repeat step 2 with a slightly modified prompt
  5. Continue until it sort of works

There was no concept of component reuse. Why reuse when you can regenerate? Need a button that's slightly different? That's a new 200-line component. Need to display user data? Better generate a fresh implementation of the user service, just to be safe.

The Cost of AI Magic

The real tragedy here isn't just the messy code - it's the hidden costs:

  • The production environment was making thousands of redundant API calls because each component implemented its own data fetching
  • The bundle size was astronomical because every component brought its own set of dependencies
  • Simple changes required updating dozens of files because the same logic was duplicated everywhere
  • Nobody knew which components were actually being used in production

The Breaking Point

The moment Sarah decided to leave came during a production incident. A simple profile page was taking 30 seconds to load because it contained 23 different Server Components, each regenerated from scratch by v0 prompts. Despite Next.js's built-in Server Components architecture being designed for efficient data fetching, the PMs had somehow managed to create the worst possible implementation.

Each component was fetching the same data in slightly different ways - some with prisma queries copied directly into the components, others with duplicate database helper functions, and a few mysteriously connecting to a test database (because someone had copied environment variables from a different prompt). The components were all marked with force-dynamic, making them refetch on every request, completely defeating the purpose of Next.js's caching mechanisms.

What should have been a simple shared layout with properly cached Server Components had become a tangled mess of duplicate data fetching logic. The suggestion to refactor this into a proper component hierarchy with structured data fetching was met with: "Why do we need that when we can just generate new components? V0 will optimize it for us!"

The Epilogue

Sarah left PromptCorp after three months. The codebase continues to grow. Some say that to this day, new UserProfileCard components are still being generated, each slightly different from the last.

Remember: just because we can generate infinite code doesn't mean we should.


*This story is based on real events, though details have been changed to protect the innocent (and the guilty). No AI models were harmed in the making of this codebase, though several probably should have been.

Top comments (0)