Planning JavaScript for Larger Teams - Draft & Handout version

Preview:

DESCRIPTION

This is the original draft of my talk at @mediaAjax, I discarded it as it is way too much for one hour :) Hopefully it still is of use for you

Citation preview

Planning JavaScript and Ajax for larger teams

Christian Heilmann@media Ajax, London, November 2007

Achtung alles Lookenpeepers!Dies Machine is nicht fur gefingerpoken

und mittengraben. Is easy schnappen der springenwerk, blowenfusen und poppencorken mit spitzensparken. Is nicht fur gewerken by das dummkopfen. Das rubbernecken sightseeren keepen Cottenpickenen hands in das pockets - relaxen und Watch Das Blinken Lights. 

Do not fiddle with other people’s knobs unless you know what you are doing.

Spoilers:– Some of the following advice will

seem very basic to you.– This is not based on me thinking

you need this.– It is meant as a reminder. Next

time you encounter these problems you won’t have to think about them.

… and Harry Potter dies, but comes back using a magic stone which is a third of the Deathly Hallows.

There is one main fatal mistake any developer

can make:

There is one main fatal mistake any developer

can make:

Make assumptions

“I don’t need to tell people that, they know

already.”

“Surely this has been done already, and by

people better than me.”

“This works right now, there won’t be a need to

change it.”

“This never worked in the past, it won’t work now.”

“We hack that now, and will get time later to fix

it.”

“This is a minor issue only for this instance, no need

to file a bug.”

All these mistakes are yours to make.

Sometimes they need to be made in order to prove a new point or prove an issue and its solution.

However, avoiding them means you help all of us

working together.

No more heroes!

A good developer is not a very gifted and

impressive developer.

It is a developer that can work with others and

works for the next developer to take over.

People will move from product to product or leave the company.

Web products are never finished.

Don’t leave a mess behind; work as if you

won’t see the code ever again.

Mistakes we make:

1. Mixing structure, presentation and behaviour.

2. Trusting the browser.3. Trusting the markup.4. Not planning for inclusion.5. Not planning for failure.6. Premature optimization.

This should be a no-brainer by now but we

still do it.

Separation means:

–Working in parallel (to an extend)

–Re-skinning an application by changing the style sheet

–Caching, Minimizing, Optimizing for speed and delivery.

Not separating means:

–Harder maintenance –Larger and slower sites –Several sources for bugs without an easy way to track them.

1. Mixing structure, presentation and behaviour.

2. Trusting the browser.3. Trusting the markup.4. Not planning for inclusion.5. Not planning for failure.6. Premature optimization.

The last thing to trust is the browser.

They all have shifty eyes and will steal your loose change when you don’t

look.

Testing for a browser name and assuming it

can do certain things is a bad plan.

Browsers come in all kind of setups and with all kind of extensions.

Assume the browser will fail and keep poking it

until it tells you all is OK.

Making assumptions:

function showMessage(elm, message){

elm.firstChild.nodeValue = message;

}

function showMessage(id,message){ if(document.getElementById){ var elm = document.getElementById(id); if(elm){ if(elm.firstChild){ if(elm.firstChild.nodeType === 3){ elm.firstChild.nodeValue = message; } else { var t = document.createTextNode(message); elm.insertBefore(t,elm.firstChild); } } else { var t = document.createTextNode(message); elm.appendChild(t,elm.firstChild); } } }}

function showMessage(id,message){ if(document.getElementById){ var elm = document.getElementById(id); if(elm){ if(elm.firstChild){ if(elm.firstChild.nodeType === 3){ elm.firstChild.nodeValue = message; } else { var t = document.createTextNode(message); elm.insertBefore(t,elm.firstChild); } } else { var t = document.createTextNode(message); elm.appendChild(t,elm.firstChild); } } }}

function showMessage(id,message){ if(document.getElementById){ var elm = document.getElementById(id); if(elm){ if(elm.firstChild){ if(elm.firstChild.nodeType === 3){ elm.firstChild.nodeValue = message; } else { var t = document.createTextNode(message); elm.insertBefore(t,elm.firstChild); } } else { var t = document.createTextNode(message); elm.appendChild(t,elm.firstChild); } } }}

function showMessage(id,message){ if(document.getElementById){ var elm = document.getElementById(id); if(elm){ if(elm.firstChild){ if(elm.firstChild.nodeType === 3){ elm.firstChild.nodeValue = message; } else { var t = document.createTextNode(message); elm.insertBefore(t,elm.firstChild); } } else { var t = document.createTextNode(message); elm.appendChild(t,elm.firstChild); } } }}

function showMessage(id,message){ if(document.getElementById){ var elm = document.getElementById(id); if(elm){ if(elm.firstChild){ if(elm.firstChild.nodeType === 3){ elm.firstChild.nodeValue = message; } else { var t = document.createTextNode(message); elm.insertBefore(t,elm.firstChild); } } else { var t = document.createTextNode(message); elm.appendChild(t,elm.firstChild); } } }}

function showMessage(id,message){ if(document.getElementById){ var elm = document.getElementById(id); if(elm){ if(elm.firstChild){ if(elm.firstChild.nodeType === 3){ elm.firstChild.nodeValue = message; } else { var t = document.createTextNode(message); elm.insertBefore(t,elm.firstChild); } } else { var t = document.createTextNode(message); elm.appendChild(t,elm.firstChild); } } }}

function showMessage(id,message){ if(document.getElementById){ var elm = document.getElementById(id); if(elm){ if(elm.firstChild){ if(elm.firstChild.nodeType === 3){ elm.firstChild.nodeValue = message; } else { var t = document.createTextNode(message); elm.insertBefore(t,elm.firstChild); } } else { var t = document.createTextNode(message); elm.appendChild(t,elm.firstChild); } } }}

function showMessage(id,message){ if(document.getElementById){ var elm = document.getElementById(id); if(elm){ if(elm.firstChild){ if(elm.firstChild.nodeType === 3){ elm.firstChild.nodeValue = message; } else { var t = document.createTextNode(message); elm.insertBefore(t,elm.firstChild); } } else { var t = document.createTextNode(message); elm.appendChild(t,elm.firstChild); } } }}

function showMessage(id,message){ if(document.getElementById){ var elm = document.getElementById(id); if(elm){ if(elm.firstChild){ if(elm.firstChild.nodeType === 3){ elm.firstChild.nodeValue = message; } else { var t = document.createTextNode(message); elm.insertBefore(t,elm.firstChild); } } else { var t = document.createTextNode(message); elm.appendChild(t,elm.firstChild); } } }}

function showMessage(id,message){ if(document.getElementById){ var elm = document.getElementById(id); if(elm){ if(elm.firstChild){ if(elm.firstChild.nodeType === 3){ elm.firstChild.nodeValue = message; } else { var t = document.createTextNode(message); elm.insertBefore(t,elm.firstChild); } } else { var t = document.createTextNode(message); elm.appendChild(t,elm.firstChild); } } }}

Bit overkill, isn’t it?

–Add tests for the browser early (init method)

–Use library functions that do the testing for you.

1. Mixing structure, presentation and behaviour.

2. Trusting the browser.3. Trusting the markup.4. Not planning for inclusion.5. Not planning for failure.6. Premature optimization.

HTML markup of the document is never set in

stone.

Don’t rely on it.

document.getElementById('nav').getElementsByTagName('li')[3].firstChild.getElmentsByTagName('span')[2].nodeValue = 'offline';

document.getElementById('nav').getElementsByTagName('li')[3].firstChild.getElmentsByTagName('span')[2].nodeValue = 'offline';

1. Mixing structure, presentation and behaviour.

2. Trusting the browser.3. Trusting the markup.4. Not planning for inclusion.5. Not planning for failure.6. Premature optimization.

Assuming your script is the only one included in

the document:

Assuming your script is the only one included in

the document:

hasfail = true;

Namespace your scripts, follow a naming

convention and don’t leave any global variables

or methods.

That way your script will run even if it has to work

alongside bad code.

Bad code will be added to your documents, most of the time in the form of

advertisements or tracking code.

1. Mixing structure, presentation and behaviour.

2. Trusting the browser.3. Trusting the markup.4. Not planning for inclusion.5. Not planning for failure.6. Premature optimization.

Things will fail, plan for it!

– JavaScript will not be available.–Ajax connections will take too long or return wrong data.

–Plan for a fallback option (redirect to a static page, show an error message)

<a href="javascript:ajaxmagic('panda')">

Show results for 'panda'

</a>

<a href="javascript:ajaxmagic('panda')">

Show results for 'panda'

</a>

<a href="search.php?term=panda" id="searchlink">

Show results for 'panda‘

</a>

var YE = YAHOO.util.Event;

YE.on('searchlink','click',ajaxmagic);

function ajaxmagic(e){

var t = YE.getTarget(e);

var url = t.href.split('=')[1];

ajaxcode(url);

YE.preventDefault(e);

}

Re-use the backend script

function ajaxmagic(e){

var t = YE.getTarget(e);

var url = t.href + &output=json';

ajaxcode(url);

YE.preventDefault(e);

}

1. Mixing structure, presentation and behaviour.

2. Trusting the browser.3. Trusting the markup.4. Not planning for inclusion.5. Not planning for failure.6. Premature optimization.

Each of us has a little hacker inside who wants

to get out.

This little hacker wants to make things as fast, small

and cool as possible.

This little hacker is also very competitive and doesn’t trust reused

code or libraries.

Our job is not to give in to it when we produce production code.

Production code does not need to be optimized

from the start.

It needs to be understandable and

maintainable.

–use library code, even if it appears huge (a lot of the size is a myth)

–Use comments to explain what is going on

–Use explanatory variable and method names

–Don’t reinvent the wheel even if you consider yours superior.

Enough bad. Give us some tips!

1. Follow a diet plan.2. Have a build process.3. Have a code standard and

documentation process.4. Review and reuse code.5. Plan for extension.6. Think maintenance

Try to avoid traps that make your code hungry:

–Cut down on DOM interaction –Minimize the amount of loops –Build tool methods to deal with reoccurring problems

Use shortcut notations in JavaScript.

var links = new Array();

links[0]='http://icant.co.uk';

links[1]='http://wait-till-i.com';

links[2]='http://onlinetools.org';

var links = new Array();

links[0]='http://icant.co.uk';

links[1]='http://wait-till-i.com';

links[2]='http://onlinetools.org';

var links = [

'http://icant.co.uk', 'http://wait-till-i.com', 'http://onlinetools.org‘

];

var contact = new Object();

contact.name = 'Christian';

contact.surName = 'Heilmann';

contact.age = 32;

contact.hair = 'slightly red';

var contact = new Object();contact.name = 'Christian';contact.surName = 'Heilmann';contact.age = 32;contact.hair = 'slightly red';

var contact = {name:'Christian',surName:'Heilmann',age:32,hair:'slightly red'

};

if(hasCheeseburger){cat.mood = 'happy';

} else {cat.mood = 'sad';

}

if(hasCheeseburger){cat.mood = 'happy';

} else {cat.mood = 'sad';

}

cat.mood = hasCheeseburger?'happy':'sad';

if(hasCheeseburger){cat.mood = 'happy';

} else {cat.mood = 'sad';

}

cat.mood = hasCheeseburger?'happy':'sad';

condition

if(hasCheeseburger){cat.mood = 'happy';

} else {cat.mood = 'sad';

}

cat.mood = hasCheeseburger?'happy':'sad';

true case

if(hasCheeseburger){cat.mood = 'happy';

} else {cat.mood = 'sad';

}

cat.mood = hasCheeseburger?'happy':'sad';

false case

if(obj.getClass()==='full'){var x = screen.left;

} else {var x = current;

}

if(obj.getClass()==='full'){var x = screen.left;

} else {var x = current;

}

var x = (obj.hasClass()==='full') ? screen.left : current;

var cat;

if(canHasCheeseburger){

cat = canHasCheeseburger;

};

if(garfield){

cat = garfield;

};

var cat;

if(canHasCheeseburger){

cat = canHasCheeseburger;

};

if(garfield){

cat = garfield;

};

var cat = canHasCheeseburger || garfield;

var cat;

if(canHasCheeseburger){

cat = canHasCheeseburger;

};

if(garfield){

cat = garfield;

};

var cat = canHasCheeseburger || garfield;

one or the other, defining a fallback

var massive.name.with.long.namespace = { ...private stuff... return{ init:function(){ massive.name.with.long.namespace.show();

}, show:function(){ massive.name.with.long.namespace.test();

}, test:function(){ } }}();

var massive.name.with.long.namespace = { var pub = {};...private stuff...pub.init = function(){

pub.show();};pub.show = function(){

pub.test();};pub.test = function(){};return pub;

}();massive.name.with.long.namespace.init();

var massive.name.with.long.namespace = {function init(){

show();};function show(){

test();};function test(){};return {

init:init;show:show;test:test;

};}();massive.name.with.long.namespace.init();

1. Follow a diet plan.2. Have a build process.3. Have a code standard and

documentation process.4. Review and reuse code.5. Plan for extension.6. Think maintenance

–Production code is not live code.

–That doesn’t happen on the back-end and it shouldn’t happen on the front-end.

–Live code is there for machines, production code is there for humans.

Build process:–Validation (Tidy, JSLint)–Minification (JSMin, CSS minifier)

–Consolidation (one CSS and one JS instead of dozens)

–Tagging as “live code” – do not edit!

1. Follow a diet plan.2. Have a build process.3. Have a code standard and

documentation process.4. Review and reuse code.5. Plan for extension.6. Think maintenance

Following a code standard means you can:–Assess quality of code –Find bugs easily and create reproducible bug reports

–Have quick handover from developer to developer

–Have reliable version control

What code standard?

Whatever the team agrees on and feels comfortable with.

Document it and other teams can validate what

you have done.

In the perfect world, we’ll all follow the same code

standard.

Let’s meet again in a year! :-)

Comments are good, but they are not

documentation.

Write documentation in peer review - as the

developer you are too close to the subject.

Plan and ask for time to document what you have

done.

1. Follow a diet plan.2. Have a build process.3. Have a code standard and

documentation process.4. Review and reuse code.5. Plan for extension.6. Think maintenance

Conduct Code reviews–You find problems and solutions you can share in the team.

–You find out who knows what well and who needs training in what

–You share the knowledge throughout the team = no Divas.

– If you keep finding the same problem and different solutions for it, find a generic solution and re-use that one.

–Look at what other people have done (libraries).

–Bounce your solutions off other development teams.

1. Follow a diet plan.2. Have a build process.3. Have a code standard and

documentation process.4. Review and reuse your code.5. Plan for extension.6. Think maintenance

Whenever you write some code, don’t think that

your implementation will be the end of it.

Make sure that your code can be extended by

other people and try to build it as modular as

possible.

1. Follow a diet plan.2. Have a build process.3. Have a code standard and

documentation process.4. Review and reuse your code.5. Plan for extension.6. Think maintenance

Not everybody maintaining your code will be as good as you

are.

Trying to find where to change what in a piece of

code is terribly frustrating.

Separate out as much of the look and feel as you

can.

Also separate out labels that are likely to change and the names of CSS classes and IDs in use.

So how do you plan JavaScript and Ajax for

larger teams?

Get the team to talk and agree on what works

best.

Involve Design and Engineering in the

process and explain the rationale of your plan.

Communication and sharing information is

better than any architectural blueprint you or I could come up

with.

THANKS!

Christian Heilmann

http://wait-till-i.comchris.heilmann@gmail.com

Recommended