-
Notifications
You must be signed in to change notification settings - Fork 12k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Production build optimization breaks code #11439
Comments
I also have a problem with |
I don't think it's very accurate to list all of those issues saying they had the same cause and were ignored/closed. While it is true that some of the issues you linked are related to the Our stance about the defaults options is that they should provide the best size reduction for the vast majority of cases. For cases where the default options do not work, they can be disabled. The specific options that alter code are A similar example is how we use the While there are definitely cases for looking up function names and using side effects in getters, in our testing both So for situations like the one you describe, we feel the correct approach is to keep the defaults and manually disable I agree it would be nice to have more fine-grained control over the optimization options in the future, so I am marking this as a feature request. |
I do think it is accurate to list those issues. I agree that for the angular/devkit#388 it may be wrong here. But as far as I can tell all of them would not exist if the buid optimizer would only optimize and not break the code. People tend to turn on optimization features ("why would it hurt?") and then have no idea why their code breaks. Which one of the issues I listed do you think would be there if the default settings would have been different? Yes, there were resolutions to some of the issues. The resolution always was to disable the broken feature. As such I agree that maybe this issue (and my "ignored/archived duplicate") is the only issue that actually tries to fix the root problem, while all others just tried to get their code to run. How great are these size savings? I feel they must be enormous if they outweight the price for broken, extremely hard to debug code. Can you share some details? One could argue if it is really safe to remove such code in some projects, then this is rather a problem of these projects, rather than a problem of the buildBreaker here. In general I would really like to see the build optimizer focusing on "optimizing" the code that the user has control over and not some third party library code. |
From the UglifyJS documentation (https://github.com/mishoo/UglifyJS2): pure_getters (default: "strict") -- If you pass true for this, UglifyJS will assume that object property access (e.g. foo.bar or foo["bar"]) doesn't have any side effects. In my humble opinion the Angular CLI should not assume that code is written in a certain way. As @yGuy said, "The optimization step should optimize, but never break code." |
It is an important optimization and if absolutely essential can be disabled by turning off build-optimizer. There are no current plans to change this behavior. Further, any code that has side effects in a property access is asking for trouble. It is error-prone, highly unintuitive and unexpected from a developers point of view. Essentially, regardless of the use of uglify and the option in question, a developers application will end up with unexpected behavior at some point as a result of a library ignoring general best practices. And finally, never break code is an impossible metric. In many languages, use of exotic constructs will result in broken code when full optimizations are enabled. If code follows recommended practices full optimization can be enabled. |
For what is it an important optimization to remove code that has side-effects? How great are the gains that you get from this "optimization"? Are they truely worth it asking for troubles? Can you please elaborate on the importance of this optimization. "Any code that has side effects in a property access is asking for trouble" - sorry, but this statement is just ignorant. That's not true in the general case. Any compliant JavaScript engine and any code mangling tool that is worth its name can deal with this case properly and it is a bug if it cannot. Any code that does this and which will be "optimized" by this tool is asking for trouble, for sure. There are a great number of very valid use-cases for this. If you don't like side-effects, then don't use JavaScript but some functional language. However as a tool author you should never force your preferences onto all third party library authors. It is fine to restrict code in Angular or in code that you have control over, but if you want to support JavaScript, then you should at least make it possible to configure the "optimization" process not to break code. UglifyJS does have this option, why don't you just turn it on? "Never break code" may be impossible, but always breaking code that is perfectly valid and part of a very widely used language construct is not acceptable. Make this an opt-in, or at the very least an opt-out, but don't leave it broken for the rest of the internet. |
As mentioned previously, it is opt-out. Advanced optimizations (i.e., build optimizer) can be disabled if desired. Considering the small number of cases of this actually causing an issue (and not defects in uglify itself) and the ability to manually modify the setting if absolutely required via a post install step, there are no plans to change this. Please note that this statement "Any code that has side effects in a property access is asking for trouble" was in reference to users of a library that decides to take this approach as it is incredibly unexpected to have a property access perform additional side effects in a production scenario. Further, functional versus imperative is not a relevant distinction. Essentially every object-oriented language's best practices recommends not causing side effects within get accessors. |
I totally agree with you, here:
But this is not what the optimizer is doing. It is not deciding to optimize your code that is using a library and fixes it for you (which really should not be the job of an optimizer, IMHO). It is changing the library and the way third party code behaves that the developer does not have control over. There is no way for most devs to "fix" third party code, just like it is very difficult for most of us to fix the angular tool chain. I have seen quite a number of samples of this problem and believe me, these weren't just broken designs with unexpected side-effects. These were optimized pieces of code that resulted in very expected side effects inside the libraries. The problem with the optimization is that the expected effects don't happen anymore. Please reconsider. I am happy to tell my team to create a pull-request. I would just like to spare the effort if the pull-request will be declined, anyway, because someone thinks the current solution is "better" in some way (for which no proof whatsoever has been shown, here, so far). Would you be fine with a pull request that does:
|
Effort would most likely be better spent updating libraries/packages that are not following the recommended guidelines for advanced optimizations. However, the team would be willing to consider a PR if a list was provided of libraries/packages that were not willing, or unable, to update their code to follow the recommended guidelines. |
May I kindly ask you to post the link to the official Angular "recommended guidelines for advanced optimizations"? Thank you. |
@clydin - I always thought that Angular wanted to benefit from the node.js community and not dictate the community how they think JavaScript works and what subset of JavaScript should be supported in their opinion. That is one of the most ignorant statements I have ever read: "Why should we fix our software when everyone else in the internet can just adjust (not fix - it is not broken) their codes. If you continue to follow this road, then this is the beginning of the end of angular. If it only was about angular specific libraries. This is about all third party code on the internet and with the huge dependency trees you get nowadays (think leftpad), making sure that all your dependencies in your angular-cli application follow the NG-Script subset of EcmaScript will be too much effort for most devs and they will decide to either ditch the "optimization" or angular as a whole and switch to a framework that works with the community and not against it. Sorry for the rant, but this is really the worst kind of reaction and attitude I could imagine, here. I always thought good of angular, but this really makes me doubt the whole project leadership. Find here a list of libraries that are unlikely to be willing to "update their code to follow the recommended guidelines of the angular devs, and who would rather stick to the EcmaScript standard". There might be a tiny fraction of about 0.1% or less of libraries on this list that have been included in error, feel free to fix it for the rest of them. |
We use libraries like bitcoinjs quite often in our projects, and as Ionic has now migrated to Angular-CLI (which I think is a great change), the overly strict uglifying behavior breaks our application at runtime. See this issue thread: bitcoinjs/bitcoinjs-lib#959 At least give us an option to properly configure the uglifier - It is one of the configuration flags that makes absolutely sense I think, in order to be compatible with the vast ecosystem provided by npm. |
I am really unable to figure out why this would be a stance. Giving finer control over the settings shouldn't be a matter of this much debate. I as a developer would definitely want the choice to opt out of a feature that causes some of third party code to break. I always thought Angular was supposed to be an inclusive community and not an exclusive one. As a developer, a lot of things would be out of my control when it comes to third party library. The framework should be there to support me in these situations by at least allowing us more control over the build process. I decided to actually go ahead and test the size differences between bundles generated by three different approaches.
So I do have the option to have no loss in functionality by having only a 30kb increase in my bundle, but the recommended solution by angular is to turn off the optimizer completely and deal with my bundle being almost 3x the size. I really can't wrap my head around this. |
OK, @clydin - now that we've seen how "important" that further optimization flag is (it accounts for a whopping 0.7% size reduction and that size reduction also consists of the code that has been removed and is now broken, and should have never been removed) and now that we've had more people saying that they are affected by this issue, would you still not consider a pull request that changes the default to a less criticial value? If no, please back up your statement why saving 0.7% is a more important improvement over risking broken code. @shileen has shown that the savings in their project and I am sure that almost every developer would rather prefer code that is less likely to break in the optimized version and behaves the same as the unoptimized version vs. code that is 0.7% smaller but might be broken and is extremely difficult to debug. @filipesilva - would you still say that you feel that for situations like in Shileen's it is the right way to disable the optimizer completely rather than have the default changed or at least make this configurable? Given the above facts, I personally would be changing the default and not even make it configurable. If someone wants to have this 0.7% size savings and risk that their code breaks, they should be fine with a post install step that or a second manual optimization run that removes the unwanted code. Please consider accepting a pull-request to change this setting, even without a list of projects "that were not willing, or unable, to update their code to follow the recommended guideline", whatever these unknown "guideline" may be? |
@yGuy I understand this issue is important to you, and I've certainly been on the receiving end of subtle breakages due to an optimizer (Closure Compiler's advanced optimizations cause lots of these). |
That the discussion is getting a bit heated is understandable, given the initial response to this issue. You may have the best intentions, but you can still be wrong. One response from your team suggested to rather "focus our efforts to patch all libraries in NPM that break due to this optimization" instead of acknowledging that it IS an issue and seems to come up regularly. I stand by my previous point that this portion should be configurable, as uglifying code has been breaking stuff in JS for years now, proven by the fact that these plugins explicitly provide optional configuration to exclude stuff. |
I don't see the point for patronising users by preventing us from configuring uglifyjs options. After all, we are all engineers with the best intentions. Your default options just don't fit all use cases. Admitting this fact and letting us overwrite configuration options will empower everyone, including yourselves. |
@alexeagle Thank you for chiming in. Maybe this is a case of "lost in translation", but I really didn't mean to be any more hostile than how I feel I've been treated here. This is how I see the situation: I provide a package that is affected by this bug. I don't use angular-cli in my projects, but my users do. They blame me for a broken product, where in fact the bug is not in my code, but in the optimizer. I had to find this out for my users during painful debugging (it is hard to find a bug in uglified code if the bug is that the code in question is not there, especially if the library that breaks is 6MB in size in already minified form - that's also why "fixing my library" is not an option - I don't even know how many code parts have been removed by the setting and I certainly don't feel like I have to change hundreds of lines of working code when the actual fix is one line of code in the tool that is responsible for breaking my code.) I researched the problem and found that others were affected by the issue, too. I reported the bug and the issue simply got archived (ignored from my point of view) without being transitioned. The bug report had a complete analysis, repro, and the second time I reported it (and had researched other issues that were basically duplicates) I also included a proposed fix and asked whether and how I could help with a pull-request to fix this issue. However I was told that basically my code is wrong and it's bad style and I should simply not be doing it and I should rather spend my time "fixing" all the other packages that suffer from this problem and use a more functional coding style. We were told we should instead stick to some "recommended guidelines for advanced optimizations" if we want to use the angular tool-chain, which -although requested- we have not been given so far. And we were told that this is an important optimization that leads to huge size savings and size savings have a higher priority than handling these "corner cases" where code breaks. I asked for how much they think that this setting is saving, but also never got a response. We showed that these savings are extremely small and that many users would benefit and only very few would suffer from this change, but still they don't want to even consider our request. You labeled this as "needs-discussion". What is it that you feel needs further discussion? What facts are you missing? Given the facts that have been listed here I personally really don't see what valid arguments one could have for leaving this setting as is. Maybe there are strong facts against that, but I fail to see them; they have not been provided. So if you think that this requires further discussion, I would very kindly ask you or the others to provide a valid argument backed with facts for keeping the setting as is. With the current facts I don't think it can be "size savings". And it definitely cannot be "spec conformance" or "ease of use". I can only see a lot of room for improvements, here. I am definitely not trying to be hostile; I am trying not to be hostile. I would just like to help the users of my package and of other packages to be able to use the tool of their choice painlessly. |
"Needs discussion" is a label that we review in our team meeting, happening now. Here's our resolution:
|
Well, that is a very disappointing conclusion to this discussion. So basically we should go with:
I don't know guys, but this seems another case of forcing users to adapt a specific philosophy instead of listening to user feedback and adapting the product ever so slightly. |
This is pathetic! Shame on your failure to provide a single argument
besides "we the overlords have decided based on reasons that we dare to
communicate". Very disappointing and discurraging indeed...
|
Based on this conversation:
Switching optimization off is NOT an option. How to create a custom webpack config that will change |
As a short term solution we do the following until we have time to create our own Angular build system:
to install https://github.com/ALMaclaine/replace Then, in package.json, we added a new script "disable-pure-getters" and run it before we build for production (excerpt):
The build server runs |
@fbernhard This somehow didn't work for me. The replace library was not working as expected. So if someone needs another workaround, I use patch-package tool to patch the library. Here's a good example of how to use that. |
Since when closing tickets without resolution became a common practice? Please stop pretending that ugly workarounds are a solution and reopen the ticket. |
@alexeagle can you please consider an updated stance on this and reopen the issue? All the opinions (and thumbs up) above show the community wants this (seemingly easy to implement) change. Or can you at least provide a compelling reason not to do it? |
When we first started using Build Optimizer, we saw a lot of the savings were tied to using the Uglify/Terser `pure_getters` option. This was intimately related with the structure and shape of the Angular codebase. The measurements we did at the time on angular.io showed a significant size reduction, from 1mb to about 600kb. Of these roughly 150kb were tied to using `pure_getters` if I remember correctly. Meanwhile the Angular codebase has changed significantly and I don't really see these savings anymore, so I don't think it makes sense to keep it on given that it is known to cause problems with some libraries. Related to angular#9231, angular#11439, angular#12096.
When we first started using Build Optimizer, we saw a lot of the savings were tied to using the Uglify/Terser `pure_getters` option. This was intimately related with the structure and shape of the Angular codebase. The measurements we did at the time on angular.io showed a significant size reduction, from 1mb to about 600kb. Of these roughly 150kb were tied to using `pure_getters` if I remember correctly. Meanwhile the Angular codebase has changed significantly and I don't really see these savings anymore, so I don't think it makes sense to keep it on given that it is known to cause problems with some libraries. Closes angular#9231, angular#11439, angular#12096, angular#12128.
When we first started using Build Optimizer, we saw a lot of the savings were tied to using the Uglify/Terser `pure_getters` option. This was intimately related with the structure and shape of the Angular codebase. The measurements we did at the time on angular.io showed a significant size reduction, from 1mb to about 600kb. Of these roughly 150kb were tied to using `pure_getters` if I remember correctly. Meanwhile the Angular codebase has changed significantly and I don't really see these savings anymore, so I don't think it makes sense to keep it on given that it is known to cause problems with some libraries. Closes angular#9231, angular#11439, angular#12096, angular#12128.
When we first started using Build Optimizer, we saw a lot of the savings were tied to using the Uglify/Terser `pure_getters` option. This was intimately related with the structure and shape of the Angular codebase. The measurements we did at the time on angular.io showed a significant size reduction, from 1mb to about 600kb. Of these roughly 150kb were tied to using `pure_getters` if I remember correctly. Meanwhile the Angular codebase has changed significantly and I don't really see these savings anymore, so I don't think it makes sense to keep it on given that it is known to cause problems with some libraries. Closes #9231, #11439, #12096, #12128.
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Bug Report or Feature Request (mark with an
x
)Area
Versions
Windows
node v9.4.0
npm v5.6.0
Repro steps
Use a third party library that contain non-side effect free getters.
Run a production build.
Observer that the code breaks at runtime, whereas it runs fine without "optimization".
E.g.:
The log given by the failure
n/a - it's a runtime issue in the application
Desired functionality
The optimization step should optimize, but never break code. The "pure_getters" option should be configurable and by default "false" should be used to guarantee best possible compatibility. If someone wants to optimize further and take the risk, that's fine, but don't break code by default: Don't be optimistic or dictate how code should work in your opinion.
Ideally make it possible to exclude specific libraries from this treatment. Users may have no control over these libraries and patching them often is not an option.
Mention any other details that might be useful
This has been reported before but was always closed without comments or with a comment that the third party code should be changed to workaround this limitation in angular. This is a one-liner fix in angular, though and instead of dictating how third party code should be written, the CLI should try not to break existing code, instead. Debugging "optimized production code" is a PITA the gains are minimal compared to that. Make this a configurable setting and make it possible to exclude certain files or third party libraries.
These are the previous issues that all had the same cause and where closed or ignored with no further explanation:
#9231
#9221
angular/devkit#937
angular/devkit#388
angular/devkit#612
and probably some more.
We are a library provider and we had several customers running into that issue. It's a nightmare to debug and analyze and devs expect that there is no behavior change when the "optimizer" is turned on. But there can be and is a change: it breaks their projects in all kinds of ways.
Proposed workaround for people affected by this bug
Go and hack the buildoptimizer options in the code. Find the line that sets the pure_getters option to true or "strict" and set it to "false" instead. If there is no such line, make sure that "false" is used (because the default is true and breaks code).
The text was updated successfully, but these errors were encountered: