Back

TechnologyOct 01, 2014

Modern Web Development Best Practices Powered by Grunt.js Part 12: Feature Detection & Dead Code Removal

Fernando Berrios

As my bag of tricks dwindles, I have one last technique I can demonstrate. It’s actually more of a two-in-one, so it’s pretty good. For my next trick, I’ll be using feature detection libraries together with conditional compilation techniques and the dead code removal feature of our minification library.

FEATURE DETECTION AND DEAD CODE REMOVAL

The concept behind feature detection is to write code in such a way that it can handle browser inconsistencies. For the most part, browser developers try to implement the same array of features to stay competitive in the marketplace. Unfortunately, not every browser is developed at the same pace or with the same priorities. This leaves application developers with the job of navigating the inconsistencies. For example, Browser A might support Feature A that Browser B doesn’t or vice-versa, but they both might support Feature B. With a feature detection library, you can write conditional logic in your code to handle these cases. You can handle them by either implementing workarounds and displaying some messaging to the user or just plain ignoring it. The important thing is to not let your application explode because you assumed the user had Feature X available when they didn’t.

For robust and flexible feature detection, we’ll be using a library called has.js. This library contains a good number of built-in tests for feature detection in the browser. It also provides an API that allows you to add your own tests for use in your application. Using those built-in tests allows you to build a very resilient application, and the ability to define custom tests allows you to do some very interesting things. One of those is the ability to write code in your application that can be easily and safely removed during your build phase. For example, it is common when building web apps to include logging statements used for debugging purposes. Usually, you don’t want those statements to be shown in production, so you either add and remove them manually or hide them behind a global flag you can toggle on build. The problem is the code still remains in the final build, and even though it’s hidden, it still needs to be evaluated. So we can save a few cycles by stripping out those calls entirely. Here’s how:

tutorial:

“git checkout override-has” “bower install” “npm install”

The following is a quick way you can implement this feature using has.js and UglifyJS. Add a custom test to your application:

has.add('debug', function(){ return true; }, true);

You can use this test throughout your application like so:

if (has('debug')) { console.log('Congrats! You are in DEBUG mode'); }

Using the “grunt-override-has” plugin, we can configure a task to replace those tests with whatever value we want:

override_has: { prod: { options: { tests: { debug: false } }, files: [ { src: jsAppFinalTarget, dest: jsAppFinalTarget } ] } }

In here, we configure a task to add to our “build-prod” task that will set “has(‘debug’)” to false. When the task is run the output in the source code will be:

if (false) { console.log('Congrats! You are in DEBUG mode'); }

This ensures our debug code will never be hit. After that we make another slight change:

uglify: { options: { compress: { dead_code: true } } }

This configuration tells UglifyJS to do some analysis of the code and to remove parts that will never be hit. This will completely remove our “if (false)…” statements from the code base. A “debug” test is one of the many possibilities of custom tests. Think about having different feature branches for A/B testing and being able to define tests that will allow you to completely remove the unused code from your app, depending on your build. There are many other possibilities and following this practice has the potential to save you lots of space and time. All in all, a winner in my book.

CONCLUSION

I hope going through all the concepts and implementations outlined in this series will inspire you to consider how your projects might benefit from them. I have found that leveraging build tools into your development process has many great advantages and opens up the “traditional” web workflow in many different ways. Moreover, the quality of that build tool and the ability to customize it to your needs is essential to your development effort. So when you find one, stick with it and learn all you can about that tool. For now, I couldn’t recommend anything other than Grunt.js.

If you haven’t already, follow @CrederaMSFT on Twitter and Credera on LinkedIn for more great best practices.

To view the rest of the Modern Web Development Best Practices Powered by Grunt.js series click here.

Have a Question?

Please complete the Captcha