professional javascript: antipatterns

54
Professional JavaScript AntiPatterns By Mike Wilcox February 2014

Upload: mike-wilcox

Post on 07-Nov-2014

1.502 views

Category:

Technology


2 download

DESCRIPTION

Improperly architected applications may work, may perform well, and may meet the acceptance criteria, but the ability to maintain them degrades over time. This presentation will show some of the common mistakes made when building large web applications, how to be aware of them, correct them, and hopefully prevent them.

TRANSCRIPT

Page 1: Professional JavaScript: AntiPatterns

Professional JavaScript

AntiPatternsBy Mike WilcoxFebruary 2014

Page 2: Professional JavaScript: AntiPatterns

if (target) { if (target.targetParent.targetParent) { target.targetParent.targetParent.targetParent = new Event(this); } else { target.targetParent.targetParent = new Event(this); } return target;}

This code sucks!

Page 3: Professional JavaScript: AntiPatterns

this.graphs = utilities.settingArrayPropertyProxy( settings.graphs,

function (index, newGraph) {var currentLimits = chartEngine.getActualLimits(), newLimits;chartEngine.addGraph(newGraph, index);chartEngine.render();

},function (newGraph, oldGraph) {

result = chartEngine.updateGraph(newGraph, oldGraph);chartEngine.setLimits(self.xAxis().limits());chartEngine.render();

},function (index, removed) {

chartEngine.setLimits(self.xAxis().limits());chartEngine.render();

},function (setting) {return new Graph(self, chartEngine, setting);

},false);

WTF??

Page 4: Professional JavaScript: AntiPatterns

Mike Wilcox

Works there.

Committer!

Natch!

JavaScript ES5HTML5

CSS3UI/UX architecture

Graphic Design

Page 5: Professional JavaScript: AntiPatterns

But more importantly...Brendan Eich

me

Knighted methe

Noble Architect*

*not strictly true

Page 6: Professional JavaScript: AntiPatterns

What do I know??Seriously…

Being a committer to the Dojo Toolkit JavaScript library means not only having your code and style scrutinized by know-it-all prima donnas, open source experts, but your results also get used by thousands of hacks that expect magic developers who expect quality code.

Page 7: Professional JavaScript: AntiPatterns

AntiMatter

In particle physics, antimatter is material composed of antiparticles, which have the same mass as particles of

ordinary matter but have the opposite charge.

Encounters between particles and antiparticles lead to the annihilation of both

Page 8: Professional JavaScript: AntiPatterns

AntiPatternA commonly used process, structure or pattern of action that despite initially appearing to be an appropriate and effective response to a problem, typically has more bad

consequences than beneficial results.

Encounters between patterns and antipatterns

lead to the annihilation of both

Page 9: Professional JavaScript: AntiPatterns

What to ExpectDiscuss paradigms and patterns.

Good and bad.

Some patterns. Not all.

Focus on communications between components.

Three words:Maintainability, maintainability, maintainability

Page 10: Professional JavaScript: AntiPatterns

What is a Pattern?

A design pattern is a general reusable solution to a commonly occurring problem

Gang of Four

Addy Osmani

Page 11: Professional JavaScript: AntiPatterns

IntroductionIt is more difficult to build maintainable code in JavaScript than languages such as Java or C# due to its dynamic nature and its original intent to be a simple language with little more to do than validate forms.

There is nothing built in to the language to assist in the construction of large apps: no classes, no modules, no packages.

There is little to no formal developer training in JavaScript

The result of this is programmers attempt to solve coding problems and end up with AntiPatterns

Page 12: Professional JavaScript: AntiPatterns

Hypothetical App

The Chart class is the API and main controller.

The Graph class controls the Axes and Series (which draws the lines)

There can be multiple Graphs in a Chart.

There can be multiple Series in a Graph.

Let’s architect a chart application. The structure and functionality should look like:

Page 13: Professional JavaScript: AntiPatterns

Chart App

chart engine renderer

graph x axis y axis

series layer painter(s)

Here are the main components of our hypothetical chart app below. How do you think they should be wired up?

Page 14: Professional JavaScript: AntiPatterns

Chart App Structure

An example of how circuitous the code path can get if the structure is not carefully considered before hand.

Proxies are to hide

the mess below

chart engine renderer

graph x axis y axis

series layer painter(s)

graph-proxy series-proxy layer-proxy

Page 15: Professional JavaScript: AntiPatterns

Chart App Structure

chart engine renderer

graph x axis y axis

series layer painter(s)

When carefully considered, the code flow might look more like below. It may take a little extra code to get to this, but it would be worth it for a result that is easier to follow.

Page 16: Professional JavaScript: AntiPatterns

Code FlowMeets acceptance criteria

Done!

Clean

Readable

Maintainable

Extensible

Testable

The quick way is often not the correct way

Page 17: Professional JavaScript: AntiPatterns

Causes of Bad Architecture

Page 18: Professional JavaScript: AntiPatterns

Management AntiPatternsSlave Driving Management

Sales Driven Development

Waterfall Development

Hiring Practices

Page 19: Professional JavaScript: AntiPatterns

Developer AntiPatternsNot enough experience

The lack of working with others

Too much solo programming

Not enough collaboration

Just damn lazy

When you find a problem fix it, don't just drop a TODO

Super Star ProgrammersSmart kids with no life that work 70 hours and generate layers upon layers of code that works... but lacks best practices, code reviews, comments, tests, and sometimes even reason

Page 20: Professional JavaScript: AntiPatterns

Code AntiPatterns

Page 21: Professional JavaScript: AntiPatterns

ProceduralUsing nothing but functions is the very definition of spaghetti code

jQuery encourages procedural… and $paghetti !

Good for a web "page", not good for a web "app"

If you’re not doing some sort of OOP, you will have a massive climb toward a maintainable large application

Easier for the developer to comprehend small objects, rather than large routines

Page 22: Professional JavaScript: AntiPatterns

… or what I have dubbed: SOOP

Too much inheritance

Inheritance breaks encapsulation because the internals of parent classes are visible to subclasses

Tends to counterintuitively create tight coupling

Makes for a very difficult-to-follow API when most of the code is not in the current file

Making classes work together without stepping on methods, etc gets complex

Spaghetti Object Oriented Programming

Page 23: Professional JavaScript: AntiPatterns

FilteringSelect MappedTextBox ValidationTextBox TextBox _FormValueWidget _FormWidget _Widget _TemplatedMixin _CssStateMixin _FormWidgetMixin _FormValueMixin _FormWidgetMixin _TextBoxMixin ComboBoxMixin _HasDropDown _FocusMixin _WidgetBase Stateful Destroyable _AutoCompleterMixin SearchMixin

Even MOAH SOOPOver-inheritance is one of the current problems with Dojo

I needed to inspect this code for what the server query should look like. What file do you think that was in?

Page 24: Professional JavaScript: AntiPatterns

MOAR SOOPInconsistent object methods

Violation of the Composition pattern

chart.setRect({}); graph.rect({}); settings.rect = {}; series.dimensions({});

Too much functionality

9000-line class files defeat the whole purpose of OOP

Lack of encapsulation

foo.bar.bop.hop.doThing();

Even worse for setting properties

Page 25: Professional JavaScript: AntiPatterns

Abstractions / ProxiesDon't do it. This ain't Java.

More overhead than direct coding, wasting precious bytes

Because it is not native to the language, there is a danger of creating an unmaintainable abstraction

Alternative:

Comment your damn code.

Smaller classes

Page 26: Professional JavaScript: AntiPatterns

CallbacksYes, callbacks can be an anti-pattern

Even simple callbacks as an argument is weak: foo(x, onDone);

Passing a callback more than once quickly gets messy

You may have heard of… callback hell.

Puts ownership of the callback in the wrong object, which needs to check for existence

We have more modern techniques like Promises*

*Although most implementations suck

Page 27: Professional JavaScript: AntiPatterns

Getters and Setters

Potential for crazy side effects

The DOM has it wrong

Not just an anti-pattern, getters and setters are

Class = {get x(){

this.node.style.left = x + ‘px’;this.name = null;delete this.age;document.title = ‘My Crazy Page’;

}};

EVIL!

Page 28: Professional JavaScript: AntiPatterns

Private PropertiesCan’t use the debugger to inspect objects

Can cause major problems in inheritance

Uses extra memory (in object instances)

Especially true in library code

Business code is less critical, but then… what are you hiding, and from whom?

http://clubajax.org/javascript-private-variables-are-evil/

Suggest the underscore convention or an interface to emphasize public API

EVIL!

Page 29: Professional JavaScript: AntiPatterns

Properties

In ref to setting from an external source

The state of a class should be handled by the class

If multiple objects are setting a property and something goes wrong, you don’t know who dunnit

Settings objects and arrays may lose fields

Accessing a property may not be in sync with the DOM, or necessitate overhead to keep them in sync

Now you may think I’ve gone off the rails. But...

MSDN Properties vs. Methods

Okay, properties aren’t evil. But you should consider when and how you use them.

EVIL!

Page 30: Professional JavaScript: AntiPatterns

Refactoring

Page 31: Professional JavaScript: AntiPatterns

Why Refactor

All projects at some point need to pay down technical debt

Eventually more time will be spent servicing the debt than on increasing value

Accumulated technical debt becomes a major disincentive to work on a project

Doing things quick and dirty sets us up with a technical debt, which is similar to a financial debt, which incurs interest payments: the extra effort to do in future development

Concepts courtesy of Jeff Atwood, Coding Horror

Page 32: Professional JavaScript: AntiPatterns

When to Refactor

It's been six months and you feel you are smarter now

You read about a cool pattern on Hacker News

You found a cool jQuery plugin that has some sweet chaining

You want to completely rewrite the project because the last dev used spaces instead of tabs

Never.

Reasons we refactor:

When your boss lets you refactor:

Page 33: Professional JavaScript: AntiPatterns

How to Refactor

For small refactors, do them as you goFor medium refactors, hide them in tasksFor large refactors, use git, work in a branchMajor rewrites should be done in chunks

Don’t panic, do a little at a time, even if meaningless

Reorganize

Run tests often

ABRAlways Be Refactoring

As a project grows (and all projects grow) there will be a continual need to refactor

Page 34: Professional JavaScript: AntiPatterns

Solutions

Page 35: Professional JavaScript: AntiPatterns

require.jsYou are using it, aren’t you?

Global namespaces are SO 2011

Globals can clash with other code, they are slow to access, and they do not garbage collect

require.js provides a mini-framework, enforcing packages, namespaces, and modules

Also consider:

Browserify - CommonJS

uRequire - UMD

Page 36: Professional JavaScript: AntiPatterns

Frameworks

Dojo

Ember

Angular

ExtJS

YUI

Marionette

Good for teams with less experience

But don’t get too excited. Even with the

magical MVC patternframeworks will only get you

part way there.

Page 37: Professional JavaScript: AntiPatterns

Coupling

Tight LooseContent coupling

When one module modifies or relies on the internal workings of another module

Common couplingWhen two modules share the same global data

External couplingWhen two modules share an externally imposed data format, communication protocol, or device interface.

Control couplingOne module controlling the flow of another, by passing it information

Data-structured couplingWhen modules share a composite data structure

Data couplingWhen modules share data through, for example, parameters. Each datum is an elementary piece, and these are the only data shared

Message couplingComponent communication is done via parameters or message passing

No couplingModules do not communicate at all with one another.

Page 38: Professional JavaScript: AntiPatterns

Encapsulation my boy...Encapsulation is a form of data hiding; data, methods, and properties. Only the interface or API is exposed.

The primary goal:Easier for the developer to comprehend small objects vs. large routines

But also:Ownership of its own concepts, data, and functionalityBreaks up functionality into smaller objectsIt reduces the mystery of which module owns what

Page 39: Professional JavaScript: AntiPatterns

Object Composition: no internal details of composed objects need be visible in the code using them

Two or three inheritance levels, or, very small changes

Subclass Coupling

The child is connected to its parent, but the parent is not connected to the child

For a child to talk to its parent violates a basic programming principle of high and low level functionality

Proper Object Oriented Programming*

*I have not consider any acronyms for this

Page 40: Professional JavaScript: AntiPatterns

TDD

Reduce dependenciesEncapsulate functionalityCreate an intuitive interfaceKeep the logic simple

Though multiple simple functions may be complex

The Failures of "Intro to TDD"

Proper Test Driven Development means writing the tests first - forcing you to consider the consequences before you commit to them. You’ll find ways to:

Page 41: Professional JavaScript: AntiPatterns

Naming and ArrangingDon't expect to cram it all into preconceived model-view-controller folders, it's too limiting, and the app will grow out of it

Remember, the code path flows down, not up, so the deeper modules should not have access to root or higher level modules

Page 42: Professional JavaScript: AntiPatterns

Business LogicAs much as possible, business logic should be as separate from application logic, and especially the UI

business

application

UI

low level functionality

Even if this means extra code is needed to maintain the

separation

Page 43: Professional JavaScript: AntiPatterns

Important Patterns

Page 44: Professional JavaScript: AntiPatterns

! pubsub.publish('/module/loaded', {success:true});

! pubsub.subscribe('/module/loaded', function(object){! ! console.log('load success:', object.success);! });

pubsubPros:

Library code is very simpleCan access distant areas of app

Cons:Not guaranteed - pub can fire before sub is readyCan be hard to follow code paths between unrelated modules Over-use can lead to bad habits and sloppy codeShould only be used with very loosely coupled objects

Page 45: Professional JavaScript: AntiPatterns

ObservablesLike events that you can setSimilar to event streams in that they fire on change, and you can also read the current property

myName = obeservable('Mike');console.log(myName); // Mikeobeservable('Bob');console.log(myName); // BobmyName.subscribe(function(value){! if(value !== 'Mike'){! ! console.log('Hey! My name is not ', value);! }});

Page 46: Professional JavaScript: AntiPatterns

Template Behaviors

Attach DOM behavior to the templateSeparates behavior from widgetFurther decouples codeA good way to keep business logic separate from DOM logicHelps for unit testingMVVM is a good TDD solution

<div id='menu' data-bind='ko.menu'></div>

ko.bind(‘#menu’, widgetWithMenu);

Page 47: Professional JavaScript: AntiPatterns

PluginsUse the concept for separating codeVarious patterns for making a module pluggabledepends on codePlugin may need intimate knowledge of host objectImportance is separation of concepts for maintainabilityPlugins aren't usually portable to other host objects anyway

Page 48: Professional JavaScript: AntiPatterns

dataLoader = {! ! onDataLoaded: function(data){! ! ! this.emit('dataloaded', data);! ! }}

function onDataLoaded(data){! ! console.log(data);}dataLoader.on('dataloaded', onDataLoaded);

Event EmittersThe new hotnessMultiple connections can be madeIntent is clear - obvious they are events and what they doMuch better pattern than pubsub for objects that relate

Page 49: Professional JavaScript: AntiPatterns

graph = {! removeSeries: function(serieId){! ! var serieData = this.serieMap[serieId].data.clone();! ! this.serieMap[serieId].destroy();! ! return serieData;! }}chart = {! removeSeries: function(seriesId){! ! var graph = this.findSerieOwner();! ! var seriesData = graph.removeSeries(seriesId);! ! engine.removeSeriesData(seriesData);! ! renderer.render();! }};chart.removeSeries(seriesId);

Event Versatility

Although this code is not bad, it is rigid. To operate on the series object, access always needs to be through the top of the hierarchy: the chart.

Without the use of events, this logic is necessary for methods to fire in the correct order.

Page 50: Professional JavaScript: AntiPatterns

Event Versatilityseries = {! remove: function(){! ! this.emit('remove-series', this);

this.destroy();! }};graph = {! on('remove-series', function(series){! ! delete this.seriesMap[series.id];! }, this);};chart = {! on('remove-series', function(series){! ! engine.removeSeriesData(series.data);! ! renderer.render();! }, this);};series = chart.getSeriesById(serieId);series.remove();series.isSelected(true);series.addData([4,5,6]);

Here, remove() can be called directly on the series, from anywhere inside or outside of the chart, and events will bubble up in the correct order.

This also makes sense semantically, as series methods are called on the series, and not the chart.

Page 51: Professional JavaScript: AntiPatterns

Conclusion

Page 52: Professional JavaScript: AntiPatterns

Refactor!

Little projects always become BIG projects

You will never have all the information up front

Sales can turn your app into Frankenstein

Practice defensive coding - build it knowing it will change

Use best practices and your code will be maintainable - and more enjoyable to work with

In spite of what you might think, you don’t have the time to NOT do it right the first time

ABRAlways Be Refactoring

Page 54: Professional JavaScript: AntiPatterns