airbnb / javascript

JavaScript Style Guide
MIT License
145.3k stars 26.52k forks source link

What is the benefit of prefer-default-export? #1365

Closed CWSpear closed 7 years ago

CWSpear commented 7 years ago

The docs don't have a Why section for prefer-default-export, and I'm not seeing the benefit of it on my own. I would think that not using default is preferred. With default exports, you lose refactoring power (if you rename the source const/function/class, it won't rename default imports).

As more of an edge case: it makes code less future-proof. i.e. if you create a file that will be a collection of errors, but it only starts with one error, to follow the linting rules, you'll have to have it export default, but then when you add the 2nd error at a later time, you'll have to do a bunch of refactoring that could have been prevented if the recommendation was to avoid default export.

bradennapier commented 4 years ago

So there can't be justified convictions or categorical opinions? You might disagree with the reasons, but they're stated clearly, and it comes down to a judgement call. Replacing judgement with a vote makes it needlessly manipulable, because you can just canvass for votes. Being able to utter "subjective" doesn't entitle you to being right if you don't explain your reasons. Just a reaction to an "attitude" (someone thinking they're right) is not a reason, and it's also not a contribution to the discussion.

So to this point, I agree actually that having a strong voice and choice makes sense. You are absolutely free to fork the repo and change it -- and if the larger part of the community agrees they would use your fork... and if they don't, you would have your fork for yourself. So, the larger part of the argument is not really relevant.

Changing this would be a massive breaking change at this point anyway. While I am 100% against the rule personally, I am sure it would be insanity should they make the change.

Anyway, not like you can't just overwrite it...


My view on the rule itself is this:

  1. Named exports make it clear throughout the application that you are using a specific value. Using default exports means that each part of the app may have a different name for the same function which makes code harder to reason about.

  2. The argument about changing the name requiring all other parts of the application needing to do the same WAS VALID at one point, but with features like Rename Symbol which most IDE's - and VSCode (which lets be real most of us are using) have -- this became 100% useless.

  3. Named exports make things like Auto Imports that some language features such as TypeScript support. Without named exports, they are far more prone to issue and require context before they are ever capable of suggesting an auto import of a given value.

  4. If you end up needing to export more values from a file as the application grows, it ends up becoming quite messy to either change from default to named as this rule seems to want -- or move to more files being used. With the more files approach - if you want to keep things logically organized that may even mean you have to move things into a folder and make significant changes to the design of the project as it grows and can end up highly disorganized

    • Whereas if you default to named exports then nothing changes, you simply export more values. If you want to use folder approach, you can - just do the folder, create an index file and export named exports from that - and dependent files have no knowledge of the change required.
ljharb commented 4 years ago

@bradennapier as has been stated many times, via renaming or re-exports, named exports have precisely the same lack of guarantee for a universal name as default exports do, so this is a non-argument.

A default export is what a module is, a named export is what a module has - you shouldn’t ever need to export another thing that would warrant changing the default, since only one thing can be the module. Make a new module for that.

Fabyao commented 4 years ago

@slikts I believe they are ways to make your point without insinuating that the person on the other side of the debate is inferior. I find the choice of words by @ljharb quiet aggressive and border line rude. Indeed arrogant. This sort of behaviour kills any chance of a healthy debate and dissuade others to join in. One can have a "categorical opinion" without using words such as:

you screwed up, it's a ridiculous argument, very short-sighted approach

@ljharb - This sort of language is what leads to a toxic environment. On the other hand, some of your answers are very good:

this is a separate topic, but might i suggest that the problem is re-exports :-) ...

I think most readers would appreciate similar wording.

ljharb commented 4 years ago

@Fabyao there's no desire for a debate; this is airbnb's style guide, and while I may choose, out of the goodness of my own heart, to entertain discussion, that doesn't mean I'm obligated to spend my free time and my emotional labor fine-tuning every comment.

I hear that you find my wording aggressive and rude; while that wasn't the intention, intentions never matter - only the impact does, and I apologize. I'll try to word things more carefully in the future.

I hadn't realized until now that @citypaul had posted and deleted his comments; I take the deletion to indicate their agreement that they shouldn't have been posted in the first place. @Fabyao similarly, this thread isn't the place for personal attacks; if you have further feedback for me, please give it to me directly.

gaurav- commented 4 years ago

After reading @bradennapier's points on Typescript, I was curious about whether Typescript has any recommendation on the subject. Did some quick search and found this in their docs: If you’re only exporting a single class or function, use export default

This aligns with what I had written some time back in this thread; the thumb rules that work for me: https://github.com/airbnb/javascript/issues/1365#issuecomment-416031279

dietergeerts commented 4 years ago

Just my 2c: After working with both default exports and named ones for a while, I can clearly see that named exports are better DX wise, especially for more junior developers. I get the points on why use the one or the other on concepts etc.... But eventually, as developers, we want our code and IDE to help us write code in an easy way, and up until today, that is only working out with named exports. Default exports give no help by your IDE, and in a big project, will confuse a lot of developers.

gaurav- commented 4 years ago

Default exports give no help by your IDE, and in a big project, will confuse a lot of developers.

@dietergeerts, if by "DX" and "help by IDE" you mean IntelliSense/AutoComplete type behaviour, then it's available in IntelliJ/Webstorm for default exports and I've also seen it on VSCode of my colleagues.

In large projects, named exports are more likely to cause name collisions and hence more confusion amongst junior and senior developers alike - then you'll have to start defining conventions for aliasing the named exports - which is what you do with default exports anyway! Especially if the codebase is organized as DDD-style bounded contexts: Bounded Context

dietergeerts commented 4 years ago

Bounded Context

Thx for this example. It's the first actual example in this tread that clarifies why default exports are preferred, and can make sense to use. Though in this scenario, I would still use named exports due to the Developer Experience you get out of it. A lot of major libraries did the move to named exports last year too, because of the Developer Experience. (I also wonder how much projects really implement in a DDD style, I know it, done it, and in lots of projects that I've worked on, it doesn't add extra value.)

I, and a lot of other developers I've worked with, never experienced IDE help on default exports, even when trying to get it work and play with the settings, this goes for both WebStorm and VSCode. So if you know how to get the IDE help, could you please share this information, as that would help a lot of people in the future. If this would work, then it's a reason less against default exports.

gaurav- commented 4 years ago

@dietergeerts It just works out of the box on IntelliJ IntelliJ

And while I don't use VSCode, it worked there too (it uses commonjs style by default but you can easily configure it to use the modern syntax) VSCode Code used for the above recordings: https://github.com/gaurav-/default-export-ide-support-demo

I also wonder how much projects really implement in a DDD style, I know it, done it, and in lots of projects that I've worked on, it doesn't add extra value

I mentioned DDD with Bounded Context (BC) as an example but you don't need to use it verbatim to be prone to name collisions. It's usually the nature of large software projects to have different BCs - whether you practice DDD or not, or identify proper BC boundaries or not (but that's a separate discussion)

Funny that you mentioned libraries moving towards named exports - coincidentally I recently encountered collision of map exported by lodash and rxjs in a code I reviewed. On the other hand, there are examples of libs that extensively use default exports - like date-fns that I had shared earlier (https://github.com/airbnb/javascript/issues/1365#issuecomment-653170555). And then there are frameworks like Angular and Nestjs that tell you not to use barrel files that just import and re-export named exports - although that's for reasons specific to how their dependency injection works and not a comment on which style is better.

The bottom line is that while there may be project-specific (or package/module -specific) reasons for using named exports, the decision of the maintainers to prefer default exports is not an outlandish one!

dietergeerts commented 4 years ago

@gaurav-

I think it will be a setting then, as for our team, it's not working, not in WebStorm.

The bottom line is that while there may be project-specific (or package/module -specific) reasons for using named exports, the decision of the maintainers to prefer default exports is not an outlandish one!

I never said that it was simply a wrong decision. I actually thanked you to show us a practical example of when it is useful, because it helps us to see the situation in which it can be very useful. The fact that there is a huge amount of people not being for the default exports shows that a lot of projects are different and benefit more from using named exports. It's not about what is good and what is bad, as in programming, every option is about compromises. Each option has its pros and cons, and by showing us the example, and actually telling us the main reason for choosing default exports lets us understand the reasoning behind, which in turn helps us decide if we want to use it or not, and being able to understand that it's project specific, and thus can simply be overwritten. Up until now, it was never told why exactly (practically) it was chosen to prefer default exports, nor where examples given, instead, the theory behind modules was being explained, which isn't helping in understanding and not practical. So it would helped to describe this from the start, as is the thing actually asked from the start, to have practical examples and reasons why it is this way.

As for naming collisions, I have yet to experience them, as our files are pretty small, which works great. And in the rare cases we have them, it makes sense to name them specific, which would have been the name that would have been chosen if it was a default export. ("barrel" files should be a big no-no, as it doesn't work well, and is bad for tree-shaking etc.... Imho)

So again, thx you for the example and actual reason that default exports are preferred, it actually answers the question that started this tread: "What is the benefit of prefer-default-export?"

ruslanvs commented 4 years ago

In large projects, named exports are more likely to cause name collisions and hence more confusion amongst junior and senior developers alike

  • default exports cause confusions even on small projects, let alone larger ones, where you'd use DDD.
ljharb commented 4 years ago

@ruslanvs that's not my experience maintaining almost over 200 small projects, all of which only use default exports.

Exac commented 4 years ago

Now that @ljharb isn't working for Airbnb, can we reopen and modernize this?

ljharb commented 4 years ago

@Exac it's not solely my opinion; i still maintain this project; this is already fully modern and ideal.

gaurav- commented 4 years ago

Hey @dietergeerts I missed your comment but glad it helped. The part of my comment that you quoted was not directed at you, so apologies if you thought it was for you specifically.

I think it will be a setting then, as for our team, it's not working, not in WebStorm.

Yeah, likely. Works out of the box for me

As for naming collisions, I have yet to experience them, as our files are pretty small, which works great.

In my experience, the likelihood of naming collisions proportional to not much on the file size per se, but simply on the number of exported "names" and the "bounded contexts" (implicit if you don't follow DDD)

The nuance here is the meaning and intended use of modules as per ES2015+. It seems people who prefer named exports over default are doing so for wrong reasons or misconceptions. Nevertheless, like most "best practices", it's not something that would destroy your project if not followed to the tee :)

Sleepful commented 3 years ago

The rule should be "allow only 1 named export per file".

I guess all these people are too comfortable in their proprietary IDEs to care about how easy it is to refactor code. If you do some regexp search through a codebase that uses only named exports, you will find every file that depends on a named module easily. And if some file renames the named export, the files will still show up in your search and you will immediately see what it renames it to. But with default exports it is so much harder to know which files depend on your exported module, unless you have fancy language server protocol features I guess...... 🙄

Oh yeah, not to mention that when everyone renames default exports to whatever they want, you always have to go look at the imported file to figure out what RenamedDefaultExport really means. Even if you know the module already, you won't recognize it at first sight when it gets renamed.

Good luck when your IDE fails to refactor properly though! 💀💀💀

ljharb commented 3 years ago

@Sleepful trivial refactors are not the hardest problem to be solved, nor anywhere close to the most important. If you alter a file, whether it has a default or named exports, the linter and your unit tests will tell you what files you need to fix, which is far more reliable than a regex search. Separately, you can use an internal linter rule to enforce that default export names match the filenames (as airbnb does internally).