Skip to content

27 · Programming Languages and Backend Framework Selection

The thesis in one line: language and framework choice is not a belief system. It is a constraint problem. An architect does not ask "which one is hottest"; an architect asks "will this choice change team speed, runtime cost, performance ceiling, ecosystem availability, and future migration cost?" If yes, it is an architecture decision. If it only changes syntax, it is implementation detail.


🧰 Technology Stack Selection Track, Chapter 1 · One thing to practice

The first 26 chapters kept saying "architecture is not framework choice." That remains true. But real systems still need Java, Go, Python, TypeScript, Rust, and backend frameworks. This track does not teach syntax. It brings "what tech should we use" back into the framework from Chapter 02: requirements, constraints, quality attributes, trade-offs.


Opening: selection is not a popularity vote

Teams often choose languages like this:

   I like Go          -> use Go
   Java hiring is easy -> use Java
   AI ecosystem is Python -> use Python
   Frontend is TS     -> use TypeScript

These are signals, not decisions. Keep asking:

  • What is the bottleneck of this business path?
  • Is the team short on delivery speed, runtime efficiency, stability, or hiring supply?
  • Does the ecosystem have mature libraries, test tools, monitoring, and deployment patterns?
  • Can someone new maintain this code three years from now?

Rule of thumb: if a language/framework choice materially affects quality attributes (Chapter 06), it is architecture. If it is only code style, do not turn it into a technology war.


1. Separate language, runtime, and framework

Beginners often mix three layers:

LayerWhat it decidesExamplesArchitecture concern
LanguageExpression style, type system, ecosystem entryJava, Go, Python, TypeScript, RustTeam familiarity, maintainability, early error detection
RuntimeConcurrency, memory, startup, deployment shapeJVM, Node.js, CPython, Go runtimeLatency, throughput, resource cost, cold start
FrameworkConventions and component assemblySpring Boot, FastAPI, NestJS, GinDelivery speed, testability, plugin ecosystem, team consistency

So "Java or Go?" is not a complete question. A better question is:

Given this team, business, and quality target, what runtime and delivery model do we need?

For an internal SaaS backend such as PatchDesk, the early challenge is not maximum QPS. It is permissions, tenant isolation, audit, reports, and long-term maintenance. In that context, mature framework conventions often matter more than raw performance.


2. Five rulers before tool names

RulerQuestionWhat it pushes toward
Business complexityMany rules, states, permissions?Strong typing, clear conventions, mature testing
Performance and resourcesIs CPU, memory, or P99 tail latency central?Lower runtime overhead, clearer concurrency model
Ecosystem maturityAuth, ORM, queues, observability, SDKs ready?Deep ecosystem, stable docs, active community
Team capabilityWhat does the team know? Can it review and hire?Main team language, or a new language with controlled learning cost
Delivery and evolutionFast iteration or long-term reliability?Clear framework conventions and migration paths

Architectural wisdom: do not change stack because a language is "more modern." A new technology deserves consideration only when it clearly buys a quality attribute and you are willing to pay learning, ops, hiring, and migration costs.


3. Common backend choices as trade-offs

TechnologyCommon strengthsCommon costsGood fit
Java / Kotlin + JVMMature ecosystem, stable performance, enterprise librariesCan feel heavy; framework complexityMedium/large business systems, finance, e-commerce, SaaS
GoSimple deployment, direct concurrency, low resource useComplex business modeling needs disciplineGateways, infrastructure, microservices, realtime paths
PythonAI/data ecosystem, fast prototypingRuntime performance and concurrency need careAI services, data platforms, automation
TypeScript / Node.jsOne language across frontend/backend, I/O friendlyCPU-heavy work is a poor fit; dependency quality variesBFF, small/mid SaaS, lightweight realtime services
RustPerformance and memory safetyHigher learning curve, slower delivery for many teamsStorage, proxies, engines, safety/performance-critical components

A system does not need one language only:

   Core business service: Java / Go / TypeScript
   AI and data processing: Python
   High-performance proxy or engine: Rust / Go
   Frontend and BFF: TypeScript

Polyglot stacks are not wrong, but they carry cognitive tax: build, deploy, monitor, debug, hire, and review all get harder. A small team that uses five stacks because "each module gets the best language" is often spending organizational capacity it does not have (Chapter 15).


4. Framework selection: mature by default

Frameworks are team agreements in code:

   A framework gives:
   routing / dependency injection / config / data access / auth / testing / observability entry points

   A framework takes:
   freedom / learning cost / upgrade cost / debugging transparency

For MVPs, reduce delivery risk. For long-lived team systems, reduce collaboration risk. Often, a mature framework that looks boring is safer than a cool lightweight stack that relies on everyone maintaining perfect discipline.

QuestionIf yesTendency
Many new teammates, long-term maintenance?YesStrong conventions, good docs, mature ecosystem
Fast experimentation, unclear business?YesLightweight framework + clear module boundaries
Lots of enterprise integration, transactions, permissions?YesMature enterprise framework
High-concurrency gateway/proxy?YesLow overhead runtime and mature network model
AI/data-heavy service?YesStay close to Python/data ecosystem, wrap it with stable APIs

5. When to change language or framework

Change should be driven by signals:

SignalMeaningPossible action
P99 keeps missing SLO due to runtime mismatchNot just bad codeMove hot path to a better runtime
Delivery slows because framework conventions fight the businessComplexity outgrew the frameworkModularize first, then migrate locally
Ecosystem gaps force heavy self-buildingMaintenance cost keeps risingMove to a richer ecosystem
Hiring and review are hardTeam cannot support the choiceConverge stack or strengthen platform constraints
Security/compliance/performance bar risesOld stack cannot cover new requirementsUpgrade the critical component

As in Chapter 14, changing stack should be evolutionary: carve a boundary, run in parallel, use shadow traffic where useful, and switch gradually.


🎯 Quick check

🤔A 6-person team is building an internal ticketing SaaS. The hard parts are multi-tenancy, permissions, audit, and reports. Most of the team knows Java. What is the best architectural judgment?
  • AUse Rust everywhere immediately for maximum performance
  • BUse a familiar mature Java / Spring Boot style stack as a modular monolith and keep boundaries and tests clear
  • CLet every module owner freely choose a favorite language
🤔When does language or framework choice become an architecture decision?
  • AWhenever someone likes or dislikes a language
  • BWhen the choice materially affects performance, availability, cost, collaboration, ecosystem availability, or future migration cost
  • COnly in microservice systems

Chapter summary

  • Language/framework choice is a constraint problem: business complexity, performance, ecosystem, team, and evolution come before tool names.
  • Separate language, runtime, and framework: they affect different parts of the system.
  • Mature is the default unless a new stack clearly buys a quality attribute.
  • Polyglot has cognitive tax: build, deploy, monitor, debug, hire, and review get harder.
  • Stack changes should evolve: carve boundaries and migrate gradually, as in Chapter 14.

Next: language and frameworks decide how services run and teams collaborate. But data is the part that stays. Chapter 28 · Database and Storage Selection asks where data should live, how it should be queried, and how it should remain correct.


💬 Comments