Barrel files and why you should STOP using them now
Tags: performance, frontend
Published: 2023-08-12 14:23:39

Small decisions can also have big impacts on applications. When developers start using barrel files they might seem like a harmless pattern, but they aren't.

[UPDATE Mar, 23th of 2024]:
**BEFORE YOU KEEP READING**: this article and its metrics were created thinking on final front-end applications. Those that final users navigate such as dev.to and [my source project](https://frontend-pattern.vercel.app). So, if you have been using Barrel files in any other context, such as packages, it (Barrel files) might fit you as expected. Take a look at this amazing [comment](https://dev.to/kasper573/comment/2dnli) from 
@kasper573.

## What are barrel files?

Barrel files are an easy exportation pattern that allows developers to import different files from a unique path. 

Let's see an example: 

```TS
// ./barrelFiles/used.ts
export const HEY = 'hey used';

// ./barrelFiles/notUsed.ts
export const HEY_NOT_USED = 'hey NOT used';
```

```TS
// ./barrelFiles/index.ts
export { HEY_NOT_USED } from './notUsed';
export { HEY } from './used';

/*
This is a barrel file, which will be used as a unique path to import any files into barrelFiles folder (such as ./used).
*/
```

Using it:

```TS
// ./Component.tsx

import { HEY } from './barrelFiles'; // instead of use './barrelFiles/used'
```

In the first touch, that looks amazing, doesn't it? They are in terms of Dev Experience, but **it costs a lot**!


## What is the Barrel files cost into the app bundle?

One crucial question you must keep in mind about barrel files: What is imported when I use something from them? Does it import only what I use?

Let's see it with examples. Right, Let's add two more files to our barrel file and add some console.log to see how they work: 

```TS
// now there are 4 files in barrelFiles folder

// ./barrelFiles/used.ts
export const HEY = 'hey used';
console.log('%c Im used xD', 'color: green;');

// ./barrelFiles/notUsed.ts
export const HEY_NOT_USED = 'hey NOT used';
console.log('%c WAIT! Im NOT used', 'color: red');

// ./barrelFiles/notUsed1.ts
export const HEY_NOT_USED_1 = 'hey NOT used 1';
console.log('%c WAIT! Im NOT used 1', 'color: red');

// ./barrelFiles/notUsed2.ts
export const HEY_NOT_USED_2 = 'hey NOT used 2';
console.log('%c WAIT! Im NOT used 2', 'color: red');
```

And the barrel file:

```TS
// ./barrelFiles/index.ts
export { HEY_NOT_USED } from './notUsed';
export { HEY_NOT_USED_2 } from './notUsed2';
export { HEY_NOT_USED_1 } from './notUsed1';
export { HEY } from './used';
```

Well, getting back to the questions, it's expected if I import **HEY**, I only import **./used** file content. So let's do this by importing it into my [personal project](https://frontend-pattern.vercel.app/about) where you find more articles like this:

```TS
// ./Component.tsx

import { HEY } from './barrelFiles'; // I wanna use HEY, only
```

![Using barrel files impact on app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lce589yr8anp2ro9fkm1.png)

Do you see? Even though only **HEY** is used, it shows all console.log which means all files are imported.

Now, not importing from the barrel file.

```TS
// ./Component.tsx

import { HEY } from './barrelFiles/used'; // I wanna use HEY, only
```

![Not using barrel files impact on app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/10jwks9uh7fhy4lsc941.png)

Importing directly **one** console.log is shown.

You might be wondering: why would there be files not used in my project? 

That's a good question. Well, some points about it:

1. In this example, there are few files, but how about a huge project using dozen barrel files? Likely there would be not used files. If you work on a monorepo, it might have shared folders used for many apps. Creating patterns that avoid team-mattes (and you) doing things wrong is the best thing you can do as a developer;
2. Even though tools such as Vite and Webpack optmizining bundle and avoid to use re-import chunks, the better way to make sure you opmitizing as much as possible is understanding things work on the application. To understand it, see the next screenshots.

Let's see the bundle difference now (**please, as it's a teaching example, the difference is short. Scale it up to understand the impact**).

Using the barrel file (189,**83**kb):
![barrel file bundle](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nqvextzgvr7g8s6criay.png)

No barrel file (189,**67**kb):
![no barrel file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jl6yeufetwco756gkdzv.png)

As you can see, there is a final bundle size difference. By not using barrel file, it's smaller.

It's also applicable to third-party packages. Let's use pako as an example - a high-speed zlib port to javascript. For this project, we just wanna use inflate function from pako.

using pako barrel file size: **43.9k**
![using pako barrel file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/38evzwd8uuja3npgb6ab.png)

using pako with no barrel file: **21.5k**
![using pako with no barrel file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m7nk8k4x0fdi2rojmo0g.png)

Incredible, isn't it?

[Circular dependency](https://en.wikipedia.org/wiki/Circular_dependency) is another problem you might have with barrel files.

---

_Enjoying it? If so, don't forget to give a_ ❤️_ and follow me to keep updated. Then, I'll continue creating more content like this_

---

## What is the Barrel files cost into the unit tests?

The barrel file on unit tests is even worse, once unit tests engines such as Vitest and Jest don't care about bundle optimization as Vite and Webpack. Let's see by creating two helpers that only use HEY.

```TS
// ./helper.ts
import { HEY } from './barrelFiles';
export const helper = () => {
  console.log(HEY);
};

// ./helper2.ts
import { HEY } from './barrelFiles';
export const helper2 = () => {
  console.log(HEY);
};
```

And now import them into the test files

```TS
// ./helper.epec.ts
import { describe, it } from 'vitest';
import { helper } from './helper';

describe('helper', () => {
  it('Should only use HEY', () => {
    helper();
  });
});

// ./helper2.epec.ts
import { describe, it } from 'vitest';
import { helper2 } from './helper2';

describe('helper2', () => {
  it('Should only use HEY', () => {
    helper2();
  });
});
```

using barrel file on tests time: **2.61s**
![Unit test with barrel file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f3xewyb2isrd4u41sdwz.gif)

As you can see, there is a bunch of not-used console.log, and that gonna happen for EACH test file on the project that uses the barrel file or even use a file that uses the barrel file. Can you imagine the performance impact of it? Well... terrible. 

Now, removing the barrel files

not using barrel file on tests time: **1.32s**
![Unit test without barrel file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gfmvmc4119ycsdztfkxk.gif)


> [Buy me a coffee](https://ko-fi.com/tjordao) ☕. Hope I have helped you somehow. 🤗


## BONUS (Script to change import statements)

[UPDATE Jul, 4th of 2024]:
Check out [this](https://github.com/tassioFront/frontend-pattern/blob/main/src/scripts/changeImportStatements.js) script to change import statements automatically. It will help you to remove barrel files.

## Conclusion

[UPDATE Mar, 23th of 2024]:
In this article, you saw a bit more about the barrel file pattern and its impact on final front-end applications.

As I have said before: creating patterns that avoid team-mattes (and you) doing things wrong is the best thing you can do as a developer. That is valid to barrel files once not using them:

1. Reducing the final bundle and avoiding importing stuff are not used (same applicable to local files and third-party packages);
2. Improve the unit test performance;

Expensive.

[See my other articles](https://frontend-pattern.vercel.app/articles)