facebook / flow

Adds static typing to JavaScript to improve developer productivity and code quality.
https://flow.org/
MIT License
22.07k stars 1.86k forks source link

General Feedback #4451

Open webdesserts opened 7 years ago

webdesserts commented 7 years ago

Hi Flow Team!

I was reading this twitter thread today and it prompted me to leave some feedback on my experience with Flow. We've used both Flow & TypeScript in our team, though we started dropping Flow out of our builds about a year ago. Here are some pain points that we experienced with Flow and a comparison with our experience using Typescript:

Shared Types

Shared type definitions are better documented/easier to get started with in Typescript, specifically in the realm of sharing declarations across npm packages. Having type definitions a quick npm i away was vital for rapid adoption. It immediately provided benefits to untyped projects and helped encourage devs to start exploring the type system.

I don't think that Typescript has really "figured out" types as dependencies either. There is currently no easy way that I know of to semantically version a project that tracks another semantically versioned project so @types just tends to bump the patch number. This introduces breaking changes all the time. Also, multiple versions of the same declaration dependency will cause conflicts. This is a constant thorn in our side which also prevents us from "locking down" to specific @types versions, ensuring that the breaking changes issue I mentioned above is unavoidable.

/* @flow */ vs. .ts

I think using /* @flow */ rather than a custom extension causes usability issues. It's not as self explanatory to new developers that they are "working with a different language". When one of our new developers runs into a .ts file, they generally are able to google it, find Typescript Documentation, and at the very least familiarize themselves with the language before asking the more senior devs about it. With flow, a lot of times a dev would never know they were working with flow annotations. It wasn't clear when they were working with a "flow project" and when they weren't (it doesn't help that .flowconfig is a hidden file). This was made even more confusing by some community tools like WebStorm not requiring /* @flow */ before using flow annotations. I think I much prefer the clarity and strictness of the extension based language.

Editor Support

As far as I could tell, editor support was mostly absent for Flow unless you forced all your developers to use Nuclide. The Nuclide project seemed focused on internal use and consequently came bloated by default. It had it's own plugin manager and came with a slew of plugins that we will never need and heavily modified the atom interface to include features for languages we will never use. Considering that all I wanted was type hints this quickly turned me away from the project. I'm not sure if I ever actually got the type checks working with Nuclide.

We use WebStorm where I work and the Typescript integration was pretty solid. Most of everything you would expect "just works". There are a few cases where WebStorm will produce weird errors that tsc won't emit that can be a little annoying. Visual Studio Code is a pretty nice lightweight editor and I've been using it pretty regularly now. Having "per file" type checking by default without any extra setup is really nice. There is a bit of extra work for getting those type checks working project-wide, but it's not too bad. With both editors I've noticed that sometimes the type checks "get stuck" and you'll have to manually rerun type checks or restart the editor.

Build Integration

For us build integration with Flow was a pain. Community tooling was lacking. Webpack integration was almost hostile. This generally meant that we would have to run flow checks in a separate process from our normal watch task. This made it difficult to enforce those type checks and reduced their discoverability. Eventually I accidentally dropped flow checks from our deploy script during a rewrite and no one even noticed.

I think one of the pieces that a lot of people ignore when comparing the two languages is that Typescript is not just a language, but a build system as well. In my opinion (as the guy who worked on our site's build system) the number one differentiating factor was that working Typescript into our builds was not just easy, but was a joy. Here is a list of build steps that we got for free by dropping in a tsconfig.json:

In most cases this was enough to drop our entire build for a project. This is huge considering how much development time and maintenance goes into the average JavaScript build system these days. In this sense, Typescript is not just solving JavaScript's lack of a type system, but is reducing the complexity of the JavaScript dev environment in general. For frontend projects we still needed to keep Webpack around, but community tools like awesome-typescript-loader made Webpack integration a breeze.

On the Subject of Soundness

In this tweet Caleb mentioned that Flow can guarantee type soundness, while TypeScript can't. I agree that this is great, but it's not as strong of a selling point at the moment as one might think. The reality of JS is that most projects have no type checking at all. For a large untyped project, adding any form of type checking is going to drastically reduce the number of errors going out into production. If your product is regularly breaking in production today a low barrier to entry is much more valuable than complete type soundness. If I can't figure out how to use the tool, I'm never going to be able to reap its benefits.

This wasn't meant to pick on Caleb as it seems he agrees with the usability issues, it's just meant as a note on marketing.

Closing Remarks

I did enjoy Flow when it was working smoothly. It sounds like y'all are aware of several of these issues, but I hope that by sharing my experience it helps highlight some of the larger pain points. Some of the above issues may already be solved. It could also just be that some of these issues were not well documented. I'm not sure. Either way, best of luck as y'all continue your work to improve Flow and its community.

calebmer commented 7 years ago

Thank you so much for this! I agree with most of things you've said. To me soundness is the reason to invest in Flow as it will pay off in the long run, but you're right the benefits are not clear today and we need to fix usability issues.

It sounds like you have a project with a number of packages and a fairly complicated build system. Could you share what your dream build system would look like for your project?

webdesserts commented 7 years ago

@calebmer To clarify, I agree that soundness is the best reason to invest in Flow, it's one of the reasons we tried it first. I just think that until the barriers to entry are removed, that benefit will not be realized for some users.

For us, the ideal build system that we are working towards contains 3 major pieces: a compiler, a bundler, and a task manager. For us that's currently tsc, webpack, and gulp respectively.

The Compiler

I think that currently what typescript is doing is pretty ideal. as far as the compiler goes. It combines all the build steps that I mentioned above into one quick command, but also gives you the option of dropping the majority of it and just compiling typescript. I imagine there are a lot of potential benefits for a compiler having knowledge of the entire system. For example one of the earlier issues on typescript's github discussed possible benefits to tsc based minification. I'm not sure how much of this came to fruition, but I could imagine there's benefits outside this as well.

I think the number one thing this brings is that it takes the glue out of the average developer's hands. In my experience, if a build system is going to break, it's not going to break in the tools themselves, but in the glue between. For example, our Sourcemaps were constantly breaking when we had to manage passing the maps between build steps. Sometimes this is because api changes, other times this was due to misconfigured tools. Some tools would produce sourcemaps but couldn't consume them. It just takes 1 link in the chain to utterly break the maps and it felt like a constant balancing act. By handing over the majority of that responsibility to tsc our sourcemaps are working more consistently with far less effort on our part.

I love babel and I think it solved many of the issues that have plagued JavaScript. I could imagine a compiler like this could be built with something like babel at it's core. I fear though that the flexibility it brings might be at the opposite pole of the user experience & performance that I feel we need.

The Bundler

The compiler is probably good enough for most libraries & cli-tools but when you enter the realm of frontend, Webpack really is king right now. Webpack solves two major issues for us:

These days it's hard for me to imagine building a sizable project without some sort of dependency management and the npm ecosystem is hard to ignore. Ideally webpack would just do the bundling of our app and awesome-typescript-loader or something similar would handle the rest, but there's a few pieces of webpack magic that we probably won't be able to get rid of any time soon and for me CSS Modules sits at the top.

CSS, SVG, and other Non-JS assets have always been conceptual dependencies of our javascript components. Webpack is just one of the few projects to tackle the problem of formalizing this relationship. In particular, webpack loaders (with some... uhh..." hacks") enables us to publish our frontend assets along-side our React Components to npm. It's definitely not a perfect solution, but it works for us.

I don't see this last bit of complexity being consumed by something like tsc any time soon. However, by giving webpack control over hooking up modules, it kinda throws some wrenches in the "all consuming compiler" concept. For example, if I remember correctly, awesome-typescript-loader was having some issues making the most of the typescript's minification since it would somehow have to teach typescript about webpack's module glue.

The Task Manager

Though it sounds like a lot of people have dropped their gulp files as things like webpack and tsc have taken their place, we still use gulp on top of all of these tools. We actually don't use gulp-plugins any more (node streams are kind of a pain to work with). However, the task registries feature of gulp 4.0 makes it super easy for us to share build tasks as dependencies. This works similar to the way react-scripts bundles tasks right now in that you have all of your project tasks in one dependency and then call that bin through your npm scripts.

"scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject",
}
"devDependencies": {
  "react-scripts": "^1"
}

However in our case we break this bin into two where one dependency is the "task loader/runner" and the other dependency is the undertaker registry.

From my experience it seems like most build tools don't expect this use case so you'll generally have to find work-arounds for tools wanting to pollute your project roots with configuration files. For example almost all of our project tsconfigs just look like this:

{
  "extends": "./node_modules/project-tasks-app/tsconfig.json"
}

Kinda sucks that we have to still drop a config file in every project, but text editors can generally still figure out the tsconfig and it allows for easy extension of the default tsconfig if needed, so it's not the worst.

I hope that in the future, builds as dependencies are more widely supported and explored by community tools. In think it allows for easy adoption and makes upgrades a cinch.


Anyway, there's probably more I could say about each of these, but I think that's all I'll be able dump out on paper for now. If Flow doesn't take a similar position to tsc, it could maybe provide a react-scripts for the average js lib that comes with flow support (maybe jest as well?). I think people are probably used to complex webpack configs at this point (its getting better), but noone wants to spend hours setting up a gulpfile for a library that has a handful of files. This is where the "all-consuming" tsc compiler thrives right now.

calebmer commented 7 years ago

This is great, thanks! I’ll be thinking about this while we explore ways in which to make the JavaScript end-to-end development experience better with Flow.