SlideShare a Scribd company logo
Unbreakable
Unbreakable
Craftsmanship
Unbreakable: The Craft of Code
Unbreakable: The Craft of Code
“Woz designed all the hardware and all the circuit boards
and all the software that went into the Apple II… not one
bug has ever been found … the circuit design of the
Apple II is widely considered to be astonishingly
beautiful, as close to perfection as one can get in
engineering.”
-- Vikram Chandra Geek Sublime
Unbreakable
Unbreakable
Craftsmanship
Unbreakable
Craftsmanship
Doing something good for its own sake
Unbreakable: The Craft of Code
Unbreakable: The Craft of Code
Woz Flow Chart:
Woz Flow Chart:
Be A
Genius
Build Stuff
Unbreakable: The Craft of Code
Woz Flow Chart:
Be A
Genius
Build Stuff
Learn
Learn
Learn
Learn
Learn
Learn
Learn
LearnLearn
Learn Learn
Learn
Learn
Build
Build
Build
Build
Build
Build
BuildBuild
Build
Build
Build
Build
Build
Learn
Learn
Build
Build
Build
Learn
Learn
LearnLearn
Build
Build
Build
Learn
Build
Build
Learn
Learn
LearnLearn
Build
Build
Build
Build
Learn
Learn
Learn
Build
Build
Build
Learn
Learn
Learn
Build
Build
Learn
Learn
Build
Build
Build
Joe Morgan
Joe Morgan
@joesmorgan
Joe Morgan
@joesmorgan
Lawrence, KS
I write code
I write code
I try to get a little better everyday
Stages on craftsmanship
Apprentice
Apprentice
Apprentice: Guidance
Journeymen
Journeymen: Experience and Intuition
Master
Master: Creativity
How does that relate to software?
# of Devs
Unbreakable: The Craft of Code
Unbreakable: The Craft of Code
It works!
Unbreakable: The Craft of Code
$$
Unbreakable: The Craft of Code
Love
Self Evaluation
Frontend CodeServer CodeServers
Frontend CodeServer CodeServers
Frontend CodeServer CodeServers
Frontend CodeServer CodeServers
Server Dev Ops AngularNoSQL Functional OOAWS SQL DB
Server Dev Ops AngularNoSQL Functional OOAWS SQL DB
Server Dev Ops AngularNoSQL Functional OOAWS SQL DB
Server Dev Ops Angular 2NoSQL Functional OOAWS SQL DB
How do we improve?
Eye: What can you see?
Eye: What can you see?
Hand: What can you build?
Eye: What can you see?
Hand: What can you build?
Mind: What do you know?
Unbreakable: The Craft of Code
Unbreakable: The Craft of Code
Unbreakable: The Craft of Code
[The chipping paint] could be an
example of machine-cutting
"without sharp blades," adding
"it looks like they painted before
they actually cut the piece."
[The chipping paint] could be an
example of machine-cutting
"without sharp blades," adding
"it looks like they painted before
they actually cut the piece."
Unbreakable: The Craft of Code
Unbreakable: The Craft of Code
[The chipping paint] could be an
example of machine-cutting
"without sharp blades," adding
"it looks like they painted before
they actually cut the piece."
$offset = $argv[0];
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20
OFFSET $offset;";
“Woz designed all the hardware and all the circuit boards
and all the software that went into the Appie II… not one
bug has ever been found … the circuit design of the
Apple II is widely considered to be astonishingly
beautiful, as close to perfection as one can get in
engineering.”
-- Vikram Chandra Geek Sublime
“Woz designed all the hardware and all the circuit boards
and all the software that went into the Apple II… not one
bug has ever been found … the circuit design of the
Apple II is widely considered to be astonishingly
beautiful, as close to perfection as one can get in
engineering.”
-- Vikram Chandra Geek Sublime
Unbreakable: The Craft of Code
Hand: What we normally associate with craft
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
Mind: How much you understand
Mind: How much you understand
We can test you on this!
FizzBuzz:
Write a function that prints the numbers
from 1 to 20.
Print “Fizz” for multiples of 3
Print “Buzz” for multiples of 5
Print “FizzBuzz” for multiples of 3 and 5
FizzBuzz:
for (let i=1; i <= 20; i++) {
if (i % 3 === 0 && i % 5 === 0) {
console.log("FizzBuzz");
} else if (i % 3 === 0) {
console.log("Fizz");
} else if (i % 5 === 0) {
console.log("Buzz");
} else {
console.log(i);
}
}
for (let i=1; i <= 20; i++) {
if (i % 3 === 0 && i % 5 === 0) {
console.log("FizzBuzz");
} else if (i % 3 === 0) {
console.log("Fizz");
} else if (i % 5 === 0) {
console.log("Buzz");
} else {
console.log(i);
}
}
FizzBuzz:
Who cares?
function load($array) {
if(is_array($array)) {
foreach($array as $key=>$value) {
$this->vars[$key] = $value;
}
}
}
function unload($vars = '') {
if($vars) {
if(is_array($vars)) {
foreach($vars as $var) {
unset($this->vars[$var]);
}
}
else {
unset($this->vars[$vars]);
}
}
else {
$this->vars = array();
}
function fetch() {
$name= $argv[0];
$query = "SELECT id, $name FROM products;";
$result = pg_query($conn, $query);
}
function get_all() {
if($this->isAdmin) {
return $this->vars;
}
}
function load($array) {
if(is_array($array)) {
foreach($array as $key=>$value) {
$this->vars[$key] = $value;
}
}
}
function unload($vars = '') {
if($vars) {
if(is_array($vars)) {
foreach($vars as $var) {
unset($this->vars[$var]);
}
}
else {
unset($this->vars[$vars]);
}
}
else {
$this->vars = array();
}
function fetch() {
$name= $argv[0];
$query = "SELECT id, $name FROM products;";
$result = pg_query($conn, $query);
}
function get_all() {
if($this->isAdmin) {
return $this->vars;
}
}
function load($array) {
if(is_array($array)) {
foreach($array as $key=>$value) {
$this->vars[$key] = $value;
}
}
}
function unload($vars = '') {
if($vars) {
if(is_array($vars)) {
foreach($vars as $var) {
unset($this->vars[$var]);
}
}
else {
unset($this->vars[$vars]);
}
}
else {
$this->vars = array();
}
function fetch() {
$name= $argv[0];
$query = "SELECT id, $name FROM products;";
$result = pg_query($conn, $query);
}
function get_all() {
if($this->isAdmin) {
return $this->vars;
}
}
Unbreakable: The Craft of Code
Unbreakable: The Craft of Code
Understand Style
Understand Style
“When program statements were arranged in a
sensible order, experts were able to remember
them better than novices. When statements
were shuffled, the experts’ superiority was
reduced”
-- Steve McConnell Code Complete
Understand Style
function email() {
var form =
document.getElementById('contact-form');
var re =
/^(([^<>()[].,;:s@"]+(.[^<>()[].,;:s@"]+)*)|(".+"))@(([[0-9]{1,3}.[0-9]{1,3}
.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z-0-9]+.)+[a-zA-Z]{2,}))$/;
for(input of form.elements) {if(input.test(re)) {
return input;
}
}
else{return false;
}
}
function AddToarray(arr, x)
{
var isInteger, updatedArr;
var val
isInteger = typeof x == 'number';
if (isInteger)
x = x + ''
var shoul_Add
if(Array.isArray(x)) {shoul_Add = false}
else {
shoul_Add=true
}
if(shoul_Add) {
if(x !== '14') {
arr.push(x);
}
}
return arr
}
function AddToarray(arr, x)
{
var isInteger, updatedArr;
var val
isInteger = typeof x == 'number';
if (isInteger)
x = x + ''
var shoul_Add
if(Array.isArray(x)) {shoul_Add = false}
else {
shoul_Add=true
}
if(shoul_Add) {
if(x !== '14') {
arr.push(x);
}
}
return arr
}
It works!
function AddToarray(arr, x)
{
var isInteger, updatedArr;
var val
isInteger = typeof x == 'number';
if (isInteger)
x = x + ''
var shoul_Add
if(Array.isArray(x)) {shoul_Add = false}
else {
shoul_Add=true
}
if(shoul_Add) {
if(x !== '14') {
arr.push(x);
}
}
return arr
}
Cultivate Dissatisfaction
function AddToarray(arr, x)
{
var isInteger, updatedArr;
var val
isInteger = typeof x == 'number';
if (isInteger)
x = x + ''
var shoul_Add
if(Array.isArray(x)) {shoul_Add = false}
else {
shoul_Add=true
}
if(shoul_Add) {
if(x !== '14') {
arr.push(x);
}
}
return arr
}
function AddToarray(arr, x)
{
var isInteger, updatedArr;
var val
isInteger = typeof x == 'number';
if (isInteger)
x = x + ''
var shoul_Add
if(Array.isArray(x)) {shoul_Add = false}
else {
shoul_Add=true
}
if(shoul_Add) {
if(x !== '14') {
arr.push(x);
}
}
return arr
}
Learn Community Standards
function AddToarray(arr, x)
{
var isInteger, updatedArr;
var val
isInteger = typeof x == 'number';
if (isInteger)
x = x + ''
var shoul_Add
if(Array.isArray(x)) {shoul_Add = false}
else {
shoul_Add=true
}
if(shoul_Add) {
if(x !== '14') {
arr.push(x);
}
}
return arr
}
Learn Community Standards:
Read Code
function AddToarray(arr, x)
{
var isInteger, updatedArr;
var val
isInteger = typeof x == 'number';
if (isInteger)
x = x + ''
var shoul_Add
if(Array.isArray(x)) {shoul_Add = false}
else {
shoul_Add=true
}
if(shoul_Add) {
if(x !== '14') {
arr.push(x);
}
}
return arr
}
Use A Linter
function addStringToArray(arr, value) {
const blackListedValue = '14';
if (Array.isArray(value)) {
return arr;
}
if (typeof value === 'number') {
value = value + '';
}
if (value !== blackListedValue) {
arr.push(value);
}
return arr;
}
function addStringToArray(arr, value) {
const blackListedValue = '14';
if (Array.isArray(value)) {
return arr;
}
if (typeof value === 'number') {
value = value + '';
}
if (value !== blackListedValue) {
arr.push(value);
}
return arr;
}
Readable.
Not Perfect.
Get Feedback
<?
class House {
public $rooms = 3;
public function getRooms() {
return $this->rooms;
}
}
$h = new House();
$h->getRooms();
// 3
<?
class House {
public $rooms = 3;
static $bathrooms = 1;
public function getRooms() {
return $this->rooms;
}
static function getBathrooms() {
return self::$bathrooms;
}
}
$h = new House();
$h->getRooms();
// 3
$h::getBathroom();
// 1
<?
class House {
public $rooms = 3;
static $bathrooms = 1;
public function getRooms() {
return $this->rooms;
}
static function getBathrooms() {
return self::$bathrooms;
}
}
$h = new House();
$h->getRooms();
// 3
$h::getBathroom();
// 1
Code Review
<?
class House {
public $rooms = 3;
static $bathrooms = 1;
public function getRooms() {
return $this->rooms;
}
static function getBathrooms() {
return self::$bathrooms;
}
}
$h = new House();
$h->getRooms();
// 3
$h::getBathroom();
// 1
Why is this static?
<?
class House {
public $rooms = 3;
static $bathrooms = 1;
public function getRooms() {
return $this->rooms;
}
static function getBathrooms() {
return self::$bathrooms;
}
}
$h = new House();
$h->getRooms();
// 3
$h::getBathroom();
// 1
Uhh…..
<?
class House {
public $rooms = 3;
static $bathrooms = 1;
public function getRooms() {
return $this->rooms;
}
static function getBathrooms() {
return self::$bathrooms;
}
}
$h = new House();
$h->getRooms();
// 3
$h::getBathroom();
// 1
Be Deliberate
<?
class House {
public $rooms = 3;
public function getRooms() {
return $this->rooms;
}
static $bath_rooms = '1';
static function get_Bathrooms() {
$arr = [];
$arr.push(self::$bath_rooms);
if(false){
return self;
}
return self::$bath_rooms;
}
}
<?
class House {
public $rooms = 3;
public function getRooms() {
return $this->rooms;
}
static $bath_rooms = '1';
static function get_Bathrooms() {
$arr = [];
$arr.push(self::$bath_rooms);
if(false){
return self;
}
return self::$bath_rooms;
}
}
Do it again. Then
give it back to me.
<?
class House {
public $rooms = 3;
public function getRooms() {
return $this->rooms;
}
static $bath_rooms = '1';
static function get_Bathrooms() {
$arr = [];
$arr.push(self::$bath_rooms);
if(false){
return self;
}
return self::$bath_rooms;
}
}
Reviewers
<?
class House {
public $rooms = 3;
public function getRooms() {
return $this->rooms;
}
static $bath_rooms = '1';
static function get_Bathrooms() {
$arr = [];
$arr.push(self::$bath_rooms);
if(false){
return self;
}
return self::$bath_rooms;
}
}
Reviewers: You are
not being nice when
you withhold
feedback.
<?
class House {
public $rooms = 3;
public function getRooms() {
return $this->rooms;
}
static $bath_rooms = '1';
static function get_Bathrooms() {
$arr = [];
$arr.push(self::$bath_rooms);
if(false){
return self;
}
return self::$bath_rooms;
}
}
No feedback?
<?
class House {
public $rooms = 3;
public function getRooms() {
return $this->rooms;
}
static $bath_rooms = '1';
static function get_Bathrooms() {
$arr = [];
$arr.push(self::$bath_rooms);
if(false){
return self;
}
return self::$bath_rooms;
}
}
No feedback?
Write Test
function addAuthor() {
fetch('http://foo.com')
.then(data => {
return data.json();
})
.then(author => {
const { name } = author;
if(!name) {
dispatchState('Unknown')
} else {
if(name.indexOf(',')) {
const names = name.split(',');
dispatchState(`${name[0]} ${names[1]}`)
} else {
dispatchState(name);
}
}
fetchPostsByAuthor(author.id);
})
}
function addAuthor() {
fetch('http://foo.com')
.then(data => {
return data.json();
})
.then(author => {
const { name } = author;
if(!name) {
dispatchState('Unknown')
} else {
if(name.indexOf(',')) {
const names = name.split(',');
dispatchState(`${name[0]} ${names[1]}`)
} else {
dispatchState(name);
}
}
fetchPostsByAuthor(author.id);
})
}
Test
function addAuthor() {
fetch('http://foo.com')
.then(data => {
return data.json();
})
.then(author => {
const { name } = author;
if(!name) {
dispatchState('Unknown')
} else {
if(name.indexOf(',')) {
const names = name.split(',');
dispatchState(`${name[0]} ${names[1]}`)
} else {
dispatchState(name);
}
}
fetchPostsByAuthor(author.id);
})
}
Async
Lots of conditionals
Calls another
method
Calls another
method
function addAuthor() {
fetch('http://foo.com')
.then(data => {
return data.json();
})
.then(author => {
const { name } = author;
if(!name) {
dispatchState('Unknown')
} else {
if(name.indexOf(',')) {
const names = name.split(',');
dispatchState(`${name[0]} ${names[1]}`)
} else {
dispatchState(name);
}
}
fetchPostsByAuthor(author.id);
})
}
Every test and every
assertion is a statement
of why code exists
function addAuthor() {
fetch('http://foo.com')
.then(data => {
return data.json();
})
.then(author => {
const { name } = author;
if(!name) {
dispatchState('Unknown')
} else {
if(name.indexOf(',')) {
const names = name.split(',');
dispatchState(`${name[0]} ${names[1]}`)
} else {
dispatchState(name);
}
}
fetchPostsByAuthor(author.id);
})
}
If it’s hard to test
function addAuthor() {
fetch('http://foo.com')
.then(data => {
return data.json();
})
.then(author => {
const { name } = author;
if(!name) {
dispatchState('Unknown')
} else {
if(name.indexOf(',')) {
const names = name.split(',');
dispatchState(`${name[0]} ${names[1]}`)
} else {
dispatchState(name);
}
}
fetchPostsByAuthor(author.id);
})
}
If it’s hard to test,
make it easier to test
Mind:
Mind: Not as important as you think
Mind: Not as important as you think
In Shakespeare:
The top 10 most frequently occuring words make up
21.4% of all words.
The top 100 most frequently occuring words make up
53.9% of all words.
Mind: Not as important as you think
✔ Basics
✔ Things work (at least it seems so)
✔ Learning by reading/doing
Relationships between parts
Relationships between parts
DNS Server Server Nginx/Apache
Index file
(backend)
RouterRender DOM
JavaScript
and CSS
Relationships between parts
/etc
/var
/sys
/sbin
/usr
Unbreakable: The Craft of Code
Make Code Beautiful
function formatPathStr(config, preferences, defaultPath, alternativePath, reroute, cb) {
var path;
// We need to check if they want
if(config && config.path && !config.path.match(/tmp/) && (defaultPath ||
alternativePath)) {
if(preferences.reroute && alternativePath) {
path = alternativePath + '/zone2/' + config.path;
return reroute(path); //Promise
}
if(!preferences.reroute && alternativePath) {
if(!alternativePath.match(/user/)) {
if(preferences.send) {
path = alternativePath + '/zone3' + config.path;
cb();
return;
}
path = alternativePath + '/zone2' + config.path;
}
}
// This goes on and on
}
return config.name + '/zone5/' + config.path;
}
Unbreakable: The Craft of Code
?????
Unbreakable: The Craft of Code
function formatPathStr(config, preferences, defaultPath, alternativePath, reroute, cb) {
var path;
// We need to check if they want
if(config && config.path && !config.path.match(/tmp/) && (defaultPath ||
alternativePath)) {
if(preferences.reroute && alternativePath) {
path = alternativePath + '/zone2/' + config.path;
return reroute(path); //Promise
}
if(!preferences.reroute && alternativePath) {
if(!alternativePath.match(/user/)) {
if(preferences.send) {
path = alternativePath + '/zone3' + config.path;
cb();
return;
}
path = alternativePath + '/zone2' + config.path;
}
}
// This goes on and on
}
return config.name + '/zone5/' + config.path;
}
function formatPathStr(config, preferences, defaultPath, alternativePath, reroute, cb) {
var path;
// We need to check if they want
if(config && config.path && !config.path.match(/tmp/) && (defaultPath ||
alternativePath)) {
if(preferences.reroute && alternativePath) {
path = alternativePath + '/zone2/' + config.path;
return reroute(path); //Promise
}
if(!preferences.reroute && alternativePath) {
if(!alternativePath.match(/user/)) {
if(preferences.send) {
path = alternativePath + '/zone3' + config.path;
cb();
return;
}
path = alternativePath + '/zone2' + config.path;
}
}
// This goes on and on
}
return config.name + '/zone5/' + config.path;
}
Smelly Code
function formatPathStr(config, preferences, defaultPath, alternativePath, reroute, cb) {
var path;
// We need to check if they want
if(config && config.path && !config.path.match(/tmp/) && (defaultPath ||
alternativePath)) {
if(preferences.reroute && alternativePath) {
path = alternativePath + '/zone2/' + config.path;
return reroute(path); //Promise
}
if(!preferences.reroute && alternativePath) {
if(!alternativePath.match(/user/)) {
if(preferences.send) {
path = alternativePath + '/zone3' + config.path;
cb();
return;
}
path = alternativePath + '/zone2' + config.path;
}
}
// This goes on and on
}
return config.name + '/zone5/' + config.path;
}
Long parameter list
Complex conditional blocks
Uncommunicative name
Differing return statements
function formatPathStr(config, preferences, defaultPath, alternativePath, reroute, cb) {
var path;
// We need to check if they want
if(config && config.path && !config.path.match(/tmp/) && (defaultPath ||
alternativePath)) {
if(preferences.reroute && alternativePath) {
path = alternativePath + '/zone2/' + config.path;
return reroute(path); //Promise
}
if(!preferences.reroute && alternativePath) {
if(!alternativePath.match(/user/)) {
if(preferences.send) {
path = alternativePath + '/zone3' + config.path;
cb();
return;
}
path = alternativePath + '/zone2' + config.path;
}
}
// This goes on and on
}
return config.name + '/zone5/' + config.path;
}
Coding Horror
Code Smells
function formatPathStr(config, preferences, defaultPath, alternativePath, reroute, cb) {
var path;
// We need to check if they want
if(config && config.path && !config.path.match(/tmp/) && (defaultPath ||
alternativePath)) {
if(preferences.reroute && alternativePath) {
path = alternativePath + '/zone2/' + config.path;
return reroute(path); //Promise
}
if(!preferences.reroute && alternativePath) {
if(!alternativePath.match(/user/)) {
if(preferences.send) {
path = alternativePath + '/zone3' + config.path;
cb();
return;
}
path = alternativePath + '/zone2' + config.path;
}
}
// This goes on and on
}
return config.name + '/zone5/' + config.path;
}
Don’t
memorize
smells.
function formatPathStr(config, preferences, defaultPath, alternativePath, reroute, cb) {
var path;
// We need to check if they want
if(config && config.path && !config.path.match(/tmp/) && (defaultPath ||
alternativePath)) {
if(preferences.reroute && alternativePath) {
path = alternativePath + '/zone2/' + config.path;
return reroute(path); //Promise
}
if(!preferences.reroute && alternativePath) {
if(!alternativePath.match(/user/)) {
if(preferences.send) {
path = alternativePath + '/zone3' + config.path;
cb();
return;
}
path = alternativePath + '/zone2' + config.path;
}
}
// This goes on and on
}
return config.name + '/zone5/' + config.path;
}
Trust yourself
Unbreakable: The Craft of Code
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
Unbreakable: The Craft of Code
Build Intuition and Refactoring
Pop Quiz
Build Intuition and Refactoring
1. Get an array of unique items
2. Determines if a string starts
with an uppercase letter
3. Calculate the area of a circle
given radius
function getUniqueItems(arr) {
let unique = [];
for(let item of arr) {
if(!unique.includes(item)) {
unique.push(item);
}
}
return unique;
}
function getUniqueItems(arr) {
let unique = [];
for(let item of arr) {
if(!unique.includes(item)) {
unique.push(item);
}
}
return unique;
}
Cultivate Disatisfaction
function getUniqueItems(arr) {
let unique = [];
for(let item of arr) {
if(!unique.includes(item)) {
unique.push(item);
}
}
return unique;
}
You can look up
solved problems
function getUniqueItems(arr) {
return arr.reduce((unique, item) => {
return unique.includes(item) ? unique : [...unique, item];
}, [])
}
function getUniqueItems(arr) {
return arr.reduce((unique, item) => {
return unique.includes(item) ? unique : [...unique, item];
}, [])
}
Iterate Over Ideas
function getUniqueItems(items) {
return [...new Set(items)];
}
function getUniqueItems(items) {
return [...new Set(items)];
}
Test
Tools, Principles, Patterns
Why do things exist
Why do things exist
Why do things exist
const x = ['a', 'b', 'c'];
...x
// 'a', 'b', 'c'
Solve the problem
const x = ['a', 'b', 'c'];
x.push('d');
x;
['a', 'b', 'c', 'd'];
const x = ['a', 'b', 'c'];
x.push('d');
x;
['a', 'b', 'c', 'd'];
What is the [X] way to solve the
problem?
const x = ['a', 'b', 'c'];
x.push('d');
x;
['a', 'b', 'c', 'd'];
What is the JavaScript way to
solve the problem?
const x = ['a', 'b', 'c'];
x.push('d');
x;
['a', 'b', 'c', 'd'];
What is the JavaScript way to
solve the problem?
mutation
const x = ['a', 'b', 'c'];
const y = [...x, 'd'];
y;
['a', 'b', 'c', 'd'];
x;
['a', 'b', 'c'];
What is the JavaScript way to
solve the problem?
const x = ['a', 'a', 'b', 'c'];
const copyX = [...x];
const combined = [...x, ...y];
const unique = [...new Set(x)];
What is the JavaScript way to
solve the problem?
What is the JavaScript way to
solve the problem?
const x = ['a', 'a', 'b', 'c'];
const copyX = [...x];
const combined = [...x, ...y];
const unique = [...new Set(x)];
No Mutations
What is the Python way to solve
the problem?
Suspicious Python Devs
Unbreakable: The Craft of Code
Unbreakable: The Craft of Code
Learn Principles
Learn Principles
… BUT they should really be
there to solidify what you’ve
been seeing
Learn Principles
This was the book that showed me there were
names for so many patterns that I’d discovered
purely through fumbling around with my own code.
-- Rebecca Murphey on JavaScript Patterns
Learn Principles
… they are there to help
communicate
Learn Principles
Experience without theory is
blind, but theory without
experience is mere intellectual
play -- Immanuel Kant
Learn Principles
… run everything through your
hands or eyes
Learn Principles
… Before I created Rails, I redrew many of the
diagrams in OmniGraffle for Martin Fowler because
I liked the book so much
-- David on Patterns of Enterprise Architecture
Learn Principles
…The trick to reading this book is to carefully read
through every single refactoring pattern and then
try to apply it on your code base (you don’t have to
commit if it doesn’t fix things). You can’t just blow
through it or you won’t really learn it.
-- David on Refactoring
Learn Principles
✔ Algorithms
✔ Mutations/Side Effects
✔ Patterns (Gang of Four)
✔ SOLID
Unbreakable: The Craft of Code
Unbreakable: The Craft of Code
See Abstractions Across Projects
Unbreakable: The Craft of Code
“We have included only designs
that have been applied more
than once in different systems…
Most … have never been
documented before”
-- Gang of Four Design Patterns
“How many times have you
had design déjà vu -- that
feeling that you’ve solved a
problem before but not
knowing exactly where or
how?
Unbreakable: The Craft of Code
Foo.js
Foo.js -- src
-- components
-- button.js
-- link.js
-- footer.js
-- util
-- colors
Foo.js -- src
-- components
-- button.js
-- link.js
-- footer.js
-- util
-- colors
Reusable
Rule of three:
“The first time you do something, you just do
it. The second time you do something similar,
you wince at the duplication, but you do the
duplicate thing anyway. Third you do
something similar, you refactor.”
-- Martin Fowler Refactoring
Decoupled
Unbreakable: The Craft of Code
import fetch from 'isomorphic-fetch';
function updateInventory(store) {
return fetch(`http://foo.com/inventory?key=abc&name=${store}`)
.then(response => response.json())
.then((result) => {
const count = result.inventory.toLocaleString();
return `Inventory is ${count}.`;
});
}
export default updateInventory;
import fetch from 'isomorphic-fetch';
function updateInventory(store) {
return fetch(`http://foo.com/inventory?key=abc&name=${store}`)
.then(response => response.json())
.then((result) => {
const count = result.inventory.toLocaleString();
return `Inventory is ${count}.`;
});
}
export default updateInventory;
Tightly coupled to api
import expect from 'expect';
import nock from 'nock';
import updateInventory from './coupled1';
describe('inventory update', () => {
it('should update inventory', () => {
nock('http://foo.com')
.get('/inventory')
.query({
key: 'abc',
name: 'bar',
})
.reply(200, { inventory: 1350 });
return updateInventory('bar').then((result) => {
expect(result).toEqual('Inventory is 1,350.');
});
});
});
import expect from 'expect';
import nock from 'nock';
import updateInventory from './coupled1';
describe('inventory update', () => {
it('should update inventory', () => {
nock('http://foo.com')
.get('/inventory')
.query({
key: 'abc',
name: 'bar',
})
.reply(200, { inventory: 1350 });
return updateInventory('bar').then((result) => {
expect(result).toEqual('Inventory is 1,350.');
});
});
});
Hijack the http request
import { getInventory } from './inventoryManager';
function updateInventory(store) {
return getInventory(store)
.then((result) => {
const count = result.inventory.toLocaleString();
return `Inventory is ${count}.`;
});
}
export default updateInventory;
import { getInventory } from './inventoryManager';
function updateInventory(store) {
return getInventory(store)
.then((result) => {
const count = result.inventory.toLocaleString();
return `Inventory is ${count}.`;
});
}
export default updateInventory;
Endpoint removed
import expect from 'expect';
import sinon from 'sinon';
import updateInventory from './coupled';
import * as i from './inventoryManager';
describe('inventory update', () => {
beforeEach(() => {
const fetchInventory = new Promise(resolve => resolve({ inventory: 1350 }));
sinon.stub(i, 'getInventory').returns(fetchInventory);
});
afterEach(() => {
i.getInventory.restore();
});
it('should update inventory', () => updateInventory('bar').then((result) => {
expect(result).toEqual('Inventory is 1,350.');
}));
});
import expect from 'expect';
import sinon from 'sinon';
import updateInventory from './coupled';
import * as i from './inventoryManager';
describe('inventory update', () => {
beforeEach(() => {
const fetchInventory = new Promise(resolve => resolve({ inventory: 1350 }));
sinon.stub(i, 'getInventory').returns(fetchInventory);
});
afterEach(() => {
i.getInventory.restore();
});
it('should update inventory', () => updateInventory('bar').then((result) => {
expect(result).toEqual('Inventory is 1,350.');
}));
});
Mocking
function updateInventory(store, fetchInventory) {
return fetchInventory
.then((result) => {
const count = result.inventory.toLocaleString();
return `Inventory is ${count}.`;
});
}
export default updateInventory;
function updateInventory(store, fetchInventory) {
return fetchInventory
.then((result) => {
const count = result.inventory.toLocaleString();
return `Inventory is ${count}.`;
});
}
export default updateInventory;
Inject everything
import expect from 'expect';
import updateInventory from './coupled3';
describe('inventory update', () => {
it('should update inventory', () => {
const fetchInventory = new Promise(resolve => {
resolve({ inventory: 1350 })
});
return updateInventory('bar', fetchInventory).then((result) => {
expect(result).toEqual('Inventory is 1,350.');
});
});
});
Unbreakable: The Craft of Code
Creatively Apply Ideas
Master the why of your language
first
Redux
Dan Abramov
Wanted to speak at a JS
conference in Europe.
Redux
Dan Abramov
Applied Elm architecture in React
Redux
Elm
UI
UpdateView
message
model
HTML
Redux
UI
ReducerView
action
state
HTML
JavaScript
Brendan Eich was recruited to make Scheme into a
scripting language. Netscape partner Sun Systems
demanded it be similar to Java.
Brendan Eich wrote it in 10 days.
Unbreakable: The Craft of Code
Linux
Linus Torvalds wanted to try out a new
chip.
Linux
I'm doing a (free) operating system (just a hobby, won't be big and
professional like gnu)... This has been brewing since april, and is
starting to get ready. I'd like any feedback on things people
like/dislike in minix, as my OS resembles it somewhat (same
physical layout of the file-system...
Why
Bored.
Curious.
Dictated.
Why
They understood their inspirational
material enough to take what they needed.
Style Feedback Flow
Beautiful Refactor Patterns
Abstract Reusable Creativity
Why?
Joe Morgan
@joesmorgan | thejoemorgan.com

More Related Content

PDF
ES6 patterns in the wild
PDF
JavaScript and the AST
PDF
ES2015 workflows
PDF
Simulator customizing & testing for Xcode 9
PDF
Inteligencia artificial 4
PDF
스위프트를 여행하는 히치하이커를 위한 스타일 안내
PDF
The Ring programming language version 1.6 book - Part 55 of 189
PDF
Essentials and Impactful Features of ES6
ES6 patterns in the wild
JavaScript and the AST
ES2015 workflows
Simulator customizing & testing for Xcode 9
Inteligencia artificial 4
스위프트를 여행하는 히치하이커를 위한 스타일 안내
The Ring programming language version 1.6 book - Part 55 of 189
Essentials and Impactful Features of ES6

What's hot (20)

PDF
Error Management: Future vs ZIO
PDF
History of jQuery
PDF
How I started to love design patterns
PDF
Building Real Time Systems on MongoDB Using the Oplog at Stripe
PDF
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
PDF
Building Real Time Systems on MongoDB Using the Oplog at Stripe
PDF
Introduction to CQRS and Event Sourcing
PDF
The Ring programming language version 1.10 book - Part 70 of 212
PDF
mobl
PDF
Node meetup feb_20_12
PDF
Symfony CoP: Form component
PDF
Writing Your App Swiftly
PDF
Hidden rocks in Oracle ADF
PDF
Say It With Javascript
PDF
Min-Maxing Software Costs
PDF
Min-Maxing Software Costs - Laracon EU 2015
PDF
Method::Signatures
PDF
Beautiful python - PyLadies
PDF
Decoupling with Design Patterns and Symfony2 DIC
PDF
Workshop 5: JavaScript testing
Error Management: Future vs ZIO
History of jQuery
How I started to love design patterns
Building Real Time Systems on MongoDB Using the Oplog at Stripe
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
Building Real Time Systems on MongoDB Using the Oplog at Stripe
Introduction to CQRS and Event Sourcing
The Ring programming language version 1.10 book - Part 70 of 212
mobl
Node meetup feb_20_12
Symfony CoP: Form component
Writing Your App Swiftly
Hidden rocks in Oracle ADF
Say It With Javascript
Min-Maxing Software Costs
Min-Maxing Software Costs - Laracon EU 2015
Method::Signatures
Beautiful python - PyLadies
Decoupling with Design Patterns and Symfony2 DIC
Workshop 5: JavaScript testing
Ad

Similar to Unbreakable: The Craft of Code (20)

PDF
Raising the Bar
ZIP
Back To The Future.Key 2
PDF
Functional Programming Patterns (BuildStuff '14)
KEY
Architectures for Inclusive Design
PDF
Legacy is Good
PDF
The Seneca Pattern at EngineYard Distill 2013 Conference
PPTX
Refactoring workshop
PDF
Software Engineering
PDF
Lessons from a coding veteran - Web Directions @Media
PDF
Clean code in JavaScript
PPTX
Old code doesn't stink
PDF
10 Reasons Why we Love Some APIs and Why we Hate Some Others
PDF
Technology: A Means to an End with Thibault Imbert
PDF
FITC '14 Toronto - Technology, a means to an end
PDF
Experiments in Reasoning
PPTX
Software Development: Beyond Training wheels
PDF
Rails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
PPTX
CPP11 - Function Design
PDF
The Functional Programming Toolkit (NDC Oslo 2019)
KEY
TxJS 2011
Raising the Bar
Back To The Future.Key 2
Functional Programming Patterns (BuildStuff '14)
Architectures for Inclusive Design
Legacy is Good
The Seneca Pattern at EngineYard Distill 2013 Conference
Refactoring workshop
Software Engineering
Lessons from a coding veteran - Web Directions @Media
Clean code in JavaScript
Old code doesn't stink
10 Reasons Why we Love Some APIs and Why we Hate Some Others
Technology: A Means to an End with Thibault Imbert
FITC '14 Toronto - Technology, a means to an end
Experiments in Reasoning
Software Development: Beyond Training wheels
Rails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
CPP11 - Function Design
The Functional Programming Toolkit (NDC Oslo 2019)
TxJS 2011
Ad

More from Joe Morgan (6)

PDF
Git the easy way
PDF
React Animations
PPTX
Reference Interviews: Where the fun never ends
PPT
Statement of Teaching Philosophy
PPT
Option 2
PPTX
Library Committee Meeting
Git the easy way
React Animations
Reference Interviews: Where the fun never ends
Statement of Teaching Philosophy
Option 2
Library Committee Meeting

Recently uploaded (20)

PDF
CIFDAQ's Teaching Thursday: Moving Averages Made Simple
PDF
madgavkar20181017ppt McKinsey Presentation.pdf
PDF
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
PPTX
ABU RAUP TUGAS TIK kelas 8 hjhgjhgg.pptx
PPTX
How to Build Crypto Derivative Exchanges from Scratch.pptx
PDF
KodekX | Application Modernization Development
PDF
Google’s NotebookLM Unveils Video Overviews
PDF
BLW VOCATIONAL TRAINING SUMMER INTERNSHIP REPORT
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
CIFDAQ's Market Wrap: Ethereum Leads, Bitcoin Lags, Institutions Shift
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PPTX
Telecom Fraud Prevention Guide | Hyperlink InfoSystem
PDF
Chapter 2 Digital Image Fundamentals.pdf
PDF
Automating ArcGIS Content Discovery with FME: A Real World Use Case
PDF
Enable Enterprise-Ready Security on IBM i Systems.pdf
PDF
Smarter Business Operations Powered by IoT Remote Monitoring
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
creating-agentic-ai-solutions-leveraging-aws.pdf
PDF
GamePlan Trading System Review: Professional Trader's Honest Take
PDF
REPORT: Heating appliances market in Poland 2024
CIFDAQ's Teaching Thursday: Moving Averages Made Simple
madgavkar20181017ppt McKinsey Presentation.pdf
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
ABU RAUP TUGAS TIK kelas 8 hjhgjhgg.pptx
How to Build Crypto Derivative Exchanges from Scratch.pptx
KodekX | Application Modernization Development
Google’s NotebookLM Unveils Video Overviews
BLW VOCATIONAL TRAINING SUMMER INTERNSHIP REPORT
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
CIFDAQ's Market Wrap: Ethereum Leads, Bitcoin Lags, Institutions Shift
Chapter 3 Spatial Domain Image Processing.pdf
Telecom Fraud Prevention Guide | Hyperlink InfoSystem
Chapter 2 Digital Image Fundamentals.pdf
Automating ArcGIS Content Discovery with FME: A Real World Use Case
Enable Enterprise-Ready Security on IBM i Systems.pdf
Smarter Business Operations Powered by IoT Remote Monitoring
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
creating-agentic-ai-solutions-leveraging-aws.pdf
GamePlan Trading System Review: Professional Trader's Honest Take
REPORT: Heating appliances market in Poland 2024

Unbreakable: The Craft of Code

  • 5. “Woz designed all the hardware and all the circuit boards and all the software that went into the Apple II… not one bug has ever been found … the circuit design of the Apple II is widely considered to be astonishingly beautiful, as close to perfection as one can get in engineering.” -- Vikram Chandra Geek Sublime
  • 12. Woz Flow Chart: Be A Genius Build Stuff
  • 14. Woz Flow Chart: Be A Genius Build Stuff Learn Learn Learn Learn Learn Learn Learn LearnLearn Learn Learn Learn Learn Build Build Build Build Build Build BuildBuild Build Build Build Build Build Learn Learn Build Build Build Learn Learn LearnLearn Build Build Build Learn Build Build Learn Learn LearnLearn Build Build Build Build Learn Learn Learn Build Build Build Learn Learn Learn Build Build Learn Learn Build Build Build
  • 19. I write code I try to get a little better everyday
  • 28. How does that relate to software?
  • 34. $$
  • 36. Love
  • 42. Server Dev Ops AngularNoSQL Functional OOAWS SQL DB
  • 43. Server Dev Ops AngularNoSQL Functional OOAWS SQL DB
  • 44. Server Dev Ops AngularNoSQL Functional OOAWS SQL DB
  • 45. Server Dev Ops Angular 2NoSQL Functional OOAWS SQL DB
  • 46. How do we improve?
  • 47. Eye: What can you see?
  • 48. Eye: What can you see? Hand: What can you build?
  • 49. Eye: What can you see? Hand: What can you build? Mind: What do you know?
  • 53. [The chipping paint] could be an example of machine-cutting "without sharp blades," adding "it looks like they painted before they actually cut the piece."
  • 54. [The chipping paint] could be an example of machine-cutting "without sharp blades," adding "it looks like they painted before they actually cut the piece."
  • 57. [The chipping paint] could be an example of machine-cutting "without sharp blades," adding "it looks like they painted before they actually cut the piece."
  • 58. $offset = $argv[0]; $query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
  • 59. “Woz designed all the hardware and all the circuit boards and all the software that went into the Appie II… not one bug has ever been found … the circuit design of the Apple II is widely considered to be astonishingly beautiful, as close to perfection as one can get in engineering.” -- Vikram Chandra Geek Sublime
  • 60. “Woz designed all the hardware and all the circuit boards and all the software that went into the Apple II… not one bug has ever been found … the circuit design of the Apple II is widely considered to be astonishingly beautiful, as close to perfection as one can get in engineering.” -- Vikram Chandra Geek Sublime
  • 62. Hand: What we normally associate with craft
  • 63. function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk;
  • 64. function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk;
  • 65. function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk;
  • 66. Mind: How much you understand
  • 67. Mind: How much you understand We can test you on this!
  • 68. FizzBuzz: Write a function that prints the numbers from 1 to 20. Print “Fizz” for multiples of 3 Print “Buzz” for multiples of 5 Print “FizzBuzz” for multiples of 3 and 5
  • 69. FizzBuzz: for (let i=1; i <= 20; i++) { if (i % 3 === 0 && i % 5 === 0) { console.log("FizzBuzz"); } else if (i % 3 === 0) { console.log("Fizz"); } else if (i % 5 === 0) { console.log("Buzz"); } else { console.log(i); } }
  • 70. for (let i=1; i <= 20; i++) { if (i % 3 === 0 && i % 5 === 0) { console.log("FizzBuzz"); } else if (i % 3 === 0) { console.log("Fizz"); } else if (i % 5 === 0) { console.log("Buzz"); } else { console.log(i); } } FizzBuzz: Who cares?
  • 71. function load($array) { if(is_array($array)) { foreach($array as $key=>$value) { $this->vars[$key] = $value; } } } function unload($vars = '') { if($vars) { if(is_array($vars)) { foreach($vars as $var) { unset($this->vars[$var]); } } else { unset($this->vars[$vars]); } } else { $this->vars = array(); } function fetch() { $name= $argv[0]; $query = "SELECT id, $name FROM products;"; $result = pg_query($conn, $query); } function get_all() { if($this->isAdmin) { return $this->vars; } }
  • 72. function load($array) { if(is_array($array)) { foreach($array as $key=>$value) { $this->vars[$key] = $value; } } } function unload($vars = '') { if($vars) { if(is_array($vars)) { foreach($vars as $var) { unset($this->vars[$var]); } } else { unset($this->vars[$vars]); } } else { $this->vars = array(); } function fetch() { $name= $argv[0]; $query = "SELECT id, $name FROM products;"; $result = pg_query($conn, $query); } function get_all() { if($this->isAdmin) { return $this->vars; } }
  • 73. function load($array) { if(is_array($array)) { foreach($array as $key=>$value) { $this->vars[$key] = $value; } } } function unload($vars = '') { if($vars) { if(is_array($vars)) { foreach($vars as $var) { unset($this->vars[$var]); } } else { unset($this->vars[$vars]); } } else { $this->vars = array(); } function fetch() { $name= $argv[0]; $query = "SELECT id, $name FROM products;"; $result = pg_query($conn, $query); } function get_all() { if($this->isAdmin) { return $this->vars; } }
  • 77. Understand Style “When program statements were arranged in a sensible order, experts were able to remember them better than novices. When statements were shuffled, the experts’ superiority was reduced” -- Steve McConnell Code Complete
  • 78. Understand Style function email() { var form = document.getElementById('contact-form'); var re = /^(([^<>()[].,;:s@"]+(.[^<>()[].,;:s@"]+)*)|(".+"))@(([[0-9]{1,3}.[0-9]{1,3} .[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z-0-9]+.)+[a-zA-Z]{2,}))$/; for(input of form.elements) {if(input.test(re)) { return input; } } else{return false; } }
  • 79. function AddToarray(arr, x) { var isInteger, updatedArr; var val isInteger = typeof x == 'number'; if (isInteger) x = x + '' var shoul_Add if(Array.isArray(x)) {shoul_Add = false} else { shoul_Add=true } if(shoul_Add) { if(x !== '14') { arr.push(x); } } return arr }
  • 80. function AddToarray(arr, x) { var isInteger, updatedArr; var val isInteger = typeof x == 'number'; if (isInteger) x = x + '' var shoul_Add if(Array.isArray(x)) {shoul_Add = false} else { shoul_Add=true } if(shoul_Add) { if(x !== '14') { arr.push(x); } } return arr } It works!
  • 81. function AddToarray(arr, x) { var isInteger, updatedArr; var val isInteger = typeof x == 'number'; if (isInteger) x = x + '' var shoul_Add if(Array.isArray(x)) {shoul_Add = false} else { shoul_Add=true } if(shoul_Add) { if(x !== '14') { arr.push(x); } } return arr } Cultivate Dissatisfaction
  • 82. function AddToarray(arr, x) { var isInteger, updatedArr; var val isInteger = typeof x == 'number'; if (isInteger) x = x + '' var shoul_Add if(Array.isArray(x)) {shoul_Add = false} else { shoul_Add=true } if(shoul_Add) { if(x !== '14') { arr.push(x); } } return arr }
  • 83. function AddToarray(arr, x) { var isInteger, updatedArr; var val isInteger = typeof x == 'number'; if (isInteger) x = x + '' var shoul_Add if(Array.isArray(x)) {shoul_Add = false} else { shoul_Add=true } if(shoul_Add) { if(x !== '14') { arr.push(x); } } return arr } Learn Community Standards
  • 84. function AddToarray(arr, x) { var isInteger, updatedArr; var val isInteger = typeof x == 'number'; if (isInteger) x = x + '' var shoul_Add if(Array.isArray(x)) {shoul_Add = false} else { shoul_Add=true } if(shoul_Add) { if(x !== '14') { arr.push(x); } } return arr } Learn Community Standards: Read Code
  • 85. function AddToarray(arr, x) { var isInteger, updatedArr; var val isInteger = typeof x == 'number'; if (isInteger) x = x + '' var shoul_Add if(Array.isArray(x)) {shoul_Add = false} else { shoul_Add=true } if(shoul_Add) { if(x !== '14') { arr.push(x); } } return arr } Use A Linter
  • 86. function addStringToArray(arr, value) { const blackListedValue = '14'; if (Array.isArray(value)) { return arr; } if (typeof value === 'number') { value = value + ''; } if (value !== blackListedValue) { arr.push(value); } return arr; }
  • 87. function addStringToArray(arr, value) { const blackListedValue = '14'; if (Array.isArray(value)) { return arr; } if (typeof value === 'number') { value = value + ''; } if (value !== blackListedValue) { arr.push(value); } return arr; } Readable. Not Perfect.
  • 89. <? class House { public $rooms = 3; public function getRooms() { return $this->rooms; } } $h = new House(); $h->getRooms(); // 3
  • 90. <? class House { public $rooms = 3; static $bathrooms = 1; public function getRooms() { return $this->rooms; } static function getBathrooms() { return self::$bathrooms; } } $h = new House(); $h->getRooms(); // 3 $h::getBathroom(); // 1
  • 91. <? class House { public $rooms = 3; static $bathrooms = 1; public function getRooms() { return $this->rooms; } static function getBathrooms() { return self::$bathrooms; } } $h = new House(); $h->getRooms(); // 3 $h::getBathroom(); // 1 Code Review
  • 92. <? class House { public $rooms = 3; static $bathrooms = 1; public function getRooms() { return $this->rooms; } static function getBathrooms() { return self::$bathrooms; } } $h = new House(); $h->getRooms(); // 3 $h::getBathroom(); // 1 Why is this static?
  • 93. <? class House { public $rooms = 3; static $bathrooms = 1; public function getRooms() { return $this->rooms; } static function getBathrooms() { return self::$bathrooms; } } $h = new House(); $h->getRooms(); // 3 $h::getBathroom(); // 1 Uhh…..
  • 94. <? class House { public $rooms = 3; static $bathrooms = 1; public function getRooms() { return $this->rooms; } static function getBathrooms() { return self::$bathrooms; } } $h = new House(); $h->getRooms(); // 3 $h::getBathroom(); // 1 Be Deliberate
  • 95. <? class House { public $rooms = 3; public function getRooms() { return $this->rooms; } static $bath_rooms = '1'; static function get_Bathrooms() { $arr = []; $arr.push(self::$bath_rooms); if(false){ return self; } return self::$bath_rooms; } }
  • 96. <? class House { public $rooms = 3; public function getRooms() { return $this->rooms; } static $bath_rooms = '1'; static function get_Bathrooms() { $arr = []; $arr.push(self::$bath_rooms); if(false){ return self; } return self::$bath_rooms; } } Do it again. Then give it back to me.
  • 97. <? class House { public $rooms = 3; public function getRooms() { return $this->rooms; } static $bath_rooms = '1'; static function get_Bathrooms() { $arr = []; $arr.push(self::$bath_rooms); if(false){ return self; } return self::$bath_rooms; } } Reviewers
  • 98. <? class House { public $rooms = 3; public function getRooms() { return $this->rooms; } static $bath_rooms = '1'; static function get_Bathrooms() { $arr = []; $arr.push(self::$bath_rooms); if(false){ return self; } return self::$bath_rooms; } } Reviewers: You are not being nice when you withhold feedback.
  • 99. <? class House { public $rooms = 3; public function getRooms() { return $this->rooms; } static $bath_rooms = '1'; static function get_Bathrooms() { $arr = []; $arr.push(self::$bath_rooms); if(false){ return self; } return self::$bath_rooms; } } No feedback?
  • 100. <? class House { public $rooms = 3; public function getRooms() { return $this->rooms; } static $bath_rooms = '1'; static function get_Bathrooms() { $arr = []; $arr.push(self::$bath_rooms); if(false){ return self; } return self::$bath_rooms; } } No feedback? Write Test
  • 101. function addAuthor() { fetch('http://foo.com') .then(data => { return data.json(); }) .then(author => { const { name } = author; if(!name) { dispatchState('Unknown') } else { if(name.indexOf(',')) { const names = name.split(','); dispatchState(`${name[0]} ${names[1]}`) } else { dispatchState(name); } } fetchPostsByAuthor(author.id); }) }
  • 102. function addAuthor() { fetch('http://foo.com') .then(data => { return data.json(); }) .then(author => { const { name } = author; if(!name) { dispatchState('Unknown') } else { if(name.indexOf(',')) { const names = name.split(','); dispatchState(`${name[0]} ${names[1]}`) } else { dispatchState(name); } } fetchPostsByAuthor(author.id); }) } Test
  • 103. function addAuthor() { fetch('http://foo.com') .then(data => { return data.json(); }) .then(author => { const { name } = author; if(!name) { dispatchState('Unknown') } else { if(name.indexOf(',')) { const names = name.split(','); dispatchState(`${name[0]} ${names[1]}`) } else { dispatchState(name); } } fetchPostsByAuthor(author.id); }) } Async Lots of conditionals Calls another method Calls another method
  • 104. function addAuthor() { fetch('http://foo.com') .then(data => { return data.json(); }) .then(author => { const { name } = author; if(!name) { dispatchState('Unknown') } else { if(name.indexOf(',')) { const names = name.split(','); dispatchState(`${name[0]} ${names[1]}`) } else { dispatchState(name); } } fetchPostsByAuthor(author.id); }) } Every test and every assertion is a statement of why code exists
  • 105. function addAuthor() { fetch('http://foo.com') .then(data => { return data.json(); }) .then(author => { const { name } = author; if(!name) { dispatchState('Unknown') } else { if(name.indexOf(',')) { const names = name.split(','); dispatchState(`${name[0]} ${names[1]}`) } else { dispatchState(name); } } fetchPostsByAuthor(author.id); }) } If it’s hard to test
  • 106. function addAuthor() { fetch('http://foo.com') .then(data => { return data.json(); }) .then(author => { const { name } = author; if(!name) { dispatchState('Unknown') } else { if(name.indexOf(',')) { const names = name.split(','); dispatchState(`${name[0]} ${names[1]}`) } else { dispatchState(name); } } fetchPostsByAuthor(author.id); }) } If it’s hard to test, make it easier to test
  • 107. Mind:
  • 108. Mind: Not as important as you think
  • 109. Mind: Not as important as you think In Shakespeare: The top 10 most frequently occuring words make up 21.4% of all words. The top 100 most frequently occuring words make up 53.9% of all words.
  • 110. Mind: Not as important as you think ✔ Basics ✔ Things work (at least it seems so) ✔ Learning by reading/doing
  • 112. Relationships between parts DNS Server Server Nginx/Apache Index file (backend) RouterRender DOM JavaScript and CSS
  • 116. function formatPathStr(config, preferences, defaultPath, alternativePath, reroute, cb) { var path; // We need to check if they want if(config && config.path && !config.path.match(/tmp/) && (defaultPath || alternativePath)) { if(preferences.reroute && alternativePath) { path = alternativePath + '/zone2/' + config.path; return reroute(path); //Promise } if(!preferences.reroute && alternativePath) { if(!alternativePath.match(/user/)) { if(preferences.send) { path = alternativePath + '/zone3' + config.path; cb(); return; } path = alternativePath + '/zone2' + config.path; } } // This goes on and on } return config.name + '/zone5/' + config.path; }
  • 118. ?????
  • 120. function formatPathStr(config, preferences, defaultPath, alternativePath, reroute, cb) { var path; // We need to check if they want if(config && config.path && !config.path.match(/tmp/) && (defaultPath || alternativePath)) { if(preferences.reroute && alternativePath) { path = alternativePath + '/zone2/' + config.path; return reroute(path); //Promise } if(!preferences.reroute && alternativePath) { if(!alternativePath.match(/user/)) { if(preferences.send) { path = alternativePath + '/zone3' + config.path; cb(); return; } path = alternativePath + '/zone2' + config.path; } } // This goes on and on } return config.name + '/zone5/' + config.path; }
  • 121. function formatPathStr(config, preferences, defaultPath, alternativePath, reroute, cb) { var path; // We need to check if they want if(config && config.path && !config.path.match(/tmp/) && (defaultPath || alternativePath)) { if(preferences.reroute && alternativePath) { path = alternativePath + '/zone2/' + config.path; return reroute(path); //Promise } if(!preferences.reroute && alternativePath) { if(!alternativePath.match(/user/)) { if(preferences.send) { path = alternativePath + '/zone3' + config.path; cb(); return; } path = alternativePath + '/zone2' + config.path; } } // This goes on and on } return config.name + '/zone5/' + config.path; } Smelly Code
  • 122. function formatPathStr(config, preferences, defaultPath, alternativePath, reroute, cb) { var path; // We need to check if they want if(config && config.path && !config.path.match(/tmp/) && (defaultPath || alternativePath)) { if(preferences.reroute && alternativePath) { path = alternativePath + '/zone2/' + config.path; return reroute(path); //Promise } if(!preferences.reroute && alternativePath) { if(!alternativePath.match(/user/)) { if(preferences.send) { path = alternativePath + '/zone3' + config.path; cb(); return; } path = alternativePath + '/zone2' + config.path; } } // This goes on and on } return config.name + '/zone5/' + config.path; } Long parameter list Complex conditional blocks Uncommunicative name Differing return statements
  • 123. function formatPathStr(config, preferences, defaultPath, alternativePath, reroute, cb) { var path; // We need to check if they want if(config && config.path && !config.path.match(/tmp/) && (defaultPath || alternativePath)) { if(preferences.reroute && alternativePath) { path = alternativePath + '/zone2/' + config.path; return reroute(path); //Promise } if(!preferences.reroute && alternativePath) { if(!alternativePath.match(/user/)) { if(preferences.send) { path = alternativePath + '/zone3' + config.path; cb(); return; } path = alternativePath + '/zone2' + config.path; } } // This goes on and on } return config.name + '/zone5/' + config.path; } Coding Horror Code Smells
  • 124. function formatPathStr(config, preferences, defaultPath, alternativePath, reroute, cb) { var path; // We need to check if they want if(config && config.path && !config.path.match(/tmp/) && (defaultPath || alternativePath)) { if(preferences.reroute && alternativePath) { path = alternativePath + '/zone2/' + config.path; return reroute(path); //Promise } if(!preferences.reroute && alternativePath) { if(!alternativePath.match(/user/)) { if(preferences.send) { path = alternativePath + '/zone3' + config.path; cb(); return; } path = alternativePath + '/zone2' + config.path; } } // This goes on and on } return config.name + '/zone5/' + config.path; } Don’t memorize smells.
  • 125. function formatPathStr(config, preferences, defaultPath, alternativePath, reroute, cb) { var path; // We need to check if they want if(config && config.path && !config.path.match(/tmp/) && (defaultPath || alternativePath)) { if(preferences.reroute && alternativePath) { path = alternativePath + '/zone2/' + config.path; return reroute(path); //Promise } if(!preferences.reroute && alternativePath) { if(!alternativePath.match(/user/)) { if(preferences.send) { path = alternativePath + '/zone3' + config.path; cb(); return; } path = alternativePath + '/zone2' + config.path; } } // This goes on and on } return config.name + '/zone5/' + config.path; } Trust yourself
  • 127. function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk;
  • 129. Build Intuition and Refactoring
  • 130. Pop Quiz Build Intuition and Refactoring
  • 131. 1. Get an array of unique items 2. Determines if a string starts with an uppercase letter 3. Calculate the area of a circle given radius
  • 132. function getUniqueItems(arr) { let unique = []; for(let item of arr) { if(!unique.includes(item)) { unique.push(item); } } return unique; }
  • 133. function getUniqueItems(arr) { let unique = []; for(let item of arr) { if(!unique.includes(item)) { unique.push(item); } } return unique; } Cultivate Disatisfaction
  • 134. function getUniqueItems(arr) { let unique = []; for(let item of arr) { if(!unique.includes(item)) { unique.push(item); } } return unique; } You can look up solved problems
  • 135. function getUniqueItems(arr) { return arr.reduce((unique, item) => { return unique.includes(item) ? unique : [...unique, item]; }, []) }
  • 136. function getUniqueItems(arr) { return arr.reduce((unique, item) => { return unique.includes(item) ? unique : [...unique, item]; }, []) } Iterate Over Ideas
  • 137. function getUniqueItems(items) { return [...new Set(items)]; }
  • 138. function getUniqueItems(items) { return [...new Set(items)]; } Test
  • 140. Why do things exist
  • 141. Why do things exist
  • 142. Why do things exist const x = ['a', 'b', 'c']; ...x // 'a', 'b', 'c'
  • 143. Solve the problem const x = ['a', 'b', 'c']; x.push('d'); x; ['a', 'b', 'c', 'd'];
  • 144. const x = ['a', 'b', 'c']; x.push('d'); x; ['a', 'b', 'c', 'd']; What is the [X] way to solve the problem?
  • 145. const x = ['a', 'b', 'c']; x.push('d'); x; ['a', 'b', 'c', 'd']; What is the JavaScript way to solve the problem?
  • 146. const x = ['a', 'b', 'c']; x.push('d'); x; ['a', 'b', 'c', 'd']; What is the JavaScript way to solve the problem? mutation
  • 147. const x = ['a', 'b', 'c']; const y = [...x, 'd']; y; ['a', 'b', 'c', 'd']; x; ['a', 'b', 'c']; What is the JavaScript way to solve the problem?
  • 148. const x = ['a', 'a', 'b', 'c']; const copyX = [...x]; const combined = [...x, ...y]; const unique = [...new Set(x)]; What is the JavaScript way to solve the problem?
  • 149. What is the JavaScript way to solve the problem? const x = ['a', 'a', 'b', 'c']; const copyX = [...x]; const combined = [...x, ...y]; const unique = [...new Set(x)]; No Mutations
  • 150. What is the Python way to solve the problem?
  • 155. Learn Principles … BUT they should really be there to solidify what you’ve been seeing
  • 156. Learn Principles This was the book that showed me there were names for so many patterns that I’d discovered purely through fumbling around with my own code. -- Rebecca Murphey on JavaScript Patterns
  • 157. Learn Principles … they are there to help communicate
  • 158. Learn Principles Experience without theory is blind, but theory without experience is mere intellectual play -- Immanuel Kant
  • 159. Learn Principles … run everything through your hands or eyes
  • 160. Learn Principles … Before I created Rails, I redrew many of the diagrams in OmniGraffle for Martin Fowler because I liked the book so much -- David on Patterns of Enterprise Architecture
  • 161. Learn Principles …The trick to reading this book is to carefully read through every single refactoring pattern and then try to apply it on your code base (you don’t have to commit if it doesn’t fix things). You can’t just blow through it or you won’t really learn it. -- David on Refactoring
  • 162. Learn Principles ✔ Algorithms ✔ Mutations/Side Effects ✔ Patterns (Gang of Four) ✔ SOLID
  • 167. “We have included only designs that have been applied more than once in different systems… Most … have never been documented before” -- Gang of Four Design Patterns
  • 168. “How many times have you had design déjà vu -- that feeling that you’ve solved a problem before but not knowing exactly where or how?
  • 170. Foo.js
  • 171. Foo.js -- src -- components -- button.js -- link.js -- footer.js -- util -- colors
  • 172. Foo.js -- src -- components -- button.js -- link.js -- footer.js -- util -- colors
  • 174. Rule of three: “The first time you do something, you just do it. The second time you do something similar, you wince at the duplication, but you do the duplicate thing anyway. Third you do something similar, you refactor.” -- Martin Fowler Refactoring
  • 177. import fetch from 'isomorphic-fetch'; function updateInventory(store) { return fetch(`http://foo.com/inventory?key=abc&name=${store}`) .then(response => response.json()) .then((result) => { const count = result.inventory.toLocaleString(); return `Inventory is ${count}.`; }); } export default updateInventory;
  • 178. import fetch from 'isomorphic-fetch'; function updateInventory(store) { return fetch(`http://foo.com/inventory?key=abc&name=${store}`) .then(response => response.json()) .then((result) => { const count = result.inventory.toLocaleString(); return `Inventory is ${count}.`; }); } export default updateInventory; Tightly coupled to api
  • 179. import expect from 'expect'; import nock from 'nock'; import updateInventory from './coupled1'; describe('inventory update', () => { it('should update inventory', () => { nock('http://foo.com') .get('/inventory') .query({ key: 'abc', name: 'bar', }) .reply(200, { inventory: 1350 }); return updateInventory('bar').then((result) => { expect(result).toEqual('Inventory is 1,350.'); }); }); });
  • 180. import expect from 'expect'; import nock from 'nock'; import updateInventory from './coupled1'; describe('inventory update', () => { it('should update inventory', () => { nock('http://foo.com') .get('/inventory') .query({ key: 'abc', name: 'bar', }) .reply(200, { inventory: 1350 }); return updateInventory('bar').then((result) => { expect(result).toEqual('Inventory is 1,350.'); }); }); }); Hijack the http request
  • 181. import { getInventory } from './inventoryManager'; function updateInventory(store) { return getInventory(store) .then((result) => { const count = result.inventory.toLocaleString(); return `Inventory is ${count}.`; }); } export default updateInventory;
  • 182. import { getInventory } from './inventoryManager'; function updateInventory(store) { return getInventory(store) .then((result) => { const count = result.inventory.toLocaleString(); return `Inventory is ${count}.`; }); } export default updateInventory; Endpoint removed
  • 183. import expect from 'expect'; import sinon from 'sinon'; import updateInventory from './coupled'; import * as i from './inventoryManager'; describe('inventory update', () => { beforeEach(() => { const fetchInventory = new Promise(resolve => resolve({ inventory: 1350 })); sinon.stub(i, 'getInventory').returns(fetchInventory); }); afterEach(() => { i.getInventory.restore(); }); it('should update inventory', () => updateInventory('bar').then((result) => { expect(result).toEqual('Inventory is 1,350.'); })); });
  • 184. import expect from 'expect'; import sinon from 'sinon'; import updateInventory from './coupled'; import * as i from './inventoryManager'; describe('inventory update', () => { beforeEach(() => { const fetchInventory = new Promise(resolve => resolve({ inventory: 1350 })); sinon.stub(i, 'getInventory').returns(fetchInventory); }); afterEach(() => { i.getInventory.restore(); }); it('should update inventory', () => updateInventory('bar').then((result) => { expect(result).toEqual('Inventory is 1,350.'); })); }); Mocking
  • 185. function updateInventory(store, fetchInventory) { return fetchInventory .then((result) => { const count = result.inventory.toLocaleString(); return `Inventory is ${count}.`; }); } export default updateInventory;
  • 186. function updateInventory(store, fetchInventory) { return fetchInventory .then((result) => { const count = result.inventory.toLocaleString(); return `Inventory is ${count}.`; }); } export default updateInventory; Inject everything
  • 187. import expect from 'expect'; import updateInventory from './coupled3'; describe('inventory update', () => { it('should update inventory', () => { const fetchInventory = new Promise(resolve => { resolve({ inventory: 1350 }) }); return updateInventory('bar', fetchInventory).then((result) => { expect(result).toEqual('Inventory is 1,350.'); }); }); });
  • 190. Master the why of your language first
  • 191. Redux
  • 192. Dan Abramov Wanted to speak at a JS conference in Europe. Redux
  • 193. Dan Abramov Applied Elm architecture in React Redux
  • 196. JavaScript Brendan Eich was recruited to make Scheme into a scripting language. Netscape partner Sun Systems demanded it be similar to Java. Brendan Eich wrote it in 10 days.
  • 198. Linux Linus Torvalds wanted to try out a new chip.
  • 199. Linux I'm doing a (free) operating system (just a hobby, won't be big and professional like gnu)... This has been brewing since april, and is starting to get ready. I'd like any feedback on things people like/dislike in minix, as my OS resembles it somewhat (same physical layout of the file-system...
  • 201. Why They understood their inspirational material enough to take what they needed.
  • 202. Style Feedback Flow Beautiful Refactor Patterns Abstract Reusable Creativity
  • 203. Why?
  • 204. Joe Morgan @joesmorgan | thejoemorgan.com