
About six months ago, CEO Reddit Steve
announced that we are redesigning the site. The main question here is how we do it. Nowadays, front-end development is very different from what it was at the time when Reddit was born. Now there is a huge choice of options for each subsystem of the web application. How to render pages? How to stylize content? How to store and maintain images and video files? How to write code? In modern conditions, none of these questions have a ready answer.
One of the first questions of this kind, to which we needed to find the answer, was: “Which language to choose?”.
About the richness of choice and language requirements
Oddly enough, JavaScript did not necessarily have to be our frontend language. Ultimately, whatever language is chosen for this purpose, the code written on it is still compiled into JavaScript. However, what exactly the code compiles is perhaps less important than what the developer writes. The choice of language was not an easy task. Here is what we had to consider:
- BuckleScript
- ClojureScript
- CoffeeScript
- Elm
- ElixirScript
- JavaScript 2016 and future language versions
- Javascript + annotations
- Nim
- PureScript
- Reason
- TypeScript
And this, by the way, is not a complete list. Each language has its pros and cons, so in order to help ourselves choose one of them, we have formulated the following requirements:
')
- This should be a strongly typed language . Types, at the micro level, play the role of documentation. They help, to a certain extent, to ensure the correctness of the code, and, more importantly, simplify refactoring. Another consideration by which we were looking for a language with strong typing was the speed of development. We sought to find a strictly typed language, because we wanted to speed up the work. Such an idea goes against the vision of typing that many programmers have developed. Namely, it is considered that this is an additional burden on the developer, which reduces the speed of work. However, increasing the speed of development also means an increase in the likelihood of errors. We needed typing in order for the code to contain as few errors as possible, even if it was written quickly. Strong typing is also useful in fast-growing projects. Our team of engineers is constantly increasing in size, and the number of those working on the code is steadily growing.
- It is necessary that there are good supporting tools . Taking into account what we were going to do (serious redesign of the site), we did not have time to create a significant number of aids on our own. It was very important that we could quickly get to work using open source solutions. More specifically, these are the tools that are being discussed. These include integration with popular build systems (for example, with Webpack), linter support, and easy integration with testing frameworks. If the integration of support systems for a language was not obvious, we no longer considered this language.
- The language was supposed to be used in serious projects . If the language looked good, but in reality it was used only in small projects of individual enthusiasts, it could well be inappropriate for our task. In addition, if the language is not used in serious projects, there are questions about how long such a language will survive, and how quickly language developers will respond to reports of errors found in it.
- Our developers must master the language quickly enough . In the above list there are excellent languages, the only disadvantage of which is too long a period of time, which developers need in order to master them. Among them, I would like to mention Elm and PureScript. We seriously discussed the issue of their use. However, in the end, it turned out that their implementation would have meant too much work required for the development of new programming concepts by developers who are not familiar with them, for them to reach a level that allows them to work productively on the project.
- The language should work on both the client and the server . SEO is very important on Reddit, so the lack of a universal system for issuing browser-ready pages is a big problem.
- Good library support . We were not going to write everything from scratch. There are some tasks that require reliable, time-tested libraries. We wanted to have the opportunity to choose what we need from existing libraries.
After reviewing these requirements, we settled on two options. The first is TypeScript. The second is JavaScript + Flow. But, before making the final choice, we wanted to understand as much as possible the features of TypeScript and Flow, as well as the differences between them.
Compile or annotate?
One of the important differences between TypeScript and Flow is that TypeScript is a language that compiles into JavaScript, and Flow is a set of type annotations that can be added to JavaScript code. The correctness of the annotated code is checked by a static analyzer.
The above differences directly affect how the programs write. Take a look, for example, on working with enumerations in TypeScript and Flow.
TypeScriptenum VoteDirection { upvoted = 1, notvoted = 0, downvoted = -1, }; const voteState: VoteDirection = VoteDirection.upvoted;
Flow const voteDirections = { upvoted: 1, notvoted: 0, downvoted: -1, }; type VoteDirection = $Keys<typeof voteDirections>; const voteState: VoteDirection = voteDirections.upvoted;
Since TypeScript is a compiled language, it can be used to create types that are defined during program execution. In Flow, types are just annotations, so we cannot rely on any code transformations to create type representations during program execution.
However, TypeScript has certain disadvantages due to the fact that it is a compiled language. When integrating TypeScript into an existing code base, there is a possibility that the compiler will complicate the build process. We have a well-established assembly process using Babel and the transfiguration layer. There are several optimizations that I would like to keep even when switching to TypeScript, so I needed a way to integrate TypeScript, which would not destroy the scheme of work that already exists. The result of implementing TypeScript would be a much more complicated build process.
In contrast to TypeScript processing, Babel automatically deletes Flow type annotations. If we had chosen Flow, the application build process would not have been complicated.
Code validation
In the area of ​​validating code, Flow usually performs better than TypeScript. In Flow, by default, it is forbidden to use types that admit a
NULL
value. TypeScript support for types that do not allow
NULL
been added to TypeScript 2.x, however, you need to enable this feature yourself. In addition, Flow better prints types, while TypeScript often refers to the type
any
.
In addition to the types that admit the value
NULL
and type inference, Flow is better in matters of
covariance and contravariance (
here is the material on this topic). One of the typical problem situations here is working with typed arrays. By default, arrays in Flow are invariant. This means that the following construct in Flow will cause an error:
Flow class Animal {} class Bird extends Animal {} const foo: Array<Bird> = []; foo.push(new Animal());
However, an attempt to do the same in TypeScript is completed without error messages.
Typescript class Animal {} class Bird extends Animal {} const foo: Array<Bird> = []; foo.push(new Animal());
You can find many more similar examples, however, the general conclusion is that Flow is much better at testing types than TypeScript.
Ecosystem
Everything that has been said so far speaks about the benefits of Flow. It is easier to integrate into the project, it is better at handling type checking. Why did we stop at TypeScript?
One of the most important advantages of TypeScript is its ecosystem. It has amazing library support. Almost all the libraries that we used either have type descriptions in the libraries themselves, or are represented in
DefinitelyTyped . In addition, TypeScript has excellent IntelliSense support in VSCode and in plugins for other popular editors that we use (for example, Atom and SublimeText are among them). Moreover, we discovered that TypeScript can handle JSDoc annotations. This turned out to be particularly useful since we switched to TypeScript with JavaScript.
In addition, TypeScript is more “socially significant”, and there is a feeling that its lifespan will be quite long. There are several large projects that use TypeScript (among them are VSCode, Rxjs, Angular, and TypeScript itself), so we have confidence that the set of its capabilities will be able to meet the goals of our project, and that the language in the coming years not going anywhere. As for Flow, we are concerned that it was created to solve specific tasks on Facebook, and that its development will be determined by the same range of tasks. TypeScript, on the other hand, is focused on a wide range of issues that Microsoft is working on. All this allows us to assume that if we encounter errors and report them, we will be heard and taken action.
In addition, since TypeScript is a language that is a superset of JavaScript, we can expect that the development of TypeScript, in particular, its support for new types and language constructs, will follow the development of JS. The language and type system will develop in parallel, as work on them is going on simultaneously.
Results
We chose TypeScript, as we are confident that we will be able to quickly train all the necessary new developers (the number of our front-end engineers has tripled over the past year). We are confident that the language meets our needs in the redesign of the site. In addition, everything suggests that TypeScript will exist for a long time, and our research has shown that it integrates well with our code base. However, for us the most important thing is the transition to a strictly typed language.
The use of a typed language has already brought some results. In the code, there are fewer errors associated with types, we are more confident in performing large refactorings. Built-in documentation is now focused on concepts, and not on the description of the internal structure of objects and parameters of functions. As a result, we can say that TypeScript is exactly what we needed.
Dear readers! Do you use TypeScript or Flow?