Image courtesy of Me

JavaScript Performance Analysis And The Mutating Interface: Part Two

Welcome Back

In the first part we looked at creating a super basic JavaScript stopwatch so we could profile some frontend code that was causing issues. In this part we are going to use that code as the subject of an experiment to see if we can learn anything.

It's Self-explanatory Right?

Let's take a look at the code from part one but this time annotated with comments:

When we were instrumenting the code with this stopwatch it seemed really simple to use but when someone on the team tried to instrument code themselves later they ran into a classic problem when writing software: what do I call and in what order?

Self-Revealing Interfaces

The previous code looks like most classes written. It has some methods and you need to understand how to use them for all the state to work out correctly.

For example what would happen if you ran this code:

When I ran it 1510 was logged to the console. What sort of broken stopwatch would start counting when you stop it and stop counting whe you start it?

Now you can see why we have the Math.abs call around the timer calculation. If that wasn't there and someone called it as we did in the above example they would get a negative time. At best they would be confused and at worst they would think their code was so performant that they had ripped a hole in the space-time continuum and executed their code in the past.

And that's the basis of today's hypothesis.

Hypothesis: Can we create an interface that ensures you execute functions in the order they were intended?

It''s easy to see what went wrong. When we originally developed the code we had a few assumptions:

Unfortunuately our model is pretty weak. Let's take a look at modeling it differently so we can prevent incorrect usage.

Using a State Based Model

A stopwatch already has a pretty solid model that it fits into and happens to be one that programmers like - state machines. State machines are great for clearly showing what the states and transitions between them are so you can reason about them.

Let's take a look at what a state diagram of our stopwatch would look like:

This looks promising. Once we initialize a stopwatch it can only be started. Then we can only stop it once it has been started. We can't get from initialized to stopped because there is no transition between them.

Let's take a look at the code we came up with:

And here is how we would use it:

Rather than use an actual state machine library we are using objects that contain methods that will transition to the next state. In other words each object represents a single state and provides operations and implementations that only apply to the particular state. The init state can only start and the start state can stop, reset, or restart.

JavaScript's dynamic nature helps us out here since we can reassign the variable timer without worrying about types.

Some interesting things to note about this implementation:

Experiment Results and Future Use

The experiment proved very interesting. We were able to model the code differently and in doing so prevented a set of problems typically seen when using shared libraries. Although the re-assigning pattern we use fro the timer variable is not optimal and takes a bit to get used to it was a small price to pay for what we achieved in return.

While it was overkill and possible ill-advised to use this technique on something as performance sensitive as a stopwatch we could could use this technique to model things in more useful domains.

How many times have you seen a Customer class that knows everything about a customer's lifecycle? What if you model each stage in the lifecycle as a seperate state-based class? UnregisteredUser transitions to UserRegistrationConfirmed which transition to ActiveCustomer. Later on there could be TerminatedCustomer or SuspendedCustomer or even VIPCustomer depending on what your domain model calls for. That would make it clear what applies to each stage of the customer's lifecycle and prevent embarrassing mistakes when you forget to check if they are a VIP customer not to mention preventing invalid state transitions like preventing a customer from being suspended when they have just confirmed their registration. It would also make the code simpler to reason about and test since each state takes a defined message to transition from one to the other.

The key takeaway from this experiment is that there is power in how we model our abstractions. Each domain usually has an assumed model and many issues can be resolved by listenening to what the domain model has to say. The more expressive the model is in our code the fewer assumptions that will trip up the next person to pick up our code. After all, it could be you who picks up the code a year from now and instead of wondering how the heck everything works you can immediately be productive and focus on delivering important value. Good luck and keep experimenting :-)

Further resources



comments powered by Disqus

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© 2017 Frank Meola

Back to top