Web App as a Graph

How visual programming makes sense for web applications

Dan Stocker
1e14
6 min readMar 27, 2019

--

TL;DR — Web apps could be programmed visually, improving not only reuse, separation of concerns, clarity, and performance; but also not sacrificing an iota of developer control. We should do it.

History

Over the past 10 years, I wrote a number of web app frameworks. It all started with these libraries (#1), then a closed-source framework for a startup (#2), then this (#3), which then evolved into this (#4), and now I’m working on this one (#5).

It’s a curse, really. Cost me a lot of time, effort, and money.

I’m not going into detail on the “why” now. There aren’t many good reasons to roll your own, but I believe I had them then, and I have them now. In this post, I’d like to focus on the evolutionary path it has taken, (really, if we look at each one closely, it’s the same framework) and where things go from here.

Coming from a classical object-oriented background (Object Pascal, C++, C#), my journey into JavaScript quickly turned into obsessing about “fixing” OOP, in a way that lets me throw a bunch of behaviors together seamlessly, and voila, I get a data-bound dropdown with multi-select option list. After 7 years, with “Blend”, (iteration #4) I finally achieved it, by inventing a whole new mixin-only class system. This dropdown ended up being composed of over 30 mixins, but it worked! (And it really was just a bunch of independent behaviors stacked up.)

What soured the victory was, well, OOP itself. In order to get everything working, we need to lift a lot ‘up in the air’, ie. creating thousands and thousands of component instances across different layers of the application — from API integration through internationalization, model entities, to UI widgets — which turns out to be pretty costly. And not just lifting them up, but keeping them in the air, too.

Given, once instances are set up and ready to sync, things go pretty well. Performance-wise at least. Syncing state is where OOP rears its ugly head again, with loads of boilerplate: subscribe — handle event — unsubscribe; in every. single. component, for every. relevant. event.

Turn thy Head

Because of my OOP-shaped tunnel vision, I was never a fan of statelessness. But once I started looking at it as my OOP approach turned 90 degrees, everything changed.

Let me explain.

My custom, override-less, diamond problem-defying classes looked like this: ‘horizontally’, I have a bunch of methods contributed by mixins; ‘vertically’, methods pass their result, and other variables to their counterparts one level up the inheritance chain, like piped functions. Some methods, such as event handlers, invoke other methods (chains of functions), often with a transformed or aggregated value (state).

This sounds a LOT like functional. Only with functions that have multiple inputs and multiple outputs, which, in turn, sounds a lot like nodes in a graph.

Early on in my OOP JS days, (2011) I already established a queryable container as the application’s single source of truth. The rest of state management ever since has been about syncing states between interacting components. I should add — for the sole purpose of detecting change.

So, in essence, single source of truth + communicating change is why I create >3000 instances on bootstrapping the application?

There‘s gotta be a better way!

Bring out the Graph

Iteration #5, aka. “Ninety”, is an MVVM-ish framework, built as a graph of functions, with a single-source-of-truth container, and passing diffs around. Because nodes in a graph don’t care what’s running them — as long as they stay connected and downstream nodes are prepared to handle asynchronous input (tools for which are provided by the underlying function graph library “1e14”) they can be easily moved around between threads, client, server, and server cores/instances! This is a long-time dream coming true about quite literally drawing the line between the client and server side of a single codebase.

And here’s to illustrate.

This is an early demo of Ninety’s view composition and rendering performance: http://1e14.org/ninety-demo-medium/

As a graph, it looks exactly like this:

I can’t stress this enough: this is not a representation or a model of the code. It is the code. Apart from a few simple mapper callbacks passed as parameters to certain nodes, there is no custom JavaScript involved. Also, for the sake of decluttering, I removed port names, node types, and node parameter values from the figure.

Now, one could say, this is nice, but it’s a demo. It’s designed to be small. What about a trading platform, or a ride-hailing app? Well, if we laid out the whole application, it would certainly present a mind-boggling image. Fortunately, that’s not how a graph like this would be edited.

As the different node styles in Fig. 1. indicate, we have two kinds of nodes: atomic (dots), and composite (circles). At the top level, we see a number of composite nodes corresponding to the renderer thread and one or more worker threads. Nothing else. Then we ‘magnify’ one of them, revealing a sub-graph of atomic and composite nodes. (See how we go from “worker.js” to “main-pg-view” in the figure.) We can continue drilling down until we arrive at a sub-graph of all atomic nodes. So, as long as the internal structure of any given composite node is comprehensible, the entire application is comprehensible, regardless of its size.

Performance

How does all this affect performance?

In the graph, functions call other functions with negligible overhead, which comes from iterating over and invoking functions connected to any given output port. Any performance implication worthy of attention comes from the framework’s design. Since application graphs are static, ie. no nodes & connections are being created or destroyed at runtime, data has to be routed to relevant nodes, and to those only. (This is what “splitter” nodes are for in the Fig. 1.) It’s the price we pay for operating any stateless system with a structure. (React does it too.)

Below we see the DevTools view of our demo app ticking at a more than acceptable frame rate in “stress test” mode, updating 1024 table cells with data and style every 100ms.

Fig. 2. Performance indicators in “stress test” mode

Prior work

Of course, I’m not the first to come up with the idea of pairing web technology with visual programming. Flowhub did a great job showing that the concept falls on fertile ground with developers.

For non-developers, “no-code” and “low-code” platforms have been around for some time and are on the rise as of late 2018, with household name contenders such as Zoho and Salesforce. These usually restrict the developer into an application niche like CMS and CRM platforms, (and some also operate at questionable performance figures) but they certainly prove the marketability of visual programming in general.

Conclusion

In physics, Newtonian, Lagrangian, and Hamiltonian mechanics describe the same motion, but from different points of view.

I believe a “low-code” platform built on Ninety would work because it’s not low-code at all. It looks low-code, but it’s no different than using a framework like React or Angular with conventional, text-based code, apart of course from its obvious advantages.

Ninety is far from being complete. At the moment its rendering engine, hash-based routing, and view composition components are operational, with some room left for optimizations.

I invite everyone interested in shaping the future of a visual web app framework to get in touch and join me on this journey.

--

--

Dan Stocker
1e14

Technologist, innovator, web front-end veteran.