Every few weeks, developers compare frameworks again.
Which one is faster?
Which one has better syntax?
Which one has the cleanest developer experience?
Which one is more “modern”?
I used to care about these comparisons a lot. As a full stack developer, I tried Go, FastAPI, Next.js, NestJS, Fiber, Elysia and several other tools because I thought choosing the newest stack would make my projects better.
After building real projects, client work, internal tools, mobile backends and SaaS experiments, my opinion changed.
Most framework comparisons focus on the wrong layer.
They compare hello world performance, request throughput, routing syntax and startup time. Those things matter in some cases, but they are rarely the main reason a product succeeds or fails.
In production, the problems are usually different.
Can you deploy it safely?
Can you debug it quickly?
Can you onboard another developer?
Can you find stable packages?
Can you migrate the database without fear?
Can you monitor errors?
Can you maintain the project after six months?
Can the business rely on it?
That is where boring technology starts to win.
A simple API endpoint looks similar in almost every framework.
# FastAPI
from fastapi import FastAPI
app = FastAPI()
@app.get("/orders")
async def get_orders():
return [
{"id": 1, "status": "completed"},
{"id": 2, "status": "pending"},
]
The same idea in Go Fiber is not fundamentally different.
// Go Fiber
app.Get("/orders", func(c *fiber.Ctx) error {
return c.JSON([]fiber.Map{
{"id": 1, "status": "completed"},
{"id": 2, "status": "pending"},
})
})
And in .NET, the shape is still the same.
// ASP.NET Core
[ApiController]
[Route("api/orders")]
public class OrdersController : ControllerBase
{
[HttpGet]
public IActionResult GetOrders()
{
return Ok(new[] {
new { Id = 1, Status = "Completed" },
new { Id = 2, Status = "Pending" }
});
}
}
The syntax changes, but the business problem does not.
Most of the time, the hard part is not writing the endpoint. The hard part is everything around it: authentication, permissions, database modeling, migrations, background jobs, caching, logging, file uploads, payments, admin workflows, SEO, analytics and deployment.
That is why I stopped treating framework choice as the most important technical decision.
For example, a boring Django model like this can create more business value than a week spent comparing backend frameworks.
# Django
from django.db import models
class Customer(models.Model):
company_name = models.CharField(max_length=255)
email = models.EmailField(unique=True)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
There is nothing exciting here. But you immediately get a stable ORM, migrations, validation patterns, admin integration and a mature ecosystem around it.
That matters when you are building something that needs to survive beyond a weekend project.
The same thing applies to .NET.
A lot of people online treat enterprise stacks as boring, but boring is not always a weakness. In many projects, boring means predictable. It means other developers can understand the codebase. It means the deployment story is known. It means companies can maintain it for years.
This is especially important when the project is not just an experiment.
A client dashboard, a CMS, a restaurant ordering system, a SaaS backend, an internal CRM or a mobile app API does not need the most exciting stack. It needs a stack that can be maintained without constant drama.
Deployment is another place where boring tools usually win.
A simple Docker Compose setup like this is not impressive, but it is practical.
services:
app:
build: .
restart: always
depends_on:
- postgres
- redis
postgres:
image: postgres:16
restart: always
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7
restart: always
volumes:
postgres_data:
This kind of setup is boring, but it gives you repeatability. You can run it locally, move it to a server, add monitoring, add backups and understand what is happening when something breaks.
That is more valuable than people admit.
Modern tools often look better in demos because demos hide operational cost.
A new framework can feel amazing when you are building the first endpoint. The routing is clean, the syntax is elegant, the benchmarks are impressive and the documentation looks fresh.
But the real test comes later.
What happens when a dependency is abandoned?
What happens when the ORM has edge cases?
What happens when the package you need is half-maintained?
What happens when AI-generated tutorials teach bad practices?
What happens when you need to hire someone?
What happens when you need to debug a production issue at night?
This is where mature ecosystems have an advantage.
Not because they are perfect.
Because their problems are already known.
Known problems are easier to price, debug and plan around. Unknown problems are expensive because they appear when the project is already under pressure.
That is the part many developers ignore when comparing frameworks.
They compare speed, but not maintenance cost.
They compare syntax, but not ecosystem risk.
They compare benchmarks, but not hiring difficulty.
They compare developer experience, but not operational complexity.
For most business software, the bottleneck is usually not the framework. It is the database design, slow queries, bad caching, unclear requirements, weak deployment process, poor monitoring or missing product distribution.
A faster router will not fix those problems.
A newer runtime will not fix a bad data model.
A cleaner syntax will not make users care about your product.
That does not mean new technology is bad. I still like trying new tools, and sometimes a newer stack is the right choice. If it reduces complexity, improves performance where it actually matters or gives the product a real advantage, then it is worth considering.
But “new” by itself is not a strategy.
Before choosing a stack now, I ask more practical questions.
Can I ship faster with this?
Can I maintain it for three years?
Can I deploy it without special tricks?
Can I find help when something breaks?
Can I reuse patterns from previous projects?
Does it reduce complexity or just move it somewhere else?
That is why I still use Django, .NET, Next.js, PostgreSQL, Flutter and Docker in many projects.
They are not always the coolest choices.
But they are stable, documented, widely used and good enough for a lot of real-world software.
And “good enough” is underrated.
Good enough lets you ship.
Good enough lets you maintain.
Good enough lets you focus on the product.
Good enough lets you build systems instead of constantly rebuilding foundations.
After spending enough time chasing frameworks, I started caring less about what looks impressive in a benchmark and more about what still works when the project becomes boring.
Because that is usually when the real work begins.
Top comments (0)