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.

3ca63d4e2f2be0ef47b841e63b564d18?s=128

Marco Cedaro

April 20, 2013
Tweet

Transcript

  1. TALK

  2. Zombie Code

  3. Zombie Code how to survive a Javascript Zombiecodeapocalypse

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

    Shazam I organize conferences with From The Front
  5. DISCLAIMER

  6. DISCLAIMER I’m strongly opinionated

  7. DISCLAIMER I’m strongly opinionated it’s a gift and a curse

  8. Basically Zombies?

  9. Basically Zombies?

  10. Zombies! “Brains, BRAINS, BRains, brains, BRAINS. BRaiNS, brains, Brains, BRAINS,

    BRains, brains, BRAINS. BRAINS, BRains, brains, BRAINS, brains.” Ryan Mecum
  11. ZOMBIE CODE?

  12. it’s not dead code http://alfasin.com/i-see-dead-code-homage-for-intellij-idea/

  13. How to identify Zombie CODE?

  14. What I can tell is..

  15. It may seems harmless http://couchpotatofiles.wordpress.com/2012/03/20/the-walking-dead-ups-the-death-count-and-the-ratings/

  16. http://couchpotatofiles.wordpress.com/2012/03/20/the-walking-dead-ups-the-death-count-and-the-ratings/ but it’s NOT

  17. and it will, eventually http://imgur.com/r/SRDBroke/JimqK CODE

  18. during estimation

  19. during debugging

  20. during development

  21. It is dumb code that makes you dumb as well

  22. Hopefully it’s not too late http://tacticaltshirts.com/shop/shirt-zombies-eat-brains/

  23. What's that smell? Zombies smell worse than anything you can

    imagine Lilith Saintcrow, Strange Angels
  24. TIp #1 Code should be appealing

  25. 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; }
  26. // 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); }
  27. 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; }
  28. HOW LONG IS THAT?

  29. 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; } });
  30. // 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; }
  31. 14 (FOURTEEN!) ifs

  32. 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; } });
  33. // 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; }
  34. are comments a bad thing?

  35. TIp #2 Code should talk to you

  36. _$ = (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) } } })({})
  37. _$ = (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
  38. _$ = (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); } }; })();
  39. don’t use comments as an excuse to write bad code

  40. //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 }
  41. //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
  42. //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
  43. //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
  44. TIp #3 Code should have boundaries

  45. Single responsibility principle your best tool against Zombie Code since1902

    (guaranteed 20 years)
  46. No global pollution http://leosabanganii.blogspot.co.uk/2012/10/zombie-dressed-activists-protest.html

  47. No coupling http://leosabanganii.blogspot.co.uk/2012/10/zombie-dressed-activists-protest.html http://ajandcharli.blogspot.co.uk/2011/05/we-dont-do-dead-people.html

  48. worst case smell

  49. worst case smell Long methods

  50. worst case smell Long methods Deep Level of Indentation

  51. worst case smell Long methods Deep Level of Indentation Hard

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

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

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

    to tell what it does Lack of portability Hardcoded style/templating Logic block duplication
  55. 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
  56. And now what?

  57. Play cool!

  58. Basically Quarantine

  59. Basically Quarantine

  60. 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
  61. The broken window

  62. “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
  63. 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
  64. Isolate the Zombies

  65. define style guidelines

  66. function Zombie(personName) { function do_something() { console.log(personName + " just

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

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

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

    ate a brain!"); } return { doSomethingZombiesDo: do_something }; } var adam = new Zombie("Adam"); adam.doSomethingZombiesDo();
  70. define style guidelines

  71. start linting your code

  72. Inversion of control freakness AM I A CONTROL FREAK?

  73. start testing your code

  74. Unit or Functional?

  75. Do both

  76. What to test Unit testing is supposed to test a

    single atomic “unit” of functionality without dependencies on anything else
  77. 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
  78. 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
  79. No matter which toolset Grunt PhantomJS JsTestDriver Buster.js Karma Chutzpah

    Testem Qunit Mocha Jasmine
  80. No matter which toolset Grunt PhantomJS JsTestDriver Buster.js Karma Chutzpah

    Testem Qunit Mocha Jasmine
  81. As long as it can be automated share identify build

    make it continuous
  82. Make it part of the process Make it part of

    the process
  83. Make it part of the process http://rosarioconsulting.net/inspiredtoeducate/?p=706 http://powerbuilder.us/ Estimate testing

    http://malyn.edublogs.org/2011/10/16/process-tools-people/
  84. 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/
  85. Make it part of the process http://rosarioconsulting.net/inspiredtoeducate/?p=706 http://powerbuilder.us/ Involve people

    http://malyn.edublogs.org/2011/10/16/process-tools-people/
  86. Fear the living? DON’T

  87. The team

  88. DEVOPS PRODUCT OWNER qa

  89. QA

  90. QA Crucial role in the process

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

    goal too
  92. QA Crucial role in the process Quality should be your

    goal too Get help for functional test coverage not to screw up refactoring
  93. Devops

  94. Devops The tough guy

  95. Devops The tough guy It could be hard to deal

    with
  96. Devops The tough guy It could be hard to deal

    with Get help setting up the automated process
  97. Product owner

  98. Product owner The less interested in code itself

  99. Product owner The less interested in code itself Bring numbers,

    not theories
  100. Product owner The less interested in code itself Bring numbers,

    not theories Get help not wasting time, staying focused on functionalities
  101. Others in the team

  102. juniors external lobbyist

  103. Juniors

  104. Juniors Pair with them, code review their (and your) code

  105. Juniors Pair with them, code review their (and your) code

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

    Involve them during the whole process definition Get help keeping things easy and accessible
  107. Lobbyists

  108. Lobbyists They will slow you down, your brain will be

    more prone to be eaten
  109. Lobbyists They will slow you down, your brain will be

    more prone to be eaten Redirect them to the product owner
  110. Basically KILL ‘EM ALL (AGAIN?)

  111. Basically KILL ‘EM ALL (AGAIN?)

  112. KILL ‘EM ALL (AGAIN?) “Nothing is impossible to kill.” Mira

    Grant, Feed
  113. but

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

    bugs to an empty text file” Louis Srygley Design for your goal
  115. Design for your goal

  116. Modular Architecture

  117. None
  118. Scalable JavaScript Application Architecture by Nicholas Zakas

  119. None
  120. None
  121. core.register("module-name", function(sandbox){ return { init:function(){ }, destroy:function(){ } }; });

  122. core.register("module-name", function(sandbox){ return { init:function(){ }, destroy:function(){ } }; });

  123. core.register("module-name", function(sandbox){ return { init:function(){ }, destroy:function(){ } }; });

  124. None
  125. None
  126. core.register("module-name", function(sandbox){ return { init:function(){ var user = sandbox.getUser(); },

    destroy:function(){ } }; });
  127. core.register("module-name", function(sandbox){ return { init:function(){ var user = sandbox.getUser(); },

    destroy:function(){ } }; });
  128. None
  129. core.register('module-name', function(sandbox){ return { init:function(config){ console.log(config.id); } }; }); core.configure('module-name',

    { id: 'container', }); core.start('module-name'); core.stop('module-name');
  130. core.register('module-name', function(sandbox){ return { init:function(config){ console.log(config.id); } }; }); core.configure('module-name',

    { id: 'container', }); core.start('module-name'); core.stop('module-name');
  131. core.register('module-name', function(sandbox){ return { init:function(config){ console.log(config.id); } }; }); core.configure('module-name',

    { id: 'container', }); core.start('module-name'); core.stop('module-name');
  132. core.register('module-name', function(sandbox){ return { init:function(config){ console.log(config.id); } }; }); core.configure('module-name',

    { id: 'container', }); core.start('module-name'); core.stop('module-name');
  133. core.register('module-name', function(sandbox){ return { init:function(config){ console.log(config.id); } }; }); core.configure('module-name',

    { id: 'container', }); core.start('module-name'); core.stop('module-name');
  134. core.register('module-name', function(sandbox){ return { init:function(config){ console.log(config.id); } }; }); core.configure('module-name',

    { id: 'container', }); core.start('module-name'); core.stop('module-name');
  135. None
  136. None
  137. Event Driven Pattern

  138. core.register("module-name", function(sandbox){ return { init: function(){ sandbox.layer("an error occured"); }

    }; });
  139. core.register("module-name", function(sandbox){ return { init: function(){ sandbox.layer("an error occured"); }

    }; });
  140. sandbox.layer("an error occured");

  141. sandbox.publish("error", { msg: "an error occured" });

  142. sandbox.publish("error", { msg: "an error occured" }); core.register("errManager", function(sandbox){ return

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

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

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

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

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

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

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

    { init: function(){ sandbox.subscribe("error", function(err) { console.log(err.msg) }); } }; });
  150. sandbox.subscribe("error", function(payload){ console.log(payload.msg); }); Advantages

  151. sandbox.subscribe("error", function(payload){ console.log(payload.msg); }); Advantages SEMANTIC

  152. sandbox.subscribe("error", function(payload){ console.log(payload.msg); }); Advantages SEMANTIC flexibility

  153. Advantages DECOUPLING

  154. “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?
  155. AMD

  156. icon by http://www.deleket.com/ jQuery Mustache Libraries Plugins Your scripts

  157. icon by http://www.deleket.com/ jQuery Mustache Libraries Plugins Your scripts

  158. <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; ?>
  159. var MyNamespace = {}; MyNamespace.MyAwesomeLibrary = function() { //implementation };

    MyNamespace.AnotherCoolOne = function() { //implementation }; MyNamespace.SlightlyCrappyLibrary = function() { //implementation }; MyNamespace.BestLibEver = function() { //implementation };
  160. //API: define(id?, dependencies?, factory); define("My-Module", ["Another-Module"], function(AnotherModule){ // Do Something

    }); one define to rule them all
  161. //API: define(id?, dependencies?, factory); define("My-Module", ["Another-Module"], function(AnotherModule){ // Do Something

    }); one define to rule them all
  162. //app/config.js define([], function() { return { url: "http://whatever.it/is/", debug: true

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

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

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

    }; }); //app/config.js define({ url: "http://whatever.it/is/", debug: true });
  166. //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; } }; }; });
  167. //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; } }; }; });
  168. //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; } }; }; });
  169. //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; } }; }; });
  170. //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; } }; }; });
  171. //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; } }; }; });
  172. //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; } }; }; });
  173. //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; } }; }; });
  174. //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; } }; }; });
  175. <script data-main="app/main" src="require.js"></script>

  176. <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(); }) });
  177. <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(); }) });
  178. <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(); }) });
  179. <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(); }) });
  180. <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(); }) });
  181. Pulling all together

  182. 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 } }; }; });
  183. 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 } }; }; });
  184. 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 } }; }; });
  185. 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 } }; }; });
  186. 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 } }; }; });
  187. require(["akase"], function(core) { core.start("module1"); core.start("module2", { config: { debug: true

    } }); core.start("module3", { event: "audio:stop" }); });
  188. require(["akase"], function(core) { core.start("module1"); core.start("module2", { config: { debug: true

    } }); core.start("module3", { event: "audio:stop" }); });
  189. require(["akase"], function(core) { core.start("module1"); core.start("module2", { config: { debug: true

    } }); core.start("module3", { event: "audio:stop" }); });
  190. require(["akase"], function(core) { core.start("module1"); core.start("module2", { config: { debug: true

    } }); core.start("module3", { event: "audio:stop" }); });
  191. require(["akase"], function(core) { core.start("module1"); core.start("module2", { config: { debug: true

    } }); core.start("module3", { event: "audio:stop" }); });
  192. ākāśe sanskrit for "in the sky"/"to the sky" https://github.com/cedmax/akase

  193. No such thing!

  194. Basically Happy Endings?

  195. Basically Happy Endings?

  196. “If you want a happy ending, that depends, of course,

    on where you stop your story.” Orson Wells Happy ending
  197. You can run, you can hide

  198. You are going to SHITTY code anyway write

  199. face You are going to SHITTY code anyway

  200. face You are going to Zombie code anyway

  201. Embrace it! http://drezner.foreignpolicy.com/posts/2009/08/18/theory_of_international_politics_and_zombies

  202. Don’t improvise

  203. Learn to know how to deal with it

  204. Until you master it

  205. in any case just remember to

  206. Aim for the head http://halloween.squidoo.com/get-spooked/aim-for-the-head

  207. None
  208. marco@fromthefront.it http://cedmax.com @cedmax any question?