A Newly-Generated Create-React-App 2.1.5 App Has 1,568 Dependencies
twitter.com
twitter.com
Dan Abramov pointed out [0] that:
> in terms of feature set (test runner, devserver, compiler with support for bundle splitting and optimizations, error overlay with editor integration, static code checker) it’s comparable to a small Xcode.
> It’s valid criticism that tools like Xcode are installed once whereas react-scripts is per project. But the upside is you never have the “different projects demand different Xcode versions” problem. And everything runs on CI thanks to full encapsulation.
> I’m hoping approaches like Yarn Plug’n’Play will give us best of both worlds. Shared install for the same versions between projects but with isolated and versioned tooling.
[0] https://twitter.com/dan_abramov/status/1097309141075456000
[0]: https://blog.npmjs.org/post/178027064160/next-generation-pac...
Long-term, stuff like the size of `node_modules` seems very solvable.
Simultaneously with that, I do think that there'd be benefit if some of the larger JS build tools started evaluating their own transitive dependencies and submitting PRs to inline some of the smaller pieces somehow [0].
Yes but good luck with React.createElement(). React without JSX is really a pain to use (or Preact, Inferno, etc).
Vue is much more straightforward for those simpler cases where you don't need/want to setup Babel, Webpack, etc.
For example, if you prefer to use small wrapper functions like `h(MyComponent)` and `div()`, there's `react-hyperscript-helpers` [0].
More recently, Jason Miller published `htm` [1], which provides near-JSX syntax using ES6 template literals, and no build step required. It appears to be a very viable alternative to JSX in many ways.
What's painful about it? I prefer it without JSX, personally; JSX is a pretty simple syntax transform that doesn't really gain you anything that I can see, makes manipulating props a lot clumsier, and needs extra tooling. Most of the time it's almost identical with or without JSX, but when there is a difference, I've always found without to be smoother and clearer. You definitely want to assign React.createElement a shorter name, though.
Not just because it's harder to use without JSX (and JSX is a great idea), but also because every Stack Overflow answer assumes you're using Webpack and JSX. For a beginner, choosing not use JSX or Webpack, etc just makes the learning cliff steeper.
https://reactjs.org/docs/add-react-to-a-website.html#quickly...
How is the browser support for this?
It seems Vue is definitely the simpler choice to write reactive components in projects without a build chain.
TBH, I personally am not too concerned with the duplication/disk usage, but the number of dependencies it pulls is a pretty big one.
What we probably need to do is walk that back a bit at a cultural level, and make our libraries just a bit more cohesive and self-contained. There are a few libraries like Lodash and Ramada that aim to be general-purpose toolkits, and Vue and Angular are good examples of reactive libraries that come with their own state management, plus some other stuff. Of course, the latter probably (I'm not actually positive) carry their own big lists of npm dependencies.
Not having to write code is great, but when you bring something in it becomes code you must maintain for however long it is in your project. The utility calculus is variant for all projects and even different phases of the same project. But the calculation should be done often and ones that fall below the utility threshold should be removed.
Unless you're deeply experienced, it's a very hard judgement to make.
Plus the time spent investigating is not small either.
My rule of thumb is to take a lib if it's not you're core concern, and write your own if it is. You only have one core concern in a project, and that's what the customer pay for.
For me, I find that it's a function of overall value to your code, number of issues you've had with it, overall size, ability to understand the code relative to what it's doing, and project health. Again, the calculus is different for everybody, but the important aspect is that you're consciously thinking about the utility of the dependency.
An ORM is hard to write, is often hard to read, but provides extreme value, and hopefully has a large community to quash issues and contribute to the project's health. To borrow an extreme example, left pad is not. Even if left pad is not in your core competency, that's not something that should be brought in because you can write your own. Even left pad may be subject to the event-stream style attack, and it being out of your control is risk you're bringing into your project.
Risk management is tricky, and you'll never get to 0 risk, but you should try to minimize it where you can reasonably do so. It's all about balance.
This means that if you have a problem with create-react-app or it were to be abandoned, you could just get a different development tool and likely make zero changes to your actual code.
Ever look in your node_modules folder? I dislike the JS ecosystem for this very reason. It's so common to find dependency soup in any non-vanilla JS app.
Tooling helps with this, but I usually do this reevaluation task when I go to do dependency bumps. "Why can't I bump this dependency to its current version?" usually causes me to reevaluate the dependency thoroughly. Many times it results in just making an upstream PR, but sometimes the decision is made to inline the parts that are used. As an example, we had a dev dependency that all it did was print tables with borders for pretty printing test failures in some capacity. When I went to do a language bump, it was incompatible, so I rewrote it in 10-15 lines using the standard language library. At the time, it was helpful, but it overstayed its welcome by preventing a language upgrade. As a counterexample, our ORM has caused me extreme strife this month because it changed a feature we were relying on heavily. That resulted in a bunch of work, but the dependency is so useful to our project that I would not have considered rewriting it.
We can strive for sustainability and autonomy, but we're forever dependent on each other's work. As they say, it's turtles all the way down.
Introducing dependencies is a quick way to get to market since you don't have to build a universe from scratch.
Once you've gotten to market, you have to support your thing. This is when dependencies start to have a non-zero cost. When you'd rather spend engineering efforts on building new features, you instead have to do regular house cleaning to make sure your thing continues to work and have a happy upgrade path.
The larger the engineering organization you work in, the more apparent this becomes. Smaller orgs can sometimes brush the ugly under the rug. Larger ones bump into issues constantly. I'd wager that 5% to 10% of engineering might be dealing with maintenance of dependencies.
(fwiw, I'm mostly speaking in terms of my experience as a backend engineer.)
* Webpack
* Unit testing (Mocha)
* Code coverage (Istanbul)
* Browser transpilation (Babel)
* Type safety (typescript)
Now for an apples to apples comparison I used to work on c#/.net. Complaining about these dependencies would be akin to me complaining that I couldn't manually audit the unit testing dlls that VS is using to do code coverage for me. Of course, no one ever audits these DLLs. A more interesting analysis is to look at how many non-dev dependencies you have (I have none, I doubt this basic react package has more than just react for the production code).
Of course one could argue "someone might stop maintaining this project", but this is equivalent to saying "I won't use open source". In fact, in my experience the opposite is true, with closed source paid software becoming obsolete more regularly than open source alternatives. If a company decides something isn't making money they will usually can it while with open source at least someone has the opportunity of picking it up when an author loses interest.
But I'm not saying it's better or worse, just different. But for sure it's harder to audit.
Those 1,568 create-react-app dependencies are maintained by people you have no formal relationship to, and are all versioned independently. Visual Studio is one monolithic package released by one of the largest companies on earth. Microsoft is not going to back door you and it has a huge incentive to stop anyone else from directly back dooring you via Visual Studio. On the other hand, the maintainer of the event-stream NPM package, with ~1.5 million weekly downloads, recently gave control to a stranger. They successfully back doored it and evaded detection for over a month. https://blog.npmjs.org/post/180565383195/details-about-the-e...
In practice, it's tremendously different.
As mentioned, a do-nothing react app has 1500 dependencies, all managed by who-knows-how-many different companies and individuals
A do-nothing aspnetcore app has maybe a few dozen dependencies, and with the exception of Newtonsoft.JSON, all of these are managed and supported by Microsoft.
When you scale that up to a real app, I don't even know what you get with Node/npm? 2500 dependencies? With aspnetcore you probably end up adding 10-20 more dependencies, and some of them are probably written by Microsoft.
So, your "potentially untrustworthy package count" in node is about 2000 packages, whereas for aspnet it's about 10 packages. You can review and pay attention to 10 packages. 2000? Not so much
Aside: A java/maven project probably falls somewhere in between, but closer to the aspnet scenario. Instead of a few dozen dependencies you might have 50, but it's still realistically possible to review them all, and they're far more likely to come from trusted publishers such as the Apache foundation.
I don't care if there are 700 packages if they all come from the Apache Foundation. I trust the Apache foundation and as such they can be considered one "block" when it comes to reviewing their chance of releasing a malicious/hacked package.
Visual studio is basically ONE package from ONE publisher Internally it's made up of thousands of tiny components, and includes third-party open source software such as Newtonsoft.JSON, but Microsoft is responsible for vetting all that stuff, and they do.
The problem with the JS ecosystem is that many of the packages come from all over the place. For example, create-react-app depends on envinfo which is published by "Trevor Brindle". It also wants cross-spawn, from "André Cruz", chalk from "Josh Junon", Commander.js from "abe tomo", fs-extra from "JP Richardson", this list goes on and on.
Who are all these people and are they all trustworthy? I don't know, I can't tell, and neither does anyone else, which is the core problem.
Still a potential problem, since each transitive dependency even gets to run arbitrary code during install on the developer's machine, but a different one.
That would not pass muster at all for me but I see it quite often and interestingly not in startups but bigger companies on departmental level (no code reviews there anyway...).
You could for example use React with TypeScript and have far, far fewer dependencies...
"JS developers install too many small packages."
Whooooooo cares. When you come up with the maximum arbitrary acceptable packages (including nested dependencies) for a project, and then also the arbitrary minimum LOC needed to publish a package (for all you leftpad haters), then throw your numbers in the toilet and stop talking.
I love that Babel, Prettier, Jest, etc use plugins as individual npm packages. I love the ecosystem it has created. I love that we have a big ecosystem that improves developer experience.
I ask because your reaction goes against everything I've ever felt, and I sincerely want to understand.
When you started in 2008, were you self-taught, college/university or bootcamp? No wrong answers.
Have you been working primarily in JS the whole time?
Did you do front-end, back-end or some blend of the two?
create-react-app is a complex tool put together by I team I largely trust. What I want on my machine is a static build of their work, not the whole sausage making factory in 1568 pieces. Once I would have the binaries, the whole of npm and the JavaScript ecosystem could go away altogether and I would still be able to build and deploy my apps.
The package, in its published form, would be vetted by the create-react-app, who have a much better idea of what's going on in their dependency tree than I do. If they decided they need that many dependencies, it is up to them to manage the complexity. That said, it would probably make their lives easier to depend on 10-20 packages, statically built by people they trust, and so on.
Even if the result of this would initially be very similar to today (still running the same code of those 1568 packages), it would make me trust this stack a lot more.
It's not surprising or concerning.
That's what it takes to get modern things built - lots of other things.
The whole brogrammer culture of "throw another library onto the fire" is the reason that simple programs require crazy amounts of computing power.
It's not just JavaScript that has dependencies - everything does.
It’s not the concept of dependencies that’s at fault, it’s the number of them proportional to the task.
Yet, we don't just eat dirt off the street because even when we eat the usual food, there's always some dirt in it.
A ton of websites out there do the latter.
I don't need to do assembly language programming because the tools I have (that are built on top of it) are actually good. Unlike those in JS land.
In fact, the 1.5k dependencies in this example is a drop in the bucket when you consider all the dependencies that your react app needs to do anything useful (http, dns, tcp, ip, browsers, OS’s, et cetera).
I’m not defending React here, I don’t know enough about it, I’m just saying 1.5k dependencies for a hello world app might be bad, but it depends on the details.
Similar case with babel and webpack, that each plugins (and loader) is a separate dependency. I don't know if it's more efficient or modular for development, but it is surely bloat the packages.
I tried it and in order to satisfy everyone's needs it seems like overkill tooling for most needs.
First, it allows React learners to set up an environment without having to learn Webpack and Babel first. Prior to that, most tutorials would waste multiple pages on "Before you can do anything else, here's how to set up a Webpack+Babel config..."
Second, it allows experienced React devs to spin up a project without having to do all the configuration work (or copy and paste it from somewhere). It also provides good defaults for build output, lints for common mistakes, and makes it really easy to get future build improvements just by updating a single `react-scripts` dependency.
Third, it also acts as a common starting point for instructions and tutorials. For example, I wrote a blog post a while back on using the Cesium.js 3D globe library with Webpack and React ( http://blog.isquaredsoftware.com/2017/03/declarative-earth-p... ), and I was able to start by just saying "Create a CRA project, eject, and modify these two config values".
Even if you do stuff by hand you can run CRA and see how the experts do it.
The unfortunate thing about CRA is that it's another tool on top of a tool to learn. It's not just instant and obvious, it has its own layer of configuration and understanding of how not to break it, how to upgrade it, etc. So people spend time learning the tool's tool instead of the tool.
I'm also not convinced this is "how the experts do it."
Create React App is by default zero configuration and we do our best not to make it possible to break it. For example you cannot access the configurations of Webpack for this reason. We do allow some configuration through environment variables[0] but try to minimize amount of those too (and some of them will probably get removed in future version too). The public API is around 4 commands (start, build, test, eject).
This makes it possible to make big changes underneath the package in even minor versions (eg. updating major version of Webpack) without breaking existing CRA-powered apps. You can see migration guides for every single version in the Changelog, usually consisting running a single command[1]. This was also true for the major update from 1.x to 2.x [2]
[0](https://facebook.github.io/create-react-app/docs/advanced-co...)
[1](https://github.com/facebook/create-react-app/blob/master/CHA...)
[2](https://github.com/facebook/create-react-app/blob/master/CHA...)
That said, it does take some effort to understand the benefits and limitations of CRA. How could we improve on that one?
Disclosure: Doing maintenance work on CRA
Its the opposite. You create a new app so infrequently that its not worth learning how to do it yourself. Just run the tool once and its all set up and you can forget about it.
I used the vuejs equivalent and it set up everything in a usable way and I have been happily developing without having to change any of those configs set up for me.
Most people don't remotely know how Webpack works. And they shouldn't have to! Same for most other developer tools. You need a few people who can dig out issues if they arise, but most of your people will be concerned with actually building things on top of those tools.
When every team starts a new project from every few months to every few years, having a boilerplate setup quickly becomes very valuable. The added benefit that third-party boilerplate has is that you don't have to maintain the whole lot yourself.
Needless to say these upsides don't come without issues, security and accountability being the big elephants in the room, but popularity helps here, too. The boilerplate is used by a lot of people, in organizations both big and small. If an issue arises, you can usually collectively work out the problem very quickly. It doesn't make the issues go away, but it gives you a good balancing point.
As a tradeoff versus either setting up every project manually or maintaining your own boilerplate project, it's definitely worth consideration — it might not match your needs, but it's a strong option.
I think maybe I just struggle with the concept of being that hands-off with the tooling. I don't think I could cede control to some one-size-fits-all solution. For example, what happens when you run into some important library that doesn't tree shake or minify properly? What do people do? Do they just pick different libraries for the wrong reason? Do they just deploy an un-optimized application?
Not knowing how the stuff you depend on works feels really scary to me. Reminds me of the Star Trek TNG episode about "The Custodian" where the colony came to depend on a tool that nobody knew how to fix, so their lives were shaped by the tool's abilities, not the people's desires.
And on top of that, it's all different now. I'm not as interested in trying to maintain all of that, on top of maintaining my app. CRA is a fantastic tool for this.
Until it breaks down leaving the project in an unmaintainable state, you're right. Then it's a scramble to understand and update that in my experience usually results in locking that dependency down to a strict version number and hoping that won't break anything else. It's great for security as pointed out in this thread /s. Not the optimal solution, but then again, they don't pay me enough/have enough staff for the optimal solution. And this is code that isn't even part of the app! Just shitty tools preventing me from even building the app. I can relate to the people complaining on here. This is with one or two dependencies too. Try solving those issues a few thousand times. You'll wish this horrific trend of front end code compilation would just stop. The problems truly never end as soon as one has to deal with npm and js dependencies. Which is not to say other languages don't have the same problems. They do, just not on this epic scale of shit.
mkdir parceldemo
cd parceldemo
yarn init
vi index.html
vi index.tsx
npx parcel index.html
The two index files are here: https://gist.github.com/ascorbic/3b075105b49917975326f1d31b3......and I have a working React app, with hot module reloading and TypeScript. Parcel installs TypeScript as a devdependency and react and react-dom as dependencies, and starts the HMR server. It took less than one minute.
When I used CRA for a project about a year ago, it spit out a gigantic README with everything anyone ever might want to know about JS development, it had a long webpack configuration that hot-loaded CSS and auto-reloaded the page on JS edits, it set up the app to use a service worker, and it used a ton of other technologies and libraries and tools that I didn't know existed. They've scaled it back quite a bit since then, but I think getting a feel for what's out there is really valuable, and even though it has way more than you need, it's nice to be able to easily try those things to learn what they are and confirm that you don't need them. Otherwise, you might end up with a community of people who don't know about ESLint because none of the tutorials thought it was critical to set up before getting started.
One mindset (not necessarily for beginners) is that when working on a serious project that will need non-trivial config, it's best to run create-react-app, eject, carve away all of the unnecessary stuff that you don't want, then take ownership over every aspect of the build system and tooling. That leaves you with a much more solid starting point than if you start totally from scratch and only add things as you need them.
The real issue is security, but that’s another issue. It’s remarkable you can just push code to npm without signing it. Every time I push a commit to GitHub and then release it on npm it really gives me the creeps. Fortunately we have CSPs.
It's worth some concern.
As the copay attack shows, that everyone uses these libraries doesn't let us rest assured. There are virtually zero eyeballs on the code of transitive dependencies because you would have to extract tarballs to read code. And attacks can be extremely targeted. The copay attack was only discovered because of a deprecation warning in the attacker's code that someone reported.
https://hackernoon.com/im-harvesting-credit-card-numbers-and...
His section about circumventing CSPs was particularly interesting and relevant. CSPs were an impediment to him, and he avoided operating his scam on sites where a CSP was installed.
It really isn't normal to have 1000+ dependencies and it really isn't safe. Just saying "CSP will protect us" isn't good enough.
I do. That is why I don't have many deps in general. Transitive or not. With only a handful of dependencies that are from trusted sources and that are audited by me I can be sure that they are safe.
With 1000+ dependencies that are from as many random sources you can not do the same.
Having 10 dependencies is drastically different from a security aspect then 1000+.
It continues to kick everything from a productivity perspective and the framework is fantastic (for business-y apps).
People used to tell me it was too heavy weight. People used to tell me jQuery or Angular were better.
No external dependencies. NPM not needed. Might be a bit old in some places but so what.
It might be old? GWT? Just go back to developing Web 1.0 applications and see how that goes. GWT is antiquated. I realize the churn associated with front end can be maddening, and you don't always need a Single Page App, but from a usability perspective you are really behind the times.
Over time, though, the actual time to make any kind of change to the app became incredibly painful. The loss of the GWT DevMode plugin back around Firefox 27 hurt too.
We just finished rewriting that app with a modern React+Redux implementation, and I took great glee in deleting the entire GWT portion of that codebase. The changes have already paid off by letting us add some additional functionality that would have been a distinct pain to implement in the old codebase.
Sure, the fact that it was the second time through was a factor, but the iteration speed and codebase structure are truly both vastly improved over the original.
Another team has a SmartGWT app that they're planning to migrate in some fashion as well. Unfortunately, they locked themselves in badly by using SmartGWT's server-side black box that hooks into a DB to send data to the client. It's going to take them a while to start teasing that apart.
It's always the not easy to throw away things that hurt the most.
Instead of guilting developers for their dependency count, maybe we should be auditing open source project dependencies or suggesting better tips for vetting dependencies.
Maybe we could at least argue about the number of digits?
I don't understand why the companies that depend on webpack haven't built a project like this yet (and made sure that the important projects use that library), but I believe it's just a matter of time.
It's hard and boring work also.
Hence things like this exist:
In JavaScript land, you need an external dependency for freaking left-pad[0]. In freer countries, that cutting-edge 1970s technology[1] is built-in.
[0] https://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/
[1] https://stackoverflow.com/questions/293438/left-pad-printf-w...
There's an effort to get at least a standard library namespace, but it's at month 8 of just deciding on the semantics of how to import: https://github.com/tc39/proposal-javascript-standard-library