I have a matlab program with a lot of variables (boolean, arrays, doubles, logicals, strings), to the point where it becomes unwieldy to alter the workflow. I often don't know which I'm going to need when I add new functionality.
That does sound problematic. The risk of breaking such tightly coupled code is high if you're not very careful.
So if I create a function with a series of recursive function calls and X layers deep I discover I need a variable I didn't feed into it, I need to add those variables in the right order as arguments to every function in the recursion. This seems both risky and tedious. It also makes for messy code as functions get passed 20+ arguments.
I agree with your assessment about the risk, mess, and tediousness.
This isn't directly related to your main question, but one way to reduce some of the risk associated with modifying or refactoring your functions would be to write tests for your functions that can reduce the chance that you'll accidentally change the behavior that some other function depends upon when you update your function. This could also help you if you want to refactor your code to make it easier to understand and extend in the future: extract a small segment of functionality into its own function, unit test it thoroughly, and add an integration or system test for the function(s) that call it.
So I created a struct "programConstants" at the beginning of the program flow that aggregates all of the variables inside of it.
IMO that's a step in the right direction, but you have everything crammed into one struct array. Are there logical groupings of the information that your functions need where you could create multiple struct arrays or perhaps even separate classes? So instead of having programConstants you could have carParameters, personParameters, etc.?
Is there a simple way I can just dump all of the programConstants fields into the scope of a function when I pass it?
There are ways to do what you ask, but I recommend against it. If one of the fields in your programConstants struct matches the name of a MATLAB function (commonly-matched functions include alpha, sum, min, max, etc.) then depending on what you do in your function MATLAB may call the function rather than referring to the variable that was "poofed" like a magician's rabbit into the workspace at run-time.
If you're only going to use a particular piece of data from the struct once in your code, consider just accessing it from the struct. If you know you're going to use a particular piece multiple times, then it may be time to define a local variable. But I would limit this to just what your function is going to use. This would be more work than simply dumping everything from the struct into the workspace, but it would also be less likely to cause the "poofing" confusion I described in the previous paragraph.