In my last blog post I talked about how, over time, a simple LabVIEW VI can turn into a tangled mess of wires that barely fits on the screen -- we call this "spaghetti code". Worse, when you try to clean up this messy code, it often has a lot of "quirks" in it, which have to be (painstakingly) fixed.
3 Steps to Avoiding Spaghetti Code
Here are some tips, below, that include some of the key takeaways from your responses, as well as some best practices the JKI team and I have learned over the years.
0) Do Not Allow Your Code to Grow Larger than One Screen
I made this one #0 (is that cheating?), because it's so important and obvious, yet it's the main reason for this problem -- people sometimes allow the code to grow and grow over time, and they don't paying much attention to the problem (until it's too late).
If you want to prevent your code from growing out of control, you've got to be committed to not allowing your code to grow larger than one screen (a reasonably sized screen of about 1024x768 pixels). Let's look for other solutions (as described below).
1) Create SubVIs (and Libraries) for Reusing Common Code
If you don't reuse code, you will naturally start duplicating yourself and have to add new features (and bug fixes) in several locations -- that's not going to scale. You can reuse code by creating subVIs (and grouping them into libraries and classes). This will also save you space on your block diagrams, since the subVI icon doesn't take up much space.
2) Group Related Data into Clusters (or Classes)
Rather than running a lot of individual scalar wires around on the block diagram (which is visually messy), you can group wires into clusters (or even create a class). You'll probably also want to make these clusters into type definitions, so that you don't have lots of copies to maintain. When you use Unbundle by Name and Bundle by Name to access elements in your clusters, it makes it easy to see what data you're accessing, because you can read the names.
3) Use a "State Machine" to Organize Your Code (or some other message handling architecture)
A state machine gives you multiple "frames" for putting your code and organizes it into logical groupings/components. There are a LOT of other benefits, too.
A state machine (shown in the screenshot below) will generally have a:
- While Loop for running your application continuously
- Data Cluster on a shift register for storing your data
- Case Structure with multiple frames for logically grouping your code
- "State" String or Enum for keeping track of which state to execute next
More advanced state machines and related templates will also have an:
- Event Structure for handling front panel user interface events, as well as dynamic events
- State Queue (an array of string/enum "states") for queuing up a sequence of states
JKI's Solution to Avoiding Spaghetti Code
At JKI, we've solved the "spaghetti code" problem by using the JKI State Machine whenever we create new VIs (and we enforce a Best Practice: Never Grow the Size of the Structures). This state machine is my preferred template for starting new code, and it's based on distilling down over a hundred engineering years of LabVIEW programming experience at JKI, into one very simple-to-use and powerful template that can be used for user interfaces, headless processes, simple sequences, and just about anything.
Of course, there are a lot of great state machine and related templates out there, and this is just one of them. I'm curious which ones you use, and why. (feel free to comment below or message me @jimkring)
Other Important Practices
Lastly, there are a few other important practices that I recommend (and many of you mentioned in your emails to me). Each of these, if you take the time to do them, will make a HUGE difference in the quality and maintainability of your code (even though they may not "feel" like writing code or getting things done in the short term -- they will have BIG payoff in the long run).
- Add comments to your code, so you know (A) what the code intended to do and (B) how it does it -- this helps you refactor (untangle) it later when you have time to clean it up.
- Clean up your code - Take a little bit of extra time to clean things up and put (simple/clear) comments in your code (also called "paying down your technical debt")
- Do code reviews with others -- Talk with other developers about best practices and review each others' code.
- Keep learning -- Part of the process or journey of becoming a better engineer is to keep learning. Learning from our mistakes, learning from others, and staying current with the latest technologies
What do you think?
I’m interested to hear what you think about this list and how *you* have solved these challenges (and whether my description of the problem rings true). You can message me on twitter @jimkring.