Symphony Apps Development logo — teal interlocking S monogram beside the studio wordmark
All writing

The tests we write before a refactor, not after

A refactor without a characterisation test is a rewrite with a reassuring name. The difference is entirely in what gets tested first.

Category
Quality
Reading time
6 min
Published
13 May 2026
Topics
Testing, Engineering, Quality

"Refactor" gets used for two different activities: changing code's structure while its behaviour stays fixed, and rewriting code while hoping the behaviour stays fixed. The difference between them is whether anyone can prove it.

Characterisation tests come first

Before touching a module we do not fully trust, we write tests that describe what it currently does — including the parts that look wrong. A characterisation test is not an endorsement of the existing behaviour. It is a fence around it, so that any change to that behaviour during the refactor is a decision someone makes on purpose, not a side effect nobody notices until later.

This feels backwards the first time

Writing a test that asserts a bug is correct behaviour is uncomfortable, and every engineer who has done this for the first time has asked whether it is a good idea. It is, because the alternative is refactoring blind: you cannot tell whether you fixed the bug on purpose or broke something else by accident, because nothing was recording the starting state.

What we separate

  • Structural change — same behaviour, different shape. Covered entirely by characterisation tests; if any of them fail, the refactor introduced a regression.
  • Behavioural change — the bug you noticed while refactoring and decided to fix. This gets its own ticket, its own test written from the correct behaviour, and its own review, separate from the structural work.

Mixing the two in one pull request is the single most common reason a "safe" refactor turns out not to have been safe.

Where this pays off most

Modules with unclear ownership — code that predates the current team, with no author left to ask what a particular branch is supposed to do. Characterisation tests let the team refactor with confidence even when nobody can explain why the original code behaves the way it does, because the tests do not require an explanation, only an observation.

A refactor that changes behaviour without anyone deciding to is not a refactor. It is a bug, wearing a more respectable job title.

What this looks like in review

A refactor pull request with passing characterisation tests and no behavioural changes gets reviewed quickly, because the reviewer's job shrinks to checking the structure is actually better. A pull request that mixes structural and behavioural change gets sent back, regardless of how good either change is on its own — not as a punishment, but because reviewing both at once catches neither properly.

Tell us what you’re trying to ship

A first call is thirty minutes and costs nothing. Bring the problem, not a spec — working out what to build is the part we are good at.

Or email office@symphonyapps.ro. We reply within one business day, in English or Romanian.