diff --git a/foundations/javascript_basics/clean_code.md b/foundations/javascript_basics/clean_code.md index a91bf9bb8b4..5576306d089 100644 --- a/foundations/javascript_basics/clean_code.md +++ b/foundations/javascript_basics/clean_code.md @@ -1,4 +1,3 @@ - ### Introduction You might think that the majority of a developer's work involves writing code. However, in reality, a significant amount of time is spent on *reading* code. This includes code written by other team members, code written by people who are no longer part of your team, and even code that you wrote two weeks ago but may not remember much about. @@ -24,11 +23,11 @@ Consider the following examples: Example A: ```javascript +const x= function (z){ + const w = "Hello "; +return w + z -const x = function (z) { - const w = "Hello "; -return w + z -}; + } x("John"); ``` @@ -44,13 +43,13 @@ const generateUserGreeting = function (name) { generateUserGreeting("John"); ``` -Which of these examples do you find easier to read? It's immediately clear that the latter one is more meaningful. Surprisingly, both of these functions perform the exact same task \(in the exact same way!\), and both are valid code. But the second one is much more readable. Why? +Which of these examples do you find easier to read? It's immediately clear that the latter one is more meaningful. Surprisingly, both of these functions perform the exact same task (in the exact same way!), and both are valid code. But the second one is much more readable. Why? -In the first example, single-letter variables are used and the indentation is inconsistent. The result is a piece of code that is confusing and messy. +In the first example, single-letter variables are used and the indentation and spacing are inconsistent. The result is a piece of code that is confusing and messy. Imagine you're collaborating on a project with someone who has written the first function. How long will it take you to decipher what's going on so you can continue with your work? Or perhaps you've written it yourself some time ago and completely forgotten that it even existed. In both situations, you will eventually understand what is happening, but it's not going to be fun. -Example B represents clean code. While you may not know what each part does, it's much easier to guess what's happening because the functions and variables are named clearly. The indentation follows a consistent and logical pattern. +Example B represents cleaner code. While you may not know what each part does, it's much easier to guess what's happening because the functions and variables are named clearly. The indentation and spacing follow a consistent and logical pattern. Single characters can be used as variable names in the context of a loop or a callback function, but avoid them elsewhere. @@ -58,7 +57,17 @@ Single characters can be used as variable names in the context of a loop or a ca camelCase is a naming convention that allows writing multiple words together without spaces or punctuation. In camelCase, when a variable name consists of multiple words like our `setTimeout` example, the first word is written completely in lowercase, while the first letter of the second word (and any subsequent words) are capitalized. -Throughout this lesson, most of our variables and functions (at least in the good examples!) will be named using camelCase. It's a good example to follow. +Throughout this lesson, most of our variables and functions will be named using camelCase. While not every language uses this convention, it's very common in JavaScript so it'll be a good example to follow. + +
+ +#### Conventions are only conventions + +While this lesson shares some examples on ways to clean up code, in reality, every organization will have different specific approaches, some of which may differ slightly from our examples in this lesson. Nothing is absolute. + +What matters most is that these approaches all serve the same overall purpose - improve code readability and maintainability. Until a time comes where you need to follow a specific set of conventions, it is sensible to follow some convention and be consistent with them. + +
### Naming functions and variables @@ -70,44 +79,46 @@ In our good example, we have a variable `greeting`, to which the parameter `name Now, try picturing a conversation with someone about the bad example. The function is named `x` with variables like `z`, and `w`. Oof, not nice. -#### Use a consistent vocabulary +#### Use consistent vocabulary -Variables of the same type should have consistent naming. Consider the following examples from a game: +Variables of the same type ideally follow a consistent naming system. Consider the following examples from a game: ```javascript - // Good +// Consistent naming function getPlayerScore(); function getPlayerName(); function getPlayerTag(); +``` -// Bad +They all follow the same naming system of "get a thing". Now consider the following: + +```javascript +// Inconsistent naming function getUserScore(); function fetchPlayerName(); function retrievePlayer1Tag(); ``` -In the bad example, three different names are used to refer to the player and the actions taken. Additionally, three different verbs are used to describe these actions. The good example maintains consistency in both variable naming and the verbs used. - -Variables should always begin with a noun or an adjective (that is, a noun phrase) and functions with a verb. +In the inconsistent example, three different verbs are used for the functions. While they all mean a similar thing, at a glance you might assume different verbs were used for a specific reason (e.g. "getting" might not be *quite* the same thing as "fetching" in some contexts). Additionally, what's the difference between `User`, `Player` and `Player1`? If there is no difference then ideally, you'd use the same name e.g. `Player`. Consistency allows for predictability. -Another set of examples can illustrate why this matters: +Variables should preferably begin with a noun or an adjective (that is, a noun phrase), as they typically represent "things", whether that thing is a string, a number etc. Functions represent actions so ideally begin with a verb. ```javascript -// Good +// Preferable const numberOfThings = 10; const myName = "Thor"; const selected = true; -// Bad (these start with verbs, could be confused for functions) +// Not preferable (these start with verbs, could be confused for functions) const getCount = 10; -const isSelected = true; +const showNorseGods = ["Odin", "Thor", "Loki"]; -// Good +// Preferable function getCount() { return numberOfThings; } -// Bad (it's a noun) +// Not preferable (myName doesn't represent some kind of action) function myName() { return "Thor"; } @@ -115,35 +126,35 @@ function myName() { ### Use searchable and immediately understandable names -Sometimes, it can be tempting to use an undeclared variable. Let's take another look at an example: +Sometimes, it can be tempting to use "magic values" i.e. explicit values, such as bare numbers or strings. Let's take another look at an example: ```javascript setTimeout(stopTimer, 3600000); ``` -The problem is obvious. What does the undeclared variable `3600000` mean, and how long is this timeout going to count down before executing `stopTimer`? Even if you know that JavaScript understands time in milliseconds, a calculator is needed. +The problem is obvious. What does the magic number `3600000` mean, and how long is this timeout going to count down before executing `stopTimer`? Even if you know that JavaScript understands time in milliseconds, you'd probably need a calculator or Google to figure out how many seconds or minutes it represents. Now, let's make this code more meaningful by introducing a descriptive variable: ```javascript -const MILLISECONDS_PER_HOUR = 60 * 60 * 1000; // 3,600,000; +const ONE_HOUR = 3600000; // Can even write as 60 * 60 * 1000; -setTimeout(stopTimer, MILLISECONDS_PER_HOUR); +setTimeout(stopTimer, ONE_HOUR); ``` Much better, isn't it? The variable is declared with a descriptive name, and you don't need to perform any calculations when reading this code. -You might wonder why this variable is declared with all caps when we recommended camelCase earlier. This is a convention to be used when the programmer is absolutely sure that the variable is *truly* a constant. We know that the milliseconds in an hour will never change, so it's appropriate here. +You might wonder why this variable is declared with all caps when we recommended camelCase earlier. This is a convention to be used when the programmer is absolutely sure that the variable is *truly* a constant, especially if it represents some kind of concept like a specific duration of time. We know that the milliseconds in an hour will never change, so it's appropriate here. Remember, this is only a convention. Not everyone will necessarily do things the same way. ### Indentation and line length Now it's time to head to more controversial topics. The war between coders that use tabs and coders that use spaces to indent their code is essentially a joke by now, as demonstrated in the [tabs versus spaces scene from the show Silicon Valley](https://www.youtube.com/watch?v=SsoOG6ZeyUI). -What actually matters is *consistency*. Choose a way to indent and stick to it. Various JS style-guides recommend different options, and one is not really superior to the other. A few popular ones are linked in the additional resources. +What actually matters is *consistency*. Choose a way to indent and stick to it. Various JavaScript style guides recommend different options, and one is not really superior to the other. We will look at style guides and related tools in more detail later in the curriculum. #### Line length -Again, different style guides will recommend different options for this one, but just about ALL of them suggest limiting the length of each line of code. +Again, different style guides will recommend different options for this one, but just about *all* of them suggest limiting the length of each line of code. Generally, your code will be easier to read if you manually break lines that are longer than about 80 characters. Many code editors have a line in the display to show when you have crossed this threshold. When manually breaking lines, you should try to break immediately *after* an operator or comma. @@ -162,21 +173,21 @@ let reallyReallyLongLine = oneMoreReallyLongThing; // Or maybe like this - let anotherReallyReallyLongLine = something + somethingElse + anotherThing + - howManyTacos + oneMoreReallyLongThing; +let anotherReallyReallyLongLine = something + somethingElse + anotherThing + + howManyTacos + oneMoreReallyLongThing; ``` +Different formats aren't necessarily right or wrong, and different people may prefer different things. Do things in a way that makes sense to you, and stay consistent with it. + ### Semicolons -Semicolons are *mostly* optional in JavaScript because the JS compiler will automatically insert them if they are omitted. This functionality CAN break in certain situations, leading to bugs in your code, so it is better to get used to adding semi-colons. +Semicolons are *mostly* optional in JavaScript because the JavaScript interpreter will automatically insert them if they are omitted. This functionality *can* break in certain situations, leading to bugs in your code, so we'd recommend getting used to adding semicolons. -Again: consistency is the main thing. +Whether you do or not, again, consistency is the main thing. ### About comments -Comments are a great tool. But like any good tool, it can be misused. Especially for someone early in their coding journey, it might be tempting to have comments that explain *everything* the code is doing. This is not a good practice. - -Next, we'll look into some common pitfalls in commenting and *why* they are pitfalls. +Comments are a great tool but like any good tool, they can be misused. Especially for someone early in their coding journey, it might be tempting to have comments that explain *everything* the code is doing. This is generally not a good practice. Let's look at some common pitfalls when commenting and *why* they are pitfalls. #### Don't comment when you should be using git @@ -206,49 +217,48 @@ theFunctionInUse(); #### Tell why, not how -The purpose of comments is not to provide pseudo code that duplicates your code. Good comments explain the *reasons* behind a piece of code. +Ideally, comments do not provide pseudocode that duplicates your code. Good comments explain the *reasons* behind a piece of code. Sometimes you won't even need a comment at all! -Let's look at an example to see this in practice: +Say we had a string where part of the text was inside square brackets and we wanted to extract the text within those brackets. ```javascript -// Bad Example - comment doesn't tell why, only what and how - -// This function increments the value of i by 1 -function incrementI(i) { - i = i + 1; // Add one to i - return i; +// Function to extract text +function extractText(s) { + // Return the string starting after the "[" and ending at "]" + return s.substring(s.indexOf("[") + 1, s.indexOf("]")); } +``` -// Better Example - comment tells a why +The comments just describe what we can tell from the code itself. Slightly more useful comments could explain the reasons behind the code. -// This function increments the value of index to move to the next element -function incrementI(i) { - i = i + 1; - return i; +```javascript +// Extracts text inside square brackets (excluding the brackets) +function extractText(s) { + return s.substring(s.indexOf("[") + 1, s.indexOf("]")); } +``` -// Good Example - the code tells all that is needed +But often, we can make the code speak for itself without comments. -function moveToNextElement(index) { - index = index + 1; - return index; +```javascript +function extractTextWithinBrackets(text) { + const bracketTextStart = text.indexOf("[") + 1; + const bracketTextEnd = text.indexOf("]"); + return text.substring(bracketTextStart, bracketTextEnd); } ``` -In the bad example, the comments explain twice what the code does. But for this, you could've just read the code, so the comments are redundant. - -In the better example, the comment clarifies the purpose of the function: moving to the next element. That's good, but we can do *even* better. +In the first example, the comments repeat twice what the code does. But for this, you could've just read the code, so the comments are redundant. -In the good example, no comments are needed at all. The use of descriptive functions and variable names eliminates the need for additional explanations. Pretty neat, huh? +In the second example, the comment clarifies the purpose of the function: extracting the text between square brackets from a string and not just "extracting text". That's handy, but we can do *even* better. -*This doesn't mean good code should lack comments*. In many situations, well-placed comments are priceless. The article linked in the assignment section goes into more depth on this. We don't want you to avoid comments; just be mindful of how they are best used. +In the last example, no comments are needed at all. The use of descriptive functions and variable names eliminates the need for additional explanations. Pretty neat, huh? -Let's look at one final example where a comment serves a good purpose: +**This doesn't mean good code should lack comments.** Let's look at an example where a comment serves a helpful purpose: ```javascript - function calculateBMI(height, weight) { - // The formula for BMI is weight in kilograms divided by height in meters squared + // The formula for BMI is weight in kilograms divided by height in meters squared const heightInMeters = height / 100; const bmi = weight / (heightInMeters * heightInMeters); return bmi; @@ -257,18 +267,22 @@ function calculateBMI(height, weight) { This comment helps to refresh the reader on how BMI is calculated in plain English, helping the reader to see why the height needs to be converted and what the following calculation is doing. We are almost there with the naming, but the comment still adds further clarity. +In many situations, well-placed comments are priceless. They might explain why an unintuitive bit of code is necessary, or perhaps the bigger picture of why a certain function is *particularly* important to be called here and not there. The article linked in the assignment section goes into more depth on this. + ### In conclusion -Now that we've covered these ideas, it's good to return to the reminder we shared at the start. Don't try to write perfectly clean code; this will only lead to frustration. Writing "spaghetti" is inevitable; everyone does it sometimes. Just keep these ideas in mind, and with time and patience, your code will start to get cleaner. +Now that we've covered these ideas, it's good to return to the reminder we shared at the start. Don't try to write perfectly clean code, this will only lead to frustration. Writing "spaghetti" is inevitable; everyone does it sometimes. Just keep these ideas in mind, and with time and patience, your code will start to get cleaner. Learning to write clean code is a process of constant improvement. One that will extend beyond you *completing* The Odin Project. This lesson is meant to serve as a primer and a starting point for that journey. +> Great code comes from experience. Experience comes from not-so-great code. + ### Assignment
1. Read [10 Principles for Keeping Your Programming Code Clean](https://onextrapixel.com/10-principles-for-keeping-your-programming-code-clean/) to get some great tips for clean code. -1. To help better understand good comment practices, read about [comments telling us how code works](https://blog.codinghorror.com/code-tells-you-how-comments-tell-you-why/) as well as how to [code without comments](https://blog.codinghorror.com/coding-without-comments/). +1. To help better understand good comment practices, read about [comments telling us why code works](https://blog.codinghorror.com/code-tells-you-how-comments-tell-you-why/) as well as how to [code without comments](https://blog.codinghorror.com/coding-without-comments/).
@@ -277,14 +291,14 @@ Learning to write clean code is a process of constant improvement. One that will The following questions are an opportunity to reflect on key topics in this lesson. If you can't answer a question, click on it to review the material, but keep in mind you are not expected to memorize or master this knowledge. - [Why is it important to write clean code?](#introduction) -- [Name 5 clean code principles previously mentioned](https://onextrapixel.com/10-principles-for-keeping-your-programming-code-clean/) +- [What are some good principles for keeping code clean?](https://onextrapixel.com/10-principles-for-keeping-your-programming-code-clean/) - [What is the difference between good comments and bad comments?](https://onextrapixel.com/10-principles-for-keeping-your-programming-code-clean/) ### Additional resources This section contains helpful links to related content. It isn't required, so consider it supplemental. -- [A nice op-ed](https://www.martinfowler.com/bliki/CodeAsDocumentation.html) +- [A nice op-ed on code as documentation](https://www.martinfowler.com/bliki/CodeAsDocumentation.html) - [Airbnb style guide](https://github.com/airbnb/javascript) - [Chaining methods to write sentences](https://web.archive.org/web/20190211152543/https://javascriptissexy.com/beautiful-javascript-easily-create-chainable-cascading-methods-for-expressiveness/) - [Clean code in JavaScript](https://github.com/ryanmcdermott/clean-code-javascript)