Gleb Bahmutov PRO
JavaScript ninja, image processing expert, software quality fanatic
Technology that brings transparency to complex systems
Harvard Sq, WTC NYC
jobs@kensho.com
At large successful companies (Facebook, Google, MathWorks) each developer brings > $1,000,000 in revenue per year.
year | new profit per year | total profit per year |
---|---|---|
1 | $100k | $100k |
2 | $100k | $200k |
3 | $100k | $300k |
... | ||
10 | $100k | $1mil |
🎉
year | new profit per year | total profit per year |
---|---|---|
1 | $100k | $100k |
2 | $100k | $200k |
3 | $100k - $100 | $200k |
... | ||
10 | $100k - $100 | $200k |
🙁
rewriting software from year #1
Your value keeps increasing as long as the software you wrote keeps working
How much of what you wrote today (MIT 6.148) will survive in 1/5/10 years?
Help needed: commit-survivor
image source: https://unsplash.com/
image source: https://unsplash.com/
Put it on GitHub and reuse yourself. Tell others.
How to write OSS library http://slides.com/kentcdodds/write-oss#/
The First Pull Request
GET /users
module.exports =
function add(a, b) {...}
/tmp/my-code $ npm init --yes
Wrote to /tmp/my-code/package.json:
{
"name": "my-code",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"no tests\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
node package manager (npm)
/tmp/my-code $ npm install --save express
/tmp/my-code $ npm i -S debug
/tmp/my-code $ npm install --save-dev qunit
/tmp/my-code $ npm i -D mocha
{
"name": "my-code",
"dependencies": {
"express": "4.0.0",
"debug": "2.6.0"
},
"devDependencies": {
"qunit": "1.0.2",
"mocha": "3.2.0"
}
}
npm help install
/tmp/my-code $ npm info express
/tmp/my-code $ npm home express
npm help info
npm help home
git clone git@github.com:bahmutov/mit-6.148.git
cd mit-6.148
npm install
npm run lint
npm test
$ npm install -g yo generator-node-bahmutov
yo node-bahmutov
$ npm init --yes
Wrote to /tmp/my-code/package.json:
linter, unit testing, all necessary fields, Docker file, security, publishing, etc.
1.0.0
Widely used in JavaScript world
major.minor.patch
semver: how does API change between releases?
current version: 1.0.0
What is the new version?
commits:
- add a button
- fix title
- remove notification email
Hard to determine "meaning" of each commit
Humans should provide the meaning
git commit -m "feat(button): add login button"
commit type
current version: 1.0.0
- feat(button): add a button - fix(title): new title text - major(email): remove notification
current version: 1.0.0 (major.minor.patch)
- feat(...): ... - fix(...): ... - major(...): ...
increments minor
increments patch
increments major
next version: 2.0.0
Automate each release!
- feat(...): ... - fix(...): ... - major(...): ...
1.1.0
1.1.1
2.0.0
1.0.0
npm install --global semantic-release-cli
semantic-release-cli setup
{
"dependencies": {
"foo": 1.0.0
}
}
"foo" has versions 1.0.1, 1.1.0, 1.2.0 and 2.0.0 available
Can I upgrade to new version of "foo"?
{
"dependencies": {
"foo": 1.0.0
}
}
"foo" has versions 1.0.1, 1.1.0, 1.2.0 and 2.0.0 available
NO
YES
{
"dependencies": {
"foo": 1.0.0
}
}
"foo" has versions 1.0.1, 1.1.0, 1.2.0 and 2.0.0 available
No one knows for sure!
source: https://www.petersons.com/graduate-schools/sample-lsat-test-questions.aspx
unit tests, unit tests, unit tests, unit tests, unit tests, unit tests
integration tests, integration tests
end to end tests
linter, unit tests, unit tests, unit tests, crash reporting
integration tests, integration tests
end to end tests
linter, unit tests, unit tests, unit tests, crash reporting
integration tests, integration tests
end to end tests
// add.js
function add(a, b) {
return a + b
}
module.exports = addition
npm install --save-dev standard
{
"scripts": {
"test": "echo \"no tests\" && exit 1",
"lint": "standard --verbose --fix *.js",
"pretest": "npm run lint"
}
}
$ npm run lint
> standard --verbose --fix *.js
standard: Use JavaScript Standard Style (http://standardjs.com)
add.js:1:10: 'add' is defined but never used. (no-unused-vars)
add.js:4:18: 'addition' is not defined. (no-undef)
// add.js
function add(a, b) {
return a + b
}
module.exports = add
npm install --save-dev mocha
{
"scripts": {
"test": "mocha *-spec.js",
"lint": "standard --verbose --fix *.js",
"pretest": "npm run lint"
}
}
/* global describe, it */
describe('add', function () {
const add = require('./add')
it('adds two numbers', () => {
console.assert(add(2, 3) === 5)
})
})
const five = Promise.resolve(5)
describe('five', function () {
it('resolves to 5', () => {
return five.then(value =>
console.assert(value === 5)
)
})
})
Mocha
Ava
Jest
Chai
lazy-ass + check-more-types
image source: http://ktla.com/2016/04/02/small-plane-crashes-into-suv-on-15-freeway-in-san-diego-county/
undefined is not a function
if (process.env.NODE_ENV === 'production') {
var raven = require('raven');
var SENTRY_DSN = 'https://<DSN>@app.getsentry.com/...';
var client = new raven.Client(SENTRY_DSN);
client.patchGlobal();
}
foo.bar // this Error will be reported
npm install raven --save
Defensive coding: checking inputs before each computation (preconditions). Sometimes checking result value before returning (postconditions).
Paranoid coding: checking inputs before each computation, as if the caller was evil and trying to break the function on purpose.
const la = require('lazy-ass')
const is = require('check-more-types')
la(is.strings(names), 'expected list of names', names)
la(is.email(loginName), 'missing email')
la(is.version(tag) || check.sha(tag), 'invalid tag', tag)
linter, unit tests, unit tests, unit tests, crash reporting
end to end tests
image source: http://www.goodwp.com/tags/mechanism/
npm install
npm run build
npm start
npm install -g cypress-cli
cypress install
cypress open
To get invite:
- enter email at https://www.cypress.io/#footer
- ask politely at https://gitter.im/cypress-io/cypress
Added this project to Cypress
describe('2048-kensho', function(){
beforeEach(function(){
cy.visit('http://localhost:3000')
})
it('has 2048 title', function(){
cy.title().should('include', '2048')
})
})
Mocha + Chai assertions
cypress/integration/2048-kensho-spec.js
Cypress in action
describe('2048-kensho', function(){
beforeEach(function(){
cy.visit('/')
})
it('has 2048 title', function(){
cy.title().should('include', '2048')
})
})
{
"baseUrl": "http://localhost:3000"
}
Set "baseUrl" in cypress.json
it('starts with 3 tiles', () => {
cy.get('.tile-container')
.find('.tile')
.should('have.length', 3)
})
it('has header', () => {
cy.get('h1.title').should('be.visible')
.contains('kensho')
})
Simplicity always wins in the long term
Zeit Now, Dokku
Auth0
SSL and CSP
all you
git-extras
Questions, career advice, public speaking, professional development, mentorship - ping me, will be happy to help
By Gleb Bahmutov
How to write software that works now and will keep working in the future. Plus career advice! Presented as a guest lecture at MIT 6.148 class, January 20th 2017. Developer value, JavaScript modules, semver, linting, unit testing, e2e testing. Video at https://www.youtube.com/watch?v=MHNB6CGENKo
JavaScript ninja, image processing expert, software quality fanatic