Debugger statement in production code



  • Looking up my current cable bill on Comcast.net today, got this cute little jem.  A debugger statement embedded into the production code, along with a nice comment from a concerned developer.

    <font size="2" face="Consolas"><font size="2" face="Consolas">accountInfoTabs.click(
    </font></font><font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas">    function</font></font></font><font size="2" face="Consolas"><font size="2" face="Consolas">(el){</font></font><font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas">debugger</font></font></font><font size="2" face="Consolas"><font size="2" face="Consolas">;
    </font></font><font size="2" face="Consolas"><font size="2" face="Consolas">        el.preventDefault();
    </font></font><font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas">        if</font></font></font><font size="2" face="Consolas"><font size="2" face="Consolas">(accountInfoTabs.length ==2)
                 </font></font><font color="#006400" size="2" face="Consolas"><font color="#006400" size="2" face="Consolas"><font color="#006400" size="2" face="Consolas">// defect 24096 Restricted Secondary & Unres without Bill pay - On Clicking Account activitiy, it is being split into two tabs and moves to the left of the page.
                 </font></font></font><font color="#006400" size="2" face="Consolas"><font color="#006400" size="2" face="Consolas"><font color="#006400" size="2" face="Consolas">// expected : Account activity should not be clickable and it should remain in the center.
                 {</font></font></font>

    <font color="#006400" size="2" face="Consolas"><font color="#006400" size="2" face="Consolas"><font color="#006400" size="2" face="Consolas"> I'm a little surprised that they don't minify their javascript.  </font></font></font>



  • I'm a little surprised your syntax highlighting is leaking.



  • @DrPepper said:

    <font color="#006400" face="Consolas" size="2"><font color="#006400" face="Consolas" size="2"><font color="#006400" face="Consolas" size="2">I'm a little surprised that they don't minify their javascript.  </font></font></font>

    I'm not - my workplace doesn't minify its JavaScript either. The bandwidth used is trivial, it's one less build step to worry about, and it means that the same code is being used in test/production.

     



  • We are now investigating using a WAF that does minification on the fly. It HDS already sped up the beta tester's site and given them a better page rank for many search terms.



  • Minified javascript is TRWTF. Either send readable code or send bytecode, but this just looks silly.



  • @Zemm said:

    We are now investigating using a WAF that does minification on the fly.
     

    Why? Does it have any advantages over minification once during deployment (to staging, etc) then caching that file?

    I take it you're not performing a minify for each request.



  • @spamcourt said:

    Minified javascript is TRWTF. Either send readable code or send bytecode, but this just looks silly.
     

    Minified JS isn't a WTF, but that said I do like the idea of precompiled javascript sent from the server, which is served, perhaps in a script tag/link with type="binary/javascript" or somesuch.

    That is, assuming that javascript interpretation is a significant problem for browsers, which I don't think it is, and if it is, I don't think it should be because then you're overstepping your boundaries for a javascript program, and I think rendering a page from complex grammars and ludicrous standards like HTML/CSS is far more taxing than a little JS compilation.



  • @ATimson said:

    I'm not - my workplace doesn't minify its JavaScript either. The bandwidth used is trivial, it's one less build step to worry about, and it means that the same code is being used in test/production.

    Development-wise, minification as a build step using a proper AST based minifier will give you some additional compile-time safety regarding syntax errors. This is especially handy if you enforce a working build on check-in, as it means you can't get half-baked syntax errors that immediately break the build. (Ofcourse, you are still open to runtime errors, which JavaScript has plenty...)

    As for the same code being used in test/production; minification should not change meaning of code. If it does, either you are using a bad minifier (or one with broken / experimental optimization features such as Closure Compiler's advanced mode) or your own code was tapping into 'JavaScript: The Bad Parts'. Having said that, if you absolutely must debug on production; use source maps, or use a staging server that is configured 100% the same as the production environment, save for minification of JavaScript.

    The test server should probably always be running with unminified code, to assist debugging and reproducing bug scenarios on a shared environment with other developers that don't know how to work with source maps, or that have to work on browser-exclusive bugs in browsers that don't support them. (Having a dedicated test server with debuggable scripts is a very good way to prevent "Works on My Machine"-syndrome as well, btw.)

    Performance-wise, minification does slightly improve parse times and it does still reduce the bandwidth usage, even though proper application of GZIP negates most of the immediate worth of the latter. Note though: if you are using hand-written cache control of scripts inside window.localStorage (like Bing and apparantly also Google do because it's faster than regular cache and more controllable than appcache manifests), then the uncompressed size suddenly starts to matter a lot more. Minification is definitely advised in those scenarios.

    Finally; you're going to optimize your number of HTTP requests anyway, which means spending time concatenating scripts in the correct dependency order. (Please tell me that on any project which exceeds the complexity of some simple jQuery eye-candy, you are using an AMD loader or equivalent with an accompanying optimization tool. Please? ...) You might as well spend a few more milliseconds applying minification to your cached, optimized files to eek out that last bit of additional performance as well, no?

    @dhromed said:

    Minified JS isn't a WTF, but that said I do like the idea of precompiled javascript sent from the server, which is served, perhaps in a script tag/link with type="binary/javascript" or somesuch.

    You'd have to deal with the WTFs of global scope pollution and eval (in non-strict mode) being able to affect the surrounding closure scope. Minified JavaScript that uses a JS file's AST to perform safe name substitution for identifiers that can never leak into the global scope is the closest thing you can reliably get to 'byte code', I think. (And even that already fails on the aforementioned eval problem...)



  • @spamcourt said:

    Minified javascript is TRWTF.
     

    You've never worked with MVC, have you? I didn't even know Javascript supported namespaces until I got this job.



  • @curtmack said:

    I didn't even know Javascript supported namespaces until I got this job.

    It doesn't. It mimics them by using expando properties on otherwise empty objects...



  • @Ragnax said:

    @curtmack said:
    I didn't even know Javascript supported namespaces until I got this job.

    It doesn't. It mimics them by using expando properties on otherwise empty objects...

    Point being, you really would rather be sending minified code when your unminified code contains stuff like "Foo.Bar.Tools.Organization.Unit.Permissions.GetDataGroup()" at a rate of least two per line.

     



  • @curtmack said:

    "Foo.Bar.Tools.Organization.Unit.Permissions.GetDataGroup()"
     

    The bastard lovechild of COBOL deflowering a young and so-unsullied JavaScript.



  • @Cassidy said:

    @curtmack said:

    "Foo.Bar.Tools.Organization.Unit.Permissions.GetDataGroup()"
     

    The bastard lovechild of COBOL deflowering a young and so-unsullied JavaScript.

     

    Javascript hasn't been unsullied since the DOM.

     



  • @curtmack said:

    Point being, you really would rather be sending minified code when your unminified code contains stuff like "Foo.Bar.Tools.Organization.Unit.Permissions.GetDataGroup()" at a rate of least two per line.

    Point being, you really would rather be sending modules when namespaces directly or indirectly are still accessible from the global scope, which means minifiers won't perform name substitution on them at all and would be quite useless indeed...

    define([ "./Permissions" ], function( Permissions ) {
      // ...
      Permissions.GetDataGroup();
      // ...
      return {}; // whatever the new module should export
    

    });

    Short relative identifiers and automatically ordered based on dependency tracing when concatenated. Nice!



  • @Cassidy said:

    @Zemm said:

    We are now investigating using a WAF that does minification on the fly.
     

    Why? Does it have any advantages over minification once during deployment (to staging, etc) then caching that file?

    I take it you're not performing a minify for each request.

    It seems to. We are getting this WAF for other reasons (PCI compliance etc) and the js/css minify are a bonus. It runs on the load balancer so separate from the app servers. Im not really up with everything yet.



  • @dhromed said:

    @spamcourt said:

    Minified javascript is TRWTF. Either send readable code or send bytecode, but this just looks silly.
     

    Minified JS isn't a WTF, but that said I do like the idea of precompiled javascript sent from the server, which is served, perhaps in a script tag/link with type="binary/javascript" or somesuch.

    That is, assuming that javascript interpretation is a significant problem for browsers, which I don't think it is, and if it is, I don't think it should be because then you're overstepping your boundaries for a javascript program, and I think rendering a page from complex grammars and ludicrous standards like HTML/CSS is far more taxing than a little JS compilation.

    This, also, you guys are forgetting eval(), setTimeout("...") and the like, coupled with the usual WTF styles of JS programming. I think the current DOM APIs are broken enough that you'd probably have to ship a parser/compiler with your JS engine in any case - and given how often eval() is still being used, I doubt it would even make a performance difference.



    This doesn't mean that current JS compilers don't try to be clever. From what I've read, Google's V8 uses some crazy runtime analytics to basically turn all the dynamically-typed JS hashes into a number of statically-typed structs - then JIT-compiles the whole thing into native code and caches it on disk - but only if the code path is executed often enough that it would be faster than interpreting it. I'm not sure what Mozilla's VM does, but probably similar stuff.



    Generally, a lot of time and effort seems to be invested by a lot of extremely smart people around the world so that a quick-and-dirty prototyping language with one of the most WTF APIs ever can run almost as fast as compiled C 10 years ago. Compiling it, ironically, doesn't seem to be an option...



  • @PSWorx said:

    also, you guys are forgetting eval(), setTimeout("...") and the like,
    Didn't forget about it; I mentioned eval() as being a thorn in the side for generating optimized code. Read closely again. ^_^

    @PSWorx said:

    I'm not sure what Mozilla's VM does, but probably similar stuff.
    Yes; Mozilla's engine does it too. Microsoft's Chakra engine as well.



  • @Ragnax said:

    (Please tell me that on any project which exceeds the complexity of some simple jQuery eye-candy, you are using an AMD loader or equivalent with an accompanying optimization tool. Please? ...)
     

    I think there's room here for subjective judgment on what is large or small javascript. I think gmail is very large. I think pretty much every component I've written, even together, are small. My pages never perform as bad as Twitter, for example. ;)

    @Ragnax said:

    You'd have to deal with the WTFs of global scope pollution and eval (in non-strict mode) being able to affect the surrounding closure scope. Minified JavaScript that uses a JS file's AST to perform safe name substitution for identifiers that can never leak into the global scope is the closest thing you can reliably get to 'byte code', I think. (And even that already fails on the aforementioned eval problem...)

    Yeah, though if you know that your shop or your project uses this hypothetical server-compiled JS, then you can easily outlaw certain things, like eval.

     

    It's been a while since I've felt those pangs of incompetence in what I consider my primary field. Thanks for stirring up my soup!



  • @PSWorx said:

    Generally, a lot of time and effort seems to be invested by a lot of extremely smart people around the world so that a quick-and-dirty prototyping language with one of the most WTF APIs ever can run almost as fast as compiled C 10 years ago. Compiling it, ironically, doesn't seem to be an option...
     

    It's like trying to get a VM to run as fast as the host machine. It's almost insane. The web has insidiously grown to become the ultimate Inner Platform. :\



  • @dhromed said:

    Yeah, though if you know that your shop or your project uses this hypothetical server-compiled JS, then you can easily outlaw certain things, like eval.

    True that. Strict mode probably helps as well with this.



  • It may be quite harmless to leave debugger code like that, as long as you have a 'var debugger;' somewhere previously.

    And the last time I tried to use JS minifiers for a project, all the ones I tested crapped out at certain syntax combinations, for example unmatched quotes or block-comment-start inside regexp literals and such... then there's also the ambigous cases where it isn't clear if line-breaks can or can not be removed or replaced with semicolons. And all in all, the whole excercise seems pointless if you use a compressed transport encoding anyway.



  • @georgir said:

    It may be quite harmless to leave debugger code like that, as long as you have a 'var debugger;' somewhere previously.

    Since all browsers now have good quality Javascript debuggers with the ability to set breakpoints, it indicates a ignorance of the state-of-the-art of writing Javascript.



  • @georgir said:

    It may be quite harmless to leave debugger code like that, as long as you have a 'var debugger;' somewhere previously.

    Absolutely false: JS runtimes treat debugger as a reserved keyword. If it appears in statement form, it can never represent a named identifier. At most it can be present in the role of named identifier when used as a property of an object, e.g. , foo.debugger. However, even that might fail. When a runtime comes across a debugger statement it generally treats it as a no-op. Only when an actual debugger is attached to the runtime, will it treat the debugger statement as an impromptu breakpoint.

    @georgir said:

    And the last time I tried to use JS minifiers for a project, all the ones I tested crapped out at certain syntax combinations, for example unmatched quotes or block-comment-start inside regexp literals and such...

    Then you were using a minifier based on regex compression rather than proper AST expression. Not the fault of the general concept of a minifier; your fault for using the wrong kind of minifier. Try using something decent, like UglifyJS next time.

    @georgir said:

    then there's also the ambigous cases where it isn't clear if line-breaks can or can not be removed or replaced with semicolons.

    First off; relying on automatic semicolon insertion (ASI) is tapping into "JavaScript: The Bad Parts". If you rely on ASI, your code probably has far far greater problems you should worry about first. Having said that; if you use a proper AST based minifier, it will interpret these cases the same as any JavaScript parser used in the process of compiling and executing your code would. In other words; again your fault for using the wrong kind of minifier.

    @georgir said:

    And all in all, the whole excercise seems pointless if you use a compressed transport encoding anyway.

    Normalized white-space is still easier for JS parsers to handle, so if you have truely large amounts of JS to parse, using a minifier beforehand will matter there. As all white-space is either contracted or removed, it means the dictionary in the compression algorithm won't have to handle different run-lengths of white-space. I.e. it will have more space to handle actual code compression. Moreover, the name substitution as employed by minifiers ensures a lot of identifies in your code will be turned into simple one letter 'a', 'b', 'c', etc. variables. Again, this reduces the variation in the dictionary and will generally improve compression.

    Though the actual net reduction in size may vary, the final transport size will (almost exclusively; like in 99.999999% of all cases) be less than when minification is not applied. Running minification is cheap, and provided you use a proper AST-based minifier is also perfectly safe. There is nothing to be lost, and always a little something to gain. There is no reason not to apply it



  •  pwnt


Log in to reply