DEV Community

Cover image for Dependencies hell
mighty_peach
mighty_peach

Posted on • Edited on

Dependencies hell

How does typical frontend project start?

git clone project

cd project

npm i
Enter fullscreen mode Exit fullscreen mode

And we have received thousands of modules installed in the OS, megabytes (or gigabytes) of files. After installation we try to start a project using npm start, but we get some node-gyp, versions errors or vulnerabilities This walls of information, that we can't fix, are really frustrating. I want to talk about some of these problems.

What’s wrong with deps

Deps size is so big

Let’s see how much size popular project Create React App has:

The Create React App empty project has around 300mbs of size

It has 300mbs that does nothing. After template was created devs can install mobx, axios, css-in-js packages, components library, something like lodash or rx.js, some type definitions for typescript, etc. In result we have half of GB space of empty project without actual code base.

I want to remind - we are in chip shortage, Apple asks ~200$ for 128GB. These gigabytes of packages, that we transfer back and forth, don’t just disappear — they are stored in our disks (and often this is global npm packages, that we can’t just delete as project dependencies), in DNS servers, in data centres. This is unbelievable how irresponsibly we develop software, without looking into future and software complexity.

Deps doesn’t update theirs deps

How regularly devs update their package dependencies? During my practice, this doesn’t happen as often as necessary. Let’s look at create-react-app for example. It has jest as dependency with version 27.4.3, when I wrote this article actual version was 27.5.1.
Jest has micromatch package with version 4.0.4 (that was actual version for jest@27.4.3). And micromatch has minimatch package with version 3.0.4. But actual version is 5.0.1, that solve, at least one major security problem.

And this is just five minutes research, Create React App has dozens of dependencies, which have dozens of dependencies, which have... One of them could be out of date or has vulnerability issues.

Deps control can be more hard with very popular projects. Cypress updates its version very often. Sometimes it happens twice a week. When I’ve started to use Cypress I updated it every two week, but then I just got tire to do this. But this was mistake - Cypress team, probably, fixed some major bugs, solved some vulnerabilities in this updates. But as developer in modern and big project, where you have many deps, you can’t hold dependencies actual every time.

Maybe you don’t need these deps

There are many instructions and StackOverflow answers that recommend: “Install this to solve your problem”. Package distribution systems (npm, yarn, pnpm) are very easy to use, they installed by default in some Unix-system, they installed with Node.js. And this ease doesn’t good for our industry.

Because complexity of IT is growing up from year to year, new folks learn only the tip of the iceberg (not all people, of course). Because the main goal of business is getting money, not creating good and robust software, we have no time to control every aspect of development. Open-source folks spend their own time to support projects.

And because of these points we don’t spend time to check package size, all dependencies (include dependency’s dependencies), vulnerabilities, but much more, we don’t think that this package is necessary. We install Axios instead of just using fetch api. We use css-in-js or css modules instead of using BEM. We could use just REST API, instead of GraphQL. And there is much more examples.

What we can do

  1. Prioritize robustness over development speed and “developer experience”.

    Ask yourself about package necessity, maybe there is standard library method for your problem, or you don’t need full module, just a part, that you can copy-paste to your project.

  2. Check not only stars and popularity, but also maintenance, update regularity, communication with community
    CRA doesn’t update about year, we don’t have any news from their team. This is red flag not to use CRA. Actually, they released new version, but anyway, this was red flag :)

  3. You can store node_modules in version control system

    That sound stupid, but this is easy way to maintain dependencies, to ask questions “why do I need this”. This care us from unexpected node_modules extending.

  4. Best practices are propaganda for juniors

    This topic can be disputed, but I think that the Patterns and other best practices, that puts into folks head to use it without thinking about necessity - are propaganda just as is. The Patterns are good, but we need to use it when we really meet a problem in code base. Sounds like I recommend to go to the Assembly and code using it, but no, I just recommend to think twice before doing something.

  5. Solve actual problem, don’t solve future problem

    I have met so many times, when “architect” decided to use something like a store, argued that in future we could have a lot of data which we can share between modules and etc. And in this scenario we could use the react context and change it with store when we actually find a problem with data management. Another problem is technology FOMO - you need to use popular solutions, be in edge of technology. And there is much more examples when we solve problems that doesn’t exist.

Easy receipts using std

I want to show easy solutions of popular problems using standard library.

Often we have installed by default Lodash in our projects. Lodash give us some handy functions, some syntax sugar. One of the best function is debounce, that we use when we need to control user input.

Receipt how to use lodash.debounce() from Lodash docs

What’s wrong, you can ask. Source code of function is unreadable. This is little function, but we install a whole Lodash for this. This is how debounce looks using plain JS:

function debounce(func, timeout = 300){
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => { func.apply(this, args); }, timeout);
  };
}
// copy-paste from https://www.freecodecamp.org/news/javascript-debounce-example/
Enter fullscreen mode Exit fullscreen mode

The difference is dozens of lines of code, is less maintainable, needs more code control.

We often use css-in-js or special setup of webpack to stop class intersection. Really simple solution is BEM — methodology of a class naming. We need to name every class like block_element-modification. Using React it looks like: componentName_someWord. This is easy.

Wrapping up

When I've written this article I never stood a goal to change your mind, but if you start to think more about dependencies - this would be cool.

I can recommend you to read or watch:

Thank you ❤️

Top comments (0)