A work in progress inspired by AirBnB's Style Guide.
To establish more consistency and readability across the Buffer team and our open source projects.
Have a bias toward clarity - from The Buffer Values
- We should always aim to write code that is clear and readable.
- Use whitespace. Add comments liberally where needed, but strive to write code that's clear and self documenting
- Always try to write code that clearly demonstrates and communicates it's intent.
Future sections may include: Commas+Semicolons, Comments, Conditional Expressions, Variables, Properties
-
Use camelCase variables and function names. use CAPS_UNDERSCORE for constants.
var updateText = 'Point Break is the best movie ever.'; function setUpdateText(arg) { //... // Constants var MAX_TWEET_LENGTH = 140;
-
Use PascalCase or CapFirst when creating a constructor or extending a Backbone class. Avoid using a capital first letter unless it's one of these.
function Profile(service){ this.service = service; } var myProfile = new Profile('facebook'); var ProfileView = Backbone.View.extend({ events: {} });
-
Use a leading _ to denote private methods
MyClass.prototype._privateMethod = function(){ //...
-
Be descriptive in the variables purpose.
var isFacebook = profile.get('service') === 'facebook'; // over... var facebook = profile.get('service') === 'facebook';
-
Remember to use
var
to declare your variables and avoid poluting the global namespace. -
Use one
var
declaration per line. Avoid re-use of a singlevar
declaration. This helps keep our commits simple when adding removing a variable since we won't have to edit spacing or commas/semi-colons.var open = false; var count = someCollection.length;
-
Declare unassigned variables last. This helps keep organized and shows what will be assigned throughout a function.
// good :) var input = document.querySelectorAll('.js-my-input'); var text = input.value; var isOverLimit; if (profile.get('service') === 'twitter') { isOverLimit = text > 140; } if (isOverLimit) { // ... } // ...not great :( if (profile.get('service') === 'twitter') { var isOverLimit = text > 140; } // variable may be undefined here if (isOverLimit) { // ... }
-
Assign variables at the top of functions. This avoids issues with hoisting and variable declaration
function myFunc($container) { var someBoolean = false; var isOpen; // ...other logic if (someBoolean === true) { isOpen = $container.hasClass('.open'); } // ...some more logic }
-
Use single quotes
''
over double quotes for strings.var update = 'Some text';
-
Strings longer than 80 characters should be written across multiple lines using string concatenation. Use
+
for concatenation, avoid escaping EOLs with\
.var update = 'Grounds, half and half, affogato sit medium, decaffeinated ' + 'cortado, acerbic whipped grinder cultivar aftertaste. Sugar, wings ' + 'robusta barista, seasonal robust, mazagran qui blue mountain organic ' + 'breve arabica.'; // or use Array#join var update = [ 'Grounds, half and half, affogato sit medium, decaffeinated ', 'cortado, acerbic whipped grinder cultivar aftertaste. Sugar, wings ', 'robusta barista, seasonal robust, mazagran qui blue mountain organic ', 'breve arabica.' ].join('');
-
Use curlys
{}
for multi-line blocks, add spacing around statements to encourage readability.if ( update.get('text').length < 140 ) { update.save(); } else { $('.js-warning').show(); } // It's ok to drop the braces if it's a short one-liner, try to keep it // under ~80 characters long so it's readable if ( update.get('text') > 140 ) this.showLengthWarning();
-
Use soft tabs set to 2 spaces.
function isAwesome() { ∙∙return this.plan === 'awesome'; } this.getDataForModel(123) ∙∙.then(this.renderTemplate); // instead of function isBusiness() { ∙∙∙∙return ['small', 'business', 'agency'].indexOf(this.plan) > -1; } this.getDataForModel(456) .then(this.renderTemplate);
-
Use whitespace with operators
var count = x + 2; // instead of var count=x+2;
-
Use indentation for longer method chaining
$update .text(newText) .removeClass('editing') .addClass('saved'); // Instead of $update.text(newText).removeClass('editing').addClass('saved');
-
Use
.js-
prefixed class selectors. This prevents confusion between classes needed for design and ones used to reference the DOM from js.<div class="update js-update-content">Some update text</div>
var $content = $('.js-update-content'); $content.text('New text');
-
Prefix jQuery object variables with a
$
. This makes it easier to distingush jQuery variables vs other variables and DOM elements.var $sidebar = $('.js-sidebar');
-
Cache jQuery lookups.
// Use this... var $sidebar = $('.js-sidebar'); $sidebar.addClass('hidden'); $sidebar.data('id', '1234'); // Instead of $('.js-sidebar').addClass('hidden'); $('.js-sidebar').data('id', '1234');
-
Use Promises with $.ajax. jQuery's docs use them as the new default. Use
.then
or.always
over.done
and.fail
$.ajax({ url: '/updates.json' }) .then(function(data){ renderUpdates(data.updates); }, function(){ alert('Request failed'); }); // Promises also work nicely for wrapping a more complex $.ajax request function saveUpdateText(updateId, text) { return $.ajax({ type: 'post'. url: '/updates/' + updateId + '.json', data: { body: text } }); } // The response function showSuccessNotification(data) { new Notification(data.message); } // Usage is very clean and easy to read saveUpdateText(123, 'My new update text') .then(showSuccessNotification);
-
Use
.bind(this)
to pass scope to functions if possible. Usevar _this = this;
if it makes your code simpler and more readable.someMethod: function(data) { this.saveMyUpdate(data) .then(function(data){ this.collection.add(data); this.showSavedNotification(); }.bind(this)); } var myUpdates = updates.filter(function(update){ return update.type === this.getCurrentType(); }.bind(this)); // Using _this - Maybe this isn't the best example, but you get the idea :) loadModelDataInForm: function(id){ var _this = this; $.ajax({ url: '/model/' + id + '/' }) .then(function(data){ _this.renderTemplate(data); $('.js-save-button').on('click', function(e){ $(this).text('Saving...'); _this .getDataFromForm() .save(); }); }, function(err){ alert(err); }); }