Skip to main content
Handling legacy research codebases

Handling legacy research codebases

Introduction

In modern scientific research, it becomes increasingly common for researchers to inherit some research codebases, which often have minimal documentation or in a semi-working state. This is not because researchers enjoy giving bad codebases to their successors, often it is just that the focus of researchers is to produce results under deadline and the accompanying codebases are merely a tool which they developed solely for themselves.
With the popularity of coding agents, it becomes easier to handle this situation. However, just prompting the coding agent with 'explain this codebase' will probably produce a well-written summary which could be wrong in a subtle way and later when you actually run the code, most likely it won't work.

What you will do in this module

This module is an attempt to provide some guidance about how you could use coding agents as an assistance when you inherit a research codebase. A lot of principles exist before the advent of LLMs and we will see how coding agents excel or probably not so helpful in understanding a research codebase.
You will produce an AGENTS.md/CLAUDE.md for a real-world research codebase in a step-by-step manner, either with your own inherited codebase or a codebase written by other researchers. Most importantly, you are not generating an AGENTS.md/CLAUDE.md with a coding agent despite commands such as /init exist or use some templates. You are going to write an AGENTS.md/CLAUDE.md by yourself with information you have personally verified. Creating AGENTS.md/CLAUDE.md files by a coding agent risks circular reasoning, which the coding agents put in false claims you are not aware of and future session reference them as source of truth. Besides, forcing yourself to write each sentence out will make sure you understand it first, as it is harder for one to write something that you do not believe. With an AGENTS.md/CLAUDE.md, it provides a first step for you to reproduce some of its results, modify the algorithm, or implement some new features with the assistance of coding agents.
Five codebases have been curated below if you do not 'bring your own codebase', and feel free to pick one closest to your domain to work in this module.
RepositoryField
SamStudio8/gretelBioinformatics, metagenomics
epicf/ef_pythonAccelerator physics, plasma physics
whaley-group-berkeley/qspectraMolecular spectroscopy, quantum dynamics
cgq-qgc/HydroSensorReaderHydrogeology, environmental data
modsim/molysoQuantitative microscopy, microbiology
These repositories are teaching examples and they were selected because they produced or support real research which are valuable. The author of this module has no intention to criticise or imply something have been done improperly.
Their authorship of the above repositories should be respected throughout this module. Work in your own clone or fork and do not submit AI-generated pull requests, issues, or comments to the original repositories. If you maintain one of these repositories and would prefer it not to be included, please contact rse-training@dtc.ox.ac.uk.

Dealing with legacy codebases

We will try to follow some steps which can help you to understand an unfamiliar research codebase and how coding agents can assist you in the process.

The entry point

When you inherited a codebase, the first thing you should find out is how the software is invoked and this is called the entry point. The best way is, of course, asking someone who knows but often, you are on your own to figure this out.

Where are the entry points?

For a Python codebase, how do you commonly locate the entry points? Be as exhaustive as possible.

How the coding agent can help

If you are well versed in the terminal, you could probably assemble some grep commands with some regex to get an initial impression. Nowadays, we have coding agents and they are powerful to look up information. However only a human with the right domain knowledge and context can decide what the real entry point is. Asking a coding agent 'Tell me the entry point of this codebase' will probably give you something like main.py or run_me.sh, which may or may not be true. A lot of codebases also have multiple entry points.

Write the prompt

You can always ask the coding agent to enumerate all the plausible entry points and you pick those that are relevant to you. Write such prompt, then evaluate the output and decide what entry points are relevant.
'What this is' section in AGENTS.md/CLAUDE.md: in your own words, a one-sentence overview of what the software does, the location of the entry point(s), and how to trigger each of them.

Run something minimal

Once you know the entry point, the next natural step is to run it to see how it goes. Unless it is a trivial script, chances are there are dependencies you would need to install or run something like make install/cmake to build the software, so we need to figure out how to install those dependencies or compile it. Afterwards, we can run something minimal or its tests to see if it works.
When running something unfamiliar to you, it is often good practice to run it in an isolated environment in case something unpleasant happen. You could consider install and run everything with a container with your chosen repository in this module.

Where do the dependencies come from?

What would you normally do to figure out the dependencies of the codebase or the steps of compilation?
However, legacy research codebases usually lack the above information, and even if you know the dependencies or how to build it, most likely it will give you errors even running something minimal. The reason is not because of the codebase itself, it is because the world has since moved on and interfaces from the dependencies may just be changed or removed. Even worse, the interfaces remain the same but the underlying implementation changed or the same parameters mean different things now etc.

How the coding agent can help

If there is completely no information about the dependencies or how to build it, you could grep or visually inspect the import statements in the case of Python. Coding agents are also quite good at figuring out the dependencies for you (it most probably just grep the import and come up with a pyproject.toml for you), or suggest the first step how you could compile it.
Coding agents are very good at looking up dependencies history, deprecation warnings, and their motivation in relevant discussion. Once you have built a virtual environment or a container with the dependencies and encountered an error after running something minimal or tests with it, give your coding agent the traceback and let it diagnose.

Write the prompt

Assume you got an error after running something minimal using an entry point with your virtual environment or container for the first time, write a prompt for a coding agent so it can assist you to know why and fix it.
Given you encounter a deprecation, it is your call whether to fix the code or pin a package to a particular version. Below are something to consider:
To pin:
  • It is faster for now.
  • It reduces one variable if you encounter any error later.
  • It preserves exactly the behaviour from that dependency.
Not to pin and fix the code:
  • It is slower but avoids accumulating further technical debt.
  • You get the new functionality or optimisation from the package.
  • A new version of the dependency may break something in other places subtly.
  • If you are going to further develop this codebase, this is usually a better option.
It is useful to record the current status of the inherited codebase by running the existing tests, if any. Some tests may already fail before you fix or implement something and with a record, it will be clearer whether this is because of your new changes or something is just not working when you inherit the codebase. In some cases, the failed tests are not relevant anymore and you can decide to remove them all together.
'Quick start' section in AGENTS.md/CLAUDE.md: in your own words, how to set up the environment, pinned dependencies with a concise reason for each pin, the exact commands/scripts that work, errors you hit with their reasons and workarounds, and the current test results.

The structure of the codebase

If you are going to use the codebase beyond anything like running it for few times to reproduce something trivial, you are going to need a clear picture of how different parts of the codebase connect with each other in order to implement a new feature or fix a bug.

How would you do it without an LLM?

Outline some methods that will be helpful in understanding an unfamiliar codebase (without any LLM).

How the coding agent can help

Prompting the coding agents with 'explain the codebase' will get you something that looks plausible, however you have no idea whether the well-organised claims are true or not. They may silently infer some claims from its knowledge as it was trained with possibly thousands similar codebases.

How to prevent this?

How can we instruct the coding agents to avoid the above problem? Or at least decrease the likelihood of hallucinating false claims about the codebase.
Research codebases typically contain experimental or unfinished features that are never used in the existing analysis. Depending on your situation, those features may be important to you as you are the one to extend some of the features in your research. Isolating them from the production run path is one of the most important insights to gain from understanding the structure of the codebase, and coding agents can be a powerful tool here as they possess some semantic understanding.

Find the dead code

After understanding the run paths from different entry points, you should have a better understanding of what functions are used and what they are for.
Assume you have some text files containing the functions used from all the entry points you are interested in and the order of which they are called, write a prompt to identify 'the dead code' (which may be of interest to you as you are going to work on them).
'The structure of the codebase' section in AGENTS.md/CLAUDE.md: in your own words, the functions/modules that really matter and what each is for, the path data takes from entry point to output, and the parts that are currently dead (unused) and why.

Reproduce previous results

Most of the research codebases you inherited have produced some sort of research outputs, such as publication or conference abstract. If not, there will be at least some reports or presentations internally in your research group. These actually give you some sort of specifications you can check the implementation against with the current codebases you inherited.
This also opens you to one of the characteristics of research codebases: the differences between what a figure or a table in a publication or a report says and what the codes actually do. This is because researchers often tinker things when new data arrived or tune some parameters in an ad-hoc way, and if you are lucky, those would be recorded in the report but often it is just in the 'mind of the researchers'. All tests and CI pipelines will pass despite the code cannot reproduce the figure or table you want.

How the coding agent can help

Be extremely cautious about the sycophancy nature of coding agents here. If you prompt with 'does this code implement what this paper describes?', you will certainly get a confident 'yes' with supporting details that look correct. It tends to find agreements and avoid any discrepancy, unless you tell the coding agents explicitly. The output will be misleading, especially if you are unfamiliar with the inherited codebase.
This is one area that the domain knowledge of a human can genuinely help a lot before asking the coding agent. You, the researcher, hopefully knows roughly, for instance, how a certain figure was produced in theory. What you do not know is how to use the inherited codebase to reproduce such figure, and with the help of coding agents, this could be cut down from months to weeks.
Equipped with the knowledge of the structure of the codebase (from last section), you should now know what functions/files are related to the production of such figure, or in the worst case, you can eliminate irrelevant code.

Write the prompt

The powerful ability of coding agents to collect information and summarise them is very useful in this situation. Write a prompt that guide the coding agent to reproduce a figure in a publication that you know was once generated by your inherited codebase.
Below are something that often result in discrepancy between the published result and the code:
  • hard-coded constant, e.g. threshold
  • convention, e.g. normalisation
  • hidden assumptions, e.g. background is removed in an ambiguous way
'How to reproduce XXX' section in AGENTS.md/CLAUDE.md: in your own words, for the result you want to work on, the claims made in the publication/report and the corresponding parts of the code, any discrepancy with your resolution, and the steps to reproduce it, at least partially.
When the coding agents fail to reproduce something, you should watch out for them silently tweaking something that makes the code agree with the published result. For instance, it may adjust some default values without any valid scientific reason just to match the numbers in the publication. Whether this is 'cheating' or not entirely relies on you to decide. When iterating with the coding agents to reproduce something, you may need to provide additional constraints progressively if you spot something it should not change.

Conclusion

When you are handed a legacy research codebase, it could be a frustrating experience, especially when there is minimal help from your colleagues (not that they hate you, but no one really knows what it is about) and you need to produce something out of it. It used to take months of significant effort from a researcher to make sense of it. With the power of modern coding agents, the time can be shortened to weeks. Coding agents genuinely provide values in modern scientific research.
This module provided some guidance about how you could use coding agents to handle legacy research codebase by manually producing an AGENTS.md/CLAUDE.md file with the assistance of coding agents using either your own inherited codebase or one of the curated ones. This AGENTS.md/CLAUDE.md file serves as the context for coding agents which is a starting point for you to further work on the codebase, such as refactoring, fixing bugs, implementing new features or algorithm, and optimisation etc.
  • Coding agents excel at gathering information from messy sources such as a legacy research codebase.
  • It is easier to start with metadata of a codebase (packaging, CI pipelines, tests, commit history etc.) than the code themselves when exploring an unfamiliar codebase.
  • Provide concrete evidence (e.g. tracebacks) to the coding agents when you want it to further investigate something.
  • Domain knowledge is invaluable in handling legacy research codebases as, for instance, only you can decide a reproduced result is actually right or not. You should make your own judgement based on evidence you have verified.