Guidelines and design principles for Node-RED users

Hello everyone, a Node-RED newbie here.
I can't say if what I'll post is reasonable or not, but I'll give it a try.

My goal is to formulate a list of guidelines based on best practices, common design principles (e.g., separation of concerns) and patterns, to be followed by (novel) users approaching Node-RED. Some design principles may be taken from the Flow Based Programming (FBS) paradigm, but yet I have found none. Concerning design patterns, I have found the following page quite interesting (maybe just a bit outdated): https://medium.com/node-red/node-red-design-patterns-893331422f42

Driven by your experience, what would you suggest to (not) do when using Node-RED in a very general scenario? For instance:

  • Do you logically organize your implementation in terms of flows/browser tabs, e.g. a tab for each device communication, or you just put everything together within a single flow/tab?
  • Would you recommend using global variables in any case? Is there a more preferable way to exchange data among unwired nodes?
  • When would you prefer using function nodes instead of more specific ones (e.g., change node)?
  • Do you always give meaningful names to nodes and messages properties? Are your flows self-documented in a way that just by reading the nodes names you understand what a specific flow does?
  • Have you personally decided a lower/upper bound in the number of nodes each flow should contain?
  • What is an acceptance number of wires entering/leaving a node pin? Is it ok for you to have bottleneck flows?

These are just some few questions and I know that cover any scenario is impossible, but still I would like to have your suggestions and opinions.

Thank you

3 Likes

Hi, some interesting questions and there is always room for more helpful information.

You will certainly want to split things - you rapidly reach a level of complexity that is very hard to follow on a single tab. I don't think there is a special right or wrong way though. I tend to keep the first tab for setup and general configuration - global reference data for example.

Here is an example from my new setup that will eventually take over from the old live system:

And here are the tabs from the current live system - this is actually a few too many to work with comfortably since you have to scroll:


image

Hopefully reasonably logical partitioning.

There are, of course, several ways. However, it isn't always convenient or sensible to rely purly on the exchange of msg objects. Reference data, for example, is often best kept to hand in a variable. The most common use for variables is to retain the last input from MQTT topics for further processing.

In common with any programming, minimising global variables is sensible but over thinking it in this context has no real gain. I make reference data global because if I make it more local, I inevitably end up trying to use it in another tab and then getting annoyed that I didn't think far enough ahead :frowning:

Now that we can retain variables as well, they've become even more useful.

However, I also make liberal use of link nodes. These really free you from the visual constraints that flow-based programming general comes with. Lots of crossed wires! I use them in the same tab to simplify the flow visually and, less commonly, between flows if I want to keep some particular process all in one place.

When I know JavaScript well enough and realise that a simple function can replace a snake-nest set of nodes :slight_smile:

When JSONata came into Node-RED, I started to go a bit mad with it until I realised that bending reality to JSONata's way of working isn't always an advantage over straight JavaScript - if you know how to write JavaScript.

The rule of thumb I now use is: Which will be simpler and which will I most likely be able to understand when I come back to it in 6 months time?

Yes(ish) to the first part. I make extensive use of descriptive "names", I tend to use names as a title more than a name. For example:

image image

I don't know, however, that flows would ever be fully self-documenting just with names. I also make good use of comment nodes as you can see - I also use them to help differentiate parts of the logic visually. I really wish for an ability in Node-RED to group nodes together (like "containers" in Visio if you've ever used that).

The latest beta for Node-RED contains the ability to add descriptive text to any node which is fantastic - it integrates to the info sidepanel which works really well.

Adding names to input and output connectors can also help remind you what data you are sending/receiving and I do that more and more as my flows get more complex.

Nope! But flows not only have a maximum canvas size, you will also quickly realise just how painful it can be to follow a flow once it gets larger than a single screen-full. I try to keep all my flows so that they don't overflow horizontally on my normal monitors. I find vertical scrolling much easier than horizontal. Any single piece of logic really needs to fit into a single screenfull however and I'd rather break things up using links than make some logic that can't be read on a single screen.

This really isn't an issue with Node-RED in my opinion. Indeed, I commonly have something like a single MQTT output node with a great many inputs. However, I now generally avoid this by using link nodes.

3 Likes

With regard to your mention of organization in tabs, I have personally evolved over time into a paradigm of separating into functional areas on tabs based on who is most likely to be editing the flow. For example, in one of my production instances, the first tab is purely for interfacing with high-impact/high-risk systems that require the utmost care (and change control process) to alter settings on. Generally, nobody should ever be on this tab. The second tab is for configuration and changes to message routing (set up to be easily managed by other maintainers), and the third tab is a scratch space for developing improvements (basically me only.) Each device being handled gets its own sub flow on tab 2 to allow for easy usage of flow context variables and lots of copy/pasting. Commenting and visual organization varies between the tabs, with #2 being as idiot proof as possible.

Thank you both for your answers, very appreciated. I hope more will come :slight_smile:

I add few more questions:
Have you ever refactored adapted and applied some of the most common code-based refactoring techniques to flows? For instance, identify a redundant portion of a flow and move it into a subflow node for multiple usages.
Have you considered to add comment nodes close to link nodes to improve the understandability of such flows, in case they are printed on paper?

Thank you again

Yes. Indeed, refactoring is particularly important in complex flows since the development of flows is commonly evolutionary in nature - Node-RED makes it easy. This is also what makes Node-RED such a good and useful prototyping tool. The downside is that your initial tries are often not the most efficient.

Firstly, I never print :slight_smile:

But I know what you mean and yes, I sometimes put comment nodes close to others in order to better document the flow. Here is a simple example though not just a link node because this flow can be triggered via both link from elsewhere (a timed trigger) and via MQTT subscription.

More commonly though, I tend to use comments as headings.

Also note that the new Node-RED 0.20 beta has additional internal documentation features.

And you can turn on the label on link nodes now if you want to

Thank you again for your answer :slight_smile:
I'll try 0.20 beta.

This is a very interesting/important topic. There have been a bunch of thoughtful comments, and I hope that @sybeleon emerges with some useful guidelines. With all due respect, however, I am tempted to ask how any of us would answer this question with regard to any other programming language. @sybeleon's questions refer to a lot of things we have been urged to do (or not do) in other environments, so the question might really be "What is different about Node-RED?". How (well or badly) do these

apply or translate to NR, and are there techniques specific to NR that we can identify and discuss?

1 Like

Indeed, that is an excellent question to ask.

Associated with that: "When is it best to use Node-RED and when is it best to use something else?"

While there is no absolute answer to that question of course, we could probably at least give some hints from different people's perspectives.

From my perspective, I would reach for Node-RED if I want to add something to my Home Automation - because I built it in Node-RED. More importantly, I would reach for NR if I wanted to be able to prototype a complex set of actions/workflows - or if I simply needed to get something up and running very quickly that would otherwise need a lot of boilerplate just to get me started.

Where I wouldn't necessarily use it is if I needed to do something specific with perhaps a specific library and where the complexity of NR outweighed the complexity of getting started with Node.JS (or Python or whatever) directly. So if I were writing a dedicated microservice to provide an API or talk to specific hardware or cloud service, it might well be easier to just write that from scratch in Node.JS. Of course, if a node already existed that worked well enough, NR might still be the best choice.

Also, the other main reason for choosing Node-RED would be if your JavaScript/Node.JS skills are limited or your HTML skills are also limited and you need a simple web interface (using Dashboard). Since NR takes care of all of the details for you and limits the amount of JavaScript you need to write in order to do complex flows.

4 Likes

Hi all,
I'm replying to this post as it seems relevant to my question. Please let me know if this should be a new issue instead.

I am doing a project that became slightly larger than what I didin the past and as sch it started becoming a mess to find anything...
Since parts of the program lent itself well to become there own modules, I transform them into subflows, and devided the main program into two flows, Client and Master. I'm now questioning my decision...

Everything is working fine at the moment on my dev computer but how do I transfer the the all thing to another computer?

Can I "package" the all thing in any way and transfer it? Do I have to do it bit by bit?

Kind Regards
L

In future it is probably better to start a new thread for something like this, rather than replying to an old thread.
To answer the question as to how to move a complete node red application to a new computer, backup the whole folder where your flows are except for the node_modules folder. Look at the startup log to see where your flows are if you don't know, it is usually called .node-red. Then on the new system, after installing node red, restore the .node-red folder from your backup, then in a command window go into that folder and run
npm install
That will install any additional nodes that you have used.

Hi Colin,
Thank you for your reply.
I did think that perhaps a new post would be better, but this one was quite relevant so I decided on it instead.

Your instructions worked well, except that I maybe deleted a file I shouldn't, perhaps the settings file... Anyway I had to also generated the file package.json from the package-lock.json by saving as and changing the name. Before I have done that I got an error that it couldn't find it. It would be great to know what I've done wrong.

Kind Regards
Luis

Some little notes I've got:

link nodes can be both local (only on that tab) or remote.

I try to name them all, but if they are local I leave them small.
If they are remote I expand them to show the full name.
I try to not have them doing both - unless REALLY needed.

eg: If the node is receiving messages from multiple points all over the place.

If - rather then comments in function node code I have comments - I put an * in the node's name, indicating to me that there is more to be read in the comments part of the node.

I try to have blocks of nodes that are all associated with the function grouped together.
I try to put outputs of these groups on the right. Unless there is an output very early on and would be very much on the left side of the block.

If I am not at loss for space, on outputs I also put a debug node (set to show needed information) there also.
So if I need to check what is being sent, I activate the debug node and check.

Sometimes - if the message is complicated and only small part of the bigger message - I use a template node before the debug node, so I see an easily read message.

1 Like