The recent post by DHH regarding their decision to drop TypeScript support in Turbo 8 has suddenly made everyone eager to express their opinion on the TypeScript vs. JavaScript dilemma, and I'm no exception. Hell, I don't even know what Turbo 8 is, but I feel like I have something to share too. Just a few weeks ago I migrated one of my two biggest projects to TypeScript while keeping the other one using JavaScript, and this is exactly how I like it to be, for now.
Level up your server-side game — join 8,000 engineers getting insightful learning materials straight to their inbox.
Before explaining my motives, let me make it clear - I love the rigor of static typing, and I also love the ease of dynamic typing. After spending years writing PHP, Python, JavaScript, TypeScript, Go, and a little bit of Java and Rust, I have learned to appreciate both paradigms. I thoroughly enjoy cracking my brain to get the types right, then reveling in the guard rails they provide, as well as fully embracing the freedom of a dynamic type system to piece things together quickly. It's simply two different kinds of joy for me.
However, what I enjoy even more is pragmatism. The main goal is to move fast at every stage of project development, and if that means a change in technology, so be it.
Now, back to my recent experience. Since last December, I've been working on two substantially sized frontend projects: a typical "website" with an API behind it, and a less typical highly dynamic Kubernetes Explorer single-page app.
I'm no professional frontend developer, and my strategy for building UIs has always been, "Keep changing the code until it somehow works, and the fewer obstacles there are along the way, the better." Despite my respect and love for statically typed languages, in the early stages of development, when the codebase can be fully rewritten multiple times in a week, types can be an obstacle. That's why I started both of these projects with JavaScript.
Nine months in, I'm still very happy with using JavaScript for the "website". The project is a combination of the UI (Vue) and the API (Nuxt) components. The UI components are plentiful but simple - most of the time, when I discover a UI regression, it's due to changes in CSS or HTML, not because I messed up with the code. The API is less trivial - the traditional backend for frontend (BFF) logic like authorization/authentication, data transformation, etc. is intertwined with custom course management and fleet orchestration logic and distributed across several dozens of API handlers. Such an architecture (or lack thereof) could have significantly slowed down development, but unlike the UI components, the API surface is a much more stable domain. Writing black-box tests for it was a no-brainer. Initially, these tests were intended to be an acceptance suite that would check the system end-to-end, including components far beyond the JavaScript API (i.e., upstream services). But over time, they also became the primary means of validating changes to the JavaScript code. I couldn't be happier with the current state of affairs in this project - achieving several goals with just one set of tests and without the need for heavy TypeScript machinery, how can it be better?
So, why did I decide to migrate my other project, the Kubernetes Explorer SPA, to TypeScript then?
The answer lies in the different set of constraints and requirements this project presents. Unlike the iximiuz Labs website, where the main complexity is concentrated in the API layer, the Kubernetes Explorer's trickiest part is its UI components. The main logic of the Explorer resides in the browser-run code, and it's no trivial matter. Dealing with numerous attributes or constructing complex graphs of Kubernetes objects without type hints is tedious, and refactoring such a codebase without type checks or tests has proven to be extremely error-prone.
After yet another massive change to the Explorer's UI - where regression hunting took me longer than the actual rewrite - I decided it was time to enhance the developer experience. Essentially, I had two options: start writing tests for the UI components or introduce types.
Tests are fantastic, and they worked well with my other project, but from previous experience, for a rapidly changing domain, the downsides of having tests can often outweigh the benefits. Maintaining the test suite can become a real burden, and developers tend to grow attached to highly-tested components, making them difficult to discard when they no longer fit well the UI.
At the same time, most of the regressions I've encountered in the project so far were due to missing attributes or objects of one shape being accidentally passed to a function expecting differently shaped objects – issues typically prevented with the help of a compiler. Hence, I decided to migrate the project to TypeScript and, for now, keep the number of tests minimal. Two weeks and three rewrites later, I couldn't be happier with my choice.
Will I add more UI tests to the Kubernetes Explorer in the future? Possibly. Will I migrate the website to TypeScript? Maybe. Will I revert to JavaScript for this app one day? Yes, if I find that it helps me progress faster.
Of course, your mileage may vary. The nature of projects varies greatly, and there is no one-size-fits-all language or solution. My advice? Stay pragmatic, choose your tools optimally, and avoid dogma. Ship fast and live long 🖖
Level up your server-side game — join 8,000 engineers getting insightful learning materials straight to their inbox: