SlideShare a Scribd company logo
1 of 74
Download to read offline
Writing
Maintainable
 JavaScript


                   Andrew Dupont
               http://andrewdupont.net
I help maintain these.
I write ugly JavaScript all the time.
I work here.
We write ugly JavaScript all the time.
“What’s the problem?”
A JavaScript codebase
gets uglier as it grows.
Day 1



$("p.neat").addClass("ohmy").show("slow");
Day 31
var trip = Gowalla.trip;
$.each(trip.spots, function(i, spot) {
  var marker = new GMarker(
    new GLatLng(spot.lat, spot.lng), {
       icon: Gowalla.createLetterIcon(i),
       title: h(spot.name)
     }
  );
  GEvent.addListener(marker, "click", function() {
       marker.openInfoWindowHtml('<div class="map-bubble"><img src="' +
        spot.image_url + '" width="50" height="50" /><b><a href="' +
        spot.url + '" style="color: #37451e;">' + h(spot.name) +
        '</a></b></div>');
       return false;
  });
  Gowalla.map.addOverlay(marker);
});
Gowalla.zoomAndCenter(trip.spots);
Day 90
options = options || {};
var params = this.getSearchParams(options);
Paginator.currentPage = 1;
Paginator.handler = Gowalla.displaySpots;
Paginator.paginate('/spots', params);
if (Gowalla.filterOptions["l"] || Gowalla.filterOptions["sw"] ||
  Gowalla.filterOptions["lat"]) {
   $('#map-wrapper').show();
   $('#spots_search_l').removeClass('off');
   if (options.l) $('#spots_search_l').val(unescape(options.l));
} else {
   $('#map-wrapper').hide();
}
if (Gowalla.mapVisible()) $('#map-placeholder').show();
$('#heading').hide();
$('#featured_spots').hide();
$('#new_spots').hide();
$.getJSON('/spots', this.getSearchParams(options), function(spots) {
   if (spots.length > 0) {
     $('.paging').show();
     $('#filter').show();
     $('#results').show();
     $('#map-placeholder').hide();
     if (Gowalla.mapVisible() && !Gowalla.map) {
       $('#map-placeholder').addClass("transparent");
       Gowalla.createMap();
       GEvent.addListener(Gowalla.map, "dragend", function() {
         var sw = this.getBounds().getSouthWest().toString();
         var ne = this.getBounds().getNorthEast().toString();
         Gowalla.searchSpots({sw:sw, ne:ne, limit:'150'});
       });
     }
   }
   Gowalla.displaySpots(spots);
});
Ugliness of Code over Time




                             (Source: gut feeling)
design patterns
    recipes
     ideas
The solution:
Use existing so ware principles
    to make your codebase
      more maintainable.
Wishes:
WISH #1:
Code that accomplishes a single task
should all live together in one place.
WISH #2:
We should be able to rewrite a component
   without affecting things elsewhere.
WISH #3:
Troubleshooting should be somewhat easy
  even if you’re unfamiliar with the code.
Plan of attack
WISH:
    Code that accomplishes a single task
    should all live together in one place.



             THEREFORE:
Divide your codebase into components,
      placing each in its own file.
“What’s a component?”
WISH:
     We should be able to rewrite a component
       without breaking things elsewhere.



                THEREFORE:
  A component should be whatever size is
necessary to isolate its details from other code.
A “component” is
   something you could
    rewrite from scratch
without affecting other stuff.
Law of Demeter:
“Each unit should have
only limited knowledge
  about other units.”
The fewer “friends”
    a component has,
the less it will be affected
 by changes elsewhere.
Gowalla.Location
handles all client-side geolocation.



 Gowalla.Location.getLocation();
 //=> [30.26800, -97.74283]

 Gowalla.Location.getLocality();
 //=> "Austin, TX"
Gowalla.ActivityFeed
handles all feeds of user activity.
Gowalla.Flash
            handles the display of
          transient status messages.

Gowalla.Flash.success("Your settings were updated.");
Gowalla.Map
handles all interaction
  with Google Maps.
Example: Gowalla.Map

function addSpotsToMap(spots) {
  Gowalla.Map.clearSpots();
  $.each(spots, function(i, spot) {
    Gowalla.Map.addSpot(spot);
  });
}
Example: Gowalla.Map

function addSpotsToMap(spots) {
  Gowalla.Map.clearSpots();
  $.each(spots, function(i, spot) {
    Gowalla.Map.addSpot(spot, { infoWindow: true });
  });
}
WISH:
We should be able to rewrite a component
  without breaking things elsewhere.



           THEREFORE:
  We should standardize the way
  components talk to one another.
Have components communicate
 through a central message bus.
       (“custom events”)
Publisher and subscriber
  don’t need to know
  about one another.
Instead, they only know about
    a central event broker.
WISH:
Troubleshooting should be somewhat easy
  even if you’re unfamiliar with the code.



            THEREFORE:
        Embrace conventions.
“Files are named according to
     their module names.”
“Componets have a
standard way of initializing.”
“Why custom events?”
Every major framework
      has them:
jQuery


$(document).bind('customevent', function(event, data) {
  // stuff
});

$('#troz').trigger('customevent', [someAssociatedData]);
Prototype

$(document).observe('custom:event', function(event) {
  var customData = event.memo;
  // stuff
});

$('troz').fire('custom:event', { foo: "bar" });
Dojo
                 (“pub-sub”)


dojo.subscribe('some-event', function(data) {
  // stuff
});

dojo.publish('some-event', someData);
A custom event is an interface that
publisher and subscriber adhere to.
As long as the interface
remains the same, either part
  can be safely rewritten.
“So I should replace
all my method calls
with custom events?
    Fat chance.”
A consistent public API
  is also an interface.
It’s OK for a subscriber
to call methods on a broadcaster,
         but not vice-versa.
Example: script.aculo.us 2.0
The auto-completer knows
    about the menu…

   var menu = new S2.UI.Menu();
   menu.addChoice("Foo");
   menu.addChoice("Bar");
   someElement.insert(menu);
   menu.open();
…but the menu doesn’t know
        about the auto-completer

menu.observe('ui:menu:selected', function(event) {
  console.log('user clicked on:', event.memo.element);
});
“What does a rewrite
    look like?”
Instead of:
function showNearbySpotsInMenu() {
  $.ajax({
    url: '/spots',
    params: { lat: someLat, lng: someLng },
    success: function(spots) {
      var html = $.map(spots, function(spot) {
        return '<li id="spot-"' + spot.id + '>' + spot.name + '</li>';
      });
      $('#spot_menu').html(html.join(''));
    }
  });
}
Do this:
function getNearbySpotsFromServer(lat, lng) {
  $.ajax({
    url: '/spots',
    params: { lat: lat, lng: lng },
    success: function(spots) {
      $(document).trigger('nearby-spots-received', [spots]);
    }
  });
}
And this:
function renderNearbySpots(event, spots) {
  var html = $.map(spots, function(spot) {
    return '<li id="spot-"' + spot.id + '>' + spot.name + '</li>';
  });
  $('#spot_menu').html(html.join(''));
}

$(document).bind('nearby-spots-received', renderNearbySpots);
Or, if you prefer…
function getNearbySpotsFromServer(lat, lng) {
  $.ajax({
    url: '/spots',
    params: { lat: lat, lng: lng },
    success: function(spots) {
      renderNearbySpots(spots);
    }
  });
}

function renderNearbySpots(spots) {
  var html = $.map(spots, function(spot) {
    return '<li id="spot-"' + spot.id + '>' + spot.name + '</li>';
  });
  $('#spot_menu').html(html.join(''));
}
Intra-module organization
(divide code up according to job)
A formal “contract”
Easier testing


function testNearbySpotsRendering() {
  renderNearbySpots(Fixtures.NEARBY_SPOTS);
  assertEqual($('#spot_menu > li').length, 3);
}
“What if it’s not enough?”
More complex web apps might need
    desktop-like architectures.
“Single-page apps” have
a few common characteristics:
maintaining data objects on
the client side, instead of expecting
   the server to do all the work;
creating views on the client side
and mapping them to data objects;
use of the URL hash for routing/permalinking
      (or HTML5 history management).
Is this MVC?
  Perhaps.
Backbone
http://documentcloud.github.com/backbone/
Models

                       define a model class     window.Todo = Backbone.Model.extend({
                                                 EMPTY: "new todo...",

property access wrapped in set/get methods      initialize: function() {
                                                   if (!this.get('content'))
                                                     this.set({ 'content': this.EMPTY });
                                                },

                                                toggle: function() {
                                                  this.set({ done: !this.get('done') });
                                                },

          triggered when the object is saved    validate: function(attributes) {
                                                   if (!attributes.content.test(/S/))
                                                     return "content can't be empty";
                                                },

                                                 // ...
                                               });
Views

                              define a view class    window.Todo.View = Backbone.View.extend({
                                                      tagName: 'li',

                bind events to pieces of the view    events: {
                                                       'dblclick div.todo-content' : 'edit',
                                                       'keypress .todo-input'      : 'updateOnEnter'
                                                     },

                                                     initialize: function() {
map to a model object; re-render when it changes       this.model.bind('change', this.render);
                                                     },

                          set the view’s contents    render: function() {
                                                       // ...
                                                     },

                                                      // ...
                                                    });
Synchronization

                                                 Backbone.sync = function(method, model, yes, no) {
determine the HTTP verb to use for this action     var type = methodMap[method];

                  serialize the object to JSON        var json = JSON.stringify(model.toJSON());

                   send the data to the server        $.ajax({
                                                        url: getUrl(model),
                                                        type: type,
                                                        data: json,
                                                        processData: false,
                                                        contentType: 'application/json',
                                                        dataType: 'json',
                                                        success: yes,
                                                        error: no
                                                      });
                                                 };
Other options:
      SproutCore
 (http://sproutcore.com/)


      Cappuccino
  (http://cappuccino.org/)


    JavaScriptMVC
(http://javascriptmvc.com/)
“Great. How do I start?”
Don’t do a
Grand Rewrite™
One strategy:
Write new code to conform to your architecture.
Improve old code little by little as you revisit it.
Maintainability
is not all-or-nothing.
Questions?



✍   PLEASE FILL OUT
    AN EVALUATION FORM
                                     Andrew Dupont
                            http://andrewdupont.net

More Related Content

What's hot

Using Objects to Organize your jQuery Code
Using Objects to Organize your jQuery CodeUsing Objects to Organize your jQuery Code
Using Objects to Organize your jQuery CodeRebecca Murphey
 
Delivering a Responsive UI
Delivering a Responsive UIDelivering a Responsive UI
Delivering a Responsive UIRebecca Murphey
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICKonstantin Kudryashov
 
jQuery Data Manipulate API - A source code dissecting journey
jQuery Data Manipulate API - A source code dissecting journeyjQuery Data Manipulate API - A source code dissecting journey
jQuery Data Manipulate API - A source code dissecting journeyHuiyi Yan
 
Advanced jQuery
Advanced jQueryAdvanced jQuery
Advanced jQuerysergioafp
 
International News | World News
International News | World NewsInternational News | World News
International News | World Newscojocarujanosko
 
Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDAleix Vergés
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patternsSamuel ROZE
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Domenic Denicola
 
Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form componentSamuel ROZE
 
06 jQuery #burningkeyboards
06 jQuery  #burningkeyboards06 jQuery  #burningkeyboards
06 jQuery #burningkeyboardsDenis Ristic
 
Introduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingIntroduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingSamuel ROZE
 
前端MVC 豆瓣说
前端MVC 豆瓣说前端MVC 豆瓣说
前端MVC 豆瓣说Ting Lv
 
05 JavaScript #burningkeyboards
05 JavaScript #burningkeyboards05 JavaScript #burningkeyboards
05 JavaScript #burningkeyboardsDenis Ristic
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony AppsKris Wallsmith
 
A evolução da persistência de dados (com sqlite) no android
A evolução da persistência de dados (com sqlite) no androidA evolução da persistência de dados (com sqlite) no android
A evolução da persistência de dados (com sqlite) no androidRodrigo de Souza Castro
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony AppsKris Wallsmith
 
¿Cómo de sexy puede hacer Backbone mi código?
¿Cómo de sexy puede hacer Backbone mi código?¿Cómo de sexy puede hacer Backbone mi código?
¿Cómo de sexy puede hacer Backbone mi código?jaespinmora
 

What's hot (20)

Using Objects to Organize your jQuery Code
Using Objects to Organize your jQuery CodeUsing Objects to Organize your jQuery Code
Using Objects to Organize your jQuery Code
 
Delivering a Responsive UI
Delivering a Responsive UIDelivering a Responsive UI
Delivering a Responsive UI
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DIC
 
jQuery Data Manipulate API - A source code dissecting journey
jQuery Data Manipulate API - A source code dissecting journeyjQuery Data Manipulate API - A source code dissecting journey
jQuery Data Manipulate API - A source code dissecting journey
 
Advanced jQuery
Advanced jQueryAdvanced jQuery
Advanced jQuery
 
International News | World News
International News | World NewsInternational News | World News
International News | World News
 
Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDD
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patterns
 
Min-Maxing Software Costs
Min-Maxing Software CostsMin-Maxing Software Costs
Min-Maxing Software Costs
 
Backbone Basics with Examples
Backbone Basics with ExamplesBackbone Basics with Examples
Backbone Basics with Examples
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
 
Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form component
 
06 jQuery #burningkeyboards
06 jQuery  #burningkeyboards06 jQuery  #burningkeyboards
06 jQuery #burningkeyboards
 
Introduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingIntroduction to CQRS and Event Sourcing
Introduction to CQRS and Event Sourcing
 
前端MVC 豆瓣说
前端MVC 豆瓣说前端MVC 豆瓣说
前端MVC 豆瓣说
 
05 JavaScript #burningkeyboards
05 JavaScript #burningkeyboards05 JavaScript #burningkeyboards
05 JavaScript #burningkeyboards
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
A evolução da persistência de dados (com sqlite) no android
A evolução da persistência de dados (com sqlite) no androidA evolução da persistência de dados (com sqlite) no android
A evolução da persistência de dados (com sqlite) no android
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
¿Cómo de sexy puede hacer Backbone mi código?
¿Cómo de sexy puede hacer Backbone mi código?¿Cómo de sexy puede hacer Backbone mi código?
¿Cómo de sexy puede hacer Backbone mi código?
 

Viewers also liked

An Introduction to Ajax Programming
An Introduction to Ajax ProgrammingAn Introduction to Ajax Programming
An Introduction to Ajax Programminghchen1
 
Ajax Introduction Presentation
Ajax   Introduction   PresentationAjax   Introduction   Presentation
Ajax Introduction Presentationthinkphp
 
Ajax ppt - 32 slides
Ajax ppt - 32 slidesAjax ppt - 32 slides
Ajax ppt - 32 slidesSmithss25
 

Viewers also liked (6)

An Introduction to Ajax Programming
An Introduction to Ajax ProgrammingAn Introduction to Ajax Programming
An Introduction to Ajax Programming
 
Introduction to ajax
Introduction to ajaxIntroduction to ajax
Introduction to ajax
 
Ajax Introduction Presentation
Ajax   Introduction   PresentationAjax   Introduction   Presentation
Ajax Introduction Presentation
 
Ajax.ppt
Ajax.pptAjax.ppt
Ajax.ppt
 
Ajax Ppt 1
Ajax Ppt 1Ajax Ppt 1
Ajax Ppt 1
 
Ajax ppt - 32 slides
Ajax ppt - 32 slidesAjax ppt - 32 slides
Ajax ppt - 32 slides
 

Similar to Writing Maintainable JavaScript

Single page webapps & javascript-testing
Single page webapps & javascript-testingSingle page webapps & javascript-testing
Single page webapps & javascript-testingsmontanari
 
Javascript Frameworks for Joomla
Javascript Frameworks for JoomlaJavascript Frameworks for Joomla
Javascript Frameworks for JoomlaLuke Summerfield
 
Taming that client side mess with Backbone.js
Taming that client side mess with Backbone.jsTaming that client side mess with Backbone.js
Taming that client side mess with Backbone.jsJarod Ferguson
 
Jquery optimization-tips
Jquery optimization-tipsJquery optimization-tips
Jquery optimization-tipsanubavam-techkt
 
international PHP2011_Bastian Feder_jQuery's Secrets
international PHP2011_Bastian Feder_jQuery's Secretsinternational PHP2011_Bastian Feder_jQuery's Secrets
international PHP2011_Bastian Feder_jQuery's Secretssmueller_sandsmedia
 
Backbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVCBackbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVCpootsbook
 
Javascript: the important bits
Javascript: the important bitsJavascript: the important bits
Javascript: the important bitsChris Saylor
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksMongoDB
 
Object-Oriented Javascript
Object-Oriented JavascriptObject-Oriented Javascript
Object-Oriented Javascriptkvangork
 
Object-Oriented JavaScript
Object-Oriented JavaScriptObject-Oriented JavaScript
Object-Oriented JavaScriptkvangork
 
Ten useful JavaScript tips & best practices
Ten useful JavaScript tips & best practicesTen useful JavaScript tips & best practices
Ten useful JavaScript tips & best practicesAnkit Rastogi
 
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Tsuyoshi Yamamoto
 
kissy-past-now-future
kissy-past-now-futurekissy-past-now-future
kissy-past-now-futureyiming he
 
KISSY 的昨天、今天与明天
KISSY 的昨天、今天与明天KISSY 的昨天、今天与明天
KISSY 的昨天、今天与明天tblanlan
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing UpDavid Padbury
 

Similar to Writing Maintainable JavaScript (20)

Single page webapps & javascript-testing
Single page webapps & javascript-testingSingle page webapps & javascript-testing
Single page webapps & javascript-testing
 
Javascript Frameworks for Joomla
Javascript Frameworks for JoomlaJavascript Frameworks for Joomla
Javascript Frameworks for Joomla
 
Taming that client side mess with Backbone.js
Taming that client side mess with Backbone.jsTaming that client side mess with Backbone.js
Taming that client side mess with Backbone.js
 
jQuery secrets
jQuery secretsjQuery secrets
jQuery secrets
 
Modular and Event-Driven JavaScript
Modular and Event-Driven JavaScriptModular and Event-Driven JavaScript
Modular and Event-Driven JavaScript
 
jQuery
jQueryjQuery
jQuery
 
Jquery optimization-tips
Jquery optimization-tipsJquery optimization-tips
Jquery optimization-tips
 
international PHP2011_Bastian Feder_jQuery's Secrets
international PHP2011_Bastian Feder_jQuery's Secretsinternational PHP2011_Bastian Feder_jQuery's Secrets
international PHP2011_Bastian Feder_jQuery's Secrets
 
Backbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVCBackbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVC
 
Javascript: the important bits
Javascript: the important bitsJavascript: the important bits
Javascript: the important bits
 
jQuery secrets
jQuery secretsjQuery secrets
jQuery secrets
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
 
Object-Oriented Javascript
Object-Oriented JavascriptObject-Oriented Javascript
Object-Oriented Javascript
 
Object-Oriented JavaScript
Object-Oriented JavaScriptObject-Oriented JavaScript
Object-Oriented JavaScript
 
jQuery: Events, Animation, Ajax
jQuery: Events, Animation, AjaxjQuery: Events, Animation, Ajax
jQuery: Events, Animation, Ajax
 
Ten useful JavaScript tips & best practices
Ten useful JavaScript tips & best practicesTen useful JavaScript tips & best practices
Ten useful JavaScript tips & best practices
 
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
 
kissy-past-now-future
kissy-past-now-futurekissy-past-now-future
kissy-past-now-future
 
KISSY 的昨天、今天与明天
KISSY 的昨天、今天与明天KISSY 的昨天、今天与明天
KISSY 的昨天、今天与明天
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing Up
 

More from Andrew Dupont

How to Argue about Code
How to Argue about CodeHow to Argue about Code
How to Argue about CodeAndrew Dupont
 
How to Argue about JavaScript
How to Argue about JavaScriptHow to Argue about JavaScript
How to Argue about JavaScriptAndrew Dupont
 
Everything is Permitted: Extending Built-ins
Everything is Permitted: Extending Built-insEverything is Permitted: Extending Built-ins
Everything is Permitted: Extending Built-insAndrew Dupont
 
Open Government: An Overview
Open Government: An OverviewOpen Government: An Overview
Open Government: An OverviewAndrew Dupont
 
Defensive, Cross-Browser Coding with Prototype
Defensive, Cross-Browser Coding with PrototypeDefensive, Cross-Browser Coding with Prototype
Defensive, Cross-Browser Coding with PrototypeAndrew Dupont
 

More from Andrew Dupont (6)

Learning new words
Learning new wordsLearning new words
Learning new words
 
How to Argue about Code
How to Argue about CodeHow to Argue about Code
How to Argue about Code
 
How to Argue about JavaScript
How to Argue about JavaScriptHow to Argue about JavaScript
How to Argue about JavaScript
 
Everything is Permitted: Extending Built-ins
Everything is Permitted: Extending Built-insEverything is Permitted: Extending Built-ins
Everything is Permitted: Extending Built-ins
 
Open Government: An Overview
Open Government: An OverviewOpen Government: An Overview
Open Government: An Overview
 
Defensive, Cross-Browser Coding with Prototype
Defensive, Cross-Browser Coding with PrototypeDefensive, Cross-Browser Coding with Prototype
Defensive, Cross-Browser Coding with Prototype
 

Recently uploaded

Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfPrecisely
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionDilum Bandara
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxBkGupta21
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 

Recently uploaded (20)

Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An Introduction
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptx
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 

Writing Maintainable JavaScript

  • 1. Writing Maintainable JavaScript Andrew Dupont http://andrewdupont.net
  • 2. I help maintain these. I write ugly JavaScript all the time.
  • 3. I work here. We write ugly JavaScript all the time.
  • 5. A JavaScript codebase gets uglier as it grows.
  • 7. Day 31 var trip = Gowalla.trip; $.each(trip.spots, function(i, spot) { var marker = new GMarker( new GLatLng(spot.lat, spot.lng), { icon: Gowalla.createLetterIcon(i), title: h(spot.name) } ); GEvent.addListener(marker, "click", function() { marker.openInfoWindowHtml('<div class="map-bubble"><img src="' + spot.image_url + '" width="50" height="50" /><b><a href="' + spot.url + '" style="color: #37451e;">' + h(spot.name) + '</a></b></div>'); return false; }); Gowalla.map.addOverlay(marker); }); Gowalla.zoomAndCenter(trip.spots);
  • 8. Day 90 options = options || {}; var params = this.getSearchParams(options); Paginator.currentPage = 1; Paginator.handler = Gowalla.displaySpots; Paginator.paginate('/spots', params); if (Gowalla.filterOptions["l"] || Gowalla.filterOptions["sw"] || Gowalla.filterOptions["lat"]) { $('#map-wrapper').show(); $('#spots_search_l').removeClass('off'); if (options.l) $('#spots_search_l').val(unescape(options.l)); } else { $('#map-wrapper').hide(); } if (Gowalla.mapVisible()) $('#map-placeholder').show(); $('#heading').hide(); $('#featured_spots').hide(); $('#new_spots').hide(); $.getJSON('/spots', this.getSearchParams(options), function(spots) { if (spots.length > 0) { $('.paging').show(); $('#filter').show(); $('#results').show(); $('#map-placeholder').hide(); if (Gowalla.mapVisible() && !Gowalla.map) { $('#map-placeholder').addClass("transparent"); Gowalla.createMap(); GEvent.addListener(Gowalla.map, "dragend", function() { var sw = this.getBounds().getSouthWest().toString(); var ne = this.getBounds().getNorthEast().toString(); Gowalla.searchSpots({sw:sw, ne:ne, limit:'150'}); }); } } Gowalla.displaySpots(spots); });
  • 9. Ugliness of Code over Time (Source: gut feeling)
  • 10. design patterns recipes ideas
  • 11. The solution: Use existing so ware principles to make your codebase more maintainable.
  • 13. WISH #1: Code that accomplishes a single task should all live together in one place.
  • 14. WISH #2: We should be able to rewrite a component without affecting things elsewhere.
  • 15. WISH #3: Troubleshooting should be somewhat easy even if you’re unfamiliar with the code.
  • 17. WISH: Code that accomplishes a single task should all live together in one place. THEREFORE: Divide your codebase into components, placing each in its own file.
  • 19. WISH: We should be able to rewrite a component without breaking things elsewhere. THEREFORE: A component should be whatever size is necessary to isolate its details from other code.
  • 20. A “component” is something you could rewrite from scratch without affecting other stuff.
  • 21. Law of Demeter: “Each unit should have only limited knowledge about other units.”
  • 22. The fewer “friends” a component has, the less it will be affected by changes elsewhere.
  • 23. Gowalla.Location handles all client-side geolocation. Gowalla.Location.getLocation(); //=> [30.26800, -97.74283] Gowalla.Location.getLocality(); //=> "Austin, TX"
  • 25. Gowalla.Flash handles the display of transient status messages. Gowalla.Flash.success("Your settings were updated.");
  • 27. Example: Gowalla.Map function addSpotsToMap(spots) { Gowalla.Map.clearSpots(); $.each(spots, function(i, spot) { Gowalla.Map.addSpot(spot); }); }
  • 28. Example: Gowalla.Map function addSpotsToMap(spots) { Gowalla.Map.clearSpots(); $.each(spots, function(i, spot) { Gowalla.Map.addSpot(spot, { infoWindow: true }); }); }
  • 29. WISH: We should be able to rewrite a component without breaking things elsewhere. THEREFORE: We should standardize the way components talk to one another.
  • 30. Have components communicate through a central message bus. (“custom events”)
  • 31. Publisher and subscriber don’t need to know about one another.
  • 32. Instead, they only know about a central event broker.
  • 33. WISH: Troubleshooting should be somewhat easy even if you’re unfamiliar with the code. THEREFORE: Embrace conventions.
  • 34. “Files are named according to their module names.”
  • 35. “Componets have a standard way of initializing.”
  • 38. jQuery $(document).bind('customevent', function(event, data) { // stuff }); $('#troz').trigger('customevent', [someAssociatedData]);
  • 39. Prototype $(document).observe('custom:event', function(event) { var customData = event.memo; // stuff }); $('troz').fire('custom:event', { foo: "bar" });
  • 40. Dojo (“pub-sub”) dojo.subscribe('some-event', function(data) { // stuff }); dojo.publish('some-event', someData);
  • 41. A custom event is an interface that publisher and subscriber adhere to.
  • 42. As long as the interface remains the same, either part can be safely rewritten.
  • 43. “So I should replace all my method calls with custom events? Fat chance.”
  • 44. A consistent public API is also an interface.
  • 45. It’s OK for a subscriber to call methods on a broadcaster, but not vice-versa.
  • 47.
  • 48. The auto-completer knows about the menu… var menu = new S2.UI.Menu(); menu.addChoice("Foo"); menu.addChoice("Bar"); someElement.insert(menu); menu.open();
  • 49. …but the menu doesn’t know about the auto-completer menu.observe('ui:menu:selected', function(event) { console.log('user clicked on:', event.memo.element); });
  • 50. “What does a rewrite look like?”
  • 51. Instead of: function showNearbySpotsInMenu() { $.ajax({ url: '/spots', params: { lat: someLat, lng: someLng }, success: function(spots) { var html = $.map(spots, function(spot) { return '<li id="spot-"' + spot.id + '>' + spot.name + '</li>'; }); $('#spot_menu').html(html.join('')); } }); }
  • 52. Do this: function getNearbySpotsFromServer(lat, lng) { $.ajax({ url: '/spots', params: { lat: lat, lng: lng }, success: function(spots) { $(document).trigger('nearby-spots-received', [spots]); } }); }
  • 53. And this: function renderNearbySpots(event, spots) { var html = $.map(spots, function(spot) { return '<li id="spot-"' + spot.id + '>' + spot.name + '</li>'; }); $('#spot_menu').html(html.join('')); } $(document).bind('nearby-spots-received', renderNearbySpots);
  • 54. Or, if you prefer… function getNearbySpotsFromServer(lat, lng) { $.ajax({ url: '/spots', params: { lat: lat, lng: lng }, success: function(spots) { renderNearbySpots(spots); } }); } function renderNearbySpots(spots) { var html = $.map(spots, function(spot) { return '<li id="spot-"' + spot.id + '>' + spot.name + '</li>'; }); $('#spot_menu').html(html.join('')); }
  • 57. Easier testing function testNearbySpotsRendering() { renderNearbySpots(Fixtures.NEARBY_SPOTS); assertEqual($('#spot_menu > li').length, 3); }
  • 58. “What if it’s not enough?”
  • 59. More complex web apps might need desktop-like architectures.
  • 60. “Single-page apps” have a few common characteristics:
  • 61. maintaining data objects on the client side, instead of expecting the server to do all the work;
  • 62. creating views on the client side and mapping them to data objects;
  • 63. use of the URL hash for routing/permalinking (or HTML5 history management).
  • 64. Is this MVC? Perhaps.
  • 66. Models define a model class window.Todo = Backbone.Model.extend({ EMPTY: "new todo...", property access wrapped in set/get methods initialize: function() { if (!this.get('content')) this.set({ 'content': this.EMPTY }); }, toggle: function() { this.set({ done: !this.get('done') }); }, triggered when the object is saved validate: function(attributes) { if (!attributes.content.test(/S/)) return "content can't be empty"; }, // ... });
  • 67. Views define a view class window.Todo.View = Backbone.View.extend({ tagName: 'li', bind events to pieces of the view events: { 'dblclick div.todo-content' : 'edit', 'keypress .todo-input' : 'updateOnEnter' }, initialize: function() { map to a model object; re-render when it changes this.model.bind('change', this.render); }, set the view’s contents render: function() { // ... }, // ... });
  • 68. Synchronization Backbone.sync = function(method, model, yes, no) { determine the HTTP verb to use for this action var type = methodMap[method]; serialize the object to JSON var json = JSON.stringify(model.toJSON()); send the data to the server $.ajax({ url: getUrl(model), type: type, data: json, processData: false, contentType: 'application/json', dataType: 'json', success: yes, error: no }); };
  • 69. Other options: SproutCore (http://sproutcore.com/) Cappuccino (http://cappuccino.org/) JavaScriptMVC (http://javascriptmvc.com/)
  • 70. “Great. How do I start?”
  • 71. Don’t do a Grand Rewrite™
  • 72. One strategy: Write new code to conform to your architecture. Improve old code little by little as you revisit it.
  • 74. Questions? ✍ PLEASE FILL OUT AN EVALUATION FORM Andrew Dupont http://andrewdupont.net