Article in English can be found on dev.to/arxeiss/dead-code-detection-in-go-monorepos-with-deadmono-3043
Na projektu máme velké Go monorepo, kde více servis sdíli interní balíčky. Po čase se může jednoduše stát, že exportované funkce se již nikde nepoužívají. A tak jsem hledal způsob, jak je detekovat a poté smazat.
Pro Go existuje několik linterů jako unused v GolangCI nebo unusedfunc v GoPLS. Ty ale dokážou detekovat pouze unexported funkce v rámci balíčku, které se nepoužívají. Go tým vytvořil skvělý nástroj nazvaný deadcode, který umí detekovat veškerý nepoužívaný kód. Jenže deadcode začíná analýzu z jediné main funkce, což je pro monorepo nedostatečné.
Problém s jednoduchým průnikem množin
Zkusil jsem napsat skript, který spustí deadcode pro každou servisu zvlášť. Pak provedu průnik množin a bude hotovo. Jenže výsledek byl, že žádná nepoužívaná funkce neexistuje. A to bylo divné.
Struktura monorepa:
├── services/
│ ├── service-a/ (importuje pkg/cache, pkg/logging)
│ ├── service-b/ (importuje pkg/cache, pkg/logging)
│ └── service-c/ (importuje pouze pkg/logging)
└── pkg/
├── cache/
│ ├── Get() ✓ Používá service-a
│ ├── Set() ✓ Používá service-b
│ └── Delete() ✗ dead code
└── logging/
└── Info()
Výstup deadcode pro každou servisu:
service-a: Reportuje cache.Set(), cache.Delete() jako nepoužívané service-b: Reportuje cache.Get(), cache.Delete() jako nepoužívané service-c: Nereportuje NIC o pkg/cache (vůbec ho neimportuje!)
Průnik těchto 3 množin je PRÁZDNÁ MNOŽINA ❌
Proč? service-c vůbec neimportuje pkg/cache, takže i v reportu vůbec není zmíněn. Výsledný průnik je tedy prázdná množina, i když cache.Delete() skutečně není používána.
Řešení: Průnik pro každý balíček zvlášť
Vytvořil jsem tedy deadmono utilitku, která tento problém řeší pomocí průniku na úrovni balíčků:
- Detekuje každou servisu zvlášť a které balíčky importuje
- Vytváří průnik výsledků pro každý balíček (pouze pokud jej importuje)
- Hlásí skutečně nepoužívané funkce
Instalace
# Instalace deadmono go install github.com/arxeiss/deadmono/cmd/deadmono@latest # Instalace požadovaného nástroje deadcode go install golang.org/x/tools/cmd/deadcode@latest
Použití
deadmono services/authn/main.go services/config/main.go services/healthcheck/main.go
Jak to funguje na výše uvedeném příkladu
Pro pkg/cache (importován service-a, service-b):
service-a hlásí: cache.Set(), cache.Delete() jako nepoužívané
service-b hlásí: cache.Get(), cache.Delete() jako nepoužívané
service-c: IGNOROVÁNO (neimportuje pkg/cache)
Průnik (service-a ∩ service-b):
✓ cache.Delete() - nepoužívané v OBOU servisách, které ho importují
Pro pkg/logging (importován všemi servisami):
Všechny servisy používají logging.Info()
Průnik: (prázdný - žádné nepoužívané funkce)
Finální výsledek:
pkg/cache/cache.go:15:1: unreachable func: Delete
Užitečné flagy
-test- Analyzuje také testovací soubory-generated- Detekuje nepoužívané funkce z generovaných souborů-tags string- Seznam build tagů oddělených čárkou-filter string- Filtruje balíčky pomocí regexu (výchozí: aktuální modul)-json- Výstup ve formátu JSON-debug- Podrobný debug výstup
Více Go modulů
Ve výchozím nastavení musí být všechny main funkce ve stejném modulu. Pro analýzu napříč moduly musí být specifikován flag -filter:
deadmono -filter "github.com/myorg/.*" \ module1/services/api/main.go \ module2/services/worker/main.go
Buď první, kdo přidá komentář. Zatím zde nic není