brief contents
contents
preface
acknowledgments
about this book
Roadmap
Source code
Author Online
About the author
About the cover
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 What is re-architecting?
5.2 Breaking up a monolithic application into modules
5.2.1 Case study—a log management application
5.2.2 Defining modules and interfaces
5.2.3 Build scripts and dependency management
5.2.4 Spinning out the modules
5.2.5 Giving it some Guice
5.2.6 Along comes Gradle
5.2.7 Conclusions
5.3 Distributing a web application into services
5.3.1 Another look at Orinoco.com
5.3.2 Choosing an architecture
5.3.3 Sticking with a monolithic architecture
5.3.4 Separating front end and back end
5.3.5 Service-oriented architecture
5.3.6 Microservices
5.3.7 What should Orinoco.com do?
5.4 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 Introducing 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
index
Symbols
Numerics
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
Y
Z