I think without in-the-trenches software engineering experience, it is hard to determine what class of issues would be a problem.

In this case, it was a dependency on the ordering of filenames. Whenever your code depends on something being in a certain order, you should ask yourself what puts the items in that order. But it's perfectly reasonable to believe that filenames are returned in order; perhaps for your small sample size or when you run "ls" they're in order (because you have ls set up to sort, but don't know it).

The difficult part that comes with experience is knowing what you can and can't trust. I would trust that "select foo from bar order by timestamp desc" to return items in order. I wouldn't trust "readdir" to return items in any particular order (maybe ordered by inode number, but I can't think of any real program where I care about order that would want that order). But you can really go down a rabbit hole when you don't have the right level of trust or experience. (I have seen a lot of unit tests in my time that do something like test a core language primitive or library; scores of tests like assert(append([1], 2) == [1,2]). This comes from a total lack of trust for things that probably can be trusted. Or from people that don't have their homedir full of files like "foo.py" to figure out how some API works and instead put them in their unit test files ;)

The other thing is figuring out what tests would be valuable and knowing that you have to write them, and the researcher on the other end needs to run them. (I remember so many bug reports like, "I force installed that library after 'make test' failed and I'm getting weird results from my program that uses the library." Well yeah.) I see a lot of people run their programs on simple input, check "yeah, that looks fine", and then trust that code forever. But somehow, it breaks, and they don't notice it on their complex inputs, and it leads to problems like the one in the article.

On the other hand, the thing about nondeterminism is that you can't test for it. Sometimes it's very obvious, you do something like (keys(foo) == bar) and because dictionary keys are explicitly randomized by your language, your test fails the first time and you immediately find your error. But if you're talking about something like OS-dependent APIs, you'll write your tests and never see your bug, because the order is consistent on your particular computer. (At least someone on another OS will eventually find it, and there are plenty of services that will CI your code on various OSes, so you do have the potential to uncover that issue yourself.) An even more severe case deals with concurrency; your unit tests run one thread, so you never see the subtle 1 in a million timing issues until you push to production and see the 1 in a million bug every second because you're processing a million records a second.

(And things get even more obscure than that. I once got paged for one replica in a large job not working correctly. Look at everything that could possibly cause the issue, nothing. "I guess I'll restart the task." Problem persists. Someone suggests "Tell the scheduler to schedule the task on a different machine." Problem goes away and never comes back. Would suck if that one-in-a-million obscure machine-specific problem happens to the machine that's producing the results for your scientific paper.)