It's easy to get a bit pedantic with "best practices," but when I'm writing code and I get a gut feeling that something's not right or that something could be changed, I go over some well known best practices and attempt to see if the code I'm writing adheres to them. SOLID, while being principles of object oriented programming, can be useful to think about in other contexts. In this case, it seems to me that the function is violating the Single Responsibility Principle:
One of the most foundational principles of good design is:
Gather together those things that change for the same reason, and separate those things that change for different reasons.
This principle is often known as the Single Responsibility Principle or SRP. In short, it says that a subsystem, module, class, or even a function, should not have more than one reason to change.
(This could perhaps be exchanged for Separation of Concerns or other similar principles for this example, but the concept is the same.)
In this case, the function has two responsibilities: (1) getting the current (or default) list associated with a filename, and (2) appending data to said list. A first pass at separating these concerns might look something like this:
function get_current_list(filename, callback) {
fs.exists(filename, function(exists) {
if (exists) {
fs.readFile(filename, function(err, data) {
if (err)
return callback(err);
callback(null, JSON.parse(data));
});
} else
callback(null, []);
});
}
function list_append(filename, doc) {
get_current_list(filename, function(err, list) {
if(err) throw err;
__list_append(filename, list, doc);
});
}
Now, get_current_list
is only responsible for getting the current list in a file (or an empty array if there is no file), and __list_append
is (assumed to be) only responsible for appending to it; list_append
is now a simple integration point between these two functions. The functions are a bit more reusable and can also be tested more easily (as an aside, a test-first or TDD approach to programming can help you notice these kinds of things up front). Furthermore, repeating callback
in get_current_list
is quite a bit more generic than repeating __list_append
; if you need to change __list_append
to something else, it now is only called in one place.