Keep your modules and functions small
This post is about a very simple thing that I’ve been doing since I started to code (by coincidence) and that I feel that increases a lot the readability and organization of my code. Some people may not agree with it but for me it makes total sense and was also documented by some experienced developers like Uncle Bob on his great book Clean Code. I tend to think that a single approach may not be the best one for everybody - since every person thinks on a different way - but I’m pretty confident that this advise will be good to a lot of people and that it will increase the overall quality of the code.
The rule is simple, split larger functions/classes into smaller specialized ones, period. It will not only increase the readability but it will also make the code more reusable since it will be easier to override the default behavior if needed (especially if extending a class or reusing a 3rd party lib). I will try to explain how and give a basic example.
Why?
Basically because smaller functions are easier to understand and smaller files are easier to read. Try to read a very large paragraph and you will see that the information is way harder to grasp.
Another huge benefit in keeping your classes/modules small is that there is a big chance that it will have a very high cohesion (all functions are strongly-related), low cohesion is a very bad code smell. By grouping functions by context it will be very easy to identify which file should be edited and where. If the bug is on the “drop down menu” you should check the DropDown
class, if you need to change the way files are written to the system than you should update the fileWriter
, etc…
I’ve said it before and I’m going to say it again:
Comments are usually a sign of poor/lazy/confusing implementations. (source)
By keeping your functions small and by using descriptive names you will reduce the amount of necessary comments a lot, a few lines of well written code can say more to a developer than many pages of documentation (your mileage may vary). Don’t describe with comments what can be described with code!!
How?
Uncle Bob says that the optimal size of a functions is 1 line, and no, he is not talking about 1 line of mangled code with lots of chained methods, he is talking about 1 line of clean code that does one thing and does it well. It may seem drastic, but I must admit that it is a very good goal to keep in mind (even if you don’t follow it by heart).
Let’s use a simple example to describe it, first some CSS:
.awsum { background-color: #0ff; position: absolute; width: 300px; height: 300px; }
Now some JS code (using jQuery for brevity):
//this whole example is an anti-pattern function AwsumWidget(parent) { this.root = $('<div class="awsum"/>'); this.root .appendTo(parent) .fadeOut(0) .fadeIn(400) .click(function(evt) { $(this).animate({ top: Math.random() * 300, left: Math.random() * 300 }, 500, 'swing'); }); } AwsumWidget.prototype = { remove: function() { if (!this.root) return; var self = this; this.root.fadeOut(400, function() { self.root.remove(); delete self.root; }); } };
Now let’s suppose you need to create a RadWidget
, it should have all the basic functionality from the AwsumWidget
but it should do a different animation on click, how would you do it? Since all the logic is inside the constructor you will need to duplicate the code just to replace the click handler, not scalable/flexible at all… First step is to refactor AwsumWidget
so it’s easier to extend it and overwrite just what we need, lets also abstract some hard-coded values to make it easier to update - that’s also a very good practice, don’t leave “magic” numbers in the middle of the code, specially if you use it more than once, giving a name to the value will help describe what it does:
var MAX_POS = 300; var TRANSITION_DURATION = 400; function AwsumWidget(parent) { // constructors should not cause side-effects! // that way it is easier to delay execution if needed and test/mock it this._parent = parent; } AwsumWidget.prototype = { init: function() { this._create(); this._animateIn(); this._attachEvents(); }, _html: '<div class="awsum"/>', _create: function(parent) { this.root = $(this._html).appendTo(this._parent); }, _attachEvents: function() { this.root.click(this._animateOnClick); }, _animateOnClick: function(evt) { $(this).animate({ top: Math.random() * MAX_POS, left: Math.random() * MAX_POS }, TRANSITION_DURATION, 'swing'); }, _animateIn: function() { this.root.fadeOut(0).fadeIn(400); }, remove: function() { if (!this.root) return; this._animateOut(); }, _animateOut: function() { this.root.fadeOut(TRANSITION_DURATION, $.proxy(this._destroy, this)); }, _destroy: function() { this.root.remove(); delete this.root; } };
It’s clearly more code, but by breaking the code into smaller specialized functions you can override just the functions you need and the function names already describe what the code is doing so it’s easy to follow the program logic, the RadWidget
can be easily implemented as:
var MIN_SIZE = 50; var MAX_SIZE = 300; function RadWidget(parent) { AwsumWidget.call(this, parent); } //let's just assume we are on an environment that supports Object.create //https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create RadWidget.prototype = Object.create(AwsumWidget.prototype); RadWidget.prototype.init = function() { AwsumWidget.prototype.init.call(this); this.root.addClass('rad'); }; RadWidget.prototype._animateOnClick = function(evt) { var size = Math.max(Math.round(Math.random() * MAX_SIZE), MIN_SIZE); $(this).animate({ width: size, height: size }, TRANSITION_DURATION, 'swing'); };
We reused all the code from the AwsumWidget
and only changed the parts that we needed, that is the true power of breaking everything into separate functions, modular systems are more flexible by nature, since each part is treated as an individual piece you can replace them at will as long as you follow the proper interface.
Sum Up
Break your code into smaller pieces, it may seem more work but the benefits on the long run outweigh the initial overhead. And if you are thinking that it will slow down your code since you have more functions calls remember about the performance dogma, unless you have a very large loop doing something trivial the extra function calls shouldn’t be the bottleneck. It is better to have a clean/organized structure and tune it if needed than to have a huge pile of mess and not being able to identify where the real bottleneck might be or react to new requirements on a timely fashion.
By breaking your code into smaller blocks it will also help you follow the Single Responsibility Principle which I consider one of the best advices about program structure.
Try it for a while, the more you do it the more natural it will fell and it’s really impressive how much it improves the code readability and reusability. It’s very hard to foresee all the needs and later on the road it might be harder to change it, so my advise is to refactor often and keep it as modular as possible. Make code easier for you and future developers to understand and maintain, it will pay-off sooner than you expect.
That’s it!