Zpět na domů

Bezpečný refaktoring modulů: zachovávejte chování | IT-praktiky

Článek odhaluje strategii bezpečné extrakce modulů z monolitních systémů. Popisuje principy zachování chování, použití feature flags a testování k prevenci regresí. Důraz na minimalizaci změn v prvním etapě.

Refaktoring bez pádů: průvodce extrakcí modulů
Advertisement 728x90

# Refaktoring bez rizik: jak extrahovat moduly při zachování chování

Při převodu modulu z monolitu do samostatné služby je hlavním úkolem zachovat chování doslova, včetně známých chyb a nedokumentovaných vlastností. Pokusy „učešat“ kód v tomto bodě často vedou k regresiím. Ukážeme, jak se vyhnout úskalím pomocí strategie minimálních změn.

Proč „nudný“ přístup chrání před regresemi

V produkčních systémech existuje implicitní smlouva, kterou formovaly roky provozu: integrační klienty, zpracování chyb, časová zpoždění, adaptace na cizí API. Tato smlouva není popsána v dokumentaci — je zakódována v logách, commitách a zkušenostech týmu. Refaktoring nesmí měnit chování, i když obsahuje chyby. Například pokud starý kód vrací prázdný řetězec místo null, změna toho chování zlomí klienty, kteří se na ni už přizpůsobili. Klíčový princip: úkol extrakce spočívá v změně formy, ale zachování sémantiky. Jakékoli odchylky od původního chování mění refaktoring v releas s neznámými důsledky.

Jeden typ změn najednou: hranice před vnitřní strukturou

Rozdělte úkoly na etapy:

Google AdInline article slot
  • Etapa A: extrakce modulu se zachováním kontraktu (HTTP, fronta, sběrnice) a zavedení feature flag.
  • Etapa B: aktualizace stacku, knihoven nebo stylu kódu.
  • Etapa C: opravy chyb.
  • Etapa D: optimalizace výkonu.

Míchání etap v jednom PR je přímá cesta k nečitelnému diffu, problémům s rollbackem a sporům „je to chyba nebo nová funkce?“. V první etapě použijte obal kolem starého kódu: nová hranice služby, stará logika uvnitř. Například při extrakci modulu směrování nechte původní funkci route(), ale obalte ji do HTTP endpointu. To zaručí, že chování zůstane identické, i když kód vypadá „nevzhledně“.

Feature flag: váš nouzový vypínač

Paralelní existence starého a nového kódu v produkci vyžaduje spínač ovládaný přes konfiguraci nebo env proměnné. To umožňuje:

  • Okamžitý rollback bez deploye při incidentu.
  • Postupné zavádění na část provozu (canary release).
  • Dlouhodobé uchování staré implementace pro porovnání chování.

Kriticky důležité je, aby spínač fungoval bez restartu služby. Jinak se incident mimo pracovní dobu změní v nouzový hovor místo jednoduché změny konfigurace. Implementujte flag na úrovni business logiky, ne infrastruktury — tak se vyhnete složitým závislostem na orchestrátorech.

Google AdInline article slot

Kopírujte chování, ne přepisujte

Instrukce pro bezpečný převod: kopírujte soubory „jak jsou“, měňte jen nutné pro build. Příklad z praxe — převod směrování z aktra na čistou funkci:

object UpdateRouting {
  def route(update: Update): Either[RoutingError, RouteTarget] =
    if (update.message.exists(_.chat.isGroupOrSupergroup)) Right(GroupFlow)
    else if (update.callbackQuery.isDefined) Right(CallbackFlow)
    else Right(PrivateFlow)
}

Při mapování modelů zachovejte i neobvyklé detaily. Pokud starý kód převáděl chybějící channelId na prázdný řetězec, udělejte totéž:

def toBusMessage(in: PlatformInMessage): BusIncoming =
  BusIncoming(
    correlationId = newCorrelationId(),
    channelId = in.channelId.getOrElse("")
  )

Jakákoli změna sémantiky (např. nahrazení prázdného řetězce null) poruší kontrakt s jinými službami. Cílem je bitově identické chování, i když kód vypadá zastarale.

Google AdInline article slot

Co kategoricky nedělat v etapě extrakce

Dokud je feature flag aktivní, vyhněte se:

  • Novým knihovnám. Každá přidává transitivní závislosti a povrch útoku. Aktualizace stacku je samostatný úkol.
  • Změnám stylu kódu. Směs starého doménového kódu a nové obálky je dočasná cena za bezpečnost.
  • Opravám nalezených chyb. Vytvořte ticket a řešte po stabilizaci. Jinak se jakýkoli incident připíše refaktoringu.
  • Optimalizacím „když už máme soubor otevřený“. Profilování a zrychlení je samostatný cyklus prací.

Tyto omezení se zdají přehnaná, ale po půl roce v git blame uvidíte čistou historii: „vyjmuli modul, zapojili sběrnici“, ne „přepsali všechno naráz“.

Testy: fixace kontraktu, ne KPI

Unit testy na čisté funkce (směrování, mapování) musí fixovat chování před převodem. Zelený test po extrakci zaručí, že logika se nezměnila. Pro integrační scénáře (síť, fronty, DB) použijte E2E testy, ale pamatujte na jejich limity:

  • Móky externích systémů mohou reprodukovat chybné předpoklady z kódu.
  • Reálné API někdy obsahuje neobvyklé nuancí (např. prefix /api/), které testy neberou v úvahu.

Povinně přidejte manuální průběh na produkčně podobném standalone. Automatizace nenahradí kontroly v podmínkách co nejblíže reálným.

Růst kódu — ne chyba, ale etapa procesu

Po první etapě často roste objem kódu: objevují se adaptéry, feature flagy, duplikace s monolitem. To je normální — platíte za bezpečnost rollbacku a postupného zavádění. Stlačení a čištění přijde ve druhé etapě, když se hranice stabilizovala, starý kód je odstraněn a testy potvrzují správnost. Nesnažte se optimalizovat hned — nejdřív zajistěte funkčnost.

Co je důležité

  • Cíl extrakce — převod chování „as is“, ne komplexní vylepšení.
  • Feature flag je povinný pro bezpečný rollback bez deploye.
  • Testy fixují kontrakt před zahájením převodu, ne po.
  • Zakázány paralelní změny stacku, bugfixů a optimalizací.
  • Kód může růst — to je součást strategie, ne chyba.

Refaktoring legacy kódu není místo pro experimenty. Čím nudnější proces, tím menší šance, že vás v 3 hodiny ráno probudí pád produkce. Dodržováním těchto pravidel změníte riskantní převod v předvídatelnou operaci.

— Editorial Team

Advertisement 728x90

Číst dál