Choosing a Programming Language for a New Web Service
- #Engineering Philosophy
- #Product Development
Choosing a Programming Language for a New Web Service
Picking a programming language becomes a critical decision when launching a new web service.
If you are building something on your own, it is natural to gravitate toward the language you already know best.
Or you might seize the chance to self-study a language that interests you but rarely appears in your day job.
However, when you are delivering a product as a company or founding a business around it, language selection matters even more.
Here I want to summarize how I think about language selection from both technical and non-technical angles.
※ The scope here is limited to backend development.
Technical Considerations
When evaluating technology, scalability is what matters most.
The requirements of a massive, mission-critical project differ from those of a PoC or a subsystem.
Popular web backend languages today include Java, PHP, Ruby, Go, Python, and Node.js.
Java tends to be favored in contract development, while interpreter languages like Ruby and PHP feel common in web companies.
Recently even web companies lean toward statically typed languages such as Go and Kotlin. (That is the zone I am focusing on here.)
Given the premise of a brand-new web service, let us assume:
- small team
- speed is paramount
- features are added and removed rapidly
In other words, think of an early-stage startup.
I have helped choose the tech stack at this stage and, a few years later, felt that the choice was a mistake. That experience is the lens for this post.
Back then our reasoning was:
- Small team → keep the language simple
- Need speed → interpreter languages feel faster to iterate in
- Prefer something un-opinionated that anyone with prior language experience can pick up
So we chose Python.
For the framework we adopted Django, the full-stack web framework.
If this had been Ruby it would have been Rails; for PHP, Laravel.
Development speed is excellent in the early phase.
All you do early on is add new features. Even when tweaks appear, the mindset is to “add more.” That means you can happily ship for the first three years or so.
Side note: You rarely need to worry about interpreter languages being too slow. I was skeptical about runtime performance at first, but unless you truly need extreme throughput, language-level performance is rarely the bottleneck for web services. (Middleware matters more, and a web service is slower than a native app anyway.) Web development also leans heavily on existing middleware, typically OSS or licensed products rather than homegrown code. Most of the performance impact stems from those components, so with some tuning you can achieve perfectly acceptable speed even in an interpreted language.
Back to the story.
Three to four years in, you begin to update and retire features constantly. That is what happened at my company.
Depending on how you design things, weakly typed interpreter stacks allow subtle type differences or missing type definitions to creep in. Ambiguity grows and development speed drops.
That is when technical debt sprouts. You are left with either a large refactor or painstakingly extracting functions bit by bit.
Either way it is a time sink that does not improve product quality directly, so the return on effort is poor.
Because of that history I am critical of casually picking a dynamically typed interpreter for the sake of early velocity or trendiness.
In fact, when features are added and removed rapidly, strong typing often works in your favor.
Strongly typed languages also make it easier to design abstractions. Doing that upfront lets you enforce constraints on how features can evolve, which is a good thing when changes are constant.
Non-Technical Considerations
Language selection also affects areas beyond pure technology.
Take hiring. Company systems are rarely built by one person, so recruiting must be part of the equation.
Popular, battle-tested languages obviously help with hiring, but deliberately adopting new technology may attract developers who crave cutting-edge work.
You also need to think about the ecosystem.
JVM-based stacks have a massive body of existing assets, so the ecosystem is highly mature. If you hit a stack trace you can probably search the answer immediately.
Conversely, languages or frameworks with little adoption often lack ecosystem maturity; if the library you need does not exist you may end up building it yourself.
Or a library you rely on might be abandoned with no replacement in sight. The effort required in those situations is non-trivial.
In Closing
This was my latest thinking on picking a language for a new web service.
At this point I believe that if you expect a service to grow, choosing a strongly typed language leads to more long-term happiness.
Frameworks like Rails that maximize early speed are undeniably attractive, but the debt piles up faster. As systems grow, debt becomes harder to pay down, so if you press on with lax design rules (for example, extending a PoC directly into production) your future velocity will suffer.
Thanks to type inference and generics, modern statically typed languages are no longer slow to develop in.
Splitting frontend and backend instead of building monoliths is now mainstream, so the paradigm is shifting away from “go as fast as possible with a full-stack framework” toward “build a healthy system that avoids debt.”
On a related note, I recently evaluated backend options again and felt uncertain—yet I now suspect server-side Kotlin might be a fantastic choice. (I’ve been using Kotlin lately.)