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.

Marco Cedaro

October 21, 2016
Tweet

More Decks by Marco Cedaro

Other Decks in Technology

Transcript

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

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

    imagine Lilith Saintcrow, Strange Angels
  3. 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; }
  4. // 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); }
  5. 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; }
  6. 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; }
  7. // 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); }
  8. 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; }
  10. // 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); }
  11. 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; }
  12. 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; }
  13. // 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); }
  14. 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; }
  15. 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; }
  16. // 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); }
  17. 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; }
  18. 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) } }))({})
  19. 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
  20. 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); } }; })();
  21. //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 }
  22. //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
  23. //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
  24. //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
  25. 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
  26. worst case smell Long methods Deep Level of Indentation Hard

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

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

    to tell what it does Lack of portability Copy/paste driven development Callback hell
  29. 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
  30. “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
  31. “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
  32. 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
  33. 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/
  34. Quality Assurance Crucial role in the process You share the

    same goals Get help for functional tests, they are a drain!
  35. Devops The tough one 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

    together Involve them during the whole process definition
  38. 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
  39. Learn to say NO! Lobbyists will slow you down, your

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

    brain will be more prone to be eaten Redirect them to the product owner
  41. but

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

    bugs to an empty text file” Louis Srygley Design for your goal
  43. 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');
  44. 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');
  45. 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');
  46. 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');
  47. 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');
  48. 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');
  49. sandbox.publish("error", { msg: "an error occured" }); core.register("errManager", function(sandbox) =>

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

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

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

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

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

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

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

    { init: function() { sandbox.subscribe("error", function(err) { console.log(err.msg) } }); });
  57. followButtonClicked({ user }) { APIClient.followUser(user.uuid) .then(() => { dispatcher.handleAction({ type:

    actionTypes.userFollowed, data: { user } }); }); } Let’s have a look at the action creator Flux
  58. 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
  59. followButtonClicked({ user }) { APIClient.followUser(user.uuid) .then(() => { dispatcher.handleAction({ type:

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

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

    actionTypes.userFollowed, data: { user } }); }); } It gets sent to the dispatcher Flux
  62. Let’s dumb it down Flux followButtonClicked({ user }) { APIClient.followUser(user.uuid)

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

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

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


    pubSub.publish( actionTypes.userFollowed, { user } ); As a matter of fact it is
  66. export default { [actionTypes.userFollowed]: function({ data }) {
 //MAGIC to

    define the new followers list
 this.updateState({ user: { followers } });
 this.emitChange(); } } Please test your magic Flux
  67. 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
  68. 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
  69. “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?
  70. “If you want a happy ending, that depends, of course,

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