Bug-fixing is one of the most common things to do in software development. Any experienced developer has already done this before: somewhere in the codebase is broken, and you need to fix it. Regardless you know the framework or you don’t, you have to fix it. Whoever wrote the code, it is now on your duty… This does not sound fun, especially come to legacy frameworks.
I have been in this situation for three years. Today, I would like to share my tips about learning and fixing bugs with legacy framework. After reading this article, you will understand how to search, read, learn, test, patch legacy code. In the following paragraphs, I will use Jersey 1.x, Google Web Kit (GWT), and jsPlumb as examples.
Read and Search Documentation
Understanding different APIs is the first step before fixing anything. In our codebase, we use Jersey 1.x as Java RESTful APIs framework. Actually, Jersey 2.0+ have been available since 2013, but we are still using Jersey 1.x because of Nuxeo Server: we develop on top of it. So as far as Nuxeo doesn’t upgrade to Jersey 2.x, we cannot upgrade.
Read Javadoc. Reading Javadoc is a good way to understand the code. For Jersey 1.x, I found Javadoc in https://jersey.github.io/apidocs/1.19.1/jersey/index.html, it helps a lot to understand different configuration in Jersey.
Search in Javadoc. Once you find the website for Javadoc, you might want to search exclusively in this website, or even a subset of the website starting with the given URL. Therefore, all results are bound to this context. You can do this by including the Javadoc URL in your search query. For example, searching “Resource Config” in https://jersey.github.io/apidocs/1.19.1/, you can use query:
site:jersey.github.io/apidocs/1.19.1/ Resource Config
As you can in the screenshot below, only one result is displayed in my search engine DuckDuckGo:
Obviously, similar techniques can be used for search on other websites, such as Stack Overflow.
In Java Enterprise Edition (Java EE), a lot of frameworks respect specifications. Java specifications are developed as JSRs: Java Specification Requests (https://jcp.org/en/jsr/overview). Understanding a JSR before fixing legacy bugs can help you get the architecture overview, terminology, and the key terms of the spec. For example, “Java API for RESTful Web Services” are developed under 3 JSRs:
- JSR 311: JAX-RS: The Java API for RESTful Web Services
- JSR 339: JAX-RS 2.0: The Java API for RESTful Web Services
- JSR 370: Java API for RESTful Web Services (JAX-RS 2.1) Specification
I downloaded the spec as PDF, and read it locally. It can be time-consuming, but also very useful for mastering some parts of it.
Search in the Codebase
Searching and reading the source code is another efficient way for understanding how legacy framework works. Here I want to introduce 3 different tools:
- rg (ripgrep)
- fd (alternative to find)
Use Git to search the commit message. For example, search all commits containing keyword “connector”:
$ git log --grep="connector"
Use Git to search the changes of a file, such as
$ git log --full-history --oneline -- **/connectors.js e407c3c6 add docs about flowchart midpoint a753cfb2 document getLength function aa5e09a8 small apidoc fix 69ce0cee update apidocs to yuidoc, build file changes to come 5d89f49a Merge branch 'master' of github.com:sporritt/jsPlumb 688e45d4 apidoc updates d2e1030c switch docs to jsdoc format f9c753fc more doc updates for transition to docular
$ brew install fd
Find all files containing keyword “connector” in the filename:
jsplumb (master u=) $ fd connectors demo/draggableConnectors doc/api/connectors.js doc/wiki/connectors.md docs/connectors.html src/connectors-bezier.js src/connectors-flowchart.js src/connectors-statemachine.js src/connectors-straight.js tests/miscellaneous/chartDemoConnectors.html tests/miscellaneous/draggableConnectorsWithArrows.html
$ brew install ripgrep
Find all files containing keyword “connector” in their content:
$ rg connector
Find JS files containing keyword “connector” in their content using glob expression /**/*.js:
$ rg connector -g /**/*.js
Testing the framework is a good way to learn and ensure the legacy framework works as expected. From my experience, it works best if you divide testing into two parts:
- Learning the framework
- Fixing the bug
At learning phrase, you can write tests in your own pace. They can be hosted in your personal repository. You are free to modify the code without colleagues permissions and review. It’s a very efficient way to learn. Personally, I did it for some frameworks / technologies: Jersey 1.x, Polymer 2, Google Web Kit (GWT), Batch etc.
At bug-fixing phrase, the tests will be completely focus on the bug. Usually, it will be much more difficult! It might mean that:
- Set up testing if not exist, using external library
- Avoid dependency conflicts
- Ensure CI environment is able to execute the tests
- Mock some parameters
- Prepare dataset for testing
- Write the tests
- Ensure no regression on production
But it is worth the price! Because it ensures that there is no regression, and the acceptance criteria is well respected. At the beginning, it might be very time consuming, but more you practice, easier you will find it to be.
Patch the Library
You can create a patched version of the library to include your own fix. Patching the library can be more or less complex.
jquery.jsPlumb-1.5.5.js # don't jquery.jsPlumb-1.5.5-patched.js # ok
Patching a library can be much more difficult, such as forking the repository and create your own release. It requires you to have a good knowledge on the build tools. Also, it requires you to have your own package repository for storing the patched version. I did it once, for patching a serialization workaround on Google Web Kit (GWT). The patched version is found here as nuxeo/gwt:2.8.2-NX1, and artifacts are uploaded to Nuxeo’s package repository.
Don’t expect that the bug can be fixed right away. Lower your expectation (and your co-workers). Fixing it might take longer than expected. Generally because:
- Lack of knowledge
- Lack of documentation
- Lack of test
- Lack of acceptance criteria
- Low popularity of the framework
- The complexity of the existing code
- Production regression (incomplete or side effect)
- Data migration
- Dependency conflicts
- Lack of build time check (compiler, lint tool, IDE inspection etc)
- Manual testing is much longer than automatic testing
In this article, I shared some tips about doing bug-fixing on legacy framework in my own experience. Reading spec and documentation, customize search query, tools for searching efficiently in the source code, writing tests, patching library and mental preparation. Hope you enjoy this article, see you the next time!
- Jersey, “jersey/jersey”, GitHub, 2018. https://github.com/jersey/jersey
- JsPlumb, “jsplumb/jsplumb”, GitHub, 2019. https://github.com/jsplumb/jsplumb
- GWT, “gwtproject/gwt”, GitHub, 2019. https://github.com/gwtproject/gwt
- fd, “sharkdp/df”, GitHub, 2019. https://github.com/sharkdp/fd
- rg, “BurntSushi/ripgrep”, GitHub, 2019. https://github.com/BurntSushi/ripgrep
- Java Community Process, “JSRs: Java Specification Requests”, JCP, 2019. https://jcp.org/en/jsr/overview