Programování

Refaktoring legacy kódu bez strachu – postup krok za krokem

Jak bezpečně upravit starý kód bez testů – charakterizační testy, malé kroky, strangler fig, feature flagy a kdy refaktoring vůbec nedělat.

12. 10. 2026 · 4 min čtení

Legacy kód je kód, který vydělává peníze, ale nikdo se ho nechce dotknout. Funkce na 800 řádků, globální proměnné, žádné testy a komentář „NEMĚNIT – nevím proč, ale funguje to“. Přepsat to celé od nuly je lákavé a skoro vždy špatné. Tady je postup, jak legacy kód zlepšovat postupně a bez rozbití produkce.

Nejdřív: proč refaktorujete?

Refaktoring je změna struktury kódu bez změny chování. Dělejte ho, když:

  • potřebujete do kódu přidat funkci a v současném stavu to nejde bezpečně,
  • se v dané části opakovaně objevují chyby,
  • kód často měníte a každá změna trvá zbytečně dlouho.

Nedělejte ho jen proto, že je kód ošklivý. Ošklivý kód, na který nikdo nesahá, nikoho nebolí.

Krok 1: zajistěte si záchrannou síť

Bez testů nevíte, jestli jste něco nerozbili. U legacy kódu ale testy nejsou a kód není napsaný tak, aby se dal snadno testovat. Řešením jsou charakterizační testy (také „golden master“ nebo „snapshot“ testy): netestují, co by kód měl dělat, ale zachycují, co skutečně dělá teď.

# tests/test_invoice_legacy.py
import json
from pathlib import Path
from legacy.invoice import render_invoice

CASES = json.loads(Path("tests/fixtures/invoices.json").read_text())

def test_render_matches_snapshot():
    for case in CASES:
        result = render_invoice(**case["input"])
        assert result == case["expected"], case["name"]

Jak získat expected? Spusťte současný kód na reprezentativních vstupech a výstup uložte. Pokud v něm najdete chybu, zatím ji neopravujte – zapište si ji. Oprava chyby je změna chování a patří do samostatného kroku.

Pro snapshoty existují i hotové nástroje: syrupy pro pytest, toMatchSnapshot() v Jestu, ApprovalTests pro více jazyků.

Krok 2: malé kroky, každý zvlášť commitnutý

Každá změna by měla být tak malá, aby se dala popsat jednou větou a vrátit jedním git revert:

  1. Přejmenování proměnné → testy → commit.
  2. Vytažení bloku do funkce → testy → commit.
  3. Odstranění mrtvého kódu → testy → commit.

Nikdy nemíchejte refaktoring a novou funkčnost v jednom commitu. Recenzent pak neumí rozlišit, co je přesun a co změna logiky.

Krok 3: bezpečné refaktoringové operace

Začněte těmi, které jsou téměř bez rizika – ideálně provedené nástrojem v IDE, ne ručně:

Operace Kdy
Rename (přejmenování) Nejasné názvy jako data2, tmp, flag
Extract function Blok s komentářem „// spočítej DPH“
Extract variable Složitý výraz v podmínce
Inline Funkce, která jen volá jinou funkci
Introduce parameter Funkce čte globální proměnnou
Replace magic number if status == 3 → if status == Status.PAID

Příklad – závislost na globálním stavu znemožňuje testování:

# Před
def is_overdue(invoice):
    return invoice.due_date < datetime.now()

# Po – čas jde zvenku, funkce je testovatelná
def is_overdue(invoice, now=None):
    now = now or datetime.now()
    return invoice.due_date < now

Krok 4: švy (seams) pro testovatelnost

Michael Feathers v knize Working Effectively with Legacy Code zavádí pojem šev – místo, kde můžete změnit chování kódu, aniž byste upravovali samotný kód. Typicky parametr, rozhraní nebo injektovaná závislost. Když funkce přímo volá databázi, e-mail a platební bránu, vytáhněte tato volání do parametrů nebo samostatných tříd. V testu je pak nahradíte falešnými implementacemi.

Krok 5: větší přestavby – strangler fig

Když je potřeba vyměnit celý modul, nepřepisujte ho najednou. Vzor strangler fig (škrtící fíkovník):

  1. Před starý kód postavte rozhraní (fasádu, router, proxy).
  2. Novou implementaci pište vedle staré, část po části.
  3. Přesměrovávejte provoz na novou implementaci postupně – funkce po funkci.
  4. Když stará část nemá žádný provoz, smažte ji.

V kombinaci s feature flagy můžete novou cestu zapnout jen pro část uživatelů nebo ji v případě problému okamžitě vypnout:

if flags.enabled("new_invoice_engine", user):
    return new_render(invoice)
return legacy_render(invoice)

Pokročilá varianta: spouštět obě implementace a porovnávat výsledky (vrací se výsledek té staré, rozdíly se logují). Knihovna GitHub Scientist tento přístup zpopularizovala.

Najděte mrtvý kód

Část legacy kódu se vůbec nevolá. Pomohou nástroje:

# Python
pip install vulture && vulture src/

# JavaScript / TypeScript
npx knip

A pak ověření v produkci: přidejte do podezřelé funkce log a pár týdnů sledujte, zda se objeví.

Refaktoring s pomocí AI

AI je při refaktoringu užitečná na vysvětlení nesrozumitelného kódu, generování charakterizačních testů a mechanické úpravy. Zásady:

  • Žádejte jeden typ změny najednou („vytáhni výpočet DPH do samostatné funkce, nic jiného neměň“).
  • Vždy spusťte testy – AI může nenápadně změnit chování (pořadí, zaokrouhlení, ošetření None).
  • U velkých souborů chtějte cílené úpravy, ne přepsání celého souboru.

V Projektech ColdHost AI umí asistent upravovat soubory blokovými SEARCH/REPLACE změnami, takže v diffu vidíte přesně, co se změnilo, a refaktoring zůstává přehledný.