Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Zombie Code

Zombie Code

Have you ever had the need to figure out how to survive a Javascript Zombiecodepocalipse? Have you ever dreamt about loads of living legacy code and the urge to run away from it? Hundreds of lines of code tightly coupled and hardly understandable there were trying to catch you?

I’ve been there and I’m a survivor because I learned the art of refactoring.
This talk is about how to deal with features request, deadlines and still increase the maintainability of your code.

Marco Cedaro

April 20, 2013
Tweet

More Decks by Marco Cedaro

Other Decks in Technology

Transcript

  1. First things first my name is @cedmax I work for

    Shazam I organize conferences with From The Front
  2. Zombies! “Brains, BRAINS, BRains, brains, BRAINS. BRaiNS, brains, Brains, BRAINS,

    BRains, brains, BRAINS. BRAINS, BRains, brains, BRAINS, brains.” Ryan Mecum
  3. What's that smell? Zombies smell worse than anything you can

    imagine Lilith Saintcrow, Strange Angels
  4. function validate( options ) { // if nothing is selected,

    return nothing; can't chain anyway if ( !this.length ) { if ( options && options.debug && window.console ) { console.warn( "Nothing selected, can't validate, returning nothing." ); } return; } // check if a validator for this form was already created var validator = $.data( this[0], "validator" ); if ( validator ) { return validator; } // Add novalidate tag if HTML5. this.attr( "novalidate", "novalidate" ); validator = new $.validator( options, this[0] ); $.data( this[0], "validator", validator ); if ( validator.settings.onsubmit ) { this.validateDelegate( ":submit", "click", function( event ) { if ( validator.settings.submitHandler ) { validator.submitButton = event.target; }
  5. // allow suppressing validation by adding a cancel class to

    the submit button if ( $(event.target).hasClass("cancel") ) { validator.cancelSubmit = true; } // allow suppressing validation by adding the html5 formnovalidate attribute to the submit button if ( $(event.target).attr("formnovalidate") !== undefined ) { validator.cancelSubmit = true; } }); // validate the form on submit this.submit( function( event ) { if ( validator.settings.debug ) { // prevent form submit to be able to see console output event.preventDefault(); } function handle() { var hidden; if ( validator.settings.submitHandler ) { if ( validator.submitButton ) { // insert a hidden input as a replacement for the missing submit button hidden = $("<input type='hidden'/>").attr("name", validator.submitButton.name).val( $ (validator.submitButton).val() ).appendTo(validator.currentForm); }
  6. validator.settings.submitHandler.call( validator, validator.currentForm, event ); if ( validator.submitButton ) {

    // and clean up afterwards; thanks to no-block-scope, hidden can be referenced hidden.remove(); } return false; } return true; } // prevent submit for invalid forms or custom submit handlers if ( validator.cancelSubmit ) { validator.cancelSubmit = false; return handle(); } if ( validator.form() ) { if ( validator.pendingRequest ) { validator.formSubmitted = true; return false; } return handle(); } else { validator.focusInvalid(); return false; } }); } return validator; }
  7. function validate( options ) { // if nothing is selected,

    return nothing; can't chain anyway if ( !this.length ) { if ( options && options.debug && window.console ) { console.warn( "Nothing selected, can't validate, returning nothing." ); } return; } // check if a validator for this form was already created var validator = $.data( this[0], "validator" ); if ( validator ) { return validator; } // Add novalidate tag if HTML5. this.attr( "novalidate", "novalidate" ); validator = new $.validator( options, this[0] ); $.data( this[0], "validator", validator ); if ( validator.settings.onsubmit ) { this.validateDelegate( ":submit", "click", function( event ) { if ( validator.settings.submitHandler ) { validator.submitButton = event.target; } // allow suppressing validation by adding a cancel class to the submit button if ( $(event.target).hasClass("cancel") ) { validator.cancelSubmit = true; } // allow suppressing validation by adding the html5 formnovalidate attribute to the submit button if ( $(event.target).attr("formnovalidate") !== undefined ) { validator.cancelSubmit = true; } });
  8. // validate the form on submit this.submit( function( event )

    { if ( validator.settings.debug ) { // prevent form submit to be able to see console output event.preventDefault(); } function handle() { var hidden; if ( validator.settings.submitHandler ) { if ( validator.submitButton ) { // insert a hidden input as a replacement for the missing submit button hidden = $("<input type='hidden'/>").attr("name", validator.submitButton.name).val( $ (validator.submitButton).val() ).appendTo(validator.currentForm); } validator.settings.submitHandler.call( validator, validator.currentForm, event ); if ( validator.submitButton ) { // and clean up afterwards; thanks to no-block-scope, hidden can be referenced hidden.remove(); } return false; } return true; } // prevent submit for invalid forms or custom submit handlers if ( validator.cancelSubmit ) { validator.cancelSubmit = false; return handle(); } if ( validator.form() ) { if ( validator.pendingRequest ) { validator.formSubmitted = true; return false; } return handle(); } else { validator.focusInvalid(); return false; } }); } return validator; }
  9. function validate( options ) { // if nothing is selected,

    return nothing; can't chain anyway if ( !this.length ) { if ( options && options.debug && window.console ) { console.warn( "Nothing selected, can't validate, returning nothing." ); } return; } // check if a validator for this form was already created var validator = $.data( this[0], "validator" ); if ( validator ) { return validator; } // Add novalidate tag if HTML5. this.attr( "novalidate", "novalidate" ); validator = new $.validator( options, this[0] ); $.data( this[0], "validator", validator ); if ( validator.settings.onsubmit ) { this.validateDelegate( ":submit", "click", function( event ) { if ( validator.settings.submitHandler ) { validator.submitButton = event.target; } // allow suppressing validation by adding a cancel class to the submit button if ( $(event.target).hasClass("cancel") ) { validator.cancelSubmit = true; } // allow suppressing validation by adding the html5 formnovalidate attribute to the submit button if ( $(event.target).attr("formnovalidate") !== undefined ) { validator.cancelSubmit = true; } });
  10. // validate the form on submit this.submit( function( event )

    { if ( validator.settings.debug ) { // prevent form submit to be able to see console output event.preventDefault(); } function handle() { var hidden; if ( validator.settings.submitHandler ) { if ( validator.submitButton ) { // insert a hidden input as a replacement for the missing submit button hidden = $("<input type='hidden'/>").attr("name", validator.submitButton.name).val( $ (validator.submitButton).val() ).appendTo(validator.currentForm); } validator.settings.submitHandler.call( validator, validator.currentForm, event ); if ( validator.submitButton ) { // and clean up afterwards; thanks to no-block-scope, hidden can be referenced hidden.remove(); } return false; } return true; } // prevent submit for invalid forms or custom submit handlers if ( validator.cancelSubmit ) { validator.cancelSubmit = false; return handle(); } if ( validator.form() ) { if ( validator.pendingRequest ) { validator.formSubmitted = true; return false; } return handle(); } else { validator.focusInvalid(); return false; } }); } return validator; }
  11. _$ = (function(_) { return { pub: function(a, b, c,

    d) { for (d=-1, c=[].concat(_[a]); c[++d];) c[d](b) }, sub: function(a, b) { (_[a] || (_[a] = [])).push(b) } } })({})
  12. _$ = (function(_) { return { pub: function(a, b, c,

    d) { for (d=-1, c=[].concat(_[a]); c[++d];) c[d](b) }, sub: function(a, b) { (_[a] || (_[a] = [])).push(b) } } })({}) #140bytes
  13. _$ = (function() { var registered = {}; return {

    pub: function(event, memo) { if (registered[event] instanceof Array){ var handlers = [].concat(registered[event]); for (var i=0, handler; (handler = handlers[i]); i++){ handler.call(this, memo); } } }, sub: function(event, handler) { if (typeof registered[event] === "undefined"){ registered[event] = []; } registered[event].push(handler); } }; })();
  14. //used translate3d to trigger hardware acceleration in webViews //http://www.youtube.com/watch?v=IKl78ZgJzm4 .animated

    { translate: translate3d(0,0,0) } /** * Returns a unique ID for use in HTML id attribute. * @param {String/Number} nr A name or number of the ID. * @param {String} [prefix="id-"] The prefix for the ID. * @return {String} the new ID */ function createId(nr, prefix){ //TODO implementation }
  15. //used translate3d to trigger hardware acceleration in webViews //http://www.youtube.com/watch?v=IKl78ZgJzm4 .animated

    { translate: translate3d(0,0,0) } /** * Returns a unique ID for use in HTML id attribute. * @param {String/Number} nr A name or number of the ID. * @param {String} [prefix="id-"] The prefix for the ID. * @return {String} the new ID */ function createId(nr, prefix){ //TODO implementation } un-avoidable hacks explanation
  16. //used translate3d to trigger hardware acceleration in webViews //http://www.youtube.com/watch?v=IKl78ZgJzm4 .animated

    { translate: translate3d(0,0,0) } /** * Returns a unique ID for use in HTML id attribute. * @param {String/Number} nr A name or number of the ID. * @param {String} [prefix="id-"] The prefix for the ID. * @return {String} the new ID */ function createId(nr, prefix){ //TODO implementation } un-avoidable hacks explanation AUTOMATED DOC GENERATION
  17. //used translate3d to trigger hardware acceleration in webViews //http://www.youtube.com/watch?v=IKl78ZgJzm4 .animated

    { translate: translate3d(0,0,0) } /** * Returns a unique ID for use in HTML id attribute. * @param {String/Number} nr A name or number of the ID. * @param {String} [prefix="id-"] The prefix for the ID. * @return {String} the new ID */ function createId(nr, prefix){ //TODO implementation } un-avoidable hacks explanation AUTOMATED DOC GENERATION TODOs
  18. worst case smell Long methods Deep Level of Indentation Hard

    to tell what it does Lack of portability
  19. worst case smell Long methods Deep Level of Indentation Hard

    to tell what it does Lack of portability Hardcoded style/templating
  20. worst case smell Long methods Deep Level of Indentation Hard

    to tell what it does Lack of portability Hardcoded style/templating Logic block duplication
  21. worst case smell Long methods Deep Level of Indentation Hard

    to tell what it does Lack of portability Hardcoded style/templating Logic block duplication Callback hell
  22. QUARANTINE Most teams are trying to stop further spread only

    through quarantines. It's a good short-term solution, but it won't prevent long-term population loss. http://cdmx.it/quarantinequote
  23. “Don't leave "broken windows" (bad designs, wrong decisions, or poor

    code) unrepaired. Fix each one as soon as it is discovered.” Programming is insanely detail oriented, and perhaps this is why: if you're not on top of the details, the perception is that things are out of control, and it's only a matter of time before your project spins out of control. Maybe we should be sweating the small stuff. Jeff Atwood http://www.codinghorror.com/blog/2005/06/the-broken-window-theory.html The broken window
  24. Maybe we should be sweating the small stuff. Jeff Atwood

    http://www.codinghorror.com/blog/2005/06/the-broken-window-theory.html The broken window
  25. function Zombie(personName) { function do_something() { console.log(personName + " just

    ate a brain!"); } return { doSomethingZombiesDo: do_something }; } var adam = new Zombie("Adam"); adam.doSomethingZombiesDo();
  26. function Zombie(personName) { function do_something() { console.log(personName + " just

    ate a brain!"); } return { doSomethingZombiesDo: do_something }; } var adam = new Zombie("Adam"); adam.doSomethingZombiesDo();
  27. function Zombie(personName) { function do_something() { console.log(personName + " just

    ate a brain!"); } return { doSomethingZombiesDo: do_something }; } var adam = new Zombie("Adam"); adam.doSomethingZombiesDo();
  28. function Zombie(personName) { function do_something() { console.log(personName + " just

    ate a brain!"); } return { doSomethingZombiesDo: do_something }; } var adam = new Zombie("Adam"); adam.doSomethingZombiesDo();
  29. What to test Unit testing is supposed to test a

    single atomic “unit” of functionality without dependencies on anything else
  30. What to test Unit testing is supposed to test a

    single atomic “unit” of functionality without dependencies on anything else This is where you start to run into serious dependency problems due to the interrelation HTML and CSS
  31. What to test Unit testing is supposed to test a

    single atomic “unit” of functionality without dependencies on anything else This is where you start to run into serious dependency problems due to the interrelation HTML and CSS What do you test? Usually how the user interface responds to user input. Actually, the realm of functional testing
  32. Make it part of the process Do code review http://rosarioconsulting.net/inspiredtoeducate/?p=706

    http://powerbuilder.us/ http://malyn.edublogs.org/2011/10/16/process-tools-people/
  33. QA

  34. QA Crucial role in the process Quality should be your

    goal too Get help for functional test coverage not to screw up refactoring
  35. Devops The tough guy It could be hard to deal

    with Get help setting up the automated process
  36. Product owner The less interested in code itself Bring numbers,

    not theories Get help not wasting time, staying focused on functionalities
  37. Juniors Pair with them, code review their (and your) code

    Involve them during the whole process definition
  38. Juniors Pair with them, code review their (and your) code

    Involve them during the whole process definition Get help keeping things easy and accessible
  39. Lobbyists They will slow you down, your brain will be

    more prone to be eaten Redirect them to the product owner
  40. but

  41. “Without requirements or design, programming is the art of adding

    bugs to an empty text file” Louis Srygley Design for your goal
  42. sandbox.publish("error", { msg: "an error occured" }); core.register("errManager", function(sandbox){ return

    { init: function(){ sandbox.subscribe("error", function(err) { console.log(err.msg) }); } }; });
  43. sandbox.publish("error", { msg: "an error occured" }); core.register("errManager", function(sandbox){ return

    { init: function(){ sandbox.subscribe("error", function(err) { console.log(err.msg) }); } }; });
  44. sandbox.publish("error", { msg: "an error occured" }); core.register("errManager", function(sandbox){ return

    { init: function(){ sandbox.subscribe("error", function(err) { console.log(err.msg) }); } }; });
  45. sandbox.publish("error", { msg: "an error occured" }); core.register("errManager", function(sandbox){ return

    { init: function(){ sandbox.subscribe("error", function(err) { console.log(err.msg) }); } }; });
  46. sandbox.publish("error", { msg: "an error occured" }); core.register("errManager", function(sandbox){ return

    { init: function(){ sandbox.subscribe("error", function(err) { console.log(err.msg) }); } }; });
  47. sandbox.publish("error", { msg: "an error occured" }); core.register("errManager", function(sandbox){ return

    { init: function(){ sandbox.subscribe("error", function(err) { console.log(err.msg) }); } }; });
  48. sandbox.publish("error", { msg: "an error occured" }); core.register("errManager", function(sandbox){ return

    { init: function(){ sandbox.subscribe("error", function(err) { console.log(err.msg) }); } }; });
  49. sandbox.publish("error", { msg: "an error occured" }); core.register("errManager", function(sandbox){ return

    { init: function(){ sandbox.subscribe("error", function(err) { console.log(err.msg) }); } }; });
  50. “The key is to acknowledge from the start that you

    have no idea how this will grow. When you accept that you don’t know everything, you begin to design the system defensively.” Nicholas Zakas Overengineering?
  51. AMD

  52. <script src="jquery.min.js"></script> <script src="mustache.js"></script> <?php if ($env == "prod") :

    ?> <script src="my-code-bundle.js"></script> <?php else: ?> <script src="jquery.plugin_1.js"></script> <script src="jquery.plugin_2.js"></script> <script src="my-code_1.js"></script> <script src="my-code_2.js"></script> <script src="my-code_3.js"></script> <script src="my-code_4.js"></script> <script src="my-code_5.js"></script> <?php endif; ?>
  53. var MyNamespace = {}; MyNamespace.MyAwesomeLibrary = function() { //implementation };

    MyNamespace.AnotherCoolOne = function() { //implementation }; MyNamespace.SlightlyCrappyLibrary = function() { //implementation }; MyNamespace.BestLibEver = function() { //implementation };
  54. //app/config.js define([], function() { return { url: "http://whatever.it/is/", debug: true

    }; }); //app/config.js define({ url: "http://whatever.it/is/", debug: true });
  55. //app/config.js define([], function() { return { url: "http://whatever.it/is/", debug: true

    }; }); //app/config.js define({ url: "http://whatever.it/is/", debug: true });
  56. //app/config.js define([], function() { return { url: "http://whatever.it/is/", debug: true

    }; }); //app/config.js define({ url: "http://whatever.it/is/", debug: true });
  57. //app/myProduct.js define(["app/config"], function(config) { return function(id){ return { getProductUrl: function(){

    var prodPath = config.url + "product/" + id; if (config.debug){ console.log(prodPath) } return prodPath; } }; }; });
  58. //app/myProduct.js define(["app/config"], function(config) { return function(id){ return { getProductUrl: function(){

    var prodPath = config.url + "product/" + id; if (config.debug){ console.log(prodPath) } return prodPath; } }; }; });
  59. //app/myProduct.js define(["app/config"], function(config) { return function(id){ return { getProductUrl: function(){

    var prodPath = config.url + "product/" + id; if (config.debug){ console.log(prodPath) } return prodPath; } }; }; });
  60. //app/myProduct.js define(["app/config"], function(config) { return function(id){ return { getProductUrl: function(){

    var prodPath = config.url + "product/" + id; if (config.debug){ console.log(prodPath) } return prodPath; } }; }; });
  61. //app/myProduct.js define(["app/config"], function(config) { return function(id){ return { getProductUrl: function(){

    var prodPath = config.url + "product/" + id; if (config.debug){ console.log(prodPath) } return prodPath; } }; }; });
  62. //app/myProduct.js define(["app/config"], function(config) { return function(id){ return { getProductUrl: function(){

    var prodPath = config.url + "product/" + id; if (config.debug){ console.log(prodPath) } return prodPath; } }; }; });
  63. //app/myProduct.js define(["app/config"], function(config) { return function(id){ return { getProductUrl: function(){

    var prodPath = config.url + "product/" + id; if (config.debug){ console.log(prodPath) } return prodPath; } }; }; });
  64. //app/myProduct.js define(["app/config"], function(config) { return function(id){ return { getProductUrl: function(){

    var prodPath = config.url + "product/" + id; if (config.debug){ console.log(prodPath) } return prodPath; } }; }; });
  65. //app/myProduct.js define(["app/config"], function(config) { return function(id){ return { getProductUrl: function(){

    var prodPath = config.url + "product/" + id; if (config.debug){ console.log(prodPath) } return prodPath; } }; }; });
  66. <script data-main="app/main" src="require.js"></script> //app/main.js require(["jQuery", "app/myProduct"], function($, Product) { $(".product").on("click",

    function(){ var prodID = $(this).data("id"); var prod = new Product(prodID); document.location.href = prod.getProductUrl(); }) });
  67. <script data-main="app/main" src="require.js"></script> //app/main.js require(["jQuery", "app/myProduct"], function($, Product) { $(".product").on("click",

    function(){ var prodID = $(this).data("id"); var prod = new Product(prodID); document.location.href = prod.getProductUrl(); }) });
  68. <script data-main="app/main" src="require.js"></script> //app/main.js require(["jQuery", "app/myProduct"], function($, Product) { $(".product").on("click",

    function(){ var prodID = $(this).data("id"); var prod = new Product(prodID); document.location.href = prod.getProductUrl(); }) });
  69. <script data-main="app/main" src="require.js"></script> //app/main.js require(["jQuery", "app/myProduct"], function($, Product) { $(".product").on("click",

    function(){ var prodID = $(this).data("id"); var prod = new Product(prodID); document.location.href = prod.getProductUrl(); }) });
  70. <script data-main="app/main" src="require.js"></script> //app/main.js require(["jQuery", "app/myProduct"], function($, Product) { $(".product").on("click",

    function(){ var prodID = $(this).data("id"); var prod = new Product(prodID); document.location.href = prod.getProductUrl(); }) });
  71. define(function(){ 'use strict'; return function(sandbox){ //the logic of the module

    function doSomething(){ //do something } return { init: function(config){ //the initialization code sandbox.subscribe('myEventName', doSomething) }, destroy: function(){ //optional destroy method } }; }; });
  72. define(function(){ 'use strict'; return function(sandbox){ //the logic of the module

    function doSomething(){ //do something } return { init: function(config){ //the initialization code sandbox.subscribe('myEventName', doSomething) }, destroy: function(){ //optional destroy method } }; }; });
  73. define(function(){ 'use strict'; return function(sandbox){ //the logic of the module

    function doSomething(){ //do something } return { init: function(config){ //the initialization code sandbox.subscribe('myEventName', doSomething) }, destroy: function(){ //optional destroy method } }; }; });
  74. define(function(){ 'use strict'; return function(sandbox){ //the logic of the module

    function doSomething(){ //do something } return { init: function(config){ //the initialization code sandbox.subscribe('myEventName', doSomething) }, destroy: function(){ //optional destroy method } }; }; });
  75. define(function(){ 'use strict'; return function(sandbox){ //the logic of the module

    function doSomething(){ //do something } return { init: function(config){ //the initialization code sandbox.subscribe('myEventName', doSomething) }, destroy: function(){ //optional destroy method } }; }; });
  76. “If you want a happy ending, that depends, of course,

    on where you stop your story.” Orson Wells Happy ending