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

Zombie Code - Dibi London 2016

Zombie Code - Dibi London 2016

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?

Marco has been there and he’s a survivor because he learnt the art of refactoring.
In this talk he’ll share lessons learnt about how to deal with features request, deadlines and still increase the maintainability of your code.

3ca63d4e2f2be0ef47b841e63b564d18?s=128

Marco Cedaro

October 21, 2016
Tweet

Transcript

  1. TALK

  2. Zombie Code London 21st October

  3. First things first my name is @cedmax

  4. cedmax @ Kahoot!

  5. Basically Zombies?

  6. Basically Zombies?

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

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

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

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

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

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

  13. during estimation

  14. during debugging

  15. during development

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

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

  18. There isn’t a recipe https://www.etsy.com/listing/224941057/zombie-cure-pill-case-7-sections-pill

  19. How to identify Zombie CODE?

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

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

  22. 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; }
  23. // 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); }
  24. 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; }
  25. - A very long method

  26. 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; }
  27. // 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); }
  28. 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; }
  29. - 14 (FOURTEEN!) if statements

  30. 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; }
  31. // 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); }
  32. 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; }
  33. - nested function definitions

  34. 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; }
  35. // 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); }
  36. 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; }
  37. - deep level of indentation

  38. 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; }
  39. // 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); }
  40. 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; }
  41. HOLD ON THERE!
 are comments a bad thing?

  42. TIp #2 Code should talk to you

  43. var PubSub = ((_) => ({ pub:(a, b, c, d)

    => { for (d=-1, c=[].concat(_[a]); c[++d];) c[d](b) }, sub:(a, b) => { (_[a] || (_[a] = [])).push(b) } }))({})
  44. var PubSub = ((_) => ({ pub:(a, b, c, d)

    => { for (d=-1, c=[].concat(_[a]); c[++d];) c[d](b) }, sub:(a, b) => { (_[a] || (_[a] = [])).push(b) } }))({}) #140bytes
  45. var PubSub = (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); } }; })();
  46. don’t use comments as an excuse to write bad code

  47. //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 }
  48. //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
  49. //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
  50. //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
  51. TIp #3 Code should have 
 personal boundaries

  52. Define 
 boundaries

  53. Single responsibility principle your best tool against Zombie Code

  54. code-sense is the key Writing clean code requires the disciplined

    use of a myriad little techniques applied through a painstakingly acquired sense of "cleanliness". Robert C. Martin
  55. worst case smell

  56. worst case smell Long methods

  57. worst case smell Long methods Deep Level of Indentation

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

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

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

    to tell what it does Lack of portability Copy/paste driven development
  61. worst case smell Long methods Deep Level of Indentation Hard

    to tell what it does Lack of portability Copy/paste driven development Callback hell
  62. And now what?

  63. Play cool!

  64. Basically Quarantine

  65. Basically Quarantine

  66. 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
  67. The broken window

  68. “Don't leave "broken windows" (bad designs, wrong decisions, or poor

    code) unrepaired. Fix each one as soon as it is discovered.” Jeff Atwood
 http://www.codinghorror.com/blog/2005/06/the-broken-window-theory.html The broken window
  69. “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.” Jeff Atwood
 http://www.codinghorror.com/blog/2005/06/the-broken-window-theory.html The broken window
  70. 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
  71. Isolate the Zombies

  72. define a styleguide http://alistapart.com/article/creating-style-guides

  73. …and enforce it

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

  75. start testing your code

  76. Both Unit or Functional

  77. As long as it can be automated share identify build

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

    the process
  79. 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/
  80. 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/
  81. 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/
  82. Fear the living? DON’T

  83. The team

  84. PRODUCT OWNER DevOPS qa

  85. Quality Assurance

  86. Quality Assurance Crucial role in the process

  87. Quality Assurance Crucial role in the process You share the

    same goals
  88. Quality Assurance Crucial role in the process You share the

    same goals Get help for functional tests, they are a drain!
  89. Devops

  90. Devops The tough one

  91. Devops The tough one It could be hard to deal

    with
  92. Devops The tough one It could be hard to deal

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

  94. Product owner The less interested in code itself

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

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

    not theories Get help not wasting time, staying focused on functionalities
  97. Others in the company

  98. juniors external lobbyist

  99. Juniors

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

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

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

    together Involve them during the whole process definition Get help keeping things easy, accessible and understandable
  103. Learn to say NO!

  104. Learn to say NO! Lobbyists will slow you down, your

    brain will be more prone to be eaten
  105. Learn to say NO! Lobbyists will slow you down, your

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

  107. Basically KILL ‘EM ALL (AGAIN?)

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

    Grant, Feed
  109. but

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

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

  112. Modular Architecture

  113. None
  114. Scalable JavaScript Application Architecture by Nicholas Zakas
 circa 2009

  115. None
  116. None
  117. core.register("module-name",(sandbox) => { init: function() { }, destroy: function() {

    } });
  118. core.register("module-name",(sandbox) => { init: function() { }, destroy: function() {

    } });
  119. core.register("module-name",(sandbox) => { init: function() { }, destroy: function() {

    } });
  120. None
  121. None
  122. core.register("module-name",(sandbox) => { init: function() { var user = sandbox.getUser();

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

    }, destroy: function() { } });
  124. None
  125. core.register(‘module-name', (sandbox) => { init: function(config) { console.log(config.id); } });

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

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

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

    core.configure('module-name', { id: 'container', }); core.start('module-name'); core.stop('module-name');
  129. core.register(‘module-name', (sandbox) => { 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', (sandbox) => { init: function(config) { console.log(config.id); } });

    core.configure('module-name', { id: 'container', }); core.start('module-name'); core.stop('module-name');
  131. None
  132. None
  133. Event Driven Pattern

  134. core.register("module-name", function(sandbox) => { init: function() { sandbox.layer("an error occured");

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

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

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

  138. sandbox.publish("error", { msg: "an error occured" }); core.register("errManager", function(sandbox) =>

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

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

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

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

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

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

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

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

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

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

  149. Advantages DECOUPLING

  150. Flux
 circa 2014

  151. Flux

  152. Flux I can walk you through it

  153. Flux A kind of usual feature

  154. Flux The user triggers an event…

  155. onFollowButtonClick(user) { actionCreators.followButtonClicked({ user }); } …and an event handler

    kicks in Flux
  156. onFollowButtonClick(user) { actionCreators.followButtonClicked({ user }); } …and an event handler

    kicks in Flux
  157. Flux Here comes the action

  158. followButtonClicked({ user }) { APIClient.followUser(user.uuid) .then(() => { dispatcher.handleAction({ type:

    actionTypes.userFollowed, data: { user } }); }); } Let’s have a look at the action creator Flux
  159. followButtonClicked({ user }) { APIClient.followUser(user.uuid) .then(() => { dispatcher.handleAction({ type:

    actionTypes.userFollowed, data: { user } }); }); } The created action has a type and a data prop Flux
  160. followButtonClicked({ user }) { APIClient.followUser(user.uuid) .then(() => { dispatcher.handleAction({ type:

    actionTypes.userFollowed, data: { user } }); }); } Where the type is an identifier Flux
  161. followButtonClicked({ user }) { APIClient.followUser(user.uuid) .then(() => { dispatcher.handleAction({ type:

    actionTypes.userFollowed, data: { user } }); }); } The action could depend on a network call Flux
  162. followButtonClicked({ user }) { APIClient.followUser(user.uuid) .then(() => { dispatcher.handleAction({ type:

    actionTypes.userFollowed, data: { user } }); }); } It gets sent to the dispatcher Flux
  163. Flux The dispatcher is where the magic happens

  164. Let’s dumb it down Flux followButtonClicked({ user }) { APIClient.followUser(user.uuid)

    .then(() => { dispatcher.handleAction({ type: actionTypes.userFollowed, data: { user } }); }); }
  165. Let’s dumb it down Flux followButtonClicked({ user }) { APIClient.followUser(user.uuid)

    .then(() => { dispatcher.handleAction({ type: actionTypes.userFollowed, data: { user } }); }); }
  166. Doesn’t it look like… Flux dispatcher.handleAction({ type: actionTypes.userFollowed, data: {

    user } });
  167. a pub sub! Flux dispatcher.handleAction({ type: actionTypes.userFollowed, data: { user

    } });
 
 pubSub.publish( actionTypes.userFollowed, { user } );
  168. Flux dispatcher.handleAction({ type: actionTypes.userFollowed, data: { user } });
 


    pubSub.publish( actionTypes.userFollowed, { user } ); As a matter of fact it is
  169. Flux And the store is subscribed to it

  170. Flux The store is where your business logic lives

  171. export default { [actionTypes.userFollowed]: function({ data }) {
 //MAGIC to

    define the new followers list
 this.updateState({ user: { followers } });
 this.emitChange(); } } Please test your magic Flux
  172. export default { [actionTypes.userFollowed]: function({ data }) {
 //MAGIC to

    define the new followers list
 this.updateState({ user: { followers } });
 this.emitChange(); } } The store updates the model… Flux
  173. export default { [actionTypes.userFollowed]: function({ data }) {
 //MAGIC to

    define the new followers list
 this.updateState({ user: { followers } });
 this.emitChange(); } } …and it emits the change Flux
  174. Which would trigger the view to update Flux

  175. Want to unfollow? Flux

  176. Here we go again Flux

  177. “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?
  178. No such thing!

  179. Basically Happy Endings?

  180. Basically Happy Endings?

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

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

  183. You are going to SHITTY code anyway write

  184. You are going to write Zombie code anyway

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

  186. Don’t improvise

  187. Learn how to deal with it

  188. Until you master it

  189. Just remember to

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

  191. None
  192. marco@fromthefront.it http://cedmax.com @cedmax any question?