Re-Engineering Legacy Software
Chris Birchall
  • April 2016
  • ISBN 9781617292507
  • 232 pages
  • printed in black & white

A comprehensive guide to turning legacy software into modern, up-to-date projects!

Jean-François Morin, Université Laval

As a developer, you may inherit projects built on existing codebases with design patterns, usage assumptions, infrastructure, and tooling from another time and another team. Fortunately, there are ways to breathe new life into legacy projects so you can maintain, improve, and scale them without fighting their limitations.

As a developer, you may inherit projects built on existing codebases with design patterns, usage assumptions, infrastructure, and tooling from another time and another team. Fortunately, there are ways to breathe new life into legacy projects so you can maintain, improve, and scale them without fighting their limitations.

Re-Engineering Legacy Software is an experience-driven guide to revitalizing inherited projects. It covers refactoring, quality metrics, toolchain and workflow, continuous integration, infrastructure automation, and organizational culture. You'll learn techniques for introducing dependency injection for code modularity, quantitatively measuring quality, and automating infrastructure. You'll also develop practical processes for deciding whether to rewrite or refactor, organizing teams, and convincing management that quality matters. Core topics include deciphering and modularizing awkward code structures, integrating and automating tests, replacing outdated build systems, and using tools like Vagrant and Ansible for infrastructure automation.

Table of Contents detailed table of contents



about this book

Part 1: Getting started

1. Understanding the challenges of legacy projects

1.1. Definition of a legacy project

1.1.1. Characteristics of legacy projects

1.1.2. Exceptions to the rule

1.2. Legacy code

1.2.1. Untested, untestable code

1.2.2. Inflexible code

1.2.3. Code encumbered by technical debt

1.3. Legacy infrastructure

1.3.1. Development environment

1.3.2. Outdated dependencies

1.3.3. Heterogeneous environments

1.4. Legacy culture

1.4.1. Fear of change

1.4.2. Knowledge silos

1.5. Summary

2. Finding your starting point

2.1. Overcoming feelings of fear and frustration

2.1.1. Fear

2.1.2. Frustration

2.2. Gathering useful data about your software

2.2.1. Bugs and coding standard violations

2.2.2. Performance

2.2.3. Error counts

2.2.4. Timing common tasks

2.2.5. Commonly used files

2.2.6. Measure everything you can

2.3. Inspecting your codebase using FindBugs, PMD, and Checkstyle

2.3.1. Running FindBugs in your IDE

2.3.2. Handling false positives

2.3.3. PMD and Checkstyle

2.4. Continuous inspection using Jenkins

2.4.1. Continuous integration and continuous inspection

2.4.2. Installing and setting up Jenkins

2.4.3. Using Jenkins to build and inspect code

2.4.4. What else can we use Jenkins for?

2.4.5. SonarQube

2.5. Summary

Part 2: Refactoring to improve the codebase

3. Preparing to refactor

3.1. Forming a team consensus

3.1.1. The Traditionalist

3.1.2. The Iconoclast

3.1.3. It's all about communication

3.2. Gaining approval from the organization

3.2.1. Make it official

3.2.2. Plan B: The Secret 20% Project

3.3. Pick your fights

3.4. Decision time: refactor or rewrite?

3.4.1. The case against a rewrite

3.4.2. Benefits of rewriting from scratch

3.4.3. Necessary conditions for a rewrite

3.4.4. The Third Way: incremental rewrite

3.5. Summary

4. Refactoring

4.1. Disciplined refactoring

4.1.1. Avoiding the Macbeth Syndrome

4.1.2. Separate refactoring from other work

4.1.3. Lean on the IDE

4.1.4. Lean on the VCS

4.1.5. The Mikado Method

4.2. Common legacy code traits and refactorings

4.2.1. Stale code

4.2.2. Toxic tests

4.2.3. A glut of nulls

4.2.4. Needlessly mutable state

4.2.5. Byzantine business logic

4.2.6. Complexity in the view layer

4.3. Testing legacy code

4.3.1. Testing untestable code

4.3.2. Regression testing without unit tests

4.3.3. Make the users work for you

4.4. Summary

5. Re-architecting

5.1. Breaking up a monolithic application into modules

5.1.1. Case study—a log management application

5.1.2. Defining modules and interfaces

5.1.3. Build scripts and dependency management

5.1.4. Spinning out the modules

5.1.5. Giving it some Guice

5.1.6. Along comes Gradle

5.1.7. Conclusions

5.2. Distributing a web application into services

5.2.1. Another look at

5.2.2. Choosing an architecture

5.2.3. Sticking with monolithic architecture

5.2.4. Separating frontend and backend

5.2.5. Service-Oriented Architecture

5.2.6. Microservices

5.2.7. So what should do?

5.3. Summary

6. The Big Rewrite

6.1. Deciding the project scope

6.1.1. What is the project goal?

6.1.2. Documenting the project scope

6.2. Learning from the past

6.3. What to do with the DB

6.3.1. Sharing the existing DB

6.3.2. Creating a new DB

6.3.3. Inter-app communication

6.4. Summary

Part 3: Beyond refactoring—improving project workflow and infrastructure

7. Automating the development environment

7.1. First day on the job

7.1.1. Setting up the UAD development environment

7.1.2. What went wrong?

7.2. The value of a good README

7.3. Automating the development environment with Vagrant and Ansible

7.3.1. Introduction to Vagrant

7.3.2. Setting up Vagrant for the UAD project

7.3.3. Automatic provisioning using Ansible

7.3.4. Adding more roles

7.3.5. Removing the dependency on an external database

7.3.6. First day on the job—take two

7.4. Summary

8. Extending automation to test, staging and production environments

8.1. Benefits of automated infrastructure

8.1.1. Ensures parity across environments

8.1.2. Easy to update software

8.1.3. Easy to spin up new environments

8.1.4. Enables tracking of configuration changes

8.2. Extending automation to other environments

8.2.1. Refactor Ansible scripts to handle multiple environments

8.2.2. Build a library of Ansible roles and playbooks

8.2.3. Put Jenkins in charge

8.2.4. Frequently asked questions

8.3. To the cloud!

8.3.1. Immutable Infrastructure

8.3.2. DevOps

8.4. Summary

9. Modernizing the development, building and deployment of legacy software

9.1. Difficulties in developing, building and deploying legacy software

9.1.1. Lack of automation

9.1.2. Outdated tools

9.2. Updating the toolchain

9.3. Continuous integration and automation with Jenkins

9.4. Automated release and deployment

9.5. Summary

10. Stop writing legacy code!

10.1. The source code is not the whole story

10.2. Information doesn't want to be free

10.2.1. Documentation

10.2.2. Foster communication

10.3. Our work is never done

10.3.1. Periodic code reviews

10.3.2. Fix one window

10.4. Automate everything

10.4.1. Write automated tests

10.5. Small is beautiful

10.5.1. Example: the Guardian Content API

10.6. Summary


What's inside

  • Refactoring legacy codebases
  • Continuous inspection and integration
  • Automating legacy infrastructure
  • New tests for old code
  • Modularizing monolithic projects

About the reader

This book is written for developers and team leads comfortable with an OO language like Java or C#.

About the author

Chris Birchall is a senior developer at the Guardian in London, working on the back-end services that power the website.

placing your order...

Don't refresh or navigate away from the page.
print book $38.99 $64.99 pBook + eBook + liveBook
Additional shipping charges may apply
Re-Engineering Legacy Software (print book) added to cart
continue shopping
go to cart

eBook $31.19 $51.99 3 formats + liveBook
Re-Engineering Legacy Software (eBook) added to cart
continue shopping
go to cart

Prices displayed in rupees will be charged in USD when you check out.

FREE domestic shipping on three or more pBooks