Recently went down a familiar rabbit hole while building a REST API for a simple task tracker. It started off clean just a few basic CRUD endpoints. Nothing fancy. Then, like always, I thought… “what if this scales?”

That’s where things went sideways.

I added nested routes, polymorphic relationships, custom middleware for edge cases I hadn’t even seen yet, and somehow convinced myself I needed event sourcing. For a task tracker. With zero users.

Two weeks later, I had something that looked impressive on paper but was painful in reality. Deployments were messy, debugging took forever, and performance? It struggled with even 10 concurrent users.

That’s when it clicked, I wasn’t building for real problems. I was building for imaginary ones.

So I stripped everything back.

I kept the API simple, clear endpoints, straightforward DTOs, basic validation using Zod, and plain Postgres queries. No magic. No overthinking.

The result? Everything got better. Performance jumped, tests became easy to write, and for the first time in days, I actually enjoyed working on it again.

The big lesson: YAGNI is real. You really aren’t gonna need it at least not yet.

Now I try to follow a simple approach:

Build the simplest version that works and Only optimize when something actually breaks

So tell me… have you ever done this too? Like overbuilt something and then regretted it?

  • JakenVeina@midwest.social
    link
    fedilink
    arrow-up
    6
    ·
    5 months ago

    Planning for the future doesn’t mean getting the architecture right the first time, it just means being modular/layered, in such a way that you CAN refactor later, without having to rewrite the whole app from scratch. Which admittedly isn’t obvious to solve, without experience under your velt.

    • chickenf622@sh.itjust.works
      link
      fedilink
      arrow-up
      2
      ·
      5 months ago

      This plus “keep it simple stupid” are 2 key things to keep in mind when writing a program. Going back to old code of mine can be painful when I didn’t keep these key principals in mind/was too inexperienced.

    • Jack Evans@programming.devOP
      link
      fedilink
      arrow-up
      2
      ·
      5 months ago

      Agree with the emphasis on modularity upfront, as it aligns with my YAGNI takeaway, start simple but layer cleanly for future refactors without a full rewrite. In my case, zero users meant no pressure for premature complexity, letting me ship fast and iterate based on actual needs. Experience definitely sharpens that balance. What’s one modular pattern you’d recommend for a basic CRUD API?

      • JakenVeina@midwest.social
        link
        fedilink
        arrow-up
        1
        ·
        5 months ago

        As far as big name patterns go, it’s VSA for me: Vertical Slice Arcitecture. I’m coming from .NET land, so a basic CRUD API to me means…

        • “Controller” classes , representing route segments
        • Methods on controller classes representing endpoints at that segment
        • “Service” classes for different “business domains” to house shared business logic, when there’s enough of it across different endpoints that it’s appropriate to extract it (or when you want a seam for testing)
        • Entity/Model classes, modeling how data is stored in the persistence layer, so the business layer can read and write without abstractions
        • “Repository” classes for different “data domains” to house shared data access logic, when there’s enough of it across different endpoints that it’s appropriate to extract it (or when you want a seam for testing)
        • Constructor-based Dependency Injection and an IoC container to orchestrate everything together