Development teams often have coding standards in order to increase code quality, and make collaboration faster and easier. Even if you're coding solo, knowing the principles of consistent, readable code will help you write more maintainable code, faster!
Programs are meant to be read by humans and only incidentally for computers to execute.
Donald Knuth
Coding standards are made up of anything your team wants. Their primary purpose is to simplify things - less decisions, fewer debates.
They have many secondary benefits as well:
…and so on.
Every team is different, and likewise for their standards. The bigger the team, the more it becomes true that the best code is the code we all agree on.
Does that mean a solo developer shouldn't have standards?
Of course they should! Opens a Simpsons meme in a new window
Depending on the team, these standards might be very loose, or they might go into minute detail. They might be enforced through code reviews by your teammates, or they might be enforced through automated testing. Most likely - a combination of the two.
There is a nearly infinite number of possibly variations of coding standards, and we obviously won't be able to review them all today. What we'll do instead is look at the common elements that are included in team coding standards, and look at two major examples that could be included in team standards - a JavaScript design pattern and a CSS methodology.
As mentioned, coding standards can include just about anything - so long as the team has determined that it's the way they're all going to do things.
Today we'll look at a few of the big categories, but if you'd like to see what all gets included in the coding standards of larger teams, here are some examples:
Your team may state programming principles that your code is expected to follow.
These are usually just so you can create a shorthand language for ideas that your team wants to follow. This shorthand makes conversations easier when defining specific rules, or just doing code review.
Some principles you might already be familiar with:
Far less abstract than programming principles are defined practices. Coding practices are usually a practical example of a principle.
5.1.2 One variable per declaration
Google JavaScript Style Guide Opens in a new window
Every local variable declaration declares only one variable: declarations such aslet a = 1, b = 2;
are not used.
In fact these practices tend to be so nuts-and-bolts - particularly with very permissive languages like CSS and JavaScript - that they are often comprised of a lot of small details (see the Airbnb summary article, or any of the coding guides, linked earlierin the notes).
While these are important, we're not going to go into all the various possibly permutations of how to handle arrays and objects in JavaScript, or when to use a SASS list.
Instead, later on in this lesson, we'll look at a "design pattern" in JavaScript. Different design patterns may be recommendations your team has, or they may simply be practical implementations of your team's principles.
For now, though, just know that your team may have specific preferences for the little details of how you write your code.
Let's look at a few categories of these details today.
There are only two hard things in Computer Science: cache invalidation and naming things.
Phil Karlton
If you'd like to learn about cache invalidation, be my guest Opens in a new window.
Today, though, let's talk about naming things.
Naming things is hard, because you need to be brief, be specific, and be flexible.
sortArrayOfFruitByShapeOrSeasonalAvailability();
every time you need to call your function, for exampleNaming things is also hard because you can pretty much name things whatever you want, at least as far as computers are concerned.
That's where team standards come in.
Your team may have a few different techniques for making the process of naming stuff easier, and to mitigate the risk of duplicating names.
In naming conventions, 'casing' refers to patterns of upper case or lower case letters. It also refers to word separators.
A very common convention to to use "camel casing" for ids. In camel casing, the first word is all lower case, and subsequent words begin with an upper case letter, with no space between the words.
<span id="camelsDoNotLookLikeThisButOk"></span>
Another common convention (often paired with camel casing for ids) is to use "kebab casing" for HTML/CSS class names. Kebab casing is all lower cased, with hyphens separating words.
<span class="looks-delicious-right"></span>
There are often naming conventions around files as well as code entities.
Typically, you'll see some rules like the following:
There may be additional naming requirements, depending on the architecture of your application. For example, when using a testing framework like Mocha or Jest, the files that contain your test frequently add .test
before the file extension, like this:
/sort-fruit.test.js
Formatting your code for proper indentation and whitespace definitely makes it more readable.
<div>
<ul class=" my-list for-list-stuff"
id="thisList">
<li>gnarly
</li>
</ul>
</div>
<div>
<ul class="my-list for-list-stuff" id="thisList">
<li>that's better</li>
</ul>
</div>
However, the reason your team might have strict requirements around consistent formatting is so that your version control system doesn't have to worry about changes that aren't meaningful.
Your code editor should have built-in capabilities and/or plugins for formatting your code. Note that each language requires a different set of formatting rules, and that your team's requirements may differ from the default formatting rules of your text editor.
The good news is that these editors and plugins should be configurable, meaning you can tweak the settings to match your team's standards.
We don't have time today to go into the details of configuring all editors and plugins, but your takeaways should be that you should learn how to format code with your editor, and if git is identifying changes that aren't meaningful, talk to your team about formatting standards.
Just like formatting, commenting is something you should be doing on your own, but may have additional considerations when working with a team.
Whatever language you're writing in, you should know how to write single- and multi-line comments.
<!-- One style for single/inline -->
<!--
or multi-line
comments
-->
/* One style for single/inline */
/*
or multi-line
comments
*/
// single line
/*
Multi
line
*/
Before we get into a broader use-case for comments, I'd just like throw it out there that, yes, comments are for describing parts of your code…
.my-class {
/* Needs to be relative for child elements
to have position:absolute relative to parent */
position: relative;
};
…but you can use if for anything that makes your code easier to read. I've often seen larger CSS files have a table of contents at the top, and section headings throughout.
/******************************
___Table of Contents___
1. Resets
2. Elements
3. Components
4. Utilities
******************************/
/*** 1. Resets ***/
*, *:before, *:after {
box-sizing: border-box;
}
…and so on
And don't forget, provided that your team is stripping out code comments before the code goes public, you can write notes & jokes to your fellow developers.
/*
#titanic {
float: none;
}
*/
Where things can get interesting on a team is when you get into auto-generated documentation.
Using special comment formatting, your team can actually generate readable documentation in HTML format that pulls information about the code from these special comments.
For example…
/**
* Represents a book.
* @constructor
* @param {string} title - The title of the book.
* @param {string} author - The author of the book.
*/
function Book(title, author) {
}
…can be used to automatically build an always-up-to-date documentation site that looks like this…
The two commonly used tools I've had experience with are:
…but the concept has been around for a long time, and there are many tools Opens in a new window for different languages including Python, PHP, as well as language-agnostic documentation generators.
I love good documentation, so I wanted to call out two free, open-source tools that make documentation for larger projects simple and manageable.
What CSS units Opens in a new window should you use, and when? Is it ever ok to use a magic number Opens in a new window? When should you use data attributes Opens in a new window? What kind of bear is best?
With the flexibility of languages and the diversity of architectures on the modern web, there are an endless number of things about which your team may find it necessary to reach an agreement. Stay focussed on making life easier, and let me re-state: the best code is the code we all agree on.
With that in mind, let's look at two common coding patterns that a lot of developers and development teams use - the JavaScript 'constructor' design pattern, and the CSS 'BEM' methodology.
In programming, 'design patterns' are "reusable solutions to commonly occurring problems" (Addy Osmani). In the same way a jazz musician will have 'riffs' that they fall back on when improvising, or a hip-hop artist will have certain rhymes that they already thought of before they start freestyling, design patterns are patterns for blocks code that you know how to write when a familiar challenge comes up.
There are tons of them, and if you'd really like to level up your javascript, there is a widely recommended book that shows you how to write dozens of well-respected javascript design patterns:
Often in JavaScript, we have to create a bunch of objects that are very similar, if not in their values, then at least in their properties.
The constructor pattern gives us a to create a template for objects that share similarities, so that creating them is faster, and more consistent.
function OldPerson(age, height, name) {
this.age = age;
this.height = height;
this.name = name;
this.author = "John Ronald Reuel Tolkien";
}
OldPerson.prototype.heightDeclaration = function() {
return this.name + " is " + this.height + " tall.";
};
Here we create a template for objects. We can pass in an age, height and name as arguments. We also set a property, author
, which will be consistent for all objects created with this OldPerson
constructor.
Additionally, we add a method (a function attached to the object). We could define it inside the constructor, but instead we define it outside of the original function call, using prototype
, so that it doesn't have to get redefined every time we create an object (thus making our JavaScript code faster).
Now we can use our constructor to create and use objects, like this:
var gandalf = new OldPerson(2000,"168cm","Mithrandir");
var sam = new OldPerson(39, "100cm", "Samwise Gamgee");
console.log( gandalf.heightDeclaration() );
/* Logs "Mithrandir is 168cm tall." */
console.log( sam.heightDeclaration() );
/* Logs "Samwise Gamgee is 100cm tall." */
Modern JavaScript uses a new syntax for templating objects, called "class".
If you have learned other programming languages, you may have certain idea of what a "class" is. As usual, JavaScript does not give a darn. So, consider this a heads-up: today we are talking about classes in JavaScript, not classes as a larger programming concept.
In ES6+ (which, don't worry, we'll learn more modern JavaScript in another class shortly), we can template objects like this:
class Person {
constructor(age, height, name) {
this.age = age;
this.height = height;
this.name = name;
this.author = "John Ronald Reuel Tolkien";
}
}
Person.prototype.ageDeclaration = function() {
return this.name + " is " + this.age + " years old.";
}
…and use it the same way as before…
var frodo = new Person( 51, "115cm", "Mr. Underhill");
var aragorn = new Person( 88, "198cm", "Strider");
console.log( frodo.author );
/* Logs "John Ronald Reuel Tolkien" */
console.log( aragorn.ageDeclaration() );
/* Logs "Strider is 88 years old." */
Whereas JavaScript is notoriously loose in how many ways you can do things, CSS is notoriously loose in how easy it is to override things.
CSS methodologies are meant to keep things organized, and reduce selector specificity (i.e. making your selectors work without adding six ancestor selectors).
/* This is nice */
.login__heading {
text-decoration: underline;
}
/* This is the result of "specificity wars" */
body main.login-page #loginForm > h3.login-page-heading:nth-of-type(1) {
text-decoration: underline !important;
}
Today, we'll look at the best-known CSS methodology, BEM. However, many CSS methodologies are complementary, rather than in competition, so check them out!
"BEM" stands for "Block Element Modifier". Unlike some methodologies, it is simply a naming convention. There's nothing to install, there's no architecture considerations. Just a reasonable way to consistently name your classes. Simple, right?
The first step in BEM is to identify components on your page - pockets of design where what's inside is interdependent, but as a whole could be moved to different parts of the page without much disruption.
The definition of a "block" is kind of ambiguous, but common examples would be:
What should be a block, and how granular you want to get, depends on the complexity of what you're creating.
Let's look at an example that usually gets described as a "card" pattern - something that might end up being repeated multiple times on the same or different pages.
See the example in the notes
We'd give the containing element a name. Let's say, "card
".
"card
" is our block.
.card {
width: 20em;
border-radius: calc(20em / 20);
box-shadow: 0 0 0.5em #777777;
border: 5px solid #eeeeee;
}
Now that we have a name for our component, our "block", we can name the elements - anything inside the block that needs a class.
We'll prefix all those elements with our block name, followed by two underscores.
.card__icon {
border-radius: 50%;
width: 3em;
margin: 0.25em;
}
There is only ever one block, meaning that we don't nest names further, even if the elements are nested in the HTML.
<div class="card">
<div class="card__header">
<img
class="card__icon"
src="/images/oscar.png"
alt="Oscar icon">
</div>
</div>
/* like this */
.card__header {
display: flex;
}
.card__icon {
margin: 0.25em;
}
/* NOT like this */
.card__header {
display: flex;
}
.card__header__icon {
margin: 0.25em;
}
Even blocks get repeated, sometimes there are variations in the design for one reason or another (variations in context or content, for example).
Likewise, sometimes elements within our blocks are mostly the same, but a little bit different.
In these cases, we append the class name with two hyphens, and name the variation.
Here's an example of modifiers for elements within the block, in this case a variation on the button:
<div class="card">
<div class="card__cta">
<a class="card__button" href="#">
I too love trash
</a>
<a
class="card__button card__button--disabled"
href="#"
disabled>
Maybe not trash?
</a>
</div>
</div>
.card__button {
display: block;
color: #555555;
text-decoration: none;
margin-right: 1em;
padding: 0.5em 0.75em;
background: #6667fe;
color: #ffffff;
box-shadow: 0 1px 2px #777777;
font-size: 0.75em;
border-radius: 0.125em;
}
.card__button--disabled {
background: #777777;
box-shadow: none;
color: #eeeeee;
}
…and here's an example of a variation of the whole block, achieved in the HTML simply by adding an extra class on the block element:
.card--collab {
border-color: #eb0237;
box-shadow: 0 0 0.5em #eb0237;
}
.card--collab .card__cta {
background: #88a070;
}
See the example in the notes
I hope it's evident how BEM serves to simplify naming in CSS, and reduce selector specificity by creating component-based namespaces.
As a little bonus, I'd like to show you something that really brings out the best in BEM - the SASS ampersand.
Using the ampersand in SASS lets us write the block name once, and nest all the elements within it. This reduces the amount of typing we have to do, and it also creates a visual representation of the namespace.
See the example in the notes
This:
.card {
width: 20em;
border-radius: calc(20em / 20);
box-shadow: 0 0 0.5em #777777;
border: 5px solid #eeeeee;
}
.card--collab {
border-color: #eb0237;
box-shadow: 0 0 0.5em #eb0237;
}
.card__header {
display: flex;
align-items: center;
padding: 0.5em 1em;
}
.card__icon {
background: green;
border-radius: 50%;
width: 3em;
margin: 0.25em;
}
.card__title {
font-size: 1.125em;
font-weight: 900;
margin: 0.5em;
color: #555555;
}
.card__image {
display: block;
width: 100%;
height: auto;
}
.card__caption {
color: #555555;
padding: 1em;
}
.card__cta {
background: #eeeeee;
padding: 1em;
display: flex;
}
.card--collab .card__cta {
background: #88a070;
}
.card__button {
display: block;
color: #555555;
text-decoration: none;
margin-right: 1em;
padding: 0.5em 0.75em;
background: #6667fe;
color: #ffffff;
box-shadow: 0 1px 2px #777777;
font-size: 0.75em;
border-radius: 0.125em;
}
.card__button:last-child {
margin-right: 0;
}
.card__button:hover {
box-shadow: 0 0 6px #777777;
color: #ffffff;
}
.card__button--disabled {
background: #777777;
box-shadow: none;
color: #eeeeee;
}
.card__button--disabled:hover {
box-shadow: none;
pointer-events: none;
}
…becomes this:
$colour-elmo: #eb0237;
$colour-oscar: #88a070;
$card-width: 20em;
$colour-gray-light: #eeeeee;
$colour-gray-dark: #777777;
$colour-text-default: #555555;
$box-shadow-default: 0 0 0.5em;
$border-radius-default: 1em;
.card {
width: $card-width;
border-radius: calc(#{$card-width} / 20);
box-shadow: $box-shadow-default $colour-gray-dark;
border: 5px solid $colour-gray-light;
&--collab {
border-color: $colour-elmo;
box-shadow: $box-shadow-default $colour-elmo;
}
&__header {
display: flex;
align-items: center;
padding: 0.5em 1em;
}
&__icon {
background: green;
border-radius: 50%;
width: 3em;
margin: 0.25em;
}
&__title {
font-size: 1.125em;
font-weight: 900;
margin: 0.5em;
color: $colour-text-default;
}
&__image {
display: block;
width: 100%;
height: auto;
}
&__caption {
color: $colour-text-default;
padding: 1em;
}
&__cta {
background: $colour-gray-light;
padding: 1em;
display: flex;
.card--collab & {
background: $colour-oscar;
}
}
&__button {
display: block;
color: $colour-text-default;
text-decoration: none;
margin-right: 1em;
padding: 0.5em 0.75em;
background: #6667fe;
color: #ffffff;
box-shadow: 0 1px 2px $colour-gray-dark;
font-size: 0.75em;
border-radius: 0.125em;
&:last-child {
margin-right: 0;
}
&:hover {
box-shadow: 0 0 6px $colour-gray-dark;
color: #ffffff;
}
&--disabled {
background: $colour-gray-dark;
box-shadow: none;
color: $colour-gray-light;
&:hover {
box-shadow: none;
pointer-events: none;
}
}
}
}