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

Is enabling full source maps in production a wise default? #769

Closed
searls opened this Issue Sep 6, 2017 · 32 comments

Comments

Projects
None yet
@searls
Copy link
Contributor

searls commented Sep 6, 2017

This ticket is about the default production configuration (both the webpack & uglify settings) in this file:

result.devtool = 'source-map'

In my experience in (mostly Node.js-based) static asset compilation ecosystems, I haven't run across very many cases where full source maps are, by default, generated and pushed to production servers. I presume the reason for this is because that modern JS build toolchains provide a sort of accidental benefit of code obfuscation, and well-generated source maps can actually make it very easy to rip off someone else's source.

The webpack docs don't actually justify this statement, but they say this much about using the source-map setting in production:

You should configure your server to disallow access to the Source Map file for normal users!

I've looked through the repo and was curious if there was much discussion/justification for enabling full source-maps in production by default and whether it'd be fair to revisit the issue in a thread here.

Curious to hear your thoughts & thanks for your time!

@searls

This comment has been minimized.

Copy link
Contributor Author

searls commented Sep 6, 2017

One other benefit of moving to a more restricted source code default would be shorter asset precompile build times. I noticed this was enabled by default when I saw source map compilation took a few seconds on a relatively small app.

@gauravtiwari

This comment has been minimized.

Copy link
Collaborator

gauravtiwari commented Sep 7, 2017

Sourcemaps are super useful for error reporting, as well as for analysing bundle size from dependencies. Whether one chooses to deploy them or not is their choice, but producing them is useful. Producing proper stack traces is another thing to consider in production with things like Rollbar or Sentry.

Please note, source maps are only downloaded if devtool is open, so the normal user won't see it unless someone opens the devtool. In terms of exposing the source code, I guess an uglified js file can be easily beautified :)

If you wish to remove sourcemaps though, this is how you can do it:

// config/webpack/production.js
const merge = require('webpack-merge')
const environment = require('./environment')

const config = environment.toWebpackConfig()
config.devtool = 'none'

module.exports = config
@searls

This comment has been minimized.

Copy link
Contributor Author

searls commented Sep 7, 2017

I don't disagree at all that sourcemaps are useful. I only opened this issue because it's a surprising default to enable it in production, for these reasons:

  1. Most other static asset compilation tools don't build and bundle sourcemaps for production by default (most people I ran this by before opening the issue were surprised to learn this default)
  2. Just like most teams/companies writing production Rails apps would be surprised if all their Ruby source listings were being published on every request, I think it's fair to assume most companies wouldn't be pleased to discover after-the-fact that they'd been "leaking" their own deobfuscated front-end source code.
  3. It comes at a non-trivial build cost, and is the slowest part of my rake assets:precompile (as best as I can tell from watching stdout)

The more I've thought about this, the more I feel that a more limited setting like 'nosources-source-map' is more appropriate, as it gives something like Rollbar or Sentry, because it will produce what's needed for proper stack traces without blatting out all of your source code.

@javan

This comment has been minimized.

Copy link
Member

javan commented Sep 7, 2017

Thanks, @searls! I think nosources-source-map would make a better production default given all the reasons you outlined. Want to open a PR and reference this to-do?

@gauravtiwari

This comment has been minimized.

Copy link
Collaborator

gauravtiwari commented Sep 7, 2017

@searls Thanks, makes sense 👍

searls added a commit to searls/webpacker that referenced this issue Sep 7, 2017

Restricts the source maps generated for production
This will generate source maps only to the extent required for stack
trace reporting tools like Sentry and Rollbar. It should result in build
time savings and prevent people's unobfuscated source code from
"leaking" with every production build. Fixes rails#769
@searls

This comment has been minimized.

Copy link
Contributor Author

searls commented Sep 7, 2017

You got it, @javan. Opened #770

@ezuk

This comment has been minimized.

Copy link
Contributor

ezuk commented Sep 11, 2017

Sorry to comment on a closed issue - but how do I actually enable source maps now, for tools like HoneyBadger? Any tips? Thank you!

@gauravtiwari

This comment has been minimized.

Copy link
Collaborator

gauravtiwari commented Sep 11, 2017

@ezuk ^_^

// config/webpack/production.js
const merge = require('webpack-merge')
const environment = require('./environment')

const config = environment.toWebpackConfig()
config.devtool = 'sourcemap'

module.exports = config
@dhh

This comment has been minimized.

Copy link
Member

dhh commented Jan 28, 2019

I was just made aware of this thread, and would like us to revert. I fully understand and respect the reasons for why some might feel the necessity to obfuscate their code, but I don't think it's a good default, and I don't think it sends a good message.

I've learned so much from View Source over the years. From structure, to CSS, to JS. That has gotten harder with minification and transpiling. That's a real shame. The web has given us so much. The default should be to give back, in all the ways we can. View Source is a heritage feature that a framework like Rails should go out of its way to pay tribute to.

So. I'd like the source-maps-by-default restored, but with an easy and documented out for people who don't feel they can or will pay tribute to the web in this fashion. Thanks!

@guilleiguaran

This comment has been minimized.

Copy link
Member

guilleiguaran commented Jan 28, 2019

Source maps are back: #1918

I'm going to write now how to disable them in docs.

@dhh

This comment has been minimized.

Copy link
Member

dhh commented Jan 28, 2019

@duaneking Are you implying that startups are going to be made or broken by whether source maps, which are only downloaded when development tools are open, are turned on by default? And that they'd somehow be oblivious to this fact right up until their death, and then they'd go, "oh fuck, IT WAS THE SOURCE MAPS THAT DID US IN!!!" 😄.

I don't find that to be a major threat for Rails to be concerned about. And certainly not compared to the gains for allowing developers of said startups to learn from other Rails apps that'll ship source maps by default.

I appreciate your time to raise the point, though. We'll make it clear in the documentation how you can turn this off, if you don't want to help others learn from view source, in the deep tradition of the web. But the default stays.

@searls

This comment has been minimized.

Copy link
Contributor Author

searls commented Jan 28, 2019

How to disable this will be critical for many, as leaving them enabled increases costs of deployments and ultimately does more harm than good once the bill for all the extra bandwidth arrives.

FWIW, I support David's decision here. As long as it's well-documented and easy to disable, having the web be more accessible by default is something that Rails can (and has, numerous times) positively influence.

I think I'd need to see some hard data that supports the argument that sourcemaps are somehow a high-cost burden. While very large projects could see longer build times when source maps are enabled, that probably won't be the case for startups on shoestring budgets. As for perceived bandwidth cost, I believe it's the case that all browsers that support loading sourcemaps will only fetch them if the user opens dev tools, which is probably an infinitesimally small number.

@SamSaffron

This comment has been minimized.

Copy link

SamSaffron commented Jan 28, 2019

I just wanted to voice my support for bringing this back by @dhh . We have been using source maps at Discourse now for 4 or so years, including maps for both JS and SCSS in production, default on.

One non-obvious but super important reason to enable this is that often JS frameworks have "production" and "development" modes. I have seen many cases over the years where a particular issue only happens in production and does not happen in development. Being able to debug properly in production is a huge life saver. Source maps are not the panacea as they still have some limitations around local var unmangling and other edge cases, but they are 100 times better than working through obfuscated minified code with magic formatting enabled.

The only legitimate possible performance concern is the cost of precompliation cause in theory this can add some time to precompilation that in turn slows down deploy cycles. From my experience at Discourse the cost has been minimal, that said I do not know what the cost is if you are chaining an enormous amount of source maps all the way down to 10,000 .js.es6 files. At Discourse we concat all our JS and then generate a single map from that and find it adequate.

But... before people freak out here about the rising cost of pre-compliation we got to get some real world numbers.

@guilleiguaran

This comment has been minimized.

Copy link
Member

guilleiguaran commented Jan 28, 2019

If bandwidth is a real concern we can gzip source maps just like we do with other assets:

-rw-r--r--  1 guille  staff   70471 Jan 28 13:57 application-1263b60505a412633ab8.js
-rw-r--r--  1 guille  staff   18045 Jan 28 13:57 application-1263b60505a412633ab8.js.gz
-rw-r--r--  1 guille  staff  204406 Jan 28 13:57 application-1263b60505a412633ab8.js.map
-rw-r--r--  1 guille  staff   47543 Jan 28 13:57 application-1263b60505a412633ab8.js.map.gz

I'm going to enable .map extension in CompressionPlugin.

@amarshall

This comment has been minimized.

Copy link

amarshall commented Jan 28, 2019

@dhh You say

…some might feel the necessity to obfuscate their code, but I don't think it's a good default, and I don't think it sends a good message … View Source is a heritage feature that a framework like Rails should go out of its way to pay tribute to

but this doesn’t jibe with:

  • You enabling compression/uglification by default in Rails back in v3.1.0 (see rails/rails@59ce0f6), which remains to this day in Rails 5.2
  • Rails lacking support for source maps in its out-of-the-box configuration (finally changing in upcoming 6.0, I believe), despite source maps having been around since 2012 (src)

So for the past 7+ years, the default in Rails, per your own change, has been to obfuscate code. Why has Rails not gone “out of its way to pay tribute” to this behavior anytime in these many years?

@levifig

This comment has been minimized.

Copy link

levifig commented Jan 28, 2019

@amarshall I see that as a change of heart and something to be praised not criticized. Just because someone took a "less than ideal" decision 7 years ago doesn't mean they can't change their minds and make a better decision in the future.

Also, we're not taking into account the massive changes Rails has seen since 3.2… JS had a VERY different proposition and role back then compared to now. It used to be just "sprinkles" and little to no logic. I believe this change of heart has a lot more to do with how much more logic is going into JS. Back in Rails 3.2, you could very easily "deobfuscate" CSS and JS locally. It's become increasingly difficult, hence this decision making a lot of sense now.

Not trying to speak for anyone, but I definitely applaud this decision.

@aequasi

This comment has been minimized.

Copy link

aequasi commented Jan 29, 2019

compression/uglification's purpose isn't mainly to just obfuscate code, but to shrink the size. The reasoning behind wanting to compress/uglify is important.

@guilleiguaran

This comment has been minimized.

Copy link
Member

guilleiguaran commented Jan 29, 2019

@aequasi that's true but compression/uglification isn't affected by source maps, you can have both simultaneously.

@amarshall

This comment has been minimized.

Copy link

amarshall commented Jan 29, 2019

I’m just pointing out that this philosophy is new for Rails. That’s okay; change is okay. But it should be a considered, reasoned change with more than just one point of consideration (that obfuscation is bad).

Rails also espouses convention over configuration, and since it is the convention of the wider JavaScript community and the recommendation of the library which this gem wraps to not provide source maps in production, deviation not just from what one has come to expect from Rails over many years, but also from the larger JavaScript ecosystem, should not be done lightly.

@levifig On the other side of “more logic is going into JS” is that technical performance considerations may have a greater impact than before due to the increased amount of code being compiled and served.

@aequasi Yes, but the point of excluding source maps is not necessarily just to obfuscate either, as there is potentially a performance impact. As Sam said above “we got to get some real world numbers”.


My own opinion is that Rails should stick with the wider community’s convention and the recommendation of Webpack (thus also adhering to the principle of least surprise). That is, until there is evidence that there is a negligible performance/bandwidth impact both in the browser and during compilation.

@haikyuu

This comment has been minimized.

Copy link

haikyuu commented Jan 29, 2019

We can add a warning

Source maps are enabled by default. This is how to disable them

@kyleholzinger

This comment has been minimized.

Copy link

kyleholzinger commented Jan 29, 2019

Question: If the docs on how to change the source maps is so well documented, why is it necessary to also change the default? It seems like it's just an opinion that there are valid arguments for/against on both sides, but ultimately not a really impactful change.

@dhh

This comment has been minimized.

Copy link
Member

dhh commented Jan 29, 2019

Because defaults matter, and the Rails default will be the one paying tribute to the web.

@dhh dhh closed this Jan 29, 2019

@jakeNiemiec

This comment has been minimized.

Copy link
Contributor

jakeNiemiec commented Jan 29, 2019

Anecdotally, @dhh & @javan were pretty cool with me stumbling over stimulus.js in the basecamp source code before it was officially released. I was also able to send the team detailed info about a basecamp JS error. (beneficial for both developer and consumer)

IMHO, if you want to run code in someone's browser, don't try and obfuscate it. If you are exposing
🔑 secrets to JS stack, no amount of obfuscation can save you. Example: https://www.youtube.com/watch?v=qzGLCqW_wrM

@jasonfb

This comment has been minimized.

Copy link

jasonfb commented Jan 30, 2019

in a high traffic prod site — especially one with a lot of JS— the benefits of having source maps in prod for error reporting alone outweight the downsides.

interaction issues, 3rd party code that runs only on prod and causes conflict with your code, etc, etc.

ironically, if you run little JS then the source maps in prod make less sense for you, but if you’re app is bloated with JS you will definitely want to see them.

would you throw away your back-end exceptions like you just don’t care that users are hitting bugs? I doubt it.

@kyleholzinger

This comment has been minimized.

Copy link

kyleholzinger commented Jan 30, 2019

@jasonfb you can retroactively apply source maps to a file 🙂 it's not a necessary step to compile them in order to deploy to production, so best not to conflate the two processes. In any case, it's an opinion and there's no one right answer, hence my previous question about why you'd change it at all. You might see them as very useful, but there are very legitimate reasons to not use them.

@thadeu

This comment has been minimized.

Copy link

thadeu commented Jan 31, 2019

I agree to keep it enabled by default! @dhh tribute to the web :)

@cycomachead

This comment has been minimized.

Copy link

cycomachead commented Feb 1, 2019

As someone who has spent a lot of time with Rails 4 apps, and that's where I started learning, I just want to put in a vote for how much default source maps would have helped me! IMO, source maps are 🔋s included -- install an exception tracking gem of choice, git push💥. In a few minutes you can have a "production-ready" setup for even a simple side project and that'd have been huge for me.

Looking forward to this.

@davidomid

This comment has been minimized.

Copy link

davidomid commented Feb 19, 2019

Sorry for posting on a closed topic, or if this seems like a shameless advertisement for my own website, but I explored the topic of production source maps extensively in a blog post and provided a solution which works really well. Essentially if you're worried about security, you can just use any form of authentication to lock down the endpoints your source maps are available at.
This way you get some great debugging and logging capabilities in production, whilst making sure the public can't read your source code easily.
The solution I provided is written in C# for .NET Core but the same principle can be easily applied to any language/platform.
https://www.davidomid.com/using-production-source-maps-securely-in-aspnet-core

@dhh

This comment has been minimized.

Copy link
Member

dhh commented Feb 19, 2019

Not interested in any of that. The point about is EXPLICITLY to let "the public" read the source code that runs in their browser. Also, if the security of your applications relies on the obfuscation provided by uglification or minification, I'd argue that's not much security at all.

@jakeNiemiec

This comment has been minimized.

Copy link
Contributor

jakeNiemiec commented Feb 19, 2019

image

@davidomid It's like going into a restaurant where you can see the big open kitchen and stacks of ingredients. You show that you are proud of the scripts you are loading in your client's browser.

Speaking of which, @dhh, while I have your attention, could you provide some project guidance in this issue: #1903?

@kyleholzinger

This comment has been minimized.

Copy link

kyleholzinger commented Feb 19, 2019

Last comment on here for me, but @jakeNiemiec I'm not proud of the scripts I am loading into my client's browser; I'm proud of the product I'm delivering to them and the use-case it solves. From my point of view, this is a purely technical tradeoff: do you want to increase your compile times and transfer times so you can look at stack traces and source-maps in production? I feel like anything more than that is more of an emotional/personal decision or irrelevant (see: security through obfuscation). To me, for the average user, this just seems like a misuse of transferred bytes for the sake of our own vanity/to pay tribute to the web. I doubt when a car is put together it's done so in a way that lets the consumer easily take it apart.

@jasonfb

This comment has been minimized.

Copy link

jasonfb commented Feb 19, 2019

I agree that there's plenty of arguments and good cases to be made that each site is unique.

I think a lot of high-traffic websites these days run LOTS of Javascript — like hundreds and hundreds of external servers loading Javascript (not that I think this a good thing, and of course I acknowledge that if it is external js that is not relevant to the sourcemap that comes from Rails). My point is just: if that's you, not looking at your JS errors is just as bad as not looking at your Ruby exceptions, and I hope we'd all agree that you want to see your Ruby exceptions.

If your site has very little Javascript and you don't need to see errors in prod by all means skip the sourcemaps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment