Hey guys, just figured out a trick with promises that makes the code look cleaner, is more readable, and just plain sweet!
Problem:
function recipeForSuccess(){
return barAsync()
.then(function(barData){
return fooAsync(barData);
})
.then(function(fooData){
// getFooBar requires fooData and myBar
// but I only have access to fooData within this functions scope
return getFooBar(fooData, myBar);
})
.catch(function (err) {
console.log(err);
});
}
Solutions:
The "Old" Way:
Whenever you needed access to data down the promise chain you tied it to a variable scoped outside of your promise chain.
Example:
function recipeForSuccess(){
var myBar; // will be defined when the barAsync promise is resolved
return barAsync()
.then(function(barData){
myBar = barData; // defining myBar for use further down the promise chain.
return fooAsync(barData);
})
.then(function(fooData){
// now I have access to the data returned from barAsync by using myBar.
return getFooBar(fooData, myBar);
})
.catch(function (err) {
console.log(err);
});
}
Downsides: This clutters the code up and when you read it from top to bottom you have to bounce around to figure out where myFoo is coming from / being defined.
The "New" Way:
Instead of declaring myFoo at the beginning and then assigning data to it half way through the Promise chain, why don't we just pass it to the next Promise in the chain? This improves the code by allowing the code to flow from top to bottom only and removes unnecessary variable declarations that clutter up the code.
function recipeForSuccess(){
return barAsync()
.then(function(barData){
return Q.all([
fooAsync(barData),
barData
]);
})
.then(function(data){
// data[0] will contain whatever is returned by barAsync(fooData);
// data[1] will contain fooData
return getFooBar(data[0], data[1]);
})
.catch(function (err) {
console.log(err);
});
}
You can also write it like this:
function recipeForSuccess(){
return barAsync()
.then(function(barData){
return Q.all([
fooAsync(barData),
barData
]);
})
.spread(function(fooData, barData){
return getFooBar(fooData, barData);
})
.catch(function (err) {
console.log(err);
});
}
Spare Me The Lecture
Simple Communication from Software Engineers to Software Engineers
Thursday, October 2, 2014
The Importance of Cards and Pull Requests
We move pretty fast compared to most development groups. A small team with lots of communication means moving fast isn't too dangerous. However, as we bring in more people and projects, that's going to get a lot harder.
As developers, there are a few simple things you can do that won't take much time, but will go a long way toward preventing critical issues and bugs in production.
Whenever you work on a new feature, enhancement, or bug fix, make sure there is a card on the project's Trello board that describes the work you will be doing. Add as much detail as makes sense without being too verbose.
Once you are sure you are working from a card, create a new branch for the changes in the card. It's important that you have a card for each change, and a branch for each card. It may not always be possible or practical to work this way, but those should be rare exceptions.
When you are finished with the branch and create a pull request to merge back to master, your pull request should only contain changes for that one card. That way it's easy to review and test pull requests and have a fairly good idea of what in the project is being affected.
This keeps each card on the fastest track to production. If multiple cards are combined in a pull request, and something needs to get to production fast, things will slow down as we untangle the implications of multiple changes in a branch and make sure everything is tested correctly.
Once your pull request is submitted, make sure you have a conversation with your code reviewer and your DevOps engineer. The conversation should cover what the implications of the change are, what else could be affected, what testing have you done on your own code, and what else may need to be tested. This conversation doesn't need to be longer than five minutes, unless your change is really large or complex.
One of the keys to moving fast is keeping things simple. A good way to keep things simple is one change per card - one card per branch/pull request - and a release conversation before every pull request gets pushed to production.
As developers, there are a few simple things you can do that won't take much time, but will go a long way toward preventing critical issues and bugs in production.
Whenever you work on a new feature, enhancement, or bug fix, make sure there is a card on the project's Trello board that describes the work you will be doing. Add as much detail as makes sense without being too verbose.
Once you are sure you are working from a card, create a new branch for the changes in the card. It's important that you have a card for each change, and a branch for each card. It may not always be possible or practical to work this way, but those should be rare exceptions.
When you are finished with the branch and create a pull request to merge back to master, your pull request should only contain changes for that one card. That way it's easy to review and test pull requests and have a fairly good idea of what in the project is being affected.
This keeps each card on the fastest track to production. If multiple cards are combined in a pull request, and something needs to get to production fast, things will slow down as we untangle the implications of multiple changes in a branch and make sure everything is tested correctly.
Once your pull request is submitted, make sure you have a conversation with your code reviewer and your DevOps engineer. The conversation should cover what the implications of the change are, what else could be affected, what testing have you done on your own code, and what else may need to be tested. This conversation doesn't need to be longer than five minutes, unless your change is really large or complex.
One of the keys to moving fast is keeping things simple. A good way to keep things simple is one change per card - one card per branch/pull request - and a release conversation before every pull request gets pushed to production.
User Experience Resources
I just wanted to throw out some great resources for anyone interested in User Experience. The concepts here are valuable to just about any software developer. Of course this list will get stale over time.
Regular articles
Foundational articles
Inspiration and ideas
Printed Resources
- http://www.amazon.com/Design-Everyday-Things-Donald-Norman/dp/0465067107/ref=sr_1_1?s=books&ie=UTF8&qid=1368494218&sr=1-1&keywords=The+Design+of+Everyday+Things
- http://www.amazon.com/Dont-Make-Me-Think-Usability/dp/0321344758/ref=pd_sim_b_3
- http://www.amazon.com/Mobile-Usability-Jakob-Nielsen/dp/0321884485/
- http://www.amazon.com/Smashing-Design-Foundations-Designing-Experiences/dp/0470666854
- http://www.amazon.com/Things-Designer-People-Voices-Matter/dp/0321767535/
- http://www.amazon.com/UX-Book-Guidelines-Ensuring-Experience/dp/0123852412/
- http://www.amazon.com/Simple-Usable-Mobile-Interaction-Design/dp/0321703545/
Statistics
Resource for resources
Wednesday, September 10, 2014
Stop using thiz, that, and self...
Quite often, I come across code that needs to keep track of "this" while still calling inner functions or callbacks. In each case, "this" has been reassigned to some other variable to keep a handle on it. See below:
You can get rid of those pesky variables (thiz, that, self, _this, whatever) by just using Function.prototype.bind like so:
Huzzah!! We've got "this" back!
Edit:
The new ES6 syntax makes this a lot easier with big arrow syntax. It gives you a shorthand for writing functions while auto-binding your context. This:
(data) => {
// do stuff
}
Is equivalent to this:
function (data) {
// do stuff
}.bind(this);
Hence, the example turns into:
userObj.getExtraData = function (cb) {
getAsyncData((data) => {
this.extraData = data;
cb();
});
};
userObj.getExtraData(function () {
console.log(userObj.extraData);
});
userObj.getExtraData = function (cb) {
var that = this;
getAsyncData(function (data) {
that.extraData = data;
cb();
});
};
userObj.getExtraData(function () {
console.log(userObj.extraData);
});
You can get rid of those pesky variables (thiz, that, self, _this, whatever) by just using Function.prototype.bind like so:
userObj.getExtraData = function (cb) {
getAsyncData(function (data) {
this.extraData = data;
cb();
}.bind(this));
};
userObj.getExtraData(function () {
console.log(userObj.extraData);
});
Huzzah!! We've got "this" back!
Edit:
The new ES6 syntax makes this a lot easier with big arrow syntax. It gives you a shorthand for writing functions while auto-binding your context. This:
(data) => {
// do stuff
}
Is equivalent to this:
function (data) {
// do stuff
}.bind(this);
userObj.getExtraData = function (cb) {
getAsyncData((data) => {
this.extraData = data;
cb();
});
};
userObj.getExtraData(function () {
console.log(userObj.extraData);
});
Thursday, September 4, 2014
Don't make promises
Wrapping promises with promises to return promises that were already promises
When I first began to understand the value that promises could be to my code, I immediately began to misuse them. I would create a deferred and resolve it like this:
function getData() {
var deferred = $q.defer();
$http.get('http://some.url.com/data')
.success(function(data) {
deferred.resolve(data);
})
.error(function(data) {
deferred.reject(data);
});
return deferred.promise;
}
Then it occurred to me that $http returns a promise. So the above code was equivalent to (but slower than) this:
function getData() {
return $http.get('http://some.url.com/data');
}
That example used the Angular.js $q promise implementation, but the same code works with all of the other major promises implementations.Dealing with callbacks
Once I figured out that I could just return the promise, I realized that the only time I needed to create promises was when I was dealing with libraries like request, nano, fs, etc. Then I discovered bluebird's promisify and promisifyAll. Bluebird's promisifyAll creates functions that return promises instead of calling callbacks for almost any library. Here is an example with request:
var Promise = require('bluebird');
var _request = require('request');
var request = Promise.promisifyAll(_request);
exports.getData() {
return request.getAsync('http://some.url.com/data')
.then(sanitizeData)
.catch(logAndRethrow);
}
Don't make promises
So now there is almost no reason to make a promise. Library authors and dumb setTimeout examples aside, creating promises should be extremely rare.What is DevOps
This post isn't to point out bad or good coding practices, but to help us think about how we can better leverage the DevOps practices we have been putting in place.
DevOps is both a role and a process. It's a way of organizing your team and sharing responsibilities in order to produce higher quality software, faster.
DevOps, while a portmanteau of 'development' and 'operations', does not mean 'development operations'. Rather, it represents a blending of the traditionally separate processes of software development and IT operations.
As a methodology, DevOps means some fundamental changes to how traditional (and even agile) software teams work. In a sense, every team member becomes a DevOps engineer, if not in title. Developers must own their code from creation to production, no more "throwing it over the wall" to QA and SysOps.
As a role, DevOps takes responsibility for everything that happens from when the developer writes his code, through the deployment and operation of software in a production environment. This includes:
- quality assurance and testing activities
- creating and maintaining the build environments
- managing dev, test, and staging lanes
- deploying, monitoring, and maintaining in production
So, in practice what does this mean?
Testing
Since the developer owns his code from inception to production, he is responsible for testing it and guaranteeing it will work in a production environment. This usually means providing valuable unit tests, as well as functional and integration level testing, both manually and through automation. A DevOps engineer supports this testing in many ways:
- Providing a robust automation framework
- Being a "second set of eyes" for functional testing
- Assisting with adding automation tests
- Ensuring all needed testing happens, regardless of who does the testing
- Stress and performance testing
- Other types of system level tests when necessary (security, etc.)
It's important to note that while a DevOps engineer may take on a lot of the testing that a developer does not have time to focus on, this should not be done without the developers participation. Being aware of what is being tested and how, and what the results are, is how the developers best improve their code and even their coding skills.
Build Management
A DevOps engineer is responsible for creating and maintaining a build environment that provides:
- Continuous integration, including automation that can run automatically and inform the developer if their new code broke anything.
- Continuous deploy to test and stage environments, including additional automation tests where appropriate.
- Continuous deploy to production, including feedback to the development team on how their new release compares to other releases through environment monitoring.
SysOps
DevOps becomes responsible for managing and monitoring the production environment to guarantee uptime and provide performance and statistical reporting for the application. This should be provided continually as feedback to the development team.
Development
DevOps engineers also participate in the software development process. Primarily this is focused on library support and building the necessary pieces to support testing, continuous integration/deployment, monitoring, security, etc. This relieves the pressure from other developers, allowing them to focus on customer requirements.
However, the purpose of using DevOps methodologies is to maximize team velocity. So, when all of the items discussed above are handled, or when priorities dictate, a DevOps engineer can participate in feature development.
Team Organization
DevOps is a separate discipline from development, and is usually managed separately and deployed to software teams as needed, but it is important that they be considered as a member of a development team as integral as any other developer. They collaborate closely with all the developers and participate in meetings and discussions such as scrum, planning meetings, and customer requirements discussions.
Summary
While the DevOps engineer takes primary responsibility for the items above, the primary principle is that the team as a whole is responsible for the entire lifecycle of their product. DevOps engineers can participate in development tasks, and developers can participate in DevOps tasks.
The end goal is to create an environment where a project development team can operate self-sufficiently, without relying on outside groups. This means a given team will never need to wait for the availability of another group's time, or jockey for priority position with other competing projects. This eliminates dead time in the product lifecycle and keeps the team producing at maximum capacity during every phase of development. It also means the team is always available to respond to customer demands as quickly and efficiently as possible.
Friday, August 29, 2014
Valuable Logging
I was attempting to debug through an issue and ran into this line in the logs:
Not having gained any additional insight or value from the message I decided to track it down to determine if it was part of my bug. It was not. Not even close. But who would have known, except for the author of log entry, what "install data" or "query" it was referring to.
In order to be valuable, log entries should probably have a little more info. I propose the following:
date:time - log level: file/location [function] MSG: reasonable description
2014-08-29T15:14:53.648Z - info: Getting install data... 2014-08-29T15:14:54.015Z - info: Query took 2msec Not having gained any additional insight or value from the message I decided to track it down to determine if it was part of my bug. It was not. Not even close. But who would have known, except for the author of log entry, what "install data" or "query" it was referring to.
In order to be valuable, log entries should probably have a little more info. I propose the following:
date:time - log level: file/location [function] MSG: reasonable description
eg.2014-08-29T15:15:36.398Z - error: util/workday.js [getLinkedPerson] ERROR: TypeError: Cannot read property '68582' of undefined
If you have an error (either from a try/catch or from a promise), I would highly recommend writing out the stack like this:
try { ... } catch(err) { log.error(file + ' [' + fn + '] ERROR: ' + err.stack);}
Subscribe to:
Comments (Atom)