This document discusses JavaScript anti-patterns and provides recommendations for improving code maintainability. It begins by describing problematic code examples and structures. Common causes of bad architecture are then examined, including development processes, team issues, and overuse of techniques like inheritance. Specific anti-patterns like spaghetti code, callbacks, and private properties are called out. The document concludes by recommending patterns and practices that support loose coupling, encapsulation, testability and refactoring.
2. This code sucks!
if (target) {
if (target.targetParent.targetParent) {
target.targetParent.targetParent.targetParent = new Event(this);
} else {
target.targetParent.targetParent = new Event(this);
}
return target;
}
3. WTF??
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);
4. ilcox
ike W
M
Co m mitter!
HTML5
Graphic Design
JavaScript ES5
UI/UX architecture
CSS3
Works there.
Natch!
6. 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.
7. 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
8. AntiPattern
A 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
9. What to Expect
Discuss paradigms and patterns.
Good and bad.
Some patterns. Not all.
Focus on communications between
components.
Three words:
Maintainability, maintainability, maintainability
10. What is a Pattern?
Gang of Four
Addy Osmani
A design pattern is a general reusable solution
to a commonly occurring problem
11. Introduction
It 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
12. Hypothetical App
Let’s architect a chart application. The structure
and functionality should look like:
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.
13. Chart App
Here are the main components of our hypothetical chart
app below. How do you think they should be wired up?
chart
engine
renderer
graph
x axis
y axis
series
layer
painter(s)
14. Chart App Structure
graph-proxy
series-proxy
e
re to hi d
Proxies ass below
the me
layer-proxy
chart
engine
renderer
graph
x axis
y axis
series
layer
painter(s)
An example of how circuitous the code path can get if the structure is not
carefully considered before hand.
15. Chart App Structure
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.
chart
engine
renderer
graph
x axis
y axis
series
layer
painter(s)
16. Code Flow
Meets acceptance criteria
Done!
Clean
Readable
Maintainable
Extensible
Testable
The quick way is often not the correct way
19. Developer AntiPatterns
Not 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 Programmers
Smart 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
21. Procedural
Using 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
22. Spaghetti Object Oriented Programming
… 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
24. MOAR SOOP
Inconsistent 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
25. Abstractions / Proxies
Don'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
26. Callbacks
Yes, 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
27. Getters and Setters
Not just an anti-pattern, getters and setters are EVIL!
Class = {
get x(){
this.node.style.left = x + ‘px’;
this.name = null;
delete this.age;
document.title = ‘My Crazy Page’;
}
};
Potential for crazy side effects
The DOM has it wrong
28. Private Properties
EVIL!
Can’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?
Suggest the underscore convention or an interface to
emphasize public API
http:/
/clubajax.org/javascript-private-variables-are-evil/
29. Properties
EVIL!
Now you may think I’ve gone off the rails. But...
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
Okay, properties aren’t evil. But you should consider
MSDN Properties vs. Methods
when and how you use them.
31. Why Refactor
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
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
Concepts courtesy of Jeff Atwood, Coding Horror
32. When to Refactor
Reasons we 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
When your boss lets you refactor:
Never.
33. How to Refactor
Always Be Refactoring
ABR
As a project grows (and all projects grow) there will
be a continual need to refactor
For small refactors, do them as you go
For medium refactors, hide them in tasks
For large refactors, use git, work in a branch
Major rewrites should be done in chunks
Don’t panic, do a little at a time, even if meaningless
Reorganize
Run tests often
35. require.js
You 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
36. Frameworks
Good for teams with less experience
Dojo
Ember
Angular
ExtJS
YUI
Marionette
But don’t get too excited.
Even with the
magical MVC pattern
frameworks will only get you
part way there.
37. Coupling
Tight
Content coupling
When one module modifies or relies
on the internal workings of another
module
Common coupling
When two modules share the same
global data
External coupling
When two modules share an
externally imposed data format,
communication protocol, or device
interface.
Control coupling
One module controlling the flow of
another, by passing it information
Loose
Data-structured coupling
When modules share a composite
data structure
Data coupling
When modules share data through,
for example, parameters. Each
datum is an elementary piece, and
these are the only data shared
Message coupling
Component communication is done
via parameters or message passing
No coupling
Modules do not communicate at all
with one another.
38. 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
functionality
Breaks up functionality into smaller objects
It reduces the mystery of which module owns what
39. Proper Object Oriented Programming*
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
*I have not consider any acronyms for this
40. 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:
Reduce dependencies
Encapsulate functionality
Create an intuitive interface
Keep the logic simple
Though multiple simple functions may be complex
The Failures of "Intro to TDD"
41. Naming and Arranging
Don't expect to cram it all into preconceived modelview-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
42. Business Logic
As much as possible, business logic should be as separate
from application logic, and especially the UI
Even if this means extra code is
needed to maintain the
separation
business
application
UI
low level functionality
44. pubsub
Pros:
Library code is very simple
Can access distant areas of app
Cons:
Not guaranteed - pub can fire before sub is ready
Can be hard to follow code paths between unrelated modules
Over-use can lead to bad habits and sloppy code
Should only be used with very loosely coupled objects
! pubsub.publish('/module/loaded', {success:true});
! pubsub.subscribe('/module/loaded', function(object){
! ! console.log('load success:', object.success);
! });
45. Observables
Like events that you can set
Similar to event streams in that they fire on change,
and you can also read the current property
myName = obeservable('Mike');
console.log(myName); // Mike
obeservable('Bob');
console.log(myName); // Bob
myName.subscribe(function(value){
! if(value !== 'Mike'){
! ! console.log('Hey! My name is not ', value);
!}
});
46. Template Behaviors
<div id='menu' data-bind='ko.menu'></div>
ko.bind(‘#menu’, widgetWithMenu);
Attach DOM behavior to the template
Separates behavior from widget
Further decouples code
A good way to keep business logic separate from
DOM logic
Helps for unit testing
MVVM is a good TDD solution
47. Plugins
Use the concept for separating code
Various patterns for making a module pluggable
depends on code
Plugin may need intimate knowledge of host object
Importance is separation of concepts for
maintainability
Plugins aren't usually portable to other host objects
anyway
48. Event Emitters
The new hotness
Multiple connections can be made
Intent is clear - obvious they are events and what they do
Much better pattern than pubsub for objects that relate
dataLoader = {
! ! onDataLoaded: function(data){
! ! ! this.emit('dataloaded', data);
!! }
}
function onDataLoaded(data){
! ! console.log(data);
}
dataLoader.on('dataloaded', onDataLoaded);
49. Event Versatility
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();
Although this code is not bad, it is
!}
rigid. To operate on the series object,
};
access always needs to be through
chart.removeSeries(seriesId);
the top of the hierarchy: the chart.
Without the use of events, this logic
is necessary for methods to fire in
the correct order.
50. Event Versatility
series = {
! 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.
52. Refactor!
Always Be Refactoring
ABR
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