Reliably Reproducing a Bug Before You Fix It

how to reproduce a bug

Can’t make the bug happen twice? Here’s how to reproduce a bug: pin down the environment, record exact steps, then strip away everything not needed until the failure still occurs on demand.

This page sits within the wider set of debugging techniques on the site and covers the step that comes before any fix.

Item Value
Clean, concise repro: time to confirm a bug A glance
Finding the culprit after confirmation Often a few minutes
Convoluted or incomplete report Dragged on for weeks
Flag for verbose logs in a uv reproduction -v
Docker image platforms published by uv linux/amd64 and linux/arm64
Git reproduction: what to pin A specific commit

The timing figures are one maintainer’s account on DEV Community, and the platform, flag and commit details come from the uv documentation. Figures can change, so check the source before you rely on them.

Why reproduce a bug before you fix it?

Without a reproduction, you cannot know whether a change fixed anything. The uv documentation says that without an example that can be used to reproduce the problem, a maintainer cannot debug it or test if it is fixed, as the uv reproducible examples page puts it. The same logic applies when you are alone at your desk.

A reliable reproduction does several jobs for you:

  • It proves the bug exists and shows what “broken” looks like.
  • It gives you a fast loop for testing each guess about the cause.
  • It becomes the check that tells you the fix actually worked.

The payoff is practical. An engineer who maintained several open-source repositories wrote that for most reports with a concise, clean repro, engineers needed a glance to determine it was a bug, and could often find the culprit in a few minutes, according to that DEV Community post. Reports with convoluted or incomplete examples dragged on for weeks in the same account.

Capture the environment and the exact steps

Start by writing down everything that could affect the result. The uv documentation lists the context someone else needs to reproduce your problem:

  • The platform you are using, such as the operating system and architecture.
  • Any relevant system state, such as explicitly set environment variables.
  • The version of the tool in question and of other relevant tools.
  • The relevant files, such as lock files and project configuration.
  • The commands to run.

Next, record what you did in order, and write it as commands rather than prose wherever you can. A command can be run again and again, while a description like “click around the settings page” drifts every time you retell it.

Keep the evidence, too. The same docs recommend including verbose logs from your reproduction because they may differ on your machine in a critical way, along with the complete error message.

Shrink it to a minimal reproducible example

Once the bug happens on demand, make it smaller. A minimal reproducible example is a simplified version of your problematic code that clearly demonstrates the problem and includes only what is necessary to show it, as the Carpentries Incubator reprex lesson describes it.

A simple reduction loop works for most bugs:

  1. Run the failing case and confirm it still fails.
  2. Remove one dependency, setting, file or chunk of data.
  3. Run it again. If it still fails, keep the removal; if it stops failing, put the piece back, because it matters.
  4. Repeat until nothing else can be removed.

The uv docs give the same instruction: to keep a reproduction minimal, remove as many dependencies, settings, and files as possible (uv documentation). If the example is not minimal, it can take a maintainer much longer to identify the root cause.

Do not confuse minimal with cryptic. The DEV Community post advises against shortening variable names if that makes the code harder to comprehend. It also suggests that, occasionally, starting a new project and writing code that replicates the bug from scratch beats trimming down the original.

Shrinking has a side benefit. The same post says ruthlessly eliminating noise builds a deeper understanding of the problem and frequently leads to the root cause and a proper fix.

The Carpentries lesson quotes Jenny Bryan calling reprexes basically the rubber duck in disguise, so if explaining the problem aloud is where you get stuck, read about rubber duck debugging as a companion habit.

Package the reproduction so anyone can run it

A reproduction that only works on your laptop is half finished. Choose a format that carries its own context, so the person running it sees what you saw.

how to reproduce a bug Laptop workspace
An open silver laptop sits on a desk alongside other electronic devices.

A Docker image

The uv docs call writing a Docker image often the best way to share a reproducible example, because it is entirely self-contained and the state of the reproducer’s system does not affect the problem. uv publishes images for linux/amd64 and linux/arm64, and you can set the platform explicitly so the reproducer gets the expected behavior (uv documentation).

There is a catch. Some bugs are specific to the operating system, so check whether the issue also occurs on Linux before you reach for a container.

A script

When a platform-specific bug cannot be reproduced in a container, the same documentation says it is best practice to include a script showing the commands that reproduce it. Add verbose logs and the complete error message, and share any external state the script relies on, such as the shell or the way a language runtime was installed.

A Git repository

If the reproduction needs many files, share a Git repository. The first step of the accompanying script should be to clone the repository and check out a specific commit, so the code cannot move under the reader, and you should still exclude every file and setting that is not required.

What to check when the bug will not reproduce

If the bug appears for you but not for anyone else, the difference is usually in the environment. Compare these first:

  • Operating system and architecture, since some bugs are specific to the operating system.
  • Environment variables and other system state that you set by hand.
  • Versions of the tool in question and of the tools around it.
  • Files that exist on your machine but are missing from the reproduction.

A container helps here because the reproducer’s own system state cannot leak in. Whatever format you choose, test your reproduction on a clean copy before sharing it, as the uv docs advise, and compare the verbose logs from both runs.

Use the reproduction to confirm the fix

Do not throw the reproduction away once you have a theory. Keep it as the test that proves the fix: in the DEV Community account, engineers frequently used the example included in a bug report to create a unit test for the fix (DEV Community).

With a failure that now happens on demand, you can weigh print debugging vs debugger by which one gives you the quicker loop. Run the reproduction before your change to see it fail and after your change to see it pass.

Frequently asked questions

What is a minimal reproducible example?

Show answer

It is a simplified version of the problematic code that clearly demonstrates the problem, includes only what is necessary, and runs easily on someone else’s computer, according to the Carpentries Incubator lesson.

Should I share a Docker image, a script or a repository?

Show answer

Use a Docker image when the issue reproduces on Linux, a script when it is platform-specific, and a Git repository when many files are needed. The uv documentation describes all of these.

What should I include alongside the reproduction?

Show answer

Include the platform, relevant system state, tool versions, relevant files, the commands to run, verbose logs and the complete error message. Leave out anything unrelated to the problem.

Can making a reproduction solve the bug by itself?

Show answer

Often it can. The DEV Community post says removing noise frequently leads to the root cause, and if it does not, you still have something to share when you ask for help.