I'm currently working on a small team to "modernise" some legacy software that is business critical.
This work is a bit different to what I'm used to. It involves lots of planning and lots of "people stuff"—we are working with the existing team and will be influencing the future structure of that team and how it operates. We will address things like barriers to deploying code easily, security risks, and outdated technology that makes it harder to hire and retain staff who are willing to work on these applications.
I want to share the lessons we're learning on this project but I'm not sure about the best way to do it. I'm thinking of compiling some kind of catalog of patterns that could be applied to other projects.
Here's a couple of examples of what that could look like, based on the work we've done so far:
- The existing build scripts used ant
- We had multiple build artefacts, ant scripts and property files
- A lot of configuration was targeted at a specific environment and OS (windows)
- Dependencies were bundled with the project (or stored somewhere on the shared development environment) as .jar files
- The build scripts were difficult to understand
- Ant is not a technology we use for other projects so developers are unfamiliar with it
- Dependency management was harder than it needed to be
- We created new build scripts using Gradle
- We checked the build artefects produced were identical to those produced by the old build by listing the contents of the jar/war files and running a tool to compare binary compatability of the packages
- The build files are a lot more concise and easy to understand
- Dependencies are declared in one place and are pulled from maven repositories at build time, in line with the 12 factor app recommendations
- The team shared a development environment, which was the only place the app could be built
- Developers each had their own configurations for building the software
- A different artefact was built for each environment
- Code branches were long lived, and changes got batched up into scheduled releases
- We had some automated tests, but many of them didn't run
- The team had migrated to git from SVN, so the old build process hadn't been developed with git in mind
- There was no immediate feedback when developing changes
- The team were not confident that the artefacts they were releasing contained the changes they expected
- The master branch couldn't be deployed at short notice so we had to make hotfix branches and keep track of them
- We created a CircleCI configuration that runs the gradle build, producing .war files
- We reviewed the tests, fixing some that didn't work and deleting others we didn't need, so the entire suite runs in CI
- We had a conversation with the whole team about branches in git and trunk-based development
- Master contains code that has passed CI and we're happy to release, so there's less need for hotfix branches
- The tests give us some confidence that a pull request hasn't broken existing functionality
- In the short term we intend to build environment specific artefacts from CI and deploy those, which will make it easier to see the link between a deployed artefact and the source code it was built from
Do you think something like this would be helpful when dealing with other legacy projects? How do you approach process improvement in your work?
Working effectively with legacy code is the go to book on legacy code, but a lot of the advice focuses on making changes to the code itself, rather than the systems around the code. Would you recommend looking at any other resources on this topic?