Software Architecture

Choosing a Framework — Spring / Next.js / FastAPI / Rails

Choosing a Framework — Spring / Next.js / FastAPI / Rails

About this article

This article is the fifth deep dive in the “Software Architecture” category of the Architecture Crash Course for the Generative-AI Era series, covering framework selection.

As the obsolescence history of AngularJS, Backbone.js, Struts, and Silverlight demonstrates, FW selection determines the next 5 years of hiring market and technical debt simultaneously — the heaviest move after language selection. The article compares major FWs per language and covers use-case selection, LTS management, vulnerability response, and AI-era generation accuracy.

What is a framework in the first place

A framework is, in a nutshell, “a skeleton and common-parts kit for building an app.”

Imagine a plastic-model kit. Parts (libraries) can be bought separately, but the skeletal frame (framework) can’t be swapped for another brand mid-build. Frameworks like Spring Boot, Next.js, Rails, and FastAPI come pre-equipped with “mechanisms every app needs” — routing, DB connections, authentication — so developers just place business logic on top of that skeleton.

Why framework selection matters

What happens if you pick the wrong framework? Swapping out the skeleton is essentially a rebuild. Netscape’s 1998 browser code rewrite that lost the browser wars symbolizes the weight of FW migration.

FW selection simultaneously determines the talent base, future viability, and library ecosystem you’ll work with — once decided, you live with it for years. Library = part, framework = skeleton. The skeleton can’t be changed later.

Deciding the “app’s skeleton”

Once language is settled, picking the mainstream framework for that language is the basis. FW selection decides hiring base, future viability, and library ecosystem simultaneously — once chosen, you live with it for years.

Library = “part.” Framework = “skeleton.” Understanding this difference matters. Libraries can be swapped later; replacing the skeleton is essentially a rebuild. Netscape’s 1998 code rewrite that lost the browser wars symbolizes the weight of FW migration.

Library = part. Framework = skeleton. The skeleton can’t be changed later.

Decision axes

FW selection isn’t a single metric like “fast” or “light”; evaluate jointly across multiple axes. Especially with long-term operation, talent-market depth and LTS presence are decisive.

flowchart TB
    FW([Candidate FW])
    M[Maturity<br/>LTS presence]
    E[Ecosystem<br/>Surrounding libraries]
    A[Adoption cases<br/>Hiring market]
    L[Learning cost<br/>Information density]
    P[Performance]
    S[Dev speed<br/>scaffold/conventions]
    GO([Candidate that runs 5 years])
    FW --> M --> GO
    FW --> E --> GO
    FW --> A --> GO
    FW --> L --> GO
    FW --> P --> GO
    FW --> S --> GO
    A -.|most important| HIGHLIGHT[Talent shortage<br/>shortens project<br/>lifespan]
    classDef root fill:#fef3c7,stroke:#d97706;
    classDef criteria fill:#dbeafe,stroke:#2563eb;
    classDef important fill:#fee2e2,stroke:#dc2626;
    classDef goal fill:#dcfce7,stroke:#16a34a;
    class FW root;
    class M,E,L,P,S criteria;
    class A,HIGHLIGHT important;
    class GO goal;
AxisSubstance
MaturityTrack-record length, stability, LTS (long-term support) presence
EcosystemSurrounding-library / plugin breadth
PerformanceThroughput, startup, memory
Learning costDocumentation and information volume
Adoption casesHiring ease, job-market breadth
Dev speedscaffold and convention richness

Performance can be optimized later, but talent shortage shortens project lifespan.

Java / Kotlin

Java FWs feature overwhelming track record, especially in large-enterprise business systems. Spring Boot is the de facto for enterprise — surrounding libraries are abundant, and information density is rich.

Java’s Spring dominance accumulated over 20+ years covering DI, AOP (Aspect-Oriented Programming — a way to inject cross-cutting concerns like logging and transactions), security, batch, messaging — meeting business requirements head-on. The ecosystem has overwhelming case examples in banking, insurance, public sector, with no shortage of precedents.

FWTrait
Spring BootDe facto. Enterprise-business standard
MicronautCompile-time DI (Dependency Injection — injecting dependent objects from outside) / fast startup
QuarkusGraalVM-aware / cloud-native oriented
Ktor (Kotlin)Lightweight, modern, async; written in Kotlin

For finance, public sector, and large-enterprise business systems, Spring Boot only.

C# / .NET

Microsoft’s official FW family pairs performance and productivity well. ASP.NET Core specifically is cross-platform and high-performance, scoring alongside Node.js and Go in benchmarks. Strong Azure integration and a first choice in companies with substantial Windows assets.

For C#, Microsoft official FWs (ASP.NET Core) are the only mainstream; the language, FW, IDE (Visual Studio), and cloud (Azure) are consistently provided by one vendor — the biggest trait. Version upgrades are managed by Microsoft directly with few compatibility issues, and the long-term operation of large enterprise business systems has good chemistry.

FWTrait
ASP.NET Core.NET official; high-performance, cross-platform
Minimal APILightweight API definition inside ASP.NET Core
BlazorBuild SPAs in C# alone (WebAssembly)

Fits: many Windows / Azure assets / large-scale business / Unity integration.

In environments leveraging Microsoft assets and Azure, this is an overwhelmingly strong FW family.

Python

Python FWs split clearly by use. New APIs: FastAPI. Full-stack with admin: Django. Free-form: Flask — that split is settled.

Python’s FW split exists because the language spans AI, data, web, scripting, with different functional needs per area. FastAPI especially fits AI / ML API wrappers via type hints + OpenAPI generation; many Python-selection rationales now lean on AI integration.

FWTrait
DjangoFull-stack with admin. Internal tools etc.
FastAPIType-hint-driven, async, OpenAPI auto-generation
FlaskMicro-FW, free, small-scale
  • New API: FastAPI (type safety + performance).
  • Want admin pages: Django.
  • AI-API wrapper: FastAPI (direct Python-asset integration).

If you pick Python for AI integration, FastAPI is essentially correct.

Go

Go’s FWs are thin because the language itself is simple. The standard library net/http covers a lot of scale, and the “FW-light, standard-library-centered” style is common.

Go culture doesn’t welcome heavy FWs; explicit code is virtuous. The result: thin routers like Gin / Echo combined with the standard library — Docker and Kubernetes codebases are written in this style.

FWTrait
Standard net/httpOften sufficient with official packages alone
GinLightweight, fast, rich middleware. Most adopted
EchoSame family as Gin; API-oriented
ChiFaithful to net/http; small, clean

For Go, thin router + standard library is the canonical path; overwhelming track record in microservices and cloud-native infrastructure.

Rust

Rust FWs shine where extreme performance and memory safety are needed. Learning cost is high; picking Rust where C# or Go would suffice sacrifices development speed. Adoption is realistic only when performance is truly necessary.

Rust web FWs have coalesced around the tokio async runtime with Axum effectively becoming the mainstream. The ecosystem is still developing; no all-in-one FW like Spring or Rails. Rust style is combining small crates (libraries).

FWTrait
Axumtokio-official lineage; type-driven and easy to write
Actix-webHigh performance, mature; consistently top in benchmarks
RocketErgonomics-focused; writeability priority

Fits: extreme performance / memory safety as absolute requirement / edge computing / small distributable binaries.

For new builds, Axum first. Actix-web for maturity, Rocket for writeability.

Node.js / TypeScript

Node.js’s hallmark is “too many choices.” From the elder Express to type-friendly Fastify, well-DI’d NestJS, edge-runtime-aware Hono — pick by use. Frontend-integrated Next.js / Nuxt / SvelteKit configurations that “co-host APIs” are increasing.

The Node.js ecosystem’s FW proliferation is driven by JavaScript’s openness and npm’s explosive scale. No single decisive FW emerges; instead you have flexibility to pick the optimum per use, and “unifying frontend and backend in one language (TypeScript)” is a strong AI-era benefit.

FWTrait
ExpressEldest, most adopted. Thin and free
FastifyFast, type-friendly, fast JSON
NestJSDI, structured. Angular-like writing. Good for large scale
HonoEdge-runtime-aware, lightweight, TypeScript-first

Frontend-integrated: Next.js (React) / Nuxt (Vue) / SvelteKit — co-hosting SSR (Server-Side Rendering — server builds HTML before returning) and APIs.

For TypeScript, NestJS (structure focus) or Hono (lightweight, edge). Express is rarely worth picking for new builds today.

PHP / Ruby

PHP

PHP FWs are “Laravel-dominated.” scaffold, ORM, auth, queues all bundled with mature documentation; the learning curve is gentle. The legacy maintenance pile is huge, so the talent market persists.

FWTrait
LaravelMost adopted / fast dev / rich docs
SymfonyRobust, large-scale-oriented, enterprise-friendly
CakePHPConvention-focused, persistent adoption in Japan

For WordPress projects and small/mid business apps, Laravel only. New adoption is decreasing.

Ruby

Ruby’s FW situation is essentially Ruby on Rails. The originator of “convention > configuration”; the dev experience of one scaffold command auto-generating features still beats other languages. But Rails itself is heavy with poor FaaS / microservices fit, so adoption skews toward startup MVPs.

FWTrait
Ruby on RailsOriginator of “convention > configuration”; fastest MVP
SinatraMicro-FW. Small or partial use
HanamiModern, clean-architecture-oriented

Still primary for “fastest startup prototype.”

Recommendations by case

CaseRecommended
Large enterprise businessSpring Boot / ASP.NET Core
High-speed API / AI integrationFastAPI / Fastify / Axum
Startup MVPRuby on Rails / Laravel / Next.js
MicroservicesGo (Gin/Chi) / gRPC
Full-stack JSNext.js / Nuxt / SvelteKit
Extreme performance / embeddedRust (Axum / Actix-web)
Internal business tools (admin needed)Django

Language and FW selection go together as the rule; deciding language and “FW later” is a recipe for failure.

Framework × use ladder

Locking “this for this use” in numbers speeds judgment. Practical mapping as of April 2026:

UseTeam sizeFirst-choice FWLTS periodHiring difficulty
Large enterprise business30+Spring Boot (Java)3-5 yrLow
Large .NET business30+ASP.NET Core (C#)3 yrMid
New web SaaS / B2C5-30Next.js (TS)annual majorLow
New API-centric web3-30NestJS (TS) / FastAPI (Python)1 yrMid
Startup MVP1-5Rails / Laravel / Next.jsLTS availableLow
Microservices (internal)10+Go + Gin / gRPC1 yrMid-high
Edge computing1-10Hono (TS + Edge)NewMid
Admin-screen-centric1-5Django (Python)3 yrLow

FW major upgrades happen “every 1-3 years”; picking non-LTS produces yearly migration work. Spring Boot, Django, Rails, Laravel are LTS-explicit; business systems should pick LTS only. FWs with “chase the latest” culture like Next.js assume yearly major-version response.

Don’t run non-LTS versions in production. Every year becomes a migration project.

Framework selection / operation traps

Forbidden moveWhy
Adopt a niche emerging FW (SolidStart, Qwik) for productionEngineers don’t come; project freezes via hiring difficulty
Run non-LTS in productionShort support window; yearly major migrations
Keep using a deprecated FW like AngularJSOfficial support ended January 2022; sites that couldn’t migrate had to rewrite
Leave critical patches 2+ weeksEquifax 2017 left an Apache Struts 2 patch unpatched 2 months, leaking 147M people’s records. 72-hour Critical-patch SLA is the floor
Touch FW internals deeplyMajor upgrades break compatibility; migration cost 10x
Switch FW mid-streamSkeleton swap is essentially rebuild. Same fate as Netscape’s rewrite
No dependency-update strategy (no Dependabot)Vulnerability notices missed; direct Log4Shell (Dec 2021) hit
Leave version updates 3+ yearsCatching up is impossible; rewrite
Choosing a FW by performance benchmarks alonePerformance can be optimized later; talent and ecosystem depth affect project lifespan far more
Choosing a FW assuming mid-stream switch is possibleSkeleton swap is essentially rebuild; the first selection decides everything

FW selection requires “design including continuous-maintenance capacity from adoption.” Set patch-application SLAs (Critical 72 hours, High 1 week, Medium 1 month) and automate PRs with Dependabot — modern standard.

FW adoption is “a contract for continuous maintenance.” Not “install and done”; weekly maintenance is the assumption.

AI decision axes

AI-era favorableAI-era unfavorable
Next.js / NestJS / FastAPI (mainstream, info-rich)Minor / emerging frameworks
Spring Boot / Rails (mature, established patterns)Custom in-house frameworks
Products with clear “the framework way”Products without conventions, too free
Rich official docs, AI extracts contextOral-tradition-centric know-how
  1. Decide together with language (language-only first is failure prone).
  2. Pick mainstream + LTS (favorable on hiring, info, AI accuracy).
  3. Prefer convention-based (consistency over freedom).
  4. Avoid emerging FWs (long-term maintainability over buzz).

Equifax and the unpatched Struts (industry case)

The Equifax 2017 incident — leaving an Apache Struts 2 known-vulnerability patch for two months, leaking 147M people’s records — confronts the industry with the link between FW selection and patch operation (full details in Appendix: Major Incident Catalog).

Few developers haven’t sweated over delaying a patch. “Big release next week, after that” thinking and then in-week internal scans flagging Critical, forcing emergency maintenance — common stories.

Adopting a FW and then “installing-and-done” can turn into a fatal hit during the weeks between vulnerability disclosure and application. Pick LTS-marked versions, build a routine for following security info — these aren’t “do later”; decide them at selection time.

FW adoption is a continuous-maintenance commitment; embed continuous-maintenance capacity into design from adoption.

What you must decide — what’s your project’s answer?

Articulate your project’s answer in 1-2 sentences for each:

  • Framework adopted (main + surrounding libraries)
  • LTS version or latest
  • ORM / DB-access library
  • Auth library (in-house or external service)
  • Test framework
  • Future version-upgrade strategy

Recording the decision rationale

Frameworks are tied directly to product lifespan, so recording the selection rationale as an ADR (Architecture Decision Record) is important. Here is a concrete example:

ItemContent
TitleAdopt Next.js as the web framework
StatusAccepted
ContextSelecting a frontend FW for a B2C e-commerce site. SSR/SSG is a mandatory requirement for SEO
DecisionAdopt Next.js 15 (App Router)
Rationale- SSR/SSG/ISR can be switched per requirement, balancing SEO and performance
- Most adoption track record in the React ecosystem, with highest AI code-generation accuracy
- Deployable not only on Vercel but also self-hosted or on AWS Amplify
Rejected alternativesNuxt.js: few Vue-experienced team members, high adoption/training cost. Remix: weak SSG support, limitations on build-time generation for product-list pages
OutcomeProduct list via ISR (60s), product detail via SSG + on-demand regeneration, my-page via CSR

When version upgrades or tech-debt discussions arise, having zero material on “why we chose Next.js back then” means zero basis for judgment. Having “why we chose this” visible at a glance later is the greatest value of an ADR.

Summary

This article covered framework selection — major FWs per language, LTS management, vulnerability response, and AI-era generation accuracy.

Enterprise: Spring Boot. New web: Next.js. Python: FastAPI / Django. Cloud-native: Go + Gin. LTS mandatory, conventions preferred, lean on AI-known mainstream FWs — the realistic answer.

The next article covers transaction design (ACID / eventual consistency / Saga / Outbox).

Back to series TOC -> ‘Architecture Crash Course for the Generative-AI Era’: How to Read This Book

I hope you’ll read the next article as well.