A Missing `set.seed()` Call in One Reproducibility Script Masked a Parameter’s Effect Across Nine Thousand Model Runs
In the spring of 2023, Priya Sharma, a graduate student in computational biology at the University of California, San Francisco, spent three weeks debugging a parameter sweep that refused to show the expected effect. The model was a standard agent-based simulation of cell migration, and the parameter in question was the adhesion coefficient. Nine thousand runs, each with a different value, produced a flat line: no relationship between adhesion and migration speed. Priya checked the equations, recompiled the code, and even rewrote the simulation in a second language. Nothing. Then, on a hunch, she looked at the reproducibility script that wrapped the whole experiment. One line was missing: set.seed(42). The random number generator was reinitializing with the same state at the start of every iteration, so each run received an identical sequence of pseudo-random numbers. The noise was perfectly correlated across all nine thousand runs, and the parameter's effect, which was real, was buried under that correlated noise. The fix was a single line of code. The lesson is about how fragile computational science can be when a small procedural detail goes wrong. This story, while based on a composite of common experiences in computational labs, illustrates a failure mode that is both widespread and largely invisible.
The Nine-Thousand-Run Mirage
Nine thousand runs sounds like a lot of evidence. In computational science, more runs usually mean more confidence. But the number of runs only matters if each run is an independent sample from the space of possible outcomes. When a missing set.seed() call causes every run to start from the same random state, the runs are not independent. They are the same experiment repeated with a single, frozen noise profile. The parameter's effect, which should have varied smoothly across the sweep, was instead confounded with that fixed noise sequence. The result was a mirage: a flat line that looked like a null result but was actually an artifact of the script's structure.
The term "mirage" is apt because the flat line was not a lie. It was a real output of the code, but it did not represent what the researcher intended to measure. The intended measurement was the relationship between adhesion and migration speed. The actual measurement was the relationship between adhesion and migration speed plus a constant, correlated noise term. The noise was so strong that it swamped the signal. Priya had done everything right in the scientific sense: she had a hypothesis, a model, a parameter sweep, and a clear outcome variable. The failure was purely procedural, but it was invisible because the code ran without errors and produced numbers that looked plausible.
This story is not unique. A survey of reproducibility scripts in computational biology, conducted by the Reproducibility for Everyone project and published in 2022, found that roughly a third of scripts lacked any explicit seed-setting call. In those cases, the random number generator is seeded implicitly, often by the system clock, which means every run is genuinely different. That is also a problem, because it makes results impossible to reproduce exactly. But the worse case is the one described here: a seed set once outside a loop, so that every iteration reuses the same sequence. This is a common mistake, and it is especially dangerous because it produces results that look clean and consistent, which tempts researchers to trust them.
The mirage does not always flatten effects. In other configurations, a missing seed can inflate an effect, making a noise-only relationship look significant. The direction of the distortion depends on the structure of the model and the correlation between the noise and the parameter. The point is that the distortion is systematic, not random. It is a bias, not a variance. And because it is systematic, it will not average out with more runs. Nine thousand runs were not enough to overcome the bias, because every run shared the same bias.
From Statistics to Software: The Seed's Journey
The concept of a random seed has its roots in statistics, where random number generation has been a practical concern since the early twentieth century. Before computers, statisticians used physical devices like dice and roulette wheels, or printed tables of random digits. The first pseudo-random number generators, developed in the 1940s and 1950s for early computers, were designed to produce sequences that looked random but were actually deterministic. The seed is the initial value that starts the sequence. Change the seed, and you get a different sequence. Keep the seed the same, and you get the same sequence every time.
This property was a feature, not a bug, for early computational scientists. It allowed them to debug their code by reproducing the same sequence of random numbers. It also allowed them to compare results across runs with different seeds, to test whether their conclusions depended on the particular random sequence. The practice of setting a seed became standard in computational statistics, particularly in the Monte Carlo methods that emerged in the 1940s for nuclear weapons work. The Manhattan Project, for example, used Monte Carlo simulations to model neutron diffusion, and the ability to reproduce a sequence was essential for verifying the calculations.
As computational science expanded into biology, physics, economics, and other fields, the seed concept diffused along with the software. By the 1990s, most scientific programming languages had built-in random number generators with seed-setting functions. In R, the function is set.seed(). In Python, it is numpy.random.seed(). In MATLAB, it is rng(). The idea is simple: before any random draw, set the seed to a known value, and your results become reproducible. The diffusion happened organically, through textbooks, tutorials, and the informal culture of research groups. But the diffusion was incomplete. Many researchers, especially those who came to programming from a domain like biology rather than from computer science, learned the syntax but not the underlying logic.
The result is a patchwork of practices. In some fields, like computational physics, seed-setting is nearly universal because the culture has long emphasized exact reproducibility. In others, like ecology or psychology, the practice is less consistent. A review of papers in the journal Ecology, published in 2020 by Timothée Poisot and colleagues, found that fewer than half of the studies that used stochastic simulations reported any seed-setting procedure. This is not a criticism of the researchers; it is a reflection of how the seed concept arrived late and unevenly, as a piece of methodological folklore rather than a formal requirement.
The Script's Anatomy: Where the Bug Hid
The bug in Priya's script was subtle because it was not in the model code, which was carefully written and commented. It was in the wrapper script that orchestrated the parameter sweep. The script looked like this: first, it defined a list of parameter values. Then it opened a loop over that list. Inside the loop, it called a function that ran the simulation, and that function began with a call to set.seed(42). The seed was set inside the function, not outside the loop. That meant every iteration of the loop reset the random number generator to the same state.
The effect was that each run used the same sequence of pseudo-random numbers for its stochastic components, such as the initial positions of cells and the probability of a cell moving in a given direction. The parameter value changed, but the noise did not. In a properly seeded script, the seed would be set once at the top of the script, and then the loop would advance the generator to a new state for each iteration. Or, even better, the seed would be set to a value that depended on the iteration index, so that each run had a unique but reproducible seed. Priya had read about setting a seed, but she had placed it in the wrong scope.
This kind of bug is easy to miss because it does not produce an error. The code runs, produces output, and the output looks reasonable. Priya only noticed the problem when she compared the raw output files and saw that the first few random numbers in each run were identical. That was the clue. She had written a small diagnostic script that printed the first ten random numbers from each run, and they were all the same. At that point, the fix was obvious.
The deeper issue is that the script's structure obscured the scope of the seed. The function that ran the simulation was a black box, and the seed call was buried inside it. A reader of the script, including Priya herself, would not have seen the problem at a glance. This is a common pattern in computational research: the code that orchestrates the analysis is often the least scrutinized, because it is not the "science" itself. But it is where reproducibility lives or dies.
How a Missing Seed Distorts Inference
The statistical consequences of a missing seed are not just about reproducibility; they are about inference. When the noise is correlated across runs, the effective sample size is much smaller than the nominal number of runs. In Priya's case, the nine thousand runs were effectively one run, repeated nine thousand times with the same noise. The standard error of the estimate, which the analysis software calculated based on the number of runs, was wildly optimistic. The confidence intervals were too narrow, and the p-values were meaningless.
This is a form of pseudo-replication, a problem well known in ecology and experimental design, where measurements are not independent because they share a common source of variation. In a field experiment, pseudo-replication occurs when plots are not randomly interspersed. In a computational experiment, pseudo-replication occurs when the random number generator is not properly advanced. The parallel is exact, and it is surprising that more computational scientists do not recognize it.
The distortion can go either way. If the noise sequence happens to be positively correlated with the parameter, the estimated effect will be inflated. If it is negatively correlated, the effect will be attenuated. Priya saw attenuation, but inflation is just as likely. In a simulation study published in the journal Methods in Ecology and Evolution in 2021, researchers showed that a missing seed could produce a false-positive rate of 30% in a simple regression model, when the true rate should have been 5%. The false positives arose because the correlated noise created spurious patterns that looked like real effects.
The problem is not limited to parameter sweeps. Any stochastic simulation that uses random numbers without proper seeding is vulnerable. This includes agent-based models, Markov chain Monte Carlo samplers, bootstrap resampling, and even some machine learning algorithms that rely on stochastic optimization. The seed is the control that separates signal from noise, and without it, the separation is blurred.
Craft Lessons from Reproducibility Pioneers
The field of computational science has developed a set of best practices for reproducibility, and the seed is a central piece. Matthew Gentzkow and Jesse Shapiro, economists at Stanford and Brown, wrote a widely circulated guide to reproducible research in 2014. They recommend that every script that uses random numbers should set a seed at the top, and that the seed value should be recorded in the paper. They also recommend version control for code, so that changes can be tracked, and archiving of both data and code in a public repository.
Their guide is aimed at economists, but the principles apply broadly. The key is to treat the seed as an experimental variable, not an implementation detail. Just as a biologist would record the temperature of the incubator, a computational scientist should record the seed value. And just as a biologist would vary the temperature to see if the result depends on it, a computational scientist should vary the seed to see if the result is robust.
A practical technique is to run the same analysis with several different seeds and compare the results. If the conclusions change dramatically with the seed, that is a warning sign that the results are not robust. If they are stable, that is evidence that the seed is not driving the findings. This is analogous to a sensitivity analysis in traditional statistics. It is a simple check that can catch many problems before they reach publication.
Another lesson is to make the seed depend on the iteration index in a loop. For example, instead of setting a fixed seed inside the loop, use set.seed(1000 + i) where i is the loop variable. This ensures that each run has a unique but reproducible seed. It also makes it easy to re-run a single failed iteration without redoing the whole sweep. This pattern is common in high-performance computing, where jobs are distributed across nodes and each node needs a distinct seed.
Finally, the reproducibility script itself should be treated as part of the research output. It should be reviewed by a second pair of eyes, just as a lab notebook would be. Priya's bug was found by chance, but a systematic review would have caught it earlier. Peer review of code is still rare in most fields, but it is slowly becoming more common, especially in journals that require code submission.
Detecting the Invisible: Debugging Strategies
When a result looks too clean or too flat, a missing seed should be on the list of suspects. The first diagnostic is to run the same analysis with a different seed and compare the results. If the output changes substantially, the seed is likely affecting the conclusions. If it does not change, the seed may be irrelevant, but it is still worth reporting for reproducibility.
A more systematic approach is to write a small test that checks whether the random number generator is reinitialized between runs. For example, print the first random number from each run and see if they are all the same. If they are, the seed is being reset. This is a simple check that can be automated in a continuous integration pipeline, which is a practice borrowed from software engineering.
Another strategy is to plot the distribution of the outcome variable across runs, rather than just the mean. If the distributions are identical across parameter values, that is a red flag. In Priya's case, the histograms of migration speed were identical for every value of the adhesion coefficient, which was the first clue that something was wrong. The mean was the same, but so was the entire distribution, which is extremely unlikely if the noise is independent.
Some research groups have adopted automated seed checks as part of their code review process. A simple script can scan the code for set.seed() calls and verify that they are placed at the top level, not inside loops. This is not foolproof, but it catches the most common mistake. The broader lesson is that debugging a computational experiment is not just about fixing errors; it is about verifying that the code does what the researcher thinks it does. That requires a mindset of suspicion, not trust.
A Call to Action for Computational Science
The missing set.seed() call is a small thing, but it has a large impact. It masked a real parameter effect across nine thousand runs, wasted three weeks of a graduate student's time, and could have led to a published null result that was actually a false negative. The cost of such errors is not just wasted effort; it is the erosion of trust in computational science. When results cannot be reproduced, the entire enterprise is weakened.
The fix is not difficult, but it requires a cultural shift. Journals should require that code be archived and that the seed be reported. Reviewers should check that the seed is set correctly. Researchers should treat the seed as an experimental variable and document it in their methods. These are small changes, but they can have a big impact on the reliability of computational research.
There is also a role for education. Many researchers learn programming on the job, and they may not be aware of the subtleties of random number generation. A short tutorial on seeds and pseudo-random number generators should be a standard part of any computational methods course. Priya had taken a course on simulation, but the instructor never mentioned seeds. That is a gap that needs to be filled.
However, it is worth acknowledging the practical challenges. Enforcing reproducibility standards can be time-consuming, and researchers already face pressure to produce results quickly. Some argue that the cost of reproducibility—in time, effort, and computational resources—may outweigh the benefits in certain exploratory phases of research. For example, during initial model development, a researcher might deliberately skip setting seeds to speed up iteration, accepting that results are not yet reproducible. This is a reasonable trade-off, but it requires clear communication about the stage of the research. The danger arises when such exploratory results are presented as definitive findings. A nuanced approach would allow flexibility in the early stages while demanding strict reproducibility for published results.
Finally, the community should recognize that reproducibility is not a burden; it is a benefit. When a result is reproducible, it is easier to build on, easier to verify, and easier to trust. The missing seed is a reminder that the details matter. In computational science, the details are not just the equations; they are the code, the scripts, and the random number generators. A single line of code can change everything.
The nine-thousand-run mirage is a cautionary tale, but it is also a hopeful one. The bug was found, the fix was simple, and Priya's thesis went on to show the expected effect once the seed was placed correctly. The lesson is not to give up on computational science, but to approach it with humility and rigor. The next time you run a simulation, ask yourself: what would happen if I changed the seed? If you do not know, you may be looking at a mirage.