Skip to content
Fred Zhao
TwitterLinkedIn

JS development in 2025

11 min read

Recently I talked to other software engineers looking to build some simple apps, and recommended RedwoodJS as a simple starting point (based on this earlier post). Since these folks haven't written production JavaScript in recent years, I realized that there were quite a few decisions to make and understand... many of which I didn't really understand myself either, despite working in JavaScript more seriously the last 5 years (and casually the 5 years prior).

So, this post is an attempt to summarize the state of JavaScript development (abbreviated JS below) going into 2025. Think of its target audience as any of these personas: a college new grad eager to jump into the industry; a vetern to programming who has been spared from needing to write JS thus far; or someone who, like me, has worked with JS for a while, but at this point feels too afraid to ask "why?"

afraid to ask andy

If any of those descriptions sounds like you, read on! We'll explore the reason JS development can be summarized as a glorious and schizophrenic chaos.

Summary

  1. Use nvm to install node
  2. Run corepack enable to allow packages to specify their package managers and node versions
  3. Read the rest of the table below

The Table

The table is divided up into 2 layers: run the code; write the code.

The "Recommendation" column assumes you can change that decision (for example, when you're starting a new project); in reality, if you're contributing to an existing codebase, it'll have a lot of decisions made already, so all recommendations go out the window.

⬇️ NOTE: "Recommendation" options denoted by (R) are supported out-of-the-box by RedwoodJS.

Layer/Decision"Why is this even needed?!"RecommendationExplanation
Run the code/
Backend ServerThere are lots of language options for the server such as Java/Kotlin, Python, Rust, etc; even within JS, several options exist today (node, deno, bun). We need to pick something to run the server codeUse nvm to install node at v22Node still has significant market share, and therefore a significant amount of community support to help you debug. For most of 2025 the long-term supported (LTS) node version will be v22, which has enhanced performance with better TypeScript integration; expanded below
Frontend Web BundlerUnlike many other programming languages, JS may need to run on browsers, and source files need to be delivered there somehow, expanded below. We need to pick a tool to help do thisvite (R)Fast build times, native support for TypeScript; see the "Dynamic vs. Static Typing" row later in this table for more on TypeScript
Target VersionJS as a language evolved over time, and many aspects from its syntax to keywords have changed significantly. For each of the backend and frontend, We need to decide on a baseline version for this interpretation standardES2022? (R)Anything ES6 (aka ES2015) and later will have the most significant and useful improvements. ES2022 balances between modern features and broad compatibility, expanded below
Package ManagerYou'll almost never build purely from scratch, and instead usually use libraries (packages) that others have published.
node ships with a manager npm, but there are also alternatives like pnpm and yarn
pnpmpnpm is mature enough, benefits from efficient disk usage, and has good dependency and workspace management
Project Module TypeThe syntax for defining and using code (both external packages and internal files from your own project) has also evolved over time. CommonJS and ES Module are two popular current choices, expanded belowES Module if possibleNew projects should benefit from the newest standard; just be aware that there are many ways to import!
Write the code/
Dynamic vs. Static TypingJS is dynamically typed, which allows for faster code writing than statically typed languages; static typing helps reduce errors, which speeds up development in the long run. We need to pick a side to err onStatic typing, using TypeScript at the latest version (R)For someone new to JS, static typing will force a more explicit understanding; for everyone, the benefits (and tradeoffs) of static typing are expanded below
LinterLinting further reduces errors, expanded belowESLint with typescript-eslintCombined JS/TS linting with type-aware rules, to help new and veteran JS programmers alike reduce errors
FormatterLanguages like Go provide default formatters like gofmt, but JS doesn'tPrettier (R)An auto-formatter helps keep a consistent code style, so you can save energy from thinking about "correct" formatting to devote to the business logic instead. Biome is up-and-coming, but let's go with Prettier as a proven and opinionated tool for now

Explanations Elaborated

"Glorious and schizophrenic chaos" is neither praise nor critique for the language, but more a reflection of how JS has evolved in a distributed way over the years, and an explanation to brace yourself for its tradeoffs.

  • The "Glorious": JS can run everywhere! Many other languages can be transpiled to run in browsers, but JS runs natively. So it can feel glorious to build an entire app using the same language... well, mostly, as explained below.
  • "Schizophrenic" is an important reminder that working with fullstack JS means writing in two languages, one for the frontend and one for the backend. They just happen to have the same file extensions and use the same syntax!
  • Finally, "Chaos" reflects the various configurations and decisions that arise from its rapid development over time.

The rest of the post will attempt to help you navigate through it all, and emerge with your sanity mostly intact.

loki timelines

Backend Server

Language choice for the backend is worth a post itself, so for brevity, we'll assume we want to use JS. While that is not necessarily the best deciding factor, having "one" language (ignoring the "target versions" consideration!) is a glorious simplification to make when starting out, before we see other factors like performance arise.

Within JS, several options exist today (node, deno, bun), and I recommend node purely from its long-established usage.

Node has different versions (just like Python, Rust, etc.), and the packages you want to use may target a minimum requirement that necessitates a higher one than what your machine currently has installed... or a lower one.

So, nvm helps quickly switch between versions. This also helps resolve seemingly random permission issues that may arise from otherwise installing node globally, as explained in this StackOverflow question.

Finally, the choice for node v22 in 2025 is simply because it's new enough to be officially well-supported (as the current Long Time Support edition), but not so new that there are still bugs to be ironed out.

Frontend Web Bundling

Unlike the execution of JS on the backend that has direct access to source files, frontend JS execution requires the code to somehow be delivered to users' browsers. This has evolved quite a bit over time, in both the way a programmer needs to separate their modules and how those modules will actually be assembled and delivered.

This article by Devlin Glasman on Modules and Bundling does a great job elaborating on the history over time.

The choice for vite is due to its speed and plugin system. I've also seen other options like webpack and esbuild, but vite seems to be the current widely preferred choice.

Target Version

JS's language specification is abbreviated ES for ECMAScript, whose namesake Wikipedia article elaborates on the history. To over-simplify a bit, we can think of these as synonymous for the purpose of the rest of this post:

  • JS
  • JavaScript
  • ES
  • ECMAScript

For a language like Python, it's both a language and an implementation: python.org defines the language specification and provides a default implementation CPython, elaborated in this Reddit thread. That allows for a more straightforward standardization process.

In contrast, JS is just a language standard, and there's no prescriptive implementation when you (or your users) open a browser or fire up a backend service. But over time, it inevitably had to evolve to fill in various gaps, like how to handle async processing (callbacks -> promises -> async functions) and what default functions exist on Array objects.

Put another way, JS has had a distributed evolution, from different engines implementing a language specification standard, that happened simultaneously with the evolution of that standard itself.

  • Runtime engines include JavaScriptCore, SpiderMonkey, V8, each of which are used in some browser frontends and server backends
  • The language specification has evolved as part of a committee called TC39. In theory, anyone can make recommendations and proposals to the committee; someone I knew from college added Array.prototype.includes!

Long story short, the choice of ES2022 is a balance between "new-enough" language specifications, and old-enough to have enough browser support. This is similar to the decision for the Backend Server, but can be more conservative and prefer an even older option if your app needs to run on older browsers.

Module Type

There are two considerations here:

  • Type of the module for my own project, as defined by a Node project's package.json
  • Type of the module I want to use/import

The earlier mentioned article by Devlin Glasman on Modules and Bundling is also a relevant read here that explains the difference between CommonJS and ES Modules.

The recommendation for ES Module is for the first consideration ("my own project"), since they can also easily import CommonJS modules,

The reverse was not true for a while: CommonJS projects could not easily reuse ES Modules. And today, many packages publish as ES Modules only, with many of them linking to this disclaimer. But there seems to be some good news: if you have to use CommonJS as the module system for yoru project, you may be able to take advantage of a relatively recent feature in node v22 by Joyee Cheung to use them as well.

Typing and TypeScript

Keep in mind that a required tradeoff is that Static Typing will reject some valid code by design, which may necessitate writing the code differently.

For the latest version of TypeScript: features tend to be additive, so picking whatever is the latest version should be safe. Just be sure to install at the project scope and not global scope!

Some helpful options are off by default, and need to be enabled from a configuration file called tsconfig.json. This article provides a good initial config file. I like it because it has noUncheckedIndexedAccess enabled by default, which passes my bar for a thoughtful starting point.

Linting

Linting is very closely related to typing. It helps maintain clean coding conventions. Similar to static typing, it will reject more buggy code at the tradeoff of also rejecting some benign code. This is usually still worth it!

Other considerations

The above considerations aren't the only decisions you may need to make. The chaos of JS development means there are even more rabbit holes you can dive into.

Here is a slightly simpler table, without the "why does this exist" column since all of them have the same reason: JS has a huge ecosystem with a lot of options!

I wrote down some current recommendations and explanations here too, but consider them "smaller" recommendations -- they may still turn out to be important considerations for your app building experience, but I expect them to be more framework- or project-driven decisions not always within your control.

Decision2025 RecommendationExplanation
Client <-> Server ProtocolGraphQL (R)Similar to using TypeScript, adding more static typing helps avoid errors. Another alternative is trpc; just keep in mind that client and server are really TWO languages, and you may need to address backwards compatibility
IDE/EditorVSCodeStrong TypeScript support, integrated debugging. I use vim myself, but would recommend VSCode as a much more feature-fledged IDE for someone totally new
Frontend UIreact at v18.3 (R)Huge ecosystem to get community help and learn from examples. v18 has been out since 2022 and v18.3 adds helpful warnings for upgrading to v19 as mentioned in the latter's upgrade guide
Testing FrameworkStorybook for UI (R), Jest for everything elseAs you work further on your project, adding automated tests will help prevent regressions
StylingTailwindCSS (R) plus a corresponding component library like flowbiteI'm not a frontend expert, so if you aren't one either, you'll want to save time by reusing existing style libraries!

Inspirations and References

If you enjoyed this post, please check out some of the inspirations and references that helped me write it!