Introduction to Professional Coding

May 12, 2020

This article is a written version of Rithm School's Introduction to Professional Coding lecture.

The goals of this lecture are to:

  1. Learn the critical parts of professional coding
  2. Introduce good programming style (names, functions, documentation)
  3. Review previously introduced debugging tools

Programming Style

Just like with other written languages, being mindful of programming style as you code is an important consideration as a software engineer.

Unlike projects that you might create only for your own use in a local environment, or for portfolio purposes, real-world programs often need to be maintained for years. Over the course of those years, hundreds of developers may have a say in and leave their mark on the code that lies within.

As such, considering the experiences of our fellow developers (as well as our future selves!), we should always strive to write the best, most intuitive and readable code possible.

A Note on Variable Naming

It seems simple and straightforward, but crafting good variable names often proves a harder task than most initially think.

It's a skill that we expect you to take seriously and work to cultivate from day one.

Whether it's a value or a function, your variable names should clearly indicate what it represents and/or does.

Generally speaking, names in JavaScript are camelCase, but we will meet some exceptions, particularly when we start building more complex applications.

Functions

Ah, the humble function: the most basic unit of thought/action in programming.

Those new to programming often create too few functions and, as a result, make them way too long and logic-heavy.

Take, for example, a function for a game of Tic-Tac-Toe we're building:

function playTacToe() {
  // setup board
  // ...

  while (!winner) {
    // show board
    for (let y = 0; y < board.height; y++)
      for (let x = 0; x < board.width; x++)
        console.log(board[y][x]);

    // get move
    // ...

    // check for win ...
    // ...
  }
}

Our use of comments lend themselves to allowing us to easily follow what's happening, but, as the reader, we have to actually read the code in order to understand what is going on.

We'd like to avoid this, if at all possible.

Why? As developers, we spend 80% of our time reading code, anyway. In order to make changes or to fix bugs, we need to first understand the intricacies of the code we're working in.

The easier and faster we can reach this understanding, the faster and more productive we can be.

One way to refactor the code above towards this end is to break out our blocks of logic into separate functions:

function playTicTacToe() {
  let winner;
  setupBoard();

  while (!winner) {
    displayBoard();
    const move = getMove();
    makeMove(move);
    winner = getWinner();
  }
}

In the code above, we don't need to dive into the functionality of each individual function unless we want or need to. Even without exploring these functions, we can tell immediately from the names given to them what they do.

What's more, if we've been told that there is a bug that's occuring when the winner is determined, we know exactly where to start looking.

And, though we haven't dove deeply into the world of testing just yet, you can imagine that the smaller the function, the easier it is to test.

Function Comments

As you're getting used to the ins-and-outs of your own personal programming style, we ask that you are a little more liberal with your use of comments in order to practice and develop intuition as to when they are necessary and when they are not.

Typically, the comment will contain a summary as to what the function does and/or what it returns, as seen below:

/* Checks board for 3-in-a-row; returns "X", "O", or null */
function checkForWinner() {
  // ...
}

Function Names

Again, variable names can be hard.

In the case of functions, the number one priority is to be clear and explicit. If that requires the function name to be a little bit more verbose on the first pass, this is completely OK -- you can always rename it if you think of a better name later.

Function names are also "verby" -- meaning that they are actions, rather than nouns.

In terms of a function name, calculateTax is much more clear than tax. The latter can be misinterpreted as merely a String or Number, rather than the return value of a particular function.

Remember: understanding what the code is doing is the key here. Appropriate variable names are a major part of that.

Variables

When it comes to good variable names, they should be clear in terms of what they represent, but not necessarily long.

If we're dealing with an Array of values, the name arr indicates the data type, but doesn't tell us anything about what the structure holds within. A better name might be numArr or strArr, from which we can immediately deduce that we're dealing with an Array of Numbers/Strings.

Globals

Global variables are variables that are used throughout the entire program and are not scoped to one particular function.

Often, new programmers have come to the conclusion that global variables are a bad thing, but this isn't always the case. When used mindfully and purposefully, global variables can serve to make our programs more robust in a completely "safe" way, without polluting the global scope.

When considering whether to make a global variable or not, ask yourself if the same end can be accomplished by passing a non-global variable to the functions that need it. If so, that's often preferable than to have a potentially forgotten variable accessible everywhere.

These types of global variables should be long and descriptive, making it impossible not to understand immediately what purpose they serve. Since they are shared across the entire program and not in the context of a specific function, it's naturally harder to understand exactly what they're used for, so we must take a little bit more care to combat that.

Global Constants

Global constants, which are a specific type of global variables, are variables that are accessible throughout the entire application but do not change.

We declare these variables at the top of our program, using the const keyword, and give them names in ALL_CAPS to help others understand the role they play.

Comments

Though we encourage you to use comments frequently when you're just starting out, as time goes on, you'll become more attuned to when comments are necessary and/or appropriate.

Some comments are pointless and should be avoided:

price = price * 2;  // double price

In the above snippet, we can easily conclude that by multiplying by 2, we are doubling the price. This comment is superfluous and doesn't add any value.

If, however, we are using some sort of non-obvious logic, as we see below:

price = price * 1.25;  // add excise tax

A comment is helpful in this case, as it might not be immediately obvious to the reader why the value is 1.25. With a comment, that doubt is cleared up.

Let Your Logic Speak For Itself

It should be rare that you need a comment in order to help the reader understand what your code is doing. This is usually an indication that the logic is overengineered and/or badly organized.

If it's the case that things aren't clear, you should consider refactoring it to make it so.

Take, for example, the conditional below:

if (cust[0] >= 18 && cust[1].length === 0 && reduce((n, acc) => n + acc, sals) > 10000) { }

Upon discovering that this works, you might sit back and feel quite proud: it makes total sense to you.

Buuut.. you're also putting the onus on whoever comes after you to be able to intuit the meaning of this, without all of the time you've had to craft it.

This isn't considerate and therefore not good form. A minor refactor would solve this:

const votingAge = cust[0] >= 18;
const numVotes = cust[1].length;
const sumSalary = reduce((n, acc) => n + acc, sals);

if (votingAge && numVotes === 0 && sumSalary > 10000) { }

Though we've made the function a bit more verbose, it takes a fraction of the time for those reading this to understand what is going on.

That is what we're aiming for.

JavaScript Debugger

As we covered during lecture yesterday, using the JavaScript debugger allows us to pause the execution of our code and examine the state of things at that point in time.

Bult into Chrome, all browsers have this ability. And, each allows us to debug directly in the browser or in Node (when we get to that portion of the program).

By opening up the Developer Tools and navigating to the Sources tab, we merely need to select a line to add what is referred to as a "breakpoint", or the point at which we want the code to pause.

Alternatively, we can always add the keyword debugger directly to our code, accomplishing the same "stopping of time" when that point in the code is reached.