Over the past couple years at MathWorks we have been transitioning from a legacy procedural testing framework to what is now MATLAB's native unit test framework. It has been great to see the internal adoption rate of this framework increase steadily as more teams begin to see and utilize its various benefits.
However one of the biggest challenges we have seen in migrating to the new MATLAB unit testing paradigm is the existence of team specific helpers, tools, and ecosystems that are incompatible with the new framework.
Why are they incompatible?
Well, because a central feature of the legacy framework is globally available and Global Variables Destroy Design Information. Read that post first it isn't too long. Near the end of these comments I started jumping out of my chair with my fist in the air yelling Yes! YES! YESSS!!!
Go ahead read it. I'll wait...
Ready? Great stuff eh? Any of that sound familiar? How about:
"Many people say that the wouldn't want to [design without globals] because they imagine that they'd end up passing extra parameters all over the place in their code."
"When a thing is global it is accessible everywhere, and people tend to use it without discipline."
And therein lies the true cost of using global state. There are many, many reasons why global state is a bad thing, but this is perhaps the most pervasive and sinister: global state turns the entire design of the entire ecosystem into one big coupled garbled hard-to-change mess. Global state acts as an implicit parameter to every single function and method call in your ecosystem . When we allow global state we are saying, "Feel free to use this as you like, oh and by the way, since you need it so often we will just go ahead and waive your requirement to tell me you need to use it." That's a little bit like a bank saying, "You seem to withdraw money from your account so often that we will just go ahead and waive the requirement for you to use a bank card and pin. No need to identify yourself with that context, we'll assume that whenever money is pulled from your account it is approved by you. Look how easy we've made it for you. You don't even have to carry around a wallet!"
What could go wrong?
The fallacy is that the global state makes things seem easier at first because it does not require design discipline. It's only down the road that we realize the problems it creates, but by that time the design damage has been done. It is nigh impossible to back out designs that rely on it. The poor design choice of introducing global state negatively effects the design of the entire set of tools that operate in its ecosystem.
Well, in the MATLAB unit test framework we have not relied on global state and have instead required those who operate in the framework to have the appropriate context to do things like verifications and assertions. In tests that do not require using the same ecosystem as legacy tests this has not lead to any significant problems. When the ecosystem is clean there are many in model approaches and patterns to writing tests and related test tools.
However, on the flipside when a test utilizes the full ecosystem and tools built around global accessibility of its parameters it is a much harder problem.
This is not just a testing problem, this is a very general software engineering principle. Introducing global state to the software system causes problems with concurrency (e.g. parallelism using Parallel Computing Toolbox), significantly complicates reentrancy, and is almost always marked by spooky action at a distance.
What is global state in MATLAB? The obvious answer is usage of global variables. However here is a short list of other constructs which can easily be used to introduce global state in MATLAB:
- persistent variables
- objects with Constant properties containing mutable handle objects
- default property values in MATLAB classes containing handle objects
- appdata on a global object, such as the root object
- the Singleton design pattern if it gives access to any mutable state
- Variables defined in the globally accessible (and mutable) base workspace
I have found that very rarely (almost never) does the solution to a given problem truly require using such global state. Think twice (twenty times!) about your design if you are tempted. If it is used in the end, always make sure that the exposure of this state is limited to as few clients as possible.
Get the MATLAB code
Published with MATLAB® R2015b
3 CommentsOldest to Newest
Interesting post. What are your thoughts on how this applies to Simulink models that require parameters to be in the base workspace always?
Great question. First of all, you picked up the fact that the base workspace is another very common form of global state in MATLAB. I missed that, thanks!
I would say that it definitely would be better if your Simulink models were set up to avoid the dependency on the base workspace. There are two ways Simulink models might have an effect on the global state of the base workspace. The first is through the inputs to the Simulink model (such as parameters, etc) and the second is through the outputs (if you call SIM with no output arguments it assigns variables like tout, yout, etc into the base workspace).
1) To avoid relying on the variables defined in the base workspace as model inputs, you can either use the model workspace:
…or define the variables in a function that calls sim and use the SrcWorkspace option to SIM:
2) To avoid writing variables to the base workspace, call SIM with an output argument in order to access the results of your simulation. This will ensure that the call to SIM does not modify any global state (thus causing spooky action at a distance for something else).
If you do these things then the model will be more flexible. For example, you will be able to easily call SIM within a parfor/parfeval. Also you would never have to worry about some other code “corrupting” your base workspace and thus producing an unexpected simulation.
All of that being said, there’s a definite tradeoff between production quality software that is robust and flexible and the convenience of defining parameters in the base workspace and simulating. I would encourage moving away from the base workspace dependency for the models and code that simulates them if they are part of a larger codebase. For example if you are using the SIM function instead of simulating from within the Simulink UI then you may want to think about staying away from using the base workspace. However, if someone is in a design process wherein they are iterating a design or modeling task with a Simulink model from within the UI, the convenience of defining parameters in the base workspace may be ok, just recognize the pitfalls and limitations before using it in more complex production cases.
you might also consider using the new Data Dictionary for Simulink models. This enables you to separate model and data (e.g. parameters).