Files
2nd/10_Wiki/Topic_Programming/Architecture/Breaking Dependencies.md
T
Antigravity Agent 9148c358d0 docs(10_Wiki): 위키 전체 재구성 — Topic_* 폴더를 4개 카테고리로 통합 + 대규모 중복 제거
Topic_Agent/Topic_Blog/Topics/Topics_Biz/Topics_Meeting/Topics_Rag의 마크다운 지식 문서를
Topic_General/Topic_Programming/Topic_Graphic/Topic_Business 4개 카테고리로 재분류.

- 중복 제거: frontmatter의 status:duplicate/merged + duplicate_of/redirect_to 필드로
  자기 자신을 중복으로 선언한 리다이렉트 stub 1032개 제거, 완전 동일 내용 파일 472개 제거,
  동일 파일명·다른 내용 충돌 시 더 큰(완전한) 버전만 유지(162개 제거) — 총 1639개 중복 제거.
- 분류: 폴더 단위로 명확한 항목(AI_and_ML/Coding/Architecture 등 → Programming,
  Comfyui/Visual_Effects → Graphic, Topics_Biz/Topics_Meeting/사업 등 → Business,
  Poetic_Blog_Writing/창의성/Game_Design 등 → General)은 폴더 우선순위로,
  나머지 혼재 폴더(Topic_Agent/Topic_Blog/Topics 루트/Thinking & Reasoning/Other/UI_UX_Assets)는
  title/tags 키워드 스코어링으로 파일 단위 분류(불명확한 경우 General로 폴백).
  원본 폴더명은 "From_*" 서브폴더로 보존해 추적 가능성 유지.
- 최종 배치: Programming 2784 / General 1608 / Graphic 285 / Business 249 = 4926개 문서.
- 에이전트 운영 상태(.astra/.agent/.obsidian/sessions/memory/_company/docs/lessons/_shared/src)는
  지식 콘텐츠가 아니므로 재분류 대상에서 제외하고 원위치 유지.
- Topics/Topic_email(상위 보호 폴더 Topic_email과 파일명 100% 중복) 삭제 — 보호 폴더 자체는 미변경.
- 완전히 비게 된 Topic_Agent/Topic_Blog/Topics_Biz/Topics_Rag 폴더 제거.
2026-07-05 00:33:48 +09:00

8.4 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-breaking-dependencies Breaking Dependencies 10_Wiki/Topics verified self
Decoupling
Dependency Breaking
Refactoring Legacy
none A 0.9 applied
refactoring
architecture
legacy-code
testing
2026-05-10 pending
language framework
Java / TypeScript / Python language-agnostic

Breaking Dependencies

매 한 줄

"매 untestable code 를 매 test 가능하게 만드는 첫 단계 = 매 hard dependency 를 매 break.". Michael Feathers 의 Working Effectively with Legacy Code (2004) 의 24 가지 dependency-breaking technique 가 매 canonical reference. 2026 현재 modern DI / 매 hexagonal architecture 가 매 prevention layer, but 매 legacy 에는 여전히 매 Sprout Method / Extract Interface / Adapter 가 핵심.

매 핵심

매 dependency types

  • Construction dependency: 매 new SomeService() hardcoded.
  • Static dependency: 매 Date.now(), Math.random(), Singleton.getInstance().
  • Hidden dependency: 매 method 안에서 file / DB / network 직접 호출.
  • Temporal coupling: 매 method A → B 순서 강요.
  • Inheritance dependency: 매 base class 가 매 hard-to-test 행위 가짐.

매 Feathers 의 핵심 24 technique 중 top 8

  1. Sprout Method: 매 new behavior 를 매 새 method 로 분리해 매 test 가능하게.
  2. Sprout Class: 매 new behavior 를 매 new class 로.
  3. Extract Interface: 매 concrete dep → 매 interface → mock.
  4. Extract and Override Call: 매 hard call 을 매 protected method 로 → subclass 에서 override.
  5. Subclass and Override Method: 매 testing subclass.
  6. Adapt Parameter: 매 untestable arg → 매 wrapper interface.
  7. Break Out Method Object: 매 거대 method → 매 클래스.
  8. Introduce Instance Delegator: 매 static → 매 instance method 로.

매 응용

  1. Legacy Java/C# enterprise codebase 의 unit test 도입.
  2. Module 간 cyclic dependency 끊기.
  3. 매 third-party SDK 의 hard wiring 제거.
  4. Microservice extraction 전 prep.
  5. 매 monolith 의 plugin 화.

💻 패턴

Extract Interface (Java)

// BEFORE — hard wired
public class OrderService {
    public Receipt placeOrder(Order o) {
        EmailSender sender = new EmailSender();   // hard dep
        sender.send(o.customerEmail, "...");
        return Receipt.from(o);
    }
}

// AFTER — Extract Interface + DI
public interface Notifier {
    void send(String to, String body);
}

public class EmailSender implements Notifier { /* impl */ }

public class OrderService {
    private final Notifier notifier;
    public OrderService(Notifier notifier) { this.notifier = notifier; }
    public Receipt placeOrder(Order o) {
        notifier.send(o.customerEmail, "...");
        return Receipt.from(o);
    }
}

// Test
@Test void placesOrderAndNotifies() {
    var fake = mock(Notifier.class);
    var svc = new OrderService(fake);
    svc.placeOrder(testOrder());
    verify(fake).send(eq("a@b.com"), anyString());
}

Sprout Method (TypeScript)

// BEFORE — 매 huge method, 매 add new logic 못 testable
class Invoice {
  process(): void {
    // 200 lines 의 legacy
    // 매 new logic 추가하면 매 테스트 X
  }
}

// AFTER — sprout 로 새 행동만 분리
class Invoice {
  process(): void {
    // 200 lines 의 legacy (그대로)
    this.applyLoyaltyDiscount();   // 매 sprouted
  }

  applyLoyaltyDiscount(): number {  // 매 testable in isolation
    if (this.customer.tier === 'gold') return this.total * 0.1;
    if (this.customer.tier === 'silver') return this.total * 0.05;
    return 0;
  }
}

Extract and Override Call (Python)

# BEFORE
class Job:
    def run(self):
        now = datetime.utcnow()       # 매 hard
        if now.weekday() >= 5: return
        self.do_work()

# AFTER
class Job:
    def run(self):
        if self._now().weekday() >= 5: return
        self.do_work()

    def _now(self) -> datetime:        # 매 protected — override in test
        return datetime.utcnow()

class TestJob(Job):
    def __init__(self, fixed_dt): self.fixed_dt = fixed_dt
    def _now(self): return self.fixed_dt

# Test
def test_skips_weekend():
    j = TestJob(datetime(2026, 5, 9))   # 매 Saturday
    j.do_work = MagicMock()
    j.run()
    j.do_work.assert_not_called()

Adapt Parameter (Java)

// BEFORE — HttpServletRequest hard to construct in test
public Result handle(HttpServletRequest req) {
    String userId = req.getHeader("X-User-Id");
    return repo.find(userId);
}

// AFTER — adapter interface
public interface RequestAdapter {
    String header(String name);
}

public Result handle(RequestAdapter req) {
    return repo.find(req.header("X-User-Id"));
}

// Production wraps; test = trivial map
class ServletRequestAdapter implements RequestAdapter {
    private final HttpServletRequest delegate;
    public String header(String n) { return delegate.getHeader(n); }
}

Subclass and Override (Python — break DB)

class ReportGenerator:
    def fetch(self) -> list[dict]:
        return db.query("SELECT * FROM orders")  # 매 hard

    def render(self) -> str:
        rows = self.fetch()
        return "\n".join(f"{r['id']}: {r['total']}" for r in rows)

class TestableReport(ReportGenerator):
    def __init__(self, fake_rows): self.fake_rows = fake_rows
    def fetch(self): return self.fake_rows

def test_renders():
    r = TestableReport([{"id": 1, "total": 100}])
    assert r.render() == "1: 100"

Wrap Method (legacy 호환 유지)

// 매 old API 깨면 안 됨 → 매 wrap
public void payInvoice(Invoice i) {
    audit.log(i);              // 매 new behavior
    payInvoiceLegacy(i);       // 매 unchanged
}

private void payInvoiceLegacy(Invoice i) { /* 매 untouched */ }

Seam mapping checklist

1. List 매 untestable thing in target method
   - Static call?  → Introduce Instance Delegator / Replace Function with Function Object
   - new?          → Extract Interface + inject
   - Time/random?  → Extract & Override Call
   - File/DB/HTTP? → Repository / Adapter
2. Pick 1 dep / day. 매 small step + commit.
3. 매 Test 먼저 (characterization test)
   - 매 current behavior 를 lock down
4. Refactor under green tests.

Hexagonal post-state (prevention)

[Domain Core] ← [Port (interface)] ← [Adapter (DB / HTTP / FS)]
           매 NO direct import 매 from core to adapter
           매 Test = stub adapter against port

매 결정 기준

상황 Technique
매 add new feature 매 large method Sprout Method
매 hard dep on concrete class Extract Interface + DI
매 static / time / random Extract and Override Call
매 framework type 의 arg Adapt Parameter
매 file / DB / network Repository / Port-Adapter
매 huge method 의 internal logic Break Out Method Object
매 base class behavior bad Subclass and Override

기본값: characterization test 먼저 → 매 minimal mechanical refactor (compiler-driven) → 매 small commit.

🔗 Graph

🤖 LLM 활용

언제: legacy code unit test 도입, untestable method 마주침, monolith 분해 직전 prep, third-party SDK lock-in 제거. 언제 X: 매 already clean architecture (over-refactor 금지), 매 throwaway prototype.

안티패턴

  • Big-bang rewrite: 매 일부 break → 매 전체 unstable.
  • Test 없이 refactor: 매 behavior 변경 모름 → 매 silent regression.
  • 너무 많은 mock: 매 test 가 매 implementation 에 결합.
  • Interface 의 1 implementation 영구: 매 YAGNI — 매 두 번째 user 생기면 그때 추출.
  • God adapter: 매 1 interface 에 30 method.
  • Refactor + feature 동시 commit: 매 review 불가.

🧪 검증 / 중복

  • Verified (Feathers "Working Effectively with Legacy Code" 2004, Fowler "Refactoring" 2nd ed).
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — Breaking Dependencies with Feathers techniques