Humility and Better Programming, Part 5
Programs, like any other human-made objects, are designed—or should be designed—with a definite lifespan and scope of application in mind.
…a program should have neither over-designed or under-designed parts. Yet it is an occupational disease of programmers to spend more time on those program parts that present, for some reason, the most intellectual challenge rather than on those that require the most work.
…each program has an appropriate level of care and sophistication dependent on the uses to which it will be put. Working above that level is, in a way, even less professional than working below it. If we are to know whether an individual programmer is doing a good job, we shall have to know whether or not he is working on the proper level for his problem.
Nancy and I recently got into a discussion about the best programming approach for a particular application. We both had in mind that maintainability of the application was very important. As is common for the two of us, we wanted to take different approaches to get to the same place. She proposed on OO plugin approach that I thought was more complex than needed. As we talked about it, I realized we had slightly different ideas of what “maintainability” meant.
I was thinking that the need for maintainability emphasized simplicity. She was looking down the road a bit, thinking the customer was going to need more flexibility than would have been afforded with my approach. Thus, her more scalable solution was going to be more maintainable in the long run.
Who is right?
Well, I am, of course. The customer isn’t asking for scalability right now. So Nancy is speculating. I’d rather wait until the customer asks for (and convinces me she needs) the scalability before I go to the trouble of writing it.
To be fair, Nancy’s approach is just as right, and she may know what the customer wants better than I do—and maybe even better than the customer does. Nancy may also be confident that she can add the scalability without adversely affecting the initial development time or the maintainability of the code. This seems like a prime opportunity to go ask the customer for clarification on their future plans.
This example also illustrates another key point. Nancy and I both have enough LabVIEW experience and skill to design and build an OO-based plugin architecture. I made a conscious choice to exclude it. Nancy made a conscious choice to include it. If we didn’t know about OO plugin architectures, we would not have a choice to make—we would have left out the scalable solution because we knew no better.
That right there is why I want you to become a Certified LabVIEW Architect. I want you to know enough about LabVIEW that you can decide when to use—and when not to use—the many tools that are available. Don’t let a lack of knowledge of advanced LabVIEW concepts make the decision for you.
Going back to the quote at the top of this post about using an “appropriate level of sophistication”, I’m reminded of another discussion I had with an engineer at NI. First, let me show you an example which in a loop, acquires 200 samples of analog data and scales the data based on the value of a front panel control. The acquisition timing is configured in the task in MAX. A novice programmer might program it this way… (Click the images to see full-sized versions.)
The conversation with my colleague, who is a very fast LabVIEW programmer, went something like this… “Polling user interfaces are bad. You should use an event structure. Something like this example…”
There’s a good chance that the novice programmer wouldn’t have known how to build this event-driven program. But here’s my question for the expert programmer… “Why didn’t you write it the simple way?”
In this case, the novice programmer is me. I do have the skills to build an event-driven program for this application, but I didn’t think it was appropriate. What’s an example of something I give up by not using an event structure? If my read hangs waiting for a trigger, then the stop button takes up to ten seconds to stop the VI, while I wait for the timeout on the read. What if that’s not acceptable? What if I needed to respond in less than five seconds? Before diving into a rewrite using event-driven programming, I’d first ask whether I could just lower the timeout on the Read to be five seconds. Maybe I know my trigger will never take that long without it being an error.
There are ways I can make this program more robust, more responsive, more scalable, more capable. But why do that if those aren’t requirements? Mandates such as “don’t write polling user interfaces” are too stark. Fortunately, my colleague has backed off of his tough stance against polling user interface loops. He doesn’t like them for most user interfaces, but he sees that there are times when they are the most appropriate solution.
A note about frameworks… Nancy has been thinking about software frameworks recently. (I’m hoping she’ll write a blog post about it.) Lifting from the entry in Wikipedia about software frameworks…
A software framework is a universal, reusable software platform used to develop applications
It goes beyond just a reuse library or a design pattern which provide only elements of an application. A framework imposes a structure on the entire application. One such framework is the Actor Framework, which we started shipping in LabVIEW 2012. There are many other frameworks for LabVIEW applications. Some companies have developed their own for their typical applications. Some are shared for the community to use. In the theme of being “too clever”, I have seen some customers apply their favorite framework to every programming problem they have. If all you have is a hammer, everything looks like a nail. One of the traits of a good Certified LabVIEW Architect is that they have more tools in their toolbox, and know when to use each appropriately. The Actor Framework, as an example, is one way to architect an application when you have a bunch of independently running processes that need to communicate. If you have an application that doesn’t involve a bunch of independent processes, I wouldn’t try to force my application to the framework. Instead, I’d recommend finding or creating a more appropriate framework.
If you’ve made it this far, thank you for sticking with this long post. As an old programmer, I have a fair number of battle scars (also known as “experience”) which I sometimes take for granted. I make many programming decisions almost without thinking. Some are just a force of habit, and I might not stop and teach these habits explicitly, and this blog post is an attempt to capture and share a few of those thoughts.
I would love to hear your feedback on this series. Let me know what you think. I’ve put together a short bibliography if you’re interested in additional reading.