This is the modern Javascript coding style - you install small packages that implement your required functionality. The idea is to not solve problems that were solved before by somebody else. After all, if you're going to include somebody else's code that you didn't write anyway, what's the better way to do it - copy & paste, or use a package manager with versioning and high degree of control over downloaded packages that lets you abstract the required functionality away into a single line?
What is to be avoided is inclusion of packages that implement a ton of functionalities, and then only using a very small subset of those. In these cases it's better to create your own solution.
This is what separates experienced coders from amateurs imo. I’ve seen newer programmers tend to always “find” a library that would implement their required solution even what they needed is a super small subset of that library which could have been better off by just writing their own function.
what's the better way to do it
copy & paste, or use a package manager
When you add a dependency, how do you prevent the maintainer of the dependency or any sub- or sub-sub-...dependency to break your system in the future?
You do the same thing you'd do if anything else in your toolchain or runtime environment changes - change relevant code so it works again. Browsers change all sorts of stuff all the time and break millions of websites in the process. Easier to use a package manager to propagate the required changes to all dependent projects than copying and pasting bits of code between many different files.
I can't imagine the opposite could ever happen, be it small companies or big ones - there is not a single company on Earth that has the time to audit every line of every piece of software they run. Especially if that software is proprietary and source isn't even available.
I meant as a solution to not breaking in the future. If you have a system with pinned versions that work, it will always work, regardless of the changes in new versions.
Don't mind me... I'm just rewriting the entire internet and getting laughed at while kids these days get 3x the money because they took seven external projects, linked them together in five minutes, and made a "website" (complete with all the UX dark patterns, half-baked functionality, and third-party-triggered version updates straight into a broken dependency and version conflict).
> Why do we even need package managers to install stuff?
Because it's nice to not have to write every bit yourself and end up with your own package manager anyway. (e.g. I'm not going to rewrite a ProtBuf lib for each project, I'll reuse my same code, but want to propogate update and bug fixes between projects.)
I'm less annoyed by libraries and package managers and more annoyed by "libraries" like leftpad and package managers like....well, most package managers. Here's hoping Nix or an intellectual decedents take hold and become more useful and universal. (OK, OK Cargo and RPM aren't that bad. Apt is OK, but too often leads to inconsistent states.)
Well like I said, it's a difference in mindset. For the most part I think a lot of the things people pull in dependencies for aren't actually that complicated, especially for the limited use they have for them.
I think package managers have made things much worse, because if you're going to have dependencies you shouldn't have such a complicated relationship with them that you need a tool like a package manager to deal with it, but package managers encourage that behavior. I don't think we should need things like Nix just to be able to put together a stable and consistent system.
I didn't mean it as a technical problem, more as a philosophical... If I trust one package, I don't necessarily want to trust all the other dependencies it needs.
I prefer not to copy-paste solutions from the Internet because the web and the browsers generally keep upgrading. And some solutions from the past no longer work in the future. Historically, it has been much easier to upgrade a library/package than to maintain the code myself. At least that's my experience.
> Historically, it has been much easier to upgrade a library/package than to maintain the code myself. At least that's my experience.
Everyone's experience is different, mine, in this case, has been the opposite.
Library/package breakages caused by browser updates often take longer for the maintainers to release fixes for than I'd like to wait. I can fix myself and submit PRs, but this takes orders of magnitude more effort than fixing a similar thing in a library you wrote yourself; you can't be familiar with the internals of every external dependency, particularly as they're often generalised and therefore larger and more complex than if you write them yourself for specific functionality.
There's a trade-off of course, but I'd definitely consider updating libraries in response to browser update breakages a "con" when listing the pros and cons of using external deps.
Fork, fix, create PR and point your repo to your fork until the PR is accepted.
That's how you evolve open source projects. If everyone were to simply copy paste snippets in their projects for fear of library rot, every library would rot.
This is the ideal. And I have done so when I can. But you're missing a step:
> Fork, understand, fix, create PR
That 2nd step is more time-consuming for some dependencies than others. I'm a strong proponent for understanding how your dependencies work, but there are always limits to the depth of knowledge of the internals of every one.
Sorry, I’m gonna say it straight: this is bullshits.
A bad behaving dependency (has useless deps) doesn’t invalidates the whole idea of using a package manager.
Probably the author has not enough experience in maintaining projects written by smart people where they write by themselves all the collateral parts.
Using dependencies extends the “standard” library so that it increases global (extra team, oss) reuse and the probabilty to find something “known“ in your next project.
Could be wrong, in that case I’ll stand corrected.
That may be true for backend stuff, but not for frontend development. Adding dependencies means that the user is exposed to a lot more.
I am blocking Javascript vigorously allowing the occasional first party script, if I really value the site. If you throw me into a vicious cycle of forcing me to allow more stuff at each cycle, I go away, especially on retail sites.
Modules installed from npm are typically bundled together in a single file with webpack or a similar program, and served only from a first party domain. Stuff included from third party websites is just bad design.
If you throw me into a vicious cycle of forcing me to allow
more stuff at each cycle, I go away, especially on retail sites.
Avoiding dependencies to capture the 0.01% long-tail of users that heavily block JS seems like a massive waste of time for anybody but the most specialized of retailers - especially since those abandoned visits aren't going to show up in anybody's analytics...
People are learning more and more about ad blockers and javascript blockers. The illusion that "you should trade every CPU cycle on your thing for little benefit and amusement" is going to go away, hopefully. The bullshit of front-end dev is pushing the limits of physics these days. Just like CPUs settled around a certain speed and we're witnessing the end of Moore's law, the same will happen with download speeds and bandwidth.
Maybe. But I am trying to convince everyone around me to it. As I am the most reputable person (in that regard) around this usually convinces people. And that compromised machines will be wiped vigorously.
I assume that most people around here are pretty good multipliers.
It doesn't seem that bad actually. I prefer a developer to add an existing well-proven package than copy and paste some lines from the internet, since many times these lines won't handle many corner cases. That's the strength of packages - they already handle the case of empty array, empty string, invalid data, utf-8, that glitch in Chrome and plenty of other "little" things that break Stackoverflow code snippets.
But it's 35 LOC because it's probably not handling various cases that aren't obvious at first. That particular package went through many versions and is widely used so there's a chance it's robust and will be maintained over time. That's one less maintenance task to do.
I have took a look at that particular package ([edit] not really, see below), and it seems to be a very short code, grown into more lines by providing API for configuration (delay, smooth or not, duration, offset etc.) and various convenience wrappers for easy mixing-in into your components. It also implements scroll-spy behavior. Versions (and open issues) are mostly about adding/changing/fixing yet another of those convenience wrappers.
If you do a lot of scrolling into various components on various actions on your page, this module can sure be handy. Otherwise, it doesn't really seem to do anything better than a simple snippet copy'n'pasted into proper place. Those "non obvious at first cases" in this case seem to be mostly about how developer uses it in their code, so if you just use it in one place to do one specific thing, there are none.
[edit]
Oh, sorry! I saw "react-scroll", while the article mentions "react-scroll-to-component". This one actually consists of 32 LOC and there's nothing more to it! It depends on "scroll-to", which is 66 LOC, but most of it is due to formatting and API and could be probably squeezed into a few lines without losing much clarity. Then it depends on a "tween", which actually does the smoothing and could be the one thing actually worth depending on, if that's what you need to achieve. In simplest cases it boils down into a one-liner as well, but it's a well-known library anyway.
"scroll-to" also depends on "raf", which is a polyfill you might or might not need.
What non-obvious cases does this handle? It hasn't been changed in a year, it the main function wasn't updated even once after the initial commit (so that evolution must have taken place elsewhere?).
It also uses a string as a parameter, which most people will probably supply as string literal. Use it like that here and there and you're fine, use it a lot (or in a tight loop), and you're just creating unnecessary crap for the GC to deal with.
Well, yeah, 32 fewer in your codebase, 28 fewer in your build, 10 fewer that you have to type, 15 more you have to understand, 37 more you have to debug...
I'd say rather: approach a dependency upgrade like any other code change, and be prepared to refactor aggressively (including by copying code and removing the dependency, if necessary) as with any other code change.
Your application, your call. The trade-offs between implementing functionality yourself or using existing solutions ( with their transitive dependencies ) are entirely for you to evaluate and judge.
Reminds me of the Go Proverb, "A little copying is better than a little dependency."
When I created a hello world app in React, it took what felt like several minutes to pull down what seemed like hundreds of dependencies. My uninformed opinion is that the standard operating procedure in Reactland is to pull in "well tested" dependencies, where "well tested" means "downloaded a lot" and you hope the two are correlated.
Most of the dependencies installed for a hello world React app are not runtime dependencies.
I don't know if there's a real "standard operating procedure" or not, but it seems to me that it's the responsibility of each project maintainer to evaluate the fitness of a particular dependency for that project.
Just like with every package manager that I've ever heard of, you're responsible for what you install. I'm sure you can get buggy packages with poor test coverage via apt-get install / brew install / gem install / whatever-else install.
How well this works depends a lot on the ecosystem.
When working with Node.js or JavaScript in the browser, there's often a huge number of modules available. But even though there may be six different modules trying to solve the same problem, every one of those modules may be incomplete.
When working with Ruby on Rails, I find that regular Ruby gems are pretty safe. There's often one or two popular, well-maintained solutions to a common problem, and I'd be fool to reinvent the wheel. However, gems which extend Rails itself can be more problematic, and they often break during major Rails upgrades.
Rust crates have a few nice properties: The Rust language itself has near-flawless backwards compatibility, Rust crates are immutable once published, and almost everybody uses semver. Oh, and cargo is great at managing dependencies. What this means is that it's often worth depending on very small libraries. Sure, maybe it's only 30 lines of code, but it's the right 30 lines, thanks to a process of refinement and to PRs from several people.
So the exact trade-offs here vary considerably, depending on the details of a specific ecosystem and its tools. I do agree that for browser-based stuff—especially if you're targeting evergreen browsers—that it's often better to write a few lines of code yourself than to add a badly-maintained dependency.
I love the extreme tension between "small, clean code you write/verify yourself" and "opaque-ish package for everything".
Because the latter is so much more productive, I think the real question is, how do we manage the undesirable side-effects of the package-for-everything model?
I see the major concerns as:
- Potential security problems that evolve over time.
- Reduced understanding of how the underlying code works.
- Reduced mental clarity of how the project as a whole works.
- Non-traditional approach to debugging. (Dig through foreign code, post GitHub issues.)
- Artifact size / performance / power consumption issues.
I'm curious about novel approaches to help manage these concerns.
Don't forget licensing issues, which a lot of smaller companies maybe just look the other way on. That code you just copied and pasted from the web--what was it licensed under? Is it compatible with your company's software licensing? That 3rd party package you just deemed a critical dependency, how is that licensed?
The "grownup" companies I've worked for either explicitly ban or discourage "Programming by copy/paste from the Internet" and have review processes to gate inclusion of 3rd party code so that the licensing can be reviewed.
Licensing issues aside, 1. as a tech lead, I would feel obligated to act as the gate for bringing 3rd party dependencies into the project, and 2. if you cut paste code into my project, then during code review, you should be able to explain in great detail what it does. If you can't, it gets reverted.
Make it easier to understand exactly what any library function does from its signature, i.e. strong norms of functional purity (perhaps at the language level) combined with some way of representing effects (language-level effect systems, monads and the various constructions on top of them, or something else).
This will sound obvious, but this problem is not restricted to web development. Java applications are often plagued by transitive dependencies too. I had to deal with unwanted libraries breaking WebLogic deployments more often than i'd like, up to the point of having to disable transient configurations for some apps and force explicit declaration of each dependency.
As their projects grows, they will find the need to copy/paste their code snippet all over again. As time goes, they will move on to other projects and other devs will have to understand their implementation of the code. As new browsers (and mobile devices) come out, people will have to refactor and improve their snippet of code.
Their colleague's version of the task however will simply require to update "react-scroll-to-component" to get new fixes. The component has a few closed issues and some opened ones. As there is community activity on it, they might as well leverage it.
Any new developers on their team will simply need to read the repo's documentation and examples. I doubt their version is documented.
The package "react-scroll-to-component" also has the advantage of living in the react's ecosystem. It isn't a few line of hack'ish Javascript but an actual component that you can configure in a React application. Any next places they will need to add scrolling behaviors will be an easy job. His snippet however will require playing in the source JS over and over again.
While I agree that "INSTALL ALL THE THINGS!!!" is a bad philosophy, in this case I agree with their colleague. That component only has a few dependencies and add pertinent features to the project.
59 comments
[ 5.1 ms ] story [ 28.4 ms ] threadWhat is to be avoided is inclusion of packages that implement a ton of functionalities, and then only using a very small subset of those. In these cases it's better to create your own solution.
I can't imagine any big companies work like that. Execute code on their servers which is under the control of 'somebody from the internet'.
I can not imagine that the security of Facebooks servers is at the mercy of a chain of anonymous coders.
That sounds like a lot of work. Compared to copying/writing 30 lines.
How do you know the pinned version and it's dependencies do not get changed by the maintainers?
Unfortunately it seems that people with my mindset towards dependencies are a dying breed.
Because it's nice to not have to write every bit yourself and end up with your own package manager anyway. (e.g. I'm not going to rewrite a ProtBuf lib for each project, I'll reuse my same code, but want to propogate update and bug fixes between projects.)
I'm less annoyed by libraries and package managers and more annoyed by "libraries" like leftpad and package managers like....well, most package managers. Here's hoping Nix or an intellectual decedents take hold and become more useful and universal. (OK, OK Cargo and RPM aren't that bad. Apt is OK, but too often leads to inconsistent states.)
I think package managers have made things much worse, because if you're going to have dependencies you shouldn't have such a complicated relationship with them that you need a tool like a package manager to deal with it, but package managers encourage that behavior. I don't think we should need things like Nix just to be able to put together a stable and consistent system.
Everyone's experience is different, mine, in this case, has been the opposite.
Library/package breakages caused by browser updates often take longer for the maintainers to release fixes for than I'd like to wait. I can fix myself and submit PRs, but this takes orders of magnitude more effort than fixing a similar thing in a library you wrote yourself; you can't be familiar with the internals of every external dependency, particularly as they're often generalised and therefore larger and more complex than if you write them yourself for specific functionality.
There's a trade-off of course, but I'd definitely consider updating libraries in response to browser update breakages a "con" when listing the pros and cons of using external deps.
That's how you evolve open source projects. If everyone were to simply copy paste snippets in their projects for fear of library rot, every library would rot.
> Fork, understand, fix, create PR
That 2nd step is more time-consuming for some dependencies than others. I'm a strong proponent for understanding how your dependencies work, but there are always limits to the depth of knowledge of the internals of every one.
Then use the package.
1. https://bundlephobia.com/
A bad behaving dependency (has useless deps) doesn’t invalidates the whole idea of using a package manager.
Probably the author has not enough experience in maintaining projects written by smart people where they write by themselves all the collateral parts.
Using dependencies extends the “standard” library so that it increases global (extra team, oss) reuse and the probabilty to find something “known“ in your next project.
Could be wrong, in that case I’ll stand corrected.
I am blocking Javascript vigorously allowing the occasional first party script, if I really value the site. If you throw me into a vicious cycle of forcing me to allow more stuff at each cycle, I go away, especially on retail sites.
Currently, the best malware protection is to switch Javascript off. Every user understands the consequences of crypto malware. Therefore...
I assume that most people around here are pretty good multipliers.
If you do a lot of scrolling into various components on various actions on your page, this module can sure be handy. Otherwise, it doesn't really seem to do anything better than a simple snippet copy'n'pasted into proper place. Those "non obvious at first cases" in this case seem to be mostly about how developer uses it in their code, so if you just use it in one place to do one specific thing, there are none.
[edit] Oh, sorry! I saw "react-scroll", while the article mentions "react-scroll-to-component". This one actually consists of 32 LOC and there's nothing more to it! It depends on "scroll-to", which is 66 LOC, but most of it is due to formatting and API and could be probably squeezed into a few lines without losing much clarity. Then it depends on a "tween", which actually does the smoothing and could be the one thing actually worth depending on, if that's what you need to achieve. In simplest cases it boils down into a one-liner as well, but it's a well-known library anyway.
"scroll-to" also depends on "raf", which is a polyfill you might or might not need.
What non-obvious cases does this handle? It hasn't been changed in a year, it the main function wasn't updated even once after the initial commit (so that evolution must have taken place elsewhere?).
It also uses a string as a parameter, which most people will probably supply as string literal. Use it like that here and there and you're fine, use it a lot (or in a tight loop), and you're just creating unnecessary crap for the GC to deal with.
Watch out for actively developed dependencies.
When I created a hello world app in React, it took what felt like several minutes to pull down what seemed like hundreds of dependencies. My uninformed opinion is that the standard operating procedure in Reactland is to pull in "well tested" dependencies, where "well tested" means "downloaded a lot" and you hope the two are correlated.
I don't know if there's a real "standard operating procedure" or not, but it seems to me that it's the responsibility of each project maintainer to evaluate the fitness of a particular dependency for that project.
Just like with every package manager that I've ever heard of, you're responsible for what you install. I'm sure you can get buggy packages with poor test coverage via apt-get install / brew install / gem install / whatever-else install.
When working with Node.js or JavaScript in the browser, there's often a huge number of modules available. But even though there may be six different modules trying to solve the same problem, every one of those modules may be incomplete.
When working with Ruby on Rails, I find that regular Ruby gems are pretty safe. There's often one or two popular, well-maintained solutions to a common problem, and I'd be fool to reinvent the wheel. However, gems which extend Rails itself can be more problematic, and they often break during major Rails upgrades.
Rust crates have a few nice properties: The Rust language itself has near-flawless backwards compatibility, Rust crates are immutable once published, and almost everybody uses semver. Oh, and cargo is great at managing dependencies. What this means is that it's often worth depending on very small libraries. Sure, maybe it's only 30 lines of code, but it's the right 30 lines, thanks to a process of refinement and to PRs from several people.
So the exact trade-offs here vary considerably, depending on the details of a specific ecosystem and its tools. I do agree that for browser-based stuff—especially if you're targeting evergreen browsers—that it's often better to write a few lines of code yourself than to add a badly-maintained dependency.
"Can we make it scroll to section 1 slowly, so the user sees what's going on?"
"The heading of section 1 gets covered by the sticky top menu bar, change it so scrolling places section 1 just below the sticky."
Because the latter is so much more productive, I think the real question is, how do we manage the undesirable side-effects of the package-for-everything model?
I see the major concerns as:
- Potential security problems that evolve over time.
- Reduced understanding of how the underlying code works.
- Reduced mental clarity of how the project as a whole works.
- Non-traditional approach to debugging. (Dig through foreign code, post GitHub issues.)
- Artifact size / performance / power consumption issues.
I'm curious about novel approaches to help manage these concerns.
The "grownup" companies I've worked for either explicitly ban or discourage "Programming by copy/paste from the Internet" and have review processes to gate inclusion of 3rd party code so that the licensing can be reviewed.
Licensing issues aside, 1. as a tech lead, I would feel obligated to act as the gate for bringing 3rd party dependencies into the project, and 2. if you cut paste code into my project, then during code review, you should be able to explain in great detail what it does. If you can't, it gets reverted.
Their colleague's version of the task however will simply require to update "react-scroll-to-component" to get new fixes. The component has a few closed issues and some opened ones. As there is community activity on it, they might as well leverage it.
Any new developers on their team will simply need to read the repo's documentation and examples. I doubt their version is documented.
The package "react-scroll-to-component" also has the advantage of living in the react's ecosystem. It isn't a few line of hack'ish Javascript but an actual component that you can configure in a React application. Any next places they will need to add scrolling behaviors will be an easy job. His snippet however will require playing in the source JS over and over again.
While I agree that "INSTALL ALL THE THINGS!!!" is a bad philosophy, in this case I agree with their colleague. That component only has a few dependencies and add pertinent features to the project.