Ace8 is the modernization practice of AceMQ.24/7 support from AceMQ after go-live
Book an Assessment

The short answer

Legacy code refactoring is changing the structure of old code without changing what it does. Do it in this order: write characterization tests that record current behavior, find seams where you can break dependencies, then change the code in small steps, running the tests after each one. Michael Feathers set out this method in Working Effectively with Legacy Code.

01The problem

Why can't you just clean it up?

Refactoring means changing structure while behavior stays fixed. In legacy code you cannot show that behavior stayed fixed, because there are no tests. So the first job is not to improve the code. It is to make change safe.

Feathers defines legacy code as code without tests. That is a useful definition: age is irrelevant, and the fix is the same either way.

02The sequence

Five steps for each area of code

  1. 01

    Pick a small area

    One class, one flow or one integration point. Not the whole module.

  2. 02

    Write characterization tests

    Call the code, record what it returns or does, and assert that. If the result looks wrong, still record it. The test says what is, not what should be.

  3. 03

    Find a seam

    A seam is a place where you can change behavior without editing the code around it. In Java, an interface or an overridable method is an object seam.

  4. 04

    Break the dependency

    Use the seam to replace a database call, a broker, a clock or a static call with something a test can control.

  5. 05

    Refactor and rerun

    Make one small change, run the tests, commit. Repeat until the area is clean.

Planning this for your estate? Get a fixed scope and price first.

03Step in detail

What is a characterization test?

A characterization test pins down current behavior. Write a test that calls the code with realistic input and asserts nothing yet. Run it, read the real output, then put that output in as the expected value. You now have a test that fails if behavior changes.

Aim at the awkward parts: error paths, rounding, null handling, odd input. These hold the rules nobody wrote down. Tests that record bugs are fine. Fix them as a separate, visible change later.

04Step in detail

Where do you find seams in Java?

  • A constructor that creates its own collaborator: pass the collaborator in instead.
  • A static call to a database or utility: wrap it in an instance method you can override.
  • A direct call to a vendor API, such as a message broker: put an interface in front of it.
  • A method that does two things: extract one, so the other can be tested alone.

05New code in old code

When should you add new code beside old code instead?

If a change is needed before the surrounding code is under test, write the new logic as a separate, tested method or class, and call it from the old code with one line. Feathers calls these sprout techniques. The old code stays untouched until tests exist, and the new code starts clean.

Use this to ship real changes while the safety net grows, rather than waiting for full coverage.

06A real case

What did this look like at American National?

American National, an insurer, moved from IBM MQ to RabbitMQ in phases. The application code had been written around the old broker, so the migration was also a refactoring job. The AceMQ engineering team refactored the legacy code that depended on IBM MQ so it could talk to a different broker, and moved queues and applications in steps, with each step reviewed before the next. The work was delivered by the AceMQ engineering team, with no big-bang cutover.

Read the full account in the American National case study. Scope figures will appear there once the customer approves them.

07Tooling

Where can automation help?

Deterministic recipes handle mechanical changes such as renaming packages or replacing deprecated calls. OpenRewrite is one. They do not decide where to cut a dependency or what behavior to keep. That judgment stays with an engineer, and every change is reviewed before it merges.

For the wider decision on whether to refactor at all, see the rehost, replatform, refactor and rewrite guide.

08How Ace8 helps

Refactoring with a safety net

Ace8 is the AI-assisted application modernization practice of AceMQ.

Our legacy application modernization work follows this sequence: tests first, change in slices, keep each step reversible. To see which applications to start on, begin with the modernization assessment.

FAQ

Frequently asked.

Something else on your mind? Ask an engineer.

Changing the internal structure of old code without changing its behavior, so it is easier to understand, test and change.

Start here

Know the cost before anything moves.

A fixed-price assessment gives you the inventory, the risk score and a phased plan. Scope and cost are fixed before the migration starts.

Get in touch

Talk to a modernization engineer

Tell us what you run today. An engineer, not a sales script, replies within one business day.

305-204-2607info@ace8.io

66 W. Flagler St. 9th Floor
Miami, FL 33130

What are you moving off? *

Fixed price. Scope and cost before any migration starts.