One of the core concepts that is usually taught at college during the first programming module is defense. Yet, it's something that many online courses don't really emphasize. A good programmer is a defensive one (in terms of code). Yet, what does it mean?
We know that every piece of software out there is not perfect. Perfect code does not exist, but we all should be striving to achieve the best result possible.
The issue we, JS developers, face is that our beloved language sometimes offers us too much freedom, and we might be tempted to listen to the calling of the Dark Side and start using evil antipatterns. To prevent that temptation it is important to set boundaries and act defensively whenever we code.
TypeScript and linting
Luckily for us, there are great tools we can use to improve our code, and very often they don't require us to do much work. One of the greatest tools to write clean code is using a linter, such as ESlint. This tool basically watches our code as we type it and reports on potential bugs or just low-quality code. It also helps us to maintain consistency. Prettier which can be integrated with ESlint can then help us format our code, so we don't for instance mix single and double quotes or omit semicolons. ESlint also allows us to use guidelines created by companies like Google or AirBnB to follow the best practices of the industry.
// Before linting and prettifying
let name = "Luke Skywalker";
lightSide= true
if(lightSide === true) {
console.log('Good boy' )
}
// After
const name = 'Luke Skywalker';
const lightSide = true;
if (lightSide) {
console.log('Good boy');
}
Another great addition we can implement to improve our code is strong types which means, that variables can't be reassigned to a different data type. We can use Microsoft's TypeScript or Flow that will turn our code into more predictable and less buggy. Eventually, it ends up being compiled to vanilla JS. Also transpiled by Babel or a similar tool, so we cover older browsers that do not support modern features, but are still in use.
interface Post {
title: string
body: string
published: Date
}
interface Profile {
name: string
email: string
posts: Post[]
}
const profile1: Profile = {
name: 'Obi-wan Kenobi',
email: 'obi@disney.com',
posts: [
{ title: 'Living in exile',
body: 'Waiting for Luke...',
published: Tue Feb 05 2019 12:05:22 GMT+0530 (IST)
}
]
}
profile1.nemesis = "Anakin" // Error
let robot = "R2-D2"
robot = false // Error
const sum = (a: number, b: number): number => a + b
sum(5, "7") // Error
A lot of frameworks like React allow us to use TypeScript and with create-react-app we can be up and running with no configuration required.
Testing, testing, testing...
After our code looks pretty and we discovered all potential errors it is time for testing. One of the simplest way to test is using the unit testing methodology. Basically taking parts of our app and running it with certain inputs and seeing if the functionality behaves as expected. It is important to also test with corner cases. For example, if we implement a function that expects an array as an argument we should check how the function acts if we give it an empty array.
There are a bunch of testing libraries like Jest, Mocha, Jasmine or Enzyme (React).
function sum(a, b) {
return a + b;
}
//Jest
test('adds 40 + 2 to equal 42', () => {
expect(sum(40, 2)).toBe(42);
});
Test-driven development
We can take testing even a bit further and follow the TDD methodology which requires us to write our tests first and then implement the functionality. This approach is useful when we have to tackle a very complex functionality. As we can conceptualize the code in our head, write the tests to see what we wanna achieve, and then start implementing.
Summary
This article was just a quick summary of some very basic techniques and tools we can start using right now to improve our code and save us many headaches. The topic of defensive programming is something we could about for hours, but the main outcome is to strive to BE A DEFENSIVE PROGRAMMER.
Discussion (0)