Write high quality and maintainable code with collarjs

collarjs playback feature

Why it is difficult to make high quality code?

High quality software relies on how developers understand the code:

What is the architecture? How is data structure defined? How does data flows? What does the code do? and why? Where is the best place to put my code? Will my code impacts other codes and cause bugs? Will my code cause the failure of other functions? Which framework is used in the code? How to implement it with this framework?

Developers need to have a clear answer to these questions to make high quality code.

However, these questions are not easy to answer especially when there are thousands of or millions of lines of code (LoC). It is very hard to reconstruct code-owner's thoughts by simply reading the code line by line. Even the code-owner himself can't remember his original thoughts if the code is created weeks ago.

Today, I will introduce a javascript library collar.js to help developers build high quality software.

Collar.js is a javascript library to help developer build high quality, easy to understand code. It contains two parts:

  1. An asynchronous programming library to transform developer's thoughts into code
  2. An interactive visualisation tool to reconstruct developer's thoughts directly from the code

Take a login form for example:

When the user click 'signin' button, if the email and password match, alert "login ok", otherwise alert "login fail".

You probably already know how to create such a form with one of your favourite framework. For example, with jquery, you can handle the login in $("#signin-btn").click() event listener. However, the implementation is tightly coupled with the framework you use. Your thoughts is hidden in the implementation details. Another developer needs to know jquery well and read your code line by line to guess what you thought when you created the code. If one day, your team needs to use a different framework, or immigrate your app to another platform, you need to redo everything with the new framework.

How collar.js can help?

Let's see how collar.js can do it in a better way.

First, let's focus on the business logic without thinking of the framework we use. The login process can be described as following:

  1. listen to the UI event, when user click 'signin' button
  2. get the email and password from UI
  3. check the email and password pair
  4. if email and password match, alert 'login ok'
  5. if email and password not match, alert 'login fail'

In collar.js, you can record the thoughts with the following code:

// enable dev tool
collar.enableDevtool();  
// create a namespace
var ns = collar.ns("com.collarjs.example.login"); 

var loginPipeline = ns.sensor("listen to UI event")  
  .when("user click 'signin' button")
  .do("get email and password from UI")
  .do("check email and password pair");

loginPipeline  
  .when("email and password match")
  .do("alert 'login ok'");

loginPipeline  
  .when("email and password not match")
  .do("alert 'login not ok'");

The code above is straight forward and clear enough to describe your thoughts, but collar.js can do more:

Now install and run collar-dev-server

> sudo npm install collar-dev-server -g
> collar-dev-server

Open http://localhost:7500 in your browser.

Run your app, (as the login form is a web app, you can simply refresh your login page), you will see the following flow at http://localhost:7500.

unimplemented collar login flow

From left to right, the flow demonstrates your thoughts. The nodes of the flow are all marked as grey, because they are not yet implemented.

Single Responsibility Principle

The different shapes in the flow above describe different responsibilities. Collar.js has 4 basic nodes represent 4 different responsibilities:

  1. A sensor, (slash shape) is responsible of observing external worlds, and transform the external world's event into your app's domain event.
  2. A filter, (filter shape) is responsible of controlling the signal flow with certain conditions.
  3. An actuator (triangle shape) is responsible of making side effects, it interacts with external world, but never changes the incoming signal, the incoming signal is emitted once the actuator finished its job. The result of the actuator is inserted in a special 'result' property of the incoming signal.
  4. A processor (rectangle shape) is responsible of converting incoming signal from one form to another, and emitting the new signal.

Now it is time to implement them one by one with your favourite framework/library. In this example, we will use jquery:

Implementation

We simply add a watch function to the sensor operator:

ns.sensor("listen to UI event",function(options) {  
  $( "#signin-btn" ).click(() => {
    // send the domain event: 'signin'
    this.send({
      event : 'signin'
    });
  });
})

As the sensor's duty is to detect external events, we don't make any processing here, an application's domain signal is emitted with event name 'signin'.

For the filter, we check if the signal's event field is 'signin'

.when("user clicks 'signin' button", signal => {
    return signal.get('event') === 'signin';
  })

Next, we implement the 'get email and password from UI' actuator. It interacts with environment (the UI view), gets the email and password from input control, and returns the email and password as result.

.do("get email and password from UI", signal => {
    var email = $( "#emailInput" ).val();
    var password = $( "#passwordInput" ).val();

    return {
      email : email,
      password : password
    }
  })

The 'check email and password pair' actuator will check if the email and password match or not. If match, returns "ok" as result, otherwise returns "email and password not match" as result. The email and password are obtained from the signal's result field (which is set by 'get email and password from UI' actuator).

.do("check email and password pair", signal => {
    var email = signal.getResult().email;
    var password = signal.getResult().password;

    if (email === "test@collarjs.com" && password === "collarjs") {
      return "ok";
    } else {
      return "email and password not match";
    }
  })

In this example, we simply hard code the accepted credential to make the example short. You can access a database or a Restful service to check user credential.

The rest of the nodes are implemented as:

loginPipeline  
  .when("email and password match", signal => {
    return signal.getResult() === "ok";
  })
  .do("alert 'login ok'", signal => {
    alert("login ok");
  });

loginPipeline  
  .when("email and password not match", signal => {
    return signal.getResult() !== "ok";
  })
  .do("alert 'login not ok'", signal => {
    alert("login failed");
  });

Now refresh the login page again, you will see your flow turns to white in collar dev server, as all of them are implemented.

implemented collar login flow

Understand how your code works with collarjs' playback feature

Your login form now works as expected, you can login with test@collarjs.com / collarjs, and it will give you a 'login fail' alert if you try to login with other credentials. Besides, you can see your thoughts in collar dev server at any time (by calling collar.enableDevtool()).

What about the runtime? What if you are debugging a runtime bug, and you want to know exactly how your code works? collar.js provides a record/playback feature to help you understand your code in runtime.

Let's see how your login form actually work with collar.js' record/playback feature.

First, go to collar dev server page (by default http://localhost:7500), click the start recording button on the left top corner.

collar record button

Now go back to your login page, enter email (test@collarjs.com) and password (collarjs), and click signin.

You will see a sequence of signals are recorded in collar dev tool page. When you drag the light blue bar over a signal, the node who sent this signal is highlighted, and the signal's content is displayed in the Signal panel. With the record and playback feature, you can easily understand how data flows through nodes, and how data changes during the propagation.

collarjs playback feature

Locate the exact code from collar dev tool

With collar dev tool, you can easily locate your code without searching it in a code file with thousands lines.

Click the node you are interested in. For example, we want to modify the way we check the email and password pair. Just click the node 'check email and password pair'.

collar-node-properties

You will see the Node Properties panel, in the stack section, it lists your code's call stack. In this case, the 'check email and password pair' actuator is implemented at line 25 of file login_step_2.js.

Want to use reactjs or angular instead of jquery?

With collar.js, it is easy to adapt your code to a different framework/library. As we know that only sensor and actuator interact with environment (in this example, the UI view), you only need to reimplement the 'slash's and 'triangle's shown in the flow graph. Click them and locate where the code is, replace the implementation with your framework specific code. Your data flow does not change at all! You can guarantee that your business logic is the same as the jquery implementation.

Your thoughts?

collar.js is in alpha stage now (August 2016). It is currently under active development. Your ideas and suggestions are always welcome! (mail to daily.bhou@gmail.com)


collar.js : http://collarjs.com

collar.js documentation : http://www.collarjs.com/document/index.html

You don't need to go through this tutorial step by step, try it directly at http://collarjs.com/examples/login/index.html?dev=1

Bo HOU

PhD, Engineer

Paris

Subscribe to Bo HOU's blog

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!