Thoughts on Modern Web Development …

As a depiction of the typical approach taken when solving a problem with Test First practices in mind, below is a brief excerpt from a recent conversation with a collegue who inquired of me as to how one generally goes about solving a problem using Test First methodologies. My explanation was rather simple, and read somewhat like a short story, though I describe it as being more of a step by step process from a Pair Programming perspective.

The general workflow conveyed in my description, while brief, covers the essentials:

We have a problem to solve.

We discuss the problem, asking questions as needed; then dig a bit deeper to ensure we understand what it is we are really trying to solve; and, most importantly, why.

We consider potential solutions, identifying those most relevant, evaluating each against the problem; then agree upon one which best meets our needs.

We define a placeholder test/spec where our solution will be exercised. It does nothing yet.

We implement the solution in the simplest manner possible, directly within the test itself; the code is quite ugly, and that is perfectly fine, for now. We run our test, it fails

We adjust our implementation, continuing to focus solely on solving the problem; all the while making sure not to become too distracted with implementation details at this point.

We run our test again, it passes. We’re happy, we’ve solved the problem.

We move our solution out of the test/spec to the actual method which is to be implemented, which, until now, had yet to exist.

We update our test assertions/expectations against the actual (SUT). We run our test, it passes.

We’re happy, we have a working, tested solution; however, the implementation is substandard; this has been nagging at us all along, so we shift focus to our design; refactoring our code to a more elegant, performant solution; one which we can be proud of.

We run our test again, it fails. That’s fine, perhaps even preferable, as it verifies our test is doing exactly what is expected of it; thus, we can continue to refactor in confidence.

We adjust our code, continuing to make design decisions and implementation changes as needed. We run our test again, it passes.

We refactor some more, continuing to focus freely, and without worry on the soundness of our design and our implementation. We run our test again, it passes.

Rinse and Repeat…

While the above steps are representative of a typical development work-flow based on Test First processes, it is worth noting that as one becomes more acclimated with such processes, certain steps often become unnecessary. For example, I generally omit Step #5 insofar as implementing the solution within the test/spec itself is concerned; but rather, once I understand the problem to be solved, I then determine an appropriate name for the method which is to be tested, and implement the solution within the SUT itself, as opposed to the test/spec; effectively eliminating the need for Step #8. As such, the steps can be reduced down to only those which experience proves most appropriate.

Concluding Thoughts

Having become such an integral part of my everyday workflow for many years now, I find it rather challenging to approach solving a problem without using Test First methodologies. In fact, attempting to solve a problem of even moderate complexity without approaching it from a testing perspective feels quite awkward.

The simple fact is, without following general Test First practices, we are just writing implementation code, and if we are just writing implementation code, then, in turn, we are likely not thinking through a problem in it’s entirety. Consequently, it follows then that we are also not thinking through our solutions in their entirety, and hence our designs. Because of this, solutions feel uncertain, and ultimately leave us feeling much less confident in the code we deliver.

Conversely, when following sound testing practices we afford our team and ourselves an unrivaled sense of confidence in terms of the specific problems we are solving, why we are solving them, and how we go about solving them; from that, we achieve a concerted understanding of the problem domain, as well as a much clearer, holistic understanding of our designs.

Recently, while reading the HTML5 Doctorinterview with Ian Hickson, when asked what some of his regrets have been over the years, the one he mentions, rather comically so as being his “favorite mistake”, also happened to be the one which stood out to me most; that is, his disappointment with pushState; specifically, the fact that of the three arguments accepted, the second argument is now ignored.

I can empathize with his (Hixie’s) frustration here; not simply because he is one of the most influential figures on the web – particularly for his successful work surrounding CSS, HTML5, and his responsibilities at the WHATWG in general – but rather, it is quite understandable how such a seemingly insignificant design shortcoming would bother such an obviously talented individual, especially considering the fact that pushState's parameters simply could not be changed due to the feature being used prior to completion. Indeed, the Web Platform poses some very unique and challenging constraints under which one must design.

While the ignored pushState argument is a rather trivial issue, I found it to be of particular interest as I often employ Parameter Objects to avoid similar design issues.

Parameter Objects

The term “Parameter Object” is one I use rather loosely to describe any object that simply serves as a wrapper from which all arguments are provided to a function. In the context of JavaScript, object literals serve quite well in this capacity, even for simpler cases where a function would otherwise require only a few arguments of the same type.

Parameter Objects are quite similar to that of an “Options Argument” – a pattern commonly implemented by many JavaScript libraries to simplify providing optional arguments to a function; however, I tend to use the term Parameter Objects more broadly to describe a single object parameter from which all arguments are provided to a function, optional arguments included. The two terms are often used interchangeably to describe the same pattern. However, I specifically use the term Options Argument to describe a single object which is reserved exclusively for providing optional arguments only, and is always defined as the last parameter of a function, proceeding all required arguments.

Benefits

Parameter Objects can prove beneficial in that they afford developers the ability to defer having to make any final design decisions with regard to what particular inputs are accepted by a function; thus, allowing an API to evolve gracefully over time.

For instance, using a Parameter Object, one can circumvent the general approach of implementing functions which define a fixed, specific order of parameters. As a result, should it be determined that any one particular parameter is no longer needed, API designers need not be concerned with requiring calling code to be refactored in order to allow for the removal of the parameter. Likewise, should any additional parameters need to be added, they can simply be defined as additional properties of the Parameter Object, irrespective of any particular ordering of previous parameters defined by the function.

As an example, consider a theoretical rotation function which defines five parameters:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

/*

* Performs a rotation on the given element based on the

* provided values, delegating to the respective native

* CSS3 rotation functions.

*

* @param {String} el id of the element to rotate

* @param {Number} x the x-axis of the rotation

* @param {Number} y the y-axis of the rotation

* @param {Number} z the z-axis of the rotation

* @param {Number} a the angle of the rotation

*/

varrotate=function(el,x,y,z,a){

...

};

Using a Parameter Object, we can refactor the above function to the following:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

/*

* Performs a rotation on the given element based on the

* provided values, delegating to the respective native

* CSS3 rotation functions.

*

* @param {Object} params

* params.el {String} id of the element to transform

* params.x {Number} the x-axis of the rotation

* params.y {Number} the y-axis of the rotation

* params.z {Number} the z-axis of the rotation

* params.a {Number} the angle of the rotation

*/

varrotate=function(params){

...

};

Should we wish to remove a parameter from the function, doing so simply requires making the appropriate changes at the API level without changing the actual signature of the function (assuming of course, there are no specific expectations already being made by calling code regarding the argument to be removed). Likewise, should additional parameters need to be added, such as a completion callback, etc., doing so, again, only requires making the appropriate API changes, and would not impact current calling code.

Additionally, taking these potential changes as an example, we can also see that with Parameter Objects, implementation specifics can be delegated to the API itself, rather than client code insofar that the provided arguments can be used to determine the actual behavior of the function. In this respect, Parameter Objects can also double as an Options Argument. For example, should the arguments required to perform a 3D rotation be omitted from the Parameter Object, the function can default to a 2D rotation based on the provided arguments, etc.

Convenience

Parameter Objects are rather convenient in terms of there being less mental overhead required than that of a function which requires ordered arguments; this is especially true for cases where a function defines numerous parameters, or successive parameters of the same type.

Since code is generally read much more frequently than it is written, it can be easier to understand what is being passed to a function when reading explicit property names of an object, in which each property name maps to a parameter name, and each property value maps to parameter argument. This can aid in readability where it would otherwise require reading the rather ambiguous arguments passed to a function. For example:

1

2

3

4

// call with specific arguments

rotate('#div',0.7,0.5,0.7,45);

With Parameter Objects it becomes more apparent as to which arguments correspond to each specific parameter:

1

2

3

4

5

6

7

8

9

10

// call with a Parameter Object

rotate({

'el':'#div'

,'x':0.7

,'y':0.5

,'z':0.7

,'a':45

});

As mentioned, if a function accepts multiple arguments of the same type, the likelihood that users of the API may accidentally pass them in an incorrect order increases. This can result in errors that are likely to fail silently, possibly leading to the application (or a portion thereof) becoming in an unpredictable state. With Parameter Objects, such unintentional errors are less likely to occur.

Considerations

While Parameter Objects allow for implementing flexible parameter definitions, the arguments for which being provided by a single object, they are obviously not intended as a replacement for normal function parameters in that should a function need only require a few arguments, and the function’s parameters are unlikely to change, then using a Parameter Object in place of normal function parameters is not recommended. Also, perhaps one could make the argument that creating an additional object to store parameter/argument mappings where normal arguments would suffice adds additional or unnecessary overhead; however, considering how marginal the additional footprint would be, this point is rather moot as the benefits outweigh the cost.

A Look at pushState’s Parameters

Consider the parameters defined by pushState:

data: Object

title: String

url: String

The second parameter, title, is the parameter of interest here as it is no longer used. Thus, calling push state requires passing either null or an empty String (recommended) as the second argument (i.e. title) before one can pass the third argument, url. For example:

1

2

3

4

// invoke pushState with each argument

pushState({'state':'some state'},'','some.html');

Using a Parameter Object, pushState could have been, theoretically, implemented such that only a single argument was required:

params: Object

data: Object

title: String

url: String

Thus, the ignored title argument could be safely removed from current calling code:

1

2

3

4

5

6

7

// invoke pushState with only relevant arguments

pushState({

'data':{'state':'some state'}

,'url':'some.html'

});

And simply ignored in previously implemented calls:

1

2

3

4

5

6

7

8

// invoke pushState with relevant arguments and deprecated arguments

pushState({

'data':{'state':'some state'}

,'title':'Some Title'

,'url':'some.html'

});

As can be seen, the difference between the two is quite simple: the specification for pushState accepts three arguments, whereas the theoretical Parameter Object implementation accepts a single object as an argument, which in turn provides the original arguments.

Concluding Thoughts

I certainly do not assume to understand the details surrounding pushState in enough detail to assert that the use of a Parameters Object would have addressed the issue. Thus, while this article may reference pushState as a basic example to illustrate how the use of a Parameter Object may have proved beneficial, it is really intended to highlight the value of using Parameter Objects from a general design perspective, by describing common use-cases in which they can prove useful. As such, Parameter Objects provide a valuable pattern worth considering when a function requires flexibility.

When developing single page web applications, patterns of structure, organization and reuse become ever more important. This especially holds true when there is a need to maintain mulitiple web applications, each of which targeting a specific form factor, while also sharing many of the same underlying core APIs.

In the context of client-side templating, such patterns begin to emerge, quite naturally so, when leveraging RequireJS modules and the RequireJS text plugin.

Template Modules

One specific pattern I have found myself implementing is that of a single Templates Module which provides a centralized location from which all compiled templates within an application can be referenced. A rather simple pattern, Template Modules are only concerned with loading, compiling and providing a public API to access compiled templates; that is, a Templates Module simply requires all external templates, and provides named methods for retrieving the compiled template functions of each.

A basic implementation of a Templates module is as follows (while Handlebars may be used in this example, any template engine would suffice):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

define(function(require){

varHandlebars=require('Handlebars')

,_template1=require('text!app/templates/template-1.tpl')

,_template2=require('text!app/templates/template-2.tpl')

,_template3=require('text!app/templates/template-3.tpl')

,_template4=require('text!app/templates/template-4.tpl');

return{

template1:function(){

returnHandlebars.compile(_template1);

},

template2:function(){

returnHandlebars.compile(_template2);

},

template3:function(){

returnHandlebars.compile(_template3);

},

template4:function(){

returnHandlebars.compile(_template4);

}

}

});

The main benefit of implementing a Templates Module is reuse, as different modules can use the same templates without a need for redundantly requiring and compiling the templates themselves. Additionally, Template Modules provide a convenient means of abstracting away the underlying template engine from client code, thus reducing the amount of refactoring needed should the template engine itself ever need to change.

When using the RequireJS Optimizer, each external template will be included in the optomized build and loaded synchronously, and so there is no additional overhead in terms of HTTP requests when requiring each template in a single location.

You can check out a basic example implementation of a Templates Module (in the context of Backbone) here.

When developing large scale web applications leveraging RequireJS, at times, even the most highly cohesive of modules will require quite a few other modules as dependencies. As such, maintaining the order of these dependencies can become somewhat tedious. Fortunately, RequireJS provides a means of simplifying how modules may define dependencies for such cases.

Ordering Dependencies

If we are to consider how a typical module definition specifies dependencies, it becomes clear that one must ensure each module dependency name and it’s corresponding definition function argument have been listed in the same order:

From both a design and client implementation perspective, this one-to-one correlation between dependency ordering and definition function argument ordering makes perfect sense, of course, for it would obviously be extremely confusing otherwise. In general this is rarely a concern, though when a module has many dependencies it can become cumbersome.

Adding Dependencies

The necessary side effect of the dependency/argument ordering is that as other dependencies need to be added, time must be spent ordering and re-ordering dependencies if one takes care to group dependencies categorically in order to improve readability (e.g. models…, collections…, views…, etc.).

For example, consider the following:

1

2

3

4

5

6

7

8

9

10

11

define([

'jQuery',

'Underscore',

'Backbone',

'SomeModel',

'SomeCollection'

],function($,_,Backbone,SomeModel,SomeCollection){

...

}

If we were to decide that this module also needed, say, Handlebars, we could simply add the new dependency to the end of the dependencies array, and then just add it to the end of the factory function’s arguments as follows:

1

2

3

4

5

6

7

8

9

10

11

12

define([

'jQuery',

'Underscore',

'Backbone',

'SomeModel',

'SomeCollection',

'Handlebars'

],function($,_,Backbone,SomeModel,SomeCollection,Handlebars){

...

});

While the above approach will certainly work, it fails to aid in readability as Handlebars is grouped with the application specific dependencies – the Model and Collection, as opposed to being grouped with the module’s framework dependencies. This may seem like a trivial detail, however, considering code is typically read many more times than it is written, it makes sense to organize dependencies as they are added in order to save ourselves and others time in the future when viewing the dependencies.

And so, ideally a team would have an established pattern of grouping dependencies in some kind of logical order. For example, framework specific dependencies could be listed first, followed by application specific dependencies etc. This ordering could be as simple or complex as a team collectively decides, though I would recommend keeping it generally simple.

With this in mind we could improve the above example as follows:

1

2

3

4

5

6

7

8

9

10

11

12

define([

'jQuery',

'Underscore',

'Backbone',

'Handlebars',

'SomeModel',

'SomeCollection'

],function($,_,Backbone,Handlebars,SomeModel,SomeCollection){

...

});

Organizing Dependencies

If we are to consider the above example as being somewhat typical, then it becomes rather clear that with each new dependency added we will likely have to repeat the ordering process. Again, while this may seem insignificant, it can easily lead to exceptions being thrown if any dependencies are out of order.

Fortunately, RequireJS provides a simplified CommonJS wrapping implementation, or Sugar syntax, which can be used to solve such issues. This sytax (which will feel natural to those who use Node) allows one to simply provide a module’s definition function to the module’s define call, and specifiy require as a single argument, as follows:

1

2

3

4

5

define(function(require){

...

});

Using this pattern, we can refactor the above example to be more easily managed as follows:

1

2

3

4

5

6

7

8

9

10

11

define(function(require){

var$=require('jQuery'),

_=require('Underscore'),

Backbone=require('Backbone'),

Handlebars=require('Handlebars'),

SomeModel=require('SomeModel'),

SomeCollection=require('SomeCollection');

...

});

With this pattern of dependency mapping it becomes much easier to add and remove dependencies as needed, with the added benefit of reading much more naturally. This pattern also feels more familiar as it is similar to import directives in other languages.

Conclusion

Managing module dependencies in RequireJS is quite simple and becomes even simpler when leveraging the Sugar syntax described above. When doing so, it is important to keep in mind that this syntax relies on Function.prototype.toString(), which, while having good support in most modern browsers, does not provide predictable results in certain older browsers. However, as the documentation states, using an optimizer to normalize dependencies – such as the very powerful RequireJS Optimizer – will ensure this approach works across all browsers.

As a general rule of thumb, I typically use the Sugar syntax approach when there are more than 4-5 dependencies and have found it has simplified managing dependencies in modules rather nicely.

One of the principle design philosophies I have advocated over the years, especially through various articles on this site, has been the importance of decoupling. And while I could go into significant detail to elaborate on the importance of decoupling, suffice it to say that all designs – from simple APIs to complex applications – can benefit considerably from a decoupled design; namely, with respect to testability, maintainability and reuse.

Decoupling in Backbone

Many of the examples which can be found around the web on Backbone are intentionally simple in that they focus on higher level concepts without diverging into specific implementation or design details. Of course, this makes sense in the context of basic examples and is certainly the right approach to take when explaining or learning something new. Once you get into real-world applications, though, one of the first things you’ll likely want to improve on is how modules communicate with each other; specifically, how modules can communicate without directly referencing one another.

As I have mentioned previously, Backbone is an extremely flexible framework, so there are many approaches one could take to facilitate the decoupling of modules in Backbone; the most common of which, and my preferred approach, is decoupling by way of events.

Basic Decoupling with Events

The simplest way to facilitate communication between discreet modules in Backbone is to have each module reference a shared event broker (a pub /sub implementation). Modules can register themselves to listen for events of interest with the broker, and modules can also communicate with other modules via events as needed. Implementing such an API in Backbone is amazingly simple, in fact, so much so that the documentation provides an example in the following one liner:

1

2

3

vardispatcher=_.clone(Backbone.Events);

Essentially, the dispatcher simply clones (or alternately, extends) the Backbone.Events object. Different modules can reference the same dispatcher to publish and subscribe to events of interest. For example, consider the following:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

// A basic shared event broker

varbroker=_.clone(Backbone.Events);

varUsers=Backbone.Collection.extend({

// reference the broker, subscribe to an event

initialize:function(broker){

this.broker=broker;

this.broker.on('users:add',this.add,this);

},

add:function(user){

console.log(user.id);

}

});

varUserEditor=Backbone.View.extend({

el:'#editor',

// reference the broker

initialize:function(broker){

this.broker=broker;

this.$userId=this.$('#userId');

},

add:function(){

// publish an event

varuser=newUser({

id:this.$userId().val()

});

this.broker.trigger('users:add',user);

}

});

// ...

In the above example, the UsersCollection is completely decoupled from the UserEditorView, and vice-versa. Moreover, any module can subscribe to the 'users:add'event without having any knowledge of the module from which the event was published. Such a design is extremely flexible and can be leveraged to support any number of events and use-cases. The above example is rather simple; however, it demonstrates just how easy it is to decouple modules in Backbone with a shared EventBroker.

Namespacing Events

As can be seen in the previous example, the addevent is prefixed with a users string followed by a colon. This is a common pattern used to namespace an event in order to ensure events with the same name which are used in different contexts do not conflict with one another. As a best practice, even if an application initially only has a few events, the events should be namespaced accordingly. Doing so will help to ensure that as an application grows in scope, adding additional events will not result in unintended behaviors.

A General Purpose EventBroker API

To help facilitate the decoupling of modules via namespaced events, I implemented a general purpose EventBroker which builds on the default implementation of the Backbone Events API, adding additional support for creating namespace specific EventBrokers and registering multiple events of interest for a given context.

Basic Usage

The EventBroker can be used directly to publish and subscribe to events of interest:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

varUsers=Backbone.Collection.extend({

broker:Backbone.EventBroker,

initialize:function(){

this.broker.on('users:add',this.add,this);

},

add:function(user){

console.log(user.id);

}

});

varUserEditor=Backbone.View.extend({

el:'#editor',

broker:Backbone.EventBroker,

initialize:function(broker){

this.$userId=this.$('#userId');

},

add:function(){

// publish an event

varuser=newUser({

id:this.$userId().val()

});

this.broker.trigger('users:add',user);

}

});

// ...

Creating namespaced EventBrokers

The EventBroker API can be used to create and retrieve any number of specific namespaced EventBrokers. A namespaced EventBroker ensures that all events are published and subscribed against a specific namespace.

Namespaced EventBrokers are retrieved via Backbone.EventBroker.get(namespace). If an EventBroker has not been created for the given namespace, it will be created and returned. All subsequent retrievals will return the same EventBroker instance for the specified namespace; i.e. only one unique EventBroker is created per namespace.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

varUsers=Backbone.Collection.extend({

// use the 'users' broker

userBroker:Backbone.EventBroker.get('users'),

initialize:function(broker){

this.userBroker.on('add',this.add,this);

},

add:function(user){

console.log(user.id);

}

});

varUserEditor=Backbone.View.extend({

el:'#editor',

// use the 'users' broker

usersBroker:Backbone.EventBroker.get('users'),

// also use the 'roles' broker

rolesBroker:Backbone.EventBroker.get('roles'),

initialize:function(broker){

this.$userId=this.$('#userId');

},

add:function(){

// publish an event

varuser=newUser({

id:this.$userId().val()

});

this.usersBroker.trigger('add',user);

}

});

Since namespaced EventBrokers ensure events are only piped thru the EventBroker of the given namespace, it is not necessary to prefix event names with the specific namespace to which they belong. While this can simplify implementation code, you can still prefix event names to aid in readability if desired.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

varUsers=Backbone.Collection.extend({

// use the 'users' broker

userBroker:Backbone.EventBroker.get('users'),

initialize:function(broker){

// prefix the namespace if desired

this.userBroker.on('users:add',this.add,this);

},

add:function(user){

console.log(user.id);

}

});

varUserEditor=Backbone.View.extend({

el:'#editor',

// use the 'users' broker

usersBroker:Backbone.EventBroker.get('users'),

// also use the unique 'roles' broker

rolesBroker:Backbone.EventBroker.get('roles'),

initialize:function(broker){

this.$userId=this.$('#userId');

},

add:function(){

// publish an event

varuser=newUser({

id:this.$userId().val()

});

// prefix the namespace if desired

this.usersBroker.trigger('users:add',user);

}

});

Registering Interests

Modules can register events of interest with an EventBroker via the default on method or the register method. The register method allows for registering multiple event/callback mappings for a given context in a manner similar to that of the events hash in a Backbone.View.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

// Register event/callbacks based on a hash and associated context

varUsers=Backbone.Collection.extend({

broker:Backbone.EventBroker,

initialize:function(){

this.broker.register({

'user:select':'select',

'user:deselect':'deselect',

'user:edit':'edit',

'user:update':'update',

'user:remove':'remove'

},this);

},

select:function(){...},

deselect:function(){...},

edit:function(){...},

update:function(){...},

remove:function(){...}

});

Alternately, Modules can simply define an “interests” property containing particular event/callback mappings of interests and register themselves with an EventBroker

Lately I have been finding myself writing custom Jasmine matchers which wrap the Sinon.JS API as a convenience. After repeating this process quite a few times I took a step back to see if there was a similar solution already available.

After a brief search, I quickly came across jasmine-sinon which, to my surprise, provides a very similar matcher API to that which I had began implementing. This library really is quite nice as it essentially provides matchers for all common Sinon.JS spies, stubs and mocking use-cases. I am sure jasmine-sinon has saved many developers a lot of time by not having to write their own custom matchers as it has for me.

And so, if you are using both Jasmine and Sinon.JS, and find yourself writing convenience matchers to simplify Sinon.JS integration, jasmine-sinon is an excellent addition which compliments both and is certainly worth considering.