JavaScript: var, let and const- should you stop using var?

If you are familiar with JavaScript, you may already know the var keyword. This keyword was used for ages for declaring JavaScript variables. However, in 2015, ECMAScript, which is a JavaScript standard, introduced two new ways to declare identifiers- let and const.

In this article, I try to discuss the differences between var, let and const and if you should stop using var altogether.

Hoisting

Before we get into the details of the difference between these, it is essential to know of a JS mechanism called Hoisting. JavaScript lets the user declare a variable anywhere in its body. However, internally JavaScript performs an action called Hoisting through which the declarations anywhere within a particular scope are resolved at the beginning of the scope.

As the JavaScript guru Kyle Simpson says in his article,

It's [Hoisting] not exactly a technical description for how it works, but more a metaphor

For example, consider the code snippet below:

console.log('Hello World');
var myName = 'Aswin';

Before the code is executed by a JavaScript engine, JavaScript resolves the declarations in such a way that the resulting flow almost looks like as follows:

var myName;
console.log('Hello World');
myName = 'Aswin';

In the above example, the variable is declared in a global scope. Had it been declared within a particular scope, the hoisting would have differed accordingly.

Now with hoisting in mind, let us get to how var, let and const differ from each other.

Declaration using 'var'

Scope

When a variable is declared with the var keyword, the variable declaration is defined with global scope or function scope based on where it is declared. Even if the variable is declared inside a block, like a for loop or an if condition, the variables are still hoisted outside the block.

For example, consider the code below:

function greet(gender) {
  if (gender == 'female') {
  	var name = 'Nalina';
  } else {
  	var name = 'Aswin';
  }
  console.log('Hello ' + name);
}

greet('female');

As per common programming logic, since the declaration var name = 'Nalina' is defined within an if block, the variable must exist only inside the block. The execution of the code should have resulted in an error according to conventional logic since the variable name is not defined within the scope of the line console.log('Hello ' + name).

However the output of this JavaScript code would be:

Hello Nalina

This is because the variable declaration is hoisted to the top of the function scope irrespective of the block in which it appears. The resultant program flow would be similar to:

function greet(gender) {
	var name; // hoisting equivalent

	if (gender == 'female') {
		name = 'Nalina';
	} else {
		name = 'Aswin';
	}
	console.log('Hello ' + name);
}

Initialisation of the variable

When declaring the variable using var keyword, the variable is hoisted and initialised with undefined as the value. Because of this, JavaScript lets the developers to use the variable even before it is declared.

For example, consider the code segment below:

myVar = 9;
var myVar;
console.log('The value is ' + myVar);

Execution of this code results in the following output:

The value is 9

This is as a result of hoisting. The code snippet specified above is processed by JavaScript to look as follows:

var myVar; // initialised as myVar = undefined;
myVar = 9;
console.log('The value is ' + myVar);

Now you may be able to appreciate how the code execution resulted in the output mentioned above.

Redeclaration of variables

Most programming languages do not allow to redeclare variables within the same scope. However, JavaScript supports variable re-declaration using the var keyword even within the same scope. This has turned out to be more of a mess than benefit in real life.

For example,

function doSomething() {
	// some long block of code
	var i = 100;
	// some other long block of code
	var i = 10; // unintentionally re-declared
	// do something more
}

As shown in the code snippet above, developers usually declares variables without being aware that it is already declared earlier in the code. While programming in JavaScript, this results in ambiguous results rather than straight-up throwing an error.

Declaration using 'let'

Scope

As in the case with 'var', variables can be declared using 'let' as well. When using let, the variable is block scoped. A block is the region that is surrounded by { }. During hoisting, a variable declaration using the let keyword is hoisted to the top of the block unlike the 'var' declaration which hoists to the top of the function (or global).

Let's consider the earlier example if declared using let:

function greet(gender) {
  if (gender == 'female') {
  	let name = 'Nalina';
  } else {
  	let name = 'Aswin';
  }
  console.log('Hello ' + name);
}

greet('female');

This code results in an error as opposed to the earlier case.

Uncaught ReferenceError: name is not defined

To understand why, we can check how the program flow would look like after hoisting:

function greet(gender) {
  if (gender == 'female') {
    let name;
  	name = 'Nalina';
  } else {
    let name;
  	name = 'Aswin';
  }

  console.log('Hello ' + name); // name is not defined.
}

greet('female');

As you can see, the identifier name is not defined and hence could not be referred in the console log. Recall that when declared using the var keyword, this worked because the variable declaration was hoisted to to the top of the function scope.

Initialisation of the variable

Another thing to be noted while using let is that the initialisation does not take place along with hoisting. Rather the value is initialised only at the line where the variable was originally declared.

Lets again consider the example that we took while explaining initialisation using var:

myVar = 9;
let myVar;
console.log('The value is ' + myVar);

After hoisting the program flow is made to look like:

let myVar;
myVar = 9;
myVar = undefined; // note that initialisation happens at the line where it was declared
console.log('The value is ' + myVar);

Hence the output would be:

The value is undefined

Redeclaration of variables

As opposed to using var for declaring variables, declaring with let does not allow redeclaration within the same scope. To illustrate this let's take an example:

function doSomething() {
	// some long block of code

	let i = 100;
    console.log('The value of i is ' + i);

	// some other long block of code

	let i = 10; // unintentionally re-declared
    console.log('The value of i is ' + i);

	// do something more
}

In the example above, the output would be:

Uncaught SyntaxError: Identifier 'i' has already been declared

Yes, the code didn't even start running. Redeclaration of variable that were originally declared using let is flagged even before executing the code.

let myVar = 10
var myVar = 11

As you might have guessed, the code above will also fail before execution. In fact, if a variable is declared using let, it cannot be overridden even by using var.

However, redeclaring a variable in a different scope is perfectly fine. For example, consider the code snippet below that would correctly execute:

let myName = 'Aswin';
if (myName == 'Aswin') {
  let myName = 'Nalina';
  console.log('My name inside the block: ' + myName);
}

console.log('My name in outside the block: ' + myName);
  

The program would run fine and would provide the following output:

My name inside the block: Nalina
My name outside the block: Aswin

This is because the let declarations have a block scope. Hence the initial declaration is applicable in the entire block, however it is overridden by the second declaration inside the block. The value hence will be 'Nalina' within the block, however remains to be 'Aswin' outside the block.

Declaration using const

The const keyword works exactly like a let keyword when it comes to the scope, initialisation and redeclaration. However there is a key difference - once defined, const doesn't let its value be changed!

When declaring with a const keyword, it is mandatory to provide the value. A declaration without a value would result in an error as explained with the example below:

const pi;

Output:

Uncaught SyntaxError: Missing initializer in const declaration

Also, once declared the value is not allowed to be changed, and attempting to do so would again result in an error. Again, I'll explain with an example:

const pi = 3.14;
pi = 3.14159;

The code provided above results in an error as shown below:

Uncaught TypeError: Assignment to constant variable.

Const and Immutability

I mentioned that once a constant is defined, its value cannot be modified. However, this does not mean that constants are immutable. If an object is declared using the const keyword, a different object cannot be assigned to the identifier. However, the contents of the object could well be updated.

For example,

const user = {
	name: 'Aswin',
	company: 'TCS Research'
}

user.company = 'TATA Consultancy Services';

As you can see in the code segment above, the company attribute of the author object can be modified although the identifier is declared using const.

The problem with var

As we have seen through various examples, var is a little different when it comes to usage and is mostly somewhat ambiguous. I would summarise the differences through a few key points:

  • Using let or const to define variables would keep the variables block scoped. However when using var, the variable is global/function scoped, which might be confusing for people coming from Java/C++ backgrounds.
  • Using var lets the variable be redeclared within the same scope. While this may seem like a good-to-have capability, I think this often create ambiguous results and consume a lot of time in debugging.
  • The variable declarations are hoisted in JavaScript- pulled to the top of their corresponding scopes. However, var is initialised with undefined as the value, while the initialisation takes place only at the original lines in case of let and const.

While I truly believe that usage of var is better avoided whenever possible, there is a significant community of advocates (including Kyle Simpson) of continued usage of var.

Notably Kyle Simpson argues that usage of let could result in Temporal Dead Zones (TDZ) in which a variable declared with 'let' if used before the declaration would result in an error. However, with my limited experience with Object-Oriented programming languages like Java and C#, this is more of a safety check than a disaster. And there could be numerous others that he discusses in an article, but I still feel that var is much more confusing that the let, const pair.

Conclusion

So as we have discussed through various examples, a variable in JavaScript (ES6) can be declared using the keywords - var, let and const. However, each of these are different and applicable in altogether different scenarios. The major differences between these types of declarations are in the scope, initialisation and re-declaration of the variables.

When working on ES6+ compatible environments, I think it's really a good idea to avoid using var. Using let and const would yield much more consistent and unambiguous results, at least that's what I could infer from my personal experience. However, since there are many notable individuals on both sides of the argument, it might be a subjective call to make. Perhaps reading through a few of the notable opinions on this would shed some light if you are confused.

EDIT: The original article was a bit stronger in recommendation to avoid the use of var. However, the responses that I received on various forums about this article made me rethink, particularly about the subjective nature of the topic. As I mentioned, I still believe it is for the greater good to avoid using var, but again, it could vary from person to person based on their programming background and beliefs.

Git: Clone, Fork, Pull-request and Merge-request explained

Working with Git often involves various Git platforms such as GitHub, GitLab, BitBucket etc. These platforms have Git as the underlying utility and works with the same concepts and conventions that I explained in an earlier article. However, most of such service providers provides additional functionalities that are not inherent to Git, to facilitate better collaboration across projects and teams.

Many times a project might have collaborators from outside the team working on the project. This could be the developer community as in case of open-source projects, different teams working on different aspects or use cases of an original project etc. In such scenarios, open access to the original repository would not be appropriate. To address such scenarios, there is something called a forking flow that is commonly practiced.

But before we get to discuss on forking, we can discuss about a the conventional collaboration flow.

Cloning a remote repository

Cloning is the process of checking out a Git repository into a local computer. Cloning would create something called a working-copy of the project on the machine to which the project is cloned. The working copy allows the user to make changes to the project files on this machine and lets the users commit the changes and push those to the original remote repository. This is explained in detail in my article "Understanding Git: basic concepts of Git and recommendations for use".

[ The clone project popup in GitHub ]

The remote repository doesn't contain a working directory and is called a bare-repository. The original remote repository is often referred to as the origin in the local machines to which the repository is checked out, although this can be changed if the user wish to.

The user can push changes into the origin only is he/she has write-access to the remote repository. Now as I mentioned earlier, there may be cases where the owner of a repository would allow to read the code, but does not allow writing to the repository. This is commonly the case with the thousands of open-source projects found on GitHub.

Forking a repository

In the scenarios where collaboration is to be allowed without providing explicit rights, a fork workflow is used. In this case, a developer (or a different team) would be able to fork a project into a new bare-repository. Forking is the process of creating a replica of an original repository while maintaining a pointer to the original repository.

[ A GitHub UI that allows to fork a project ]

Once a fork is created, the forked repository is completely owned by the person/team that has forked the repository. This means that they can make changes to this replica without any explicit permission from the creators or owners of the original repository.

The forked repository would still maintain a reference to the original repository which is commonly referred to as upstream. The upstream can be used when the forked replica would need to be updated with the latest changes in the original repository through a pull or fetch operation.

Pull request

One can work on an open-to-read repository by forking the repository and then cloning the forked repository to the local machine. Further changes to the original repository can be pulled and the changes that we make in the source code can be committed, and even pushed to the forked repository that we currently own. But how does this enable contributions to the original project?

This is where the reference to the original repository (namely upstream) comes into use. Most of the Git platforms such as GitHub provides an interface to request the owner of the original repository to pull the changes made in the forked repository. Such requests are called pull-requests and usually points to a specific branch in the forked repository (this is one case in which feature branches commonly used as described in my previous article). The owner(s) of the original repository would be notified by the platform and would be able to review the changes and accept them into the original project.

[ A pull request awaiting approval by the upstream project maintainers ]

Thus a seamless collaboration is facilitated without explicitly granting any privileges to the original repository. This is how thousands of open-source projects thrive in the internet utilising the millions of community developers from various parts of the world.

Is this technique limited to open-source environments? The answer is no. It is very well applicable in even closed ecosystems like that of corporations.

Applications of the fork workflow in commercial projects

In various commercial projects, the projects are hosted within private repositories, either within a Git hosting providers' eco-system, or as an independent platform hosted within the company's infrastructure. In either case, there would be usually a closed ecosystem within the organisation.

That doesn't mean that all projects within the company are accessible to all employees/teams in the company. There could be various segments within the organisation dedicated to different accounts. When collaboration is required across such teams working in silos, the fork workflow is useful.

Consider an IT services company which has a team working on a generic product to be used in various other projects of the company by customising it to the project-specific needs. The original project in the meanwhile may continue to evolve by adding generic features.

In such a scenario, it may not be appropriate for the external project teams to be given access to the product's code repository but may want to continuously take updates from the original project. Also in certain cases, the changes done for a customised version of the product (by an external team) might be good to be integrated into the original generic product.

If the fork workflow is applied here, each of the customised projects could be a fork of the original project. Thus the access control is appropriately handled, but at the same time the external projects can take specific updates from the original project as they feel necessary. Also, if certain features developed in the customised project turns out good to be promoted to the original project, a pull-request may be raised. The pull request could be accepted and the changes integrated into the original project by the project owners.

Merge requests

From whatever explained till now, pull request would turn out to be really useful - particularly when it comes to access control. So why can't the same philosophy be adopted within a single project to restrict access to specific branches?

Various Git platforms lets the owner of the repositories to protect certain branches. This way, only certain team members would have write access to the branches, or in some cases no one would have a direct write access. Many teams would have processes in place such that a review process is to be completed before a feature branch is merged on to the master branch (the main branch). To cater to such scenarios, we have the concept of merge requests.

When a specific milestone is achieved in a feature branch, a merge request could be raised to merge the changes in the feature branch with a different branch - often the master branch or another protected branch. The merge-request could be reviewed and accepted by team members who have privileges to do so.

[ A merge-request (pull request from the same repository) ready to be merged in GitHub ]

Most Git platforms allows the reviewer to compare the changes with that of the previous state of the target branch through a dedicated user interface that aid in the review process. This way an age-old problem of maintaining the source-code integrity with multiple developers involved is somewhat solved.

Conclusion

While Git itself is really powerful, the additional features offered by various Git platforms such as GitHub and GitLab are game-changers when it comes to collaboration in software development. Features such as the forking workflow and merge requests have been instrumental in the success of various open-source projects that thrives in GitHub and the like.

While getting familiarised with these concepts is essential in open-source development, it would also be useful to possess mastery over those even in large or complex commercial products. In fact, I was able to successfully adapt the concepts of Git fork workflow that could transform the working on various teams dealing with variants of an original project through forked repositories.

Understanding Git: basic concepts of Git and recommendations for use

Git has recently become an essential part of software development. For most developers, a particularly good GitHub profile is as good as or even more useful than a good resume. But Git is not GitHub. While GitHub is an amazing Git service, Git in itself is a wonderful piece of engineering that is so useful, much more worth than the hype.

Git is a version control system used to housekeep source code and such. Wikipedia describes Git as:

Git is a distributed, version-control system for tracking changes in source code during software development.

Why version control?

A popular belief about Git and other version control systems (VCS) is that those are required for large teams and open source projects to enable collaboration. While this is absolutely true, VCS has a more essential, critical use- to keep tab on the evolution of code.

Why to keep track of the evolution of code? When working on medium to large sized software projects, the software is coded over a long time period. Over time, the developer herself tends to miss the reasons for certain decisions that were taken earlier. While code comments would indeed help, comments are seldom effective in comprehending chronological changes. Also, many a times the developer may need to roll-back to or at least preview the state of the code at an earlier point in time.

A version control system allows a developer to check-in code files, often on accomplishing certain milestones, such that it will act as a checkpoint for later reference or roll-backs. The check-ins are otherwise called commits and often carry a comment along so that it helps to comprehend the state of mind at a later instance. Commits also doubles down as a code backup, and references for other people while collaborating. Many VCS solutions also allows for comparing between different commits.

What makes Git so popular?

Git definitely is not the only VCS solution out there, nor was it the first. There are others like CVS, SVN etc. However Git turned out to be the most popular one yet due to its unique way of handling the versioning. The primary distinguishing factor in my opinion is that Git is a distributed version control system. There are other things as well such as performance, integrity, flexibility and security that proved to be major factors for the success of Git. We can get into the details of some of these while explaining the Git terminology.

Git terminology

[ The 4 stages in Git. Image source: GitLab docs ]

Remote and local repositories

Git is a distributed version control system. The code repository is often stored at a central location and a copy of the repository (or a smaller part of the repository) is stored in the machines of each collaborator. Here, the repository at the central location is called a remote repository and the one in the individual developer's computer is called a local repository.

A remote repository might be checked out (or cloned, in Git terminology) into several computers where the code is updated and pushed back to the server. Similarly a local repository might as well point to multiple remote repositories. This capability is one thing that makes Git really effective in a handful of scenarios.

Working-copy

Git stores a copy of the entire repository (or that of a few branches) on the developer's computer. Such a repository maintains the current code state, changes in the code over time, commit comments and many other things. This is hidden and non-readable by humans.

However, the current state of the code that the developer is working on is stored as readable files and this is called the working copy. The working copy is usually the directory into which the code is checked out. Other data that Git maintains are stored in a .git directory located inside this location.

Commit

Git maintains a repository as a set of incremental changes or deltas. A delta would contain the changes to the files over the previous revision of the same files. When the repository is checked-out, Git computes the state by incrementally applying the deltas over the initial version of the file. Commit is a mechanism in which the developer submits the changes in the code to the Git records.

A commit also carries a commit message, time and date, author information (such as name and email) etc. along with it. In Git, every commit is identified by a unique hash code. The commits can be later viewed chronologically and filtered. In most cases, a commit is atomic and should not be altered and it is based on the commits that Git maintains its integrity.

Staging area

Git maintains an intermediate level between the local and remote repositories, often called a staging area or an index. Many a times, the developer would be working on multiple files at the same time, but not all may be ready for commit. As mentioned earlier, the commit is usually a state or a milestone and would carry a commit message, such as "Updated feature A". If the update is as a result of changes in two files, it doesn't make sense to commit those separately. Also in a practical scenario, the developer won't be able to discard the changes in a third file that is not yet ready for a commit.

This is where the stage or index comes handy. The developer could add the two files involved in the update to the staging area, and then make a commit from the staging area specifying an appropriate commit message. Staging area also facilitates provisions reviewing the changes before a commit is made.

Branches

Consider branches like the branches of a family tree. The code is evolved over time and may need to diverge into two at many instances in this evolution. Take an example in which a software would need to be continuously supported after being delivered to a customer, but would also needs to be further evolved so as to add new capabilities. Both the scenarios would require making changes to the code files, but cannot be done simultaneously. In such cases a new branch is spawned and active development would continue in both the branches until a point when both could be merged.

[ Git branches overview. Image source: GitLab docs ]

Git comes with a default branch named master and it is a convention to use this branch as the main branch when using Git. Branching is exceptionally efficient in Git and is a significant aspect of Git's popularity.

Due to the efficient versioning algorithm of Git, a developer can switch between branches rapidly and easily. Switching branches with Git doesn't need to have the entire code of a branch checked out altogether, rather would be handled by moving the pointer to the latest commit on the branch. The state marked by the latest commit in a branch is called the HEAD of the branch.

Push, Pull and Fetch

Once changes are committed to a specific branch, the changes are still in the local repository. The changes would not be reflected in the remote repository until the changes are pushed to the remote repository. Similarly, the changes others make and push to the remote repository would not be reflected or received unless the changes are pulled.

Many times it may be required to take the changes from the remote repository, but not apply it to the local code. This happens usually when the current code needs to be compared with that of the remote repository or as a backup mechanism to compare or merge later in the absence of remote connectivity. In such scenarios, the changes from the remote are fetched to the local repository, but not applied on to the working copy until the developer choose to.

Recommendations for using Git

When it comes to Git, usage is one thing even expert programmers tend to err. When moving from legacy VCS environments to Git, it takes not only a technological shift, but a mindset shift as well. Working with Git often requires an altogether different mind-set in order to harness its full power, as compared to conventional versioning frameworks like CVS and SVN.

For using Git there are certain strategies laid out by various service providers. A few notable of those are the Git flow, the GitHub flow and the GitLab flow. These are in-depth philosophies on how to make use of a powerful technology rather than a mere user manual.

I find Git flow hard and cumbersome to use and often underproductive for any team/project size. GitHub flow is usually good for small projects and medium open-source projects while GitLab flow is better suited for large projects and large teams. However, each has its own pros and cons and I suggest to have a look at those if possible.

Listed below are a few best practices that I learnt over the years working with Git. These are mainly based on my experiences with the GitHub and GitLab flows and covers certain aspects that are often not sufficiently focused while using Git.

  • Get familiar with Git command-line. This is very important. Although many IDEs come up with their own visual implementation of Git, many are known to cause unwanted issues. For example, Eclipse EGIt plugin is notorious in my opinion and almost always run into problems. I find the in-built Git features of Visual Studio Code reasonably good, but it would be still better to be familiar with Git command-line which would definitely save a lot of trouble one day.
  • Commit (and push) as frequently as possible. This may seem odd, as many are acquainted to work for hours or days and commit only once a work is done. This is problematic when it comes to multiple people working together. When the deltas are small (due to frequent commits) it is often easier to resolve merge conflicts and most of the time Git does the work by itself. Also, having regular backup is an added advantage.
  • Avoid committing anything that breaks existing functionality. While it is good to commit frequently, do not commit any code that would break existing functionalities. Committing incomplete features is fine, but make sure that the commit doesn't break anything that was working earlier. This is particularly true when many people are working on the same branch.
  • Utilise feature branches. When working in large projects, new features may take quite some time and efforts to complete and involve multiple people working. In such cases, it is always better to create a feature-specific branch. This would save time and efforts by offloading the handling of conflicts to a single time of merge. If commits were proper and frequent, even that won't be much of a trouble. However in small projects it might be okay to commit directly to the master as in the GitHub flow.
  • Break large features into smaller milestones. One thing I would recommend is to keep the feature branches alive for as short as possible. Keeping a branch alive for a longer duration would attract unwanted conflicts. Hence, it would be beneficial to break big features into small milestones and create separate branches for each milestone or keep merging the feature branch into master after every milestone.
  • Frequently merge master onto long living branches. In some rare situations, long living branches could be hard to avoid or broken down further. If such a scenario arise, it is possible to merge the master on to the branch regularly such that the branch is in sync with the master and serious conflicts at the eventual merge could be minimised.
  • Write proper commit comments. When the code is committed, avoid comments such as "updated file1", "updated configurations" etc. There can anyway be inferred from the commit details. Write appropriate comments such that you or someone else would be able to understand the purpose of the change and why the change was made.

These are just a few of the recommendations for use and the applicability may vary depending on the specific scenario. However, certain aspects like getting familiar with the Git command-line interface and the concept of frequent commits and appropriate comments are more or less always applicable.

Conclusion

Git is a wide topic to be covered in a single article like this one. The general intention of this article is to provide some insights into the various basic concepts of Git and to prevent usual mistakes. There are many more advanced concepts like rebase, blame, revert etc. that would help handle various situations and provider specific concepts like forking, pull requests, merge requests etc. For understanding more about the concepts of forking and pull/merge requests, you can read my article Git: Clone, Fork, Pull-request and Merge-request explained.

As I mentioned earlier, getting familiar with Git command-line interface is instrumental in taming Git. Most of the commands and their usage can be learnt from this beautiful TutorialsPoint tutorial. Also, it is a good idea to read further on Git philosophies from this article from GitLab docs.

Node.js: how Node revolutionised software development

JavaScript (JS) had been a backbencher among the programming languages. In fact, many didn't even consider JavaScript a programming language, rather it was viewed as a scripting language for the web. Although certain JS libraries like JQuery did exist, those were not enough to brighten up the reputation of JavaScript. Then in 2009, Ryan Dahl - an American software engineer - unveiled Node.js in the European JSConf. That was the beginning of a change, a creation that not just revolutionised the future of JavaScript, but of software development altogether.

[ The official Node.js poster image ]

Earlier in my article Rise of JS: 5 amazing benefits of learning Javascript, I mentioned about a few surprising things one can do with JavaScript. And there are even more things in the making, like Internet of Things (IoT) applications, network applications etc. Most of this was as a result of the advent of Node.js.

Over the years, Node.js grew significantly and is still growing at a very rapid pace. In fact, Node.js is now the most used programming ecosystem in the world. According to a recent GitHub State of the Octoverse report, JavaScript is the world's most popular programming language, thanks to Node.js. In this article, I try to explain how Node.js has been a game changer, and how it is for the good.

[ Top languages over time. From Github State of the Octoverse ]

What is Node.js?

Node.js is an open source project that lets programmers build applications with JavaScript. Similar to Java's JVM, Node.js makes it possible for these applications to work across platforms, be it on Mac, Windows, Linux or even several IoT devices for that matter. Wikipedia describes Node.js as

an open-source, cross-platform JavaScript run-time environment that executes JavaScript code outside of a browser.

So what exactly is Node.js? Is it a programming language or a JavaScript library? The answer is that Node.js is neither a language, nor a library. It is in fact a run-time environment that is based on a JS engine called V8. V8 is the powerhouse that interprets the JS code for browsers like Google Chrome and Microsoft Edge. Node.js is built upon the V8 engine along with a few APIs to access the core operating system features that were not part of the V8.

Benefits of Node.js

JavaScript as the language

JavaScript is very easy to learn and with the ES6 specifications, it is quite flexible as well. The greatest advantage that I always felt about JavaScript is that with much less code one can get things working compared to other popular programming languages.

Node.js lets developers create software using JavaScript. And that is a big deal! In the realm of web applications, JavaScript already had a monopoly in client side (web UI) development. The relevance of JS as a client-side language grew exponentially with rise of client-side frameworks like Angular and React. Also, most of the data exchange format in internet based applications were built around JavaScript Object Notations (JSON). Adopting JS for the server-side would mean that one would need to learn only a single language to build full stack software. One less skill is money saved when in a large scale.

Performance

Node's momentous was not purely from simply using JavaScript as the language. One of the major factor that makes Node.js outshine other ecosystems is its performance. Node.js is built on the V8 engine and is written in C++ language. Both Node.js and V8 has undergone significant changes and as a result, the current JavaScript performance matches to that of other low level programming languages such as C.

Event-driven, non-blocking architecture

This is the single most critical factor that led to the well scalable nature of Node.js. Node.js is based on an asynchronous event-driven architecture with non-blocking I/O operations support. We will get to this in detail while explaining the Node architecture. This architecture allowed Node.js to rapidly scale up through efficient usage of resources, particularly compared to the contemporary web frameworks.

Node Package Manager (NPM)

Another significant reason for a large adoption of Node.js is the Node Package Manager (npm). NPM, written by Isaac Schlueter launched in 2010 and went on to become the largest software registry in the world. Through NPM, the Node.js developers have access to a huge collection of code packages such that for a given objective, the only trouble now would be choosing between the libraries.

Node.js architecture

As I mentioned earlier, the Node architecture was the primary distinguishing factor for Node.js. Before we discuss about Node architecture, let's have a brief overview on how many popular platforms handle processes. Popular programming frameworks (like that of Java) is centred around threads. A thread is a sequence of instruction that a runtime executes, such that every instruction in a thread is invoked once the previous instruction is completely executed. That is, if a preceding instruction takes a while to complete (writing a file for example), the runtime would not head over to the next instruction in the sequence.

A common way this was tackled is by spawning new threads. When spawning a new thread, certain instructions are offloaded to the new thread and the original thread resumes its operation. But this is at the cost of memory and CPU overhead created by a large number of threads, particularly when in the waiting state.

[ An overview of the Node architecture from Microsoft Docs ]

Node.js runs on a single thread event-loop mechanism. In this approach, the developer could register callbacks for operations which would be triggered once the operation is completed. An operation completed is an event, and when such an event occurs, Node.js triggers the callback registered with the operation. This is called an event-driven architecture in which the program doesn't need to wait for a long operation to complete and could proceed with the execution. This kind of programming paradigm is referred to as asynchronous programming.

Since the handling of the event is taken care of asynchronously, such operations doesn't block the flow of the program, and hence called non-blocking operations. Concurrent operations are internally handled by Node.js in a very efficient manner using a worker pool. Node.js also offers advanced features such as cluster modules that provides greater scalability and load balancing capabilities.

Due to the asynchronous program flow in Node.js, a single thread can be reused for multiple tasks. For example in a web application, if a request from a user is waiting for an I/O operation to complete, the same thread could be re-used to cater to another user. This way, Node.js saves a significant memory and CPU overhead as compared to conventional web frameworks like Java EE.

When to and when not to use Node.js

Node.js can be used in most of the practical software development scenarios. One thing that I very frequently find myself using Node.js is to create dummy services. While developing client applications that require data from a server, I could easily set up a dummy server with Node.js that mimic the responses of the original or to-be-created server. Also, I find myself using Electron backed by Node.js to build cross-platform desktop applications like that of Blogly.

However, when building CPU intensive applications, Node.js is not yet recommended. This is because CPU intensive computations could impact the concurrency handling capabilities of Node.js and could render the application slow and sluggish. However, Node.js could still be used as the front-facing interface and to handle common logic, while the computationally expensive operations could be offloaded to more capable environments like Python and C++.

Conclusion

To summarise, Node.js offer a greater advantage in terms of flexibility, performance and scalability when compared to other popular frameworks. JavaScript, backed by Node.js, is now a multi-purpose technology and can be used in a myriad of applications as I explained in Rise of JS: 5 amazing benefits of learning Javascript.

The adoption and job market for Node.js is increasing at a rate higher than anything that was witnessed in the recent times and has went on to become one of the most used technologies. It has already made its presence in technology giants like NASA, Netflix and Uber and is getting adopted in myriad new domains. As Jeff Atwood- the co-founder of StackOverflow- said

Any application that can be written in JavaScript, will eventually be written in JavaScript.

I have been using Node.js for the past few years and was never let down. Although I feel that Node.js could still evolve by harnessing emerging technologies like TypeScript, I never found anything close to Node.js when it comes to quickly developing software products. Also, I believe that Node.js could soon be capable of handling computationally demanding tasks also.

Getting started with Node.js is really easy. For people who have some experience with programming, Node.js docs would be largely enough to get started. For novices, there are many video tutorials out there in Youtube, Udemy etc. that will give a reasonably good position to start off in a few hours. I found the recent Microsoft offering "Build JavaScript application with Node.js" also appealing and it covers the topic is a very systematic way.

Rise of JS: 5 amazing benefits of learning Javascript

Since the beginning of the lockdown, many people have been looking forward to acquire new skills. And I have seen a lot of interest in learning programming languages. I was asked by a few of my junior colleagues, interns and students on which programming language should they invest their time in.

As long as their requirement was generic application development, my advice has been mostly to learn Javascript. That might seem a little strange to many, as Javascript has been long considered as a secondary skill. This is because Javascript was mostly used to develop client side experience for web applications, and as long as you are not working in a company that has the luxury to afford a separate UI development team, Javascript was just another skill that is good to have.

[ A screenshot of an application that I am working on using Javascript ]

In this blog post, I would try to explain why it is no longer the case, and investing time in Javascript is rewarding than never before. Let's discuss what are the different things that you could do with Javascript.

1. Client-side development for web applications

This is the most obvious of all. Javascript has been in use as the client-side scripting language for years and has easily sidelined others such as VB script and many others.For those who are not aware of what client side scripting is, it is used to build most of that you see while using web applications (or in common language, websites). For an example, you must have noticed that an advertisement popup was displayed almost immediately after the page was loaded, this was done through a client side scripting language - which in my case was Javascript.

Javascript in its original form is increasingly not used for client side development. Rather, it is replaced with a class of frameworks called Javascript Libraries. A few popular examples would be React, Angular, Vue.js etc. which recently emerged, and legacy JS frameworks like ExtJS, Dojo etc.

2. Developing mobile applications

A few of you must have been surprised, but Javascript for building mobile applications has been in use for quite some time. Apache Cordova (erstwhile PhoneGap) is a framework that wraps code written in HTML, CSS and Javascript into a mobile application using something called a native container. Various functionalities of the mobile devices are exposed to the JS application via this container APIs. And thus one could easily build an app that runs on almost all smartphones and tablets.

A new entry into this field is something called React Native. Cordova used to run web pages in what is called a web view, which can be thought of as similar to web browsers. This comes with a drawback that the apps lacked native experience. Through React Native, developers could create mobile to deliver native experience through apps that are developed mostly in Javascript and React, which is a best-in-class JS library developed by Facebook.

The common thing across both the frameworks that I mentioned here, and many more, is that you would not require much programming skills in anything other than Javascript and the corresponding frameworks to build an application from scratch. These frameworks are pretty easy to learn once you are familiar with Javascript and some HTML/CSS.

3. Server-side programming

So far we have seen the capabilities of Javascript as mostly a client-side programming language, be it web application UIs or mobile applications. Most of the solutions nowadays would require a part that runs on servers (remote machines), which were conventionally developed with programming languages such as Java, Python, .Net etc. This was until recently when a revolutionary programming environment called Node.js was released in 2009.

Node.js has quickly emerged as the world's largest open source library ecosystem, primarily due to it being very easy to learn, quick to develop with and very light weight. Many popular web applications like LinkedIn, PayPal, Uber etc. use Node.js for part of their services.

You could be surprised if I tell you that you can get a web application up and running with only this much of code.

const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(port, () => console.log(`Application running at http://localhost:${port}`));

All you need is Node.js and a Node.js web framework called Express to run this and display "Hello World!" in the browser when navigated to a link http://localhost:3000.

4. Desktop application development

This would be surprising for many. Never heard that you can build desktop applications with Javascript? And what if I say that many of the popular web applications like WhatsApp Desktop, Visual Studio Code, Slack etc. are all built with a Javascript framework? Yes, that framework is Electron JS.

Electron offers an easy way to build cross platform desktop applications with the help of HTML, CSS, Javascript and Node.js. Even I have personally used Electron extensively and I am writing this post through a desktop application that I developed with it. Although the application is in a usable form, I haven't published the app yet due to it being still under development.

[ The blogging application that I am developing with Electron. You can see the current post in its draft form. ]

5. Developing machine learning models

Machine learning is a technique in which machines are given training inputs and the outputs to tune the machine to predict outputs for future inputs. This is in contrast to the conventional programming where the developer programs the machine on "how" to compute the output, rather than the machine itself figuring out the how part.

Say if you want to build a system that recognises cars on a road, you would first develop a learning algorithm, then feed it with a huge number of images with cars and without cars. Against each of the images, would it be labelled if it is a car or not. Based on the training algorithm and the data, the machine would learn to recognise if there is a car in a new image given.

The tuned system is what is in general called a machine learning (ML) model. And you can develop such models with Javascript.

Uhh, no!! Seriously? I've heard you would have to learn python, R, Matlab etc. to do machine learning stuff. Are you joking?

If this was your response, its time to check for TensorFlow.js. The most popular framework out there to build machine learning models - TensorFlow - has a Javascript version as well. You could build ML models in javascript and use these models in your Node.js applications or run directly in the browser.

Thus with Tensorflow.js, you can build machine learning features and seamlessly integrate it with your web, mobile or desktop applications that you built with the Javascript libraries.

6. Myriad of other scripting tasks

Well, I know I said 5 things, but here is a 6th thing that Javascript was always being used for. From scripting websites, to automating test workflows, Javascript is used for numerous scripting purposed. You may be surprised if I say that JS is widely used for hacking into websites and networks as well due to its capability to be injected and executed. And hence Javascript is very crucial for cybersecurity as well.

With the rising popularity of Node.js, Javascript is taking over many conventional scripting applications which were otherwise performed using languages like VBScript, Python etc.

Conclusion

You would have already inferred from these 6 points that you can build most of what you would like to develop with Javascript, right? But in reality for building most of the practical application that would have a user interface, one would have to know basics of HTML and CSS as well. These are simpler languages with which user interface elements are built, and aesthetic and behavioural styles are applied.

Also, it is always good to have some familiarity with at least one javascript library that fits your requirement. That would help you save a lot of time by not re-inventing the wheel. But these are way lot easier than you think, once you have a strong foundation of Javascript.

Getting interested to learn Javascript now? The best place out there to learn Javascript would probably be w3schools. If you get started now, you would be able to attain a significant Javascript competency before the lockdown is over. You would not need any extra tools or resources other than your browser, at least for experimenting with the basics. W3Schools itself has an online editor to try out what you learn in there. Moreover there are various other online applications like JSFiddle that allows you to experiment with Javascript code online.

In case if you do not want to use any of the online tools, just head over to Google Chrome, and click on the "Developer Tools" menu item from Menu (three dots on top right) --> More Tools --> Developer Tools. In the window/panel that opens up, head on to the console tab. You can try out most of what you learn in this console, even when you are offline.

[ Chrome DevTools where you can experiment with JS commands ]

I am also working on a programming tutorial through javascript for absolute beginners. Please do let me know as comments below if you are interested. Also, please do let me know your opinion and suggestions on this blog post as comments below and help me reach a larger audience by sharing this post.