Community코딩 & 개발github.com

armandkaufmann/refactor-pass

git 저장소에서 리팩터링이 필요한 부분과 필요하지 않은 부분을 측정합니다. 히트맵, 코드 스멜, 워크스루를 결합하여 체크리스트로 정리된 리팩터링 우선순위를 제공합니다.

refactor-pass란 무엇인가요?

이 스킬은 개발자가 직접 코딩할 때 직관적으로 느끼던 '리팩터링해야 하는 부분'과 '그냥 둬도 되는 부분'을 에이전트가 측정할 수 있도록 합니다. git 히스토리 히트맵(자주 변경되는 파일 파악), 코드 스멜 탐지(각 스멜별 리팩터링 레시피 제공), 차기 변경 비용 예측(워크스루) 세 가지 지표를 결합하여, 리팩터링 항목을 체크리스트에 우선순위별로 정리합니다. 이 체크리스트는 한 번에 한 항목씩 처리할 수 있도록 설계되어 있습니다. Claude Code, Cursor, Codex 등 AI 코딩 에이전트에서 사용할 수 있으며, 사용자가 리팩터링, 기술 부채, 코드 건강도, 핫스팟, 클린 코드 등과 관련된 질문을 할 때 자동으로 실행됩니다. 이 스킬은 단순히 모든 문제를 찾아내는 것이 아니라, 자주 변경되는 파일의 스멜만 리팩터링하고, 거의 변경되지 않는 파일은 그대로 두는 '절제된 판단'을 제공하여 불필요한 리팩터링으로 인한 시간 낭비를 방지합니다. 작업 과정은 히트맵 분석(1단계), 스멜 측정(2단계), 워크스루(3단계), 검증(4단계), 순위 결정(5단계), 체크리스트 및 보고서 작성(6단계), 사용자와 협업하여 리팩터링 실행(7단계)의 7단계로 구성됩니다.

지원 대상✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/armandkaufmann/refactor-pass/tree/HEAD

Installed? Explore more 코딩 & 개발 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Refactor pass (리팩터링 패스)

리팩터링은 한때 직관에서 시작되었습니다. 코드 작업이 어려웠습니다: 새로운 기능을 어디에 넣을지 찾기 위해 스크롤해야 했던 클래스, 하나의 분기를 변경하기 위해 머릿속에 담아두어야 했던 함수, 여덟 개의 파일을 방문해야 했던 기능. 같은 직관은 무엇을 그냥 둘지도 알려주었습니다. 건드리지 않은 지저분한 코드는 결코 피해를 주지 않았으므로, 그냥 두었습니다.

코드를 작성하는 에이전트는 그런 느낌을 전혀 느끼지 못하며, 에이전트가 작성한 코드를 주로 읽는 개발자도 마찬가지입니다. 따라서 스멜은 감지되지 않고 쌓여갑니다. 에이전트는 불평 없이 함수에 서른 번째 분기를 추가할 것입니다. 그리고 리팩터링을 요청받으면, 아무도 건드리지 않는 코드를 포함하여 지저분해 보이는 모든 것을 정리합니다. 이 스킬은 직관의 두 가지 측면을 측정 가능한 세 가지 항목으로 되돌려줍니다:

  1. 어디에서 느낄지: 히트맵. 마찰은 변경이 계속 발생하는 곳에서만 중요합니다. 여러 연구에 걸쳐, 파일 크기와 파일이 얼마나 자주 변경되는지는 어떤 단일 스멜보다 유지보수 문제를 잘 예측합니다.
  2. 무엇을 느낄지: 스멜. 확립된 스멜 카탈로그로, 각 스멜은 무엇을 셀지, 언제 스멜이 아닌지, 그리고 수십 년간의 실무 경험으로 구축된 명명된 리팩터링 플레이북으로 변환됩니다: references/smells.md.
  3. 얼마나 느낄지: 워크스루. 예정되었거나 예상되는 다음 변경 사항을 가져와서 그것이 닿게 될 모든 것을 나열합니다.

그런 다음 '어디'와 '무엇'을 결합하여 판정을 내립니다:

스멜 있음스멜 없음
자주 변경됨리팩터링괜찮다고 말하기
거의 변경 안 됨그냥 둠; 최대 "나중에" 메모아무것도 안 함

"괜찮다고 말하기" 상자는 "리팩터링" 상자만큼 중요합니다. 그것은 직관의 절제된 부분이며, 보고서에 표시됩니다.

두 가지 크기의 질문. 패스는 전체 저장소를 다룹니다: 1단계부터 7단계까지. 특정 영역에 대한 질문("X를 리팩터링해야 할까요?")은 해당 영역에 대해서만 동일한 단계를 실행합니다. 항목이 여러 개가 아닌 이상 체크리스트를 건너뛰며, 답변은 "아니오"일 수 있습니다. 두 경우 모두, 먼저 1단계의 스크립트를 --checklists와 함께 실행하십시오: 이전 패스의 체크리스트가 나열되면 이전 패스 이후를 먼저 읽으십시오.

1. 어디: 히트맵

python3 <이 스킬 디렉토리>/scripts/heatmap.py -C <저장소> <범위 ...>

<이 스킬 디렉토리>는 이 SKILL.md 파일이 있는 폴더입니다. python3 명령어가 없는 경우(Windows에서 자주 그럼), python 또는 py를 사용하십시오.

  • 범위: 소스 폴더와 테스트 폴더입니다. 예: src tests 또는 app. 문서나 에셋 폴더는 제외합니다. 프로젝트의 README, 디렉토리 목록 및 에이전트 지침(AGENTS.md, CLAUDE.md)을 읽고 결정하십시오. 이름(Board.spec.ts, test_x.py, XTest.php...) 또는 폴더로 식별되는 테스트 파일은 테이블과 페어에서 제외되며, 여러 파일과 함께 변경될 때 별도로 나열됩니다. 다른 테스트 이름에는 --test GLOB를, 테스트처럼 보이는 파일에는 --not-test GLOB를 (히트맵은 의심스러운 파일을 알려줍니다), 그리고 생성된 파일에는 --ignore GLOB를 사용하십시오.
  • 윈도우: 기본값은 최근 200개 커밋입니다. 사용자가 시작점을 언급한 경우("v2.0 이후", "청구 기능 추가 이후", "지난 3개월") --since로 대체합니다: 태그, 브랜치, 커밋 또는 날짜. 봇 커밋, 저장소가 .git-blame-ignore-revs에 나열하는 대량 리포맷, 그리고 이전 체크리스트에 기록된 커밋은 자동으로 제외됩니다. --exclude-commits는 벤더 임포트처럼 건드린 모든 것을 핫하게 보이게 만드는 다른 커밋을 제외합니다.
  • 데이터가 너무 적은가? 윈도우에 약 30개 미만의 커밋이 있으면, 카운트가 핫 파일과 콜드 파일을 구분할 수 없습니다. 윈도우를 넓히거나, 히스토리가 너무 짧아 어느 곳도 가리킬 수 없다고 말하고 스멜만으로 판단하십시오. 얕은 클론인 경우(히트맵이 알려줌) git fetch --unshallow가 필요합니다; 사용자에게 물어보십시오.

--help는 모든 열과 옵션을 설명합니다.

읽는 방법

  • 핫스팟은 커밋 수가 많고 크기도 크기 때문에, 목록이 먼저 배치됩니다: 커밋 수 곱하기 크기 순으로 정렬됩니다. 작은 파일의 많은 커밋은 종종 괜찮습니다(예: 각 기능에 대해 편집되는 설정 파일). 아무도 건드리지 않는 큰 파일은 그냥 두는 것이 저렴합니다.
  • 여전히 핫한가? recent는 윈도우의 최신 절반(시간의 절반이 아닌 커밋의 절반)에서의 커밋 수를 계산하고, last는 가장 최근 커밋의 날짜입니다. 둘 다 윈도우 날짜와 비교하여 읽으십시오: 8개의 recent 커밋이 모두 최신 절반이 시작되는 날에 있다면, 식어버린 버스트입니다. 줄어든 파일은 이미 처리되었을 수 있습니다.
  • rel 은 파일 크기에 대한 변경된 라인 수입니다. 상대적 변동(churn)은 절대적 변동보다 결함을 훨씬 잘 예측합니다. 1보다 훨씬 높으면 윈도우 내에서 파일이 다시 작성되었거나 커졌음을 의미합니다. growth와 함께 읽으십시오: 커밋할 때마다 계속 커지는 파일은 Additive-Only Growth입니다. 윈도우 내에서 생성된 파일은 growth가 크기와 같습니다.
  • 페어는 동일한 커밋에서 계속 함께 변경되는 파일입니다. 그룹은 모두 서로 페어를 이루는 세 개 이상의 파일로, 모두 변경된 커밋으로 계산됩니다; 겹치는 그룹은 하나의 영역으로 함께 나열됩니다.
    • 다른 계층의 파일이 강하게 페어를 이루거나, 계층 간 그룹은 일반적으로 하나의 기능이 계층에 걸쳐 퍼져 있음을 의미합니다(샷건 수술).
    • 각 페어를 연결하는 것이 무엇인지 확인하십시오. 임포트, 호출, 또는 무언가가 이름을 지정하는 계약(라우트, 스키마, 컨트롤러와 페이지 사이의 타입 파일)은 가시적 의존성입니다: 경계를 넘는 하나의 기능입니다. 그러한 링크 없이 함께 변경되는 파일들(예: 규칙의 두 복사본 또는 수동으로 동기화된 두 목록)은 숨겨진 의존성을 가지며, 이것이 가장 강력한 결합 형태입니다.
    • 많은 다른 파일과 페어를 이루는 파일은 모든 변경이 통과하는 허브입니다.
    • 자신의 파일 외에도 여러 파일과 함께 변경되는 것으로 나열된 테스트는 해당 파일들의 내부 구조에 묶여 있어 리팩터링을 차단합니다. 단, 해당 파일들이 테스트가 따르는 그룹인 경우는 제외합니다.
    • 스쿼시 병합하는 저장소에서처럼 많은 커밋이 페어링하기에는 너무 큰 경우(--max-files), 값을 높이거나 페어를 부분적으로 읽으십시오.
  • 변경되는 이유. 상위 몇 개 파일의 커밋 메시지(--show)를 읽고 기능별로 그룹화하십시오. 하나의 파일을 변경하는 무수히 많은 관련 없는 이유는 발산 변경(Divergent Change)이며, 이는 여기서 어떤 카운트보다 더 직접적으로 나타납니다.

2. 무엇: 스멜

  • 핫 파일을 측정하여 코드베이스 자체와, 한 언어 내에서 비교합니다. python3 <이 스킬 디렉토리>/scripts/measure.py -C <저장소> <범위 ...> 는 각 파일을 해당 언어의 70번째, 80번째 및 90번째 백분위수에 따라 대역에 배치하고, --show <파일> 은 각 함수를 보여줍니다. Big은 80번째 또는 90번째 대역입니다(대역은 카탈로그의 하한선도 적용합니다); 70번째는 자세히 볼 가치가 있습니다. 백분위수가 하나의 파일에 치우쳐 있다고 표시되면 측정의 참조점으로도 판단하십시오. 측정 방법은 나머지(분기, 매개변수, 응집도)를 계산하는 방법이나 프로젝트의 린터를 사용하는 방법도 알려줍니다.
  • 핵심 파일 상위 2~3개를 직접 읽으십시오. 숫자는 가리킬 뿐입니다; 읽는 것이 결정합니다.
  • 다른 핫 영역을 검토하십시오. 하위 에이전트를 실행할 수 있다면, 2~3개에게 각각 하나의 영역(계층 또는 디렉토리, 각 에이전트가 전체 영역을 한 번에 읽도록)을 주고 병렬로 실행하십시오. references/review-brief.md의 브리핑을 각자에게 제공하십시오. 하위 에이전트가 없으면, 동일한 브리핑을 기준으로 영역을 하나씩 직접 검토하십시오.
  • 각 스멜을 확립된 이름으로 지정하고(긴 함수, 샷건 수술, 반복되는 스위치...), 카운트에 포함시키기 전에 해당 항목의 "스멜이 아닌 경우" 조건과 대조 확인하십시오. 이름이 플레이북의 색인입니다.
  • 누락된 아이디어를 찾으십시오. 스멜은 무리 지어 다니며, 무리는 대개 코드가 항상 사용하지만 이름을 붙이지 않은 한 가지를 가리킵니다: 상태, 종류의 정의, 설정의 단일 출처. 스멜은 함께 이동한다를 참조하십시오.
  • 테스트가 핫 코드에 도달하는지 확인하십시오: 테스트되지 않은 코드를 참조하십시오.
  • 발견한 버그는 지금 수정하지 않고 F 항목으로 목록에 넣으십시오. 아직 재현되지 않은 버그는 의심입니다: 제목을 의심으로 하고("확인: 편집이 주말 규칙을 건너뜀"), 메모에 증거를 첨부하십시오. 첫 번째 단계는 실패하는 테스트입니다; 실패하는 테스트가 없으면, 찾은 내용과 함께 건너뜁니다.

3. 얼마나: 워크스루

이것은 옛날 느낌에 가장 가까운 것입니다. 또한 항목을 설득력 있게 만드는 요소입니다.

  1. 다음 변경 사항을 찾으십시오. 계획된 변경이 추론된 변경보다 낫습니다. 따라서 사용자가 앞으로 올 것이라고 말한 내용("7번 청크를 시작하기 전에")에서 먼저 가져오고, 그다음 프로젝트의 계획이나 로드맵(있는 경우)에서, 그다음 커밋 메시지에서 가져오십시오: 가장 자주 발생하는 변경 유형("새 보고서 유형 추가", "설정 추가", "화면 추가"). 한 가지 유형만이 아니라 가장 흔한 두세 가지 유형을 가져오십시오.
  2. 각 유형에 대해, 해당 유형의 가장 최근 실제 커밋을 가져와서 건드린 항목을 나열하십시오(git show --stat). 윈도우보다 오래된 경우에도 동일하게 사용하고 날짜를 제공하십시오; 오늘날 코드에 대한 워크스루는 그 이후로 무엇이 이동했는지 보여줍니다. 프로젝트 문서나 에이전트 지침에 해당 변경에 대한 레시피가 있으면 단계를 세십시오. 한 가지를 추가하기 위한 5단계 레시피는 인정하는 것입니다.
  3. 다음 변경 사항을 실제로 작성하지 않고 하나씩 살펴보십시오. 읽어야 할 모든 파일, 편집해야 할 모든 위치, 동기화해야 할 모든 복사본을 나열하십시오.
  4. 오늘의 카운트와 제안할 리팩터링 후의 카운트를 기록하십시오: "새 보고서 유형은 오늘 7개의 파일과 3개의 스위치를 건드리며, 리팩터링 후에는 1개의 파일만 건드릴 것입니다."

계획된 작업이 도달할 콜드 파일은 더 이상 콜드가 아닙니다: 다음 변경이 그 비용을 지불하므로 테이블의 상단 행에 넣으십시오. 다음 변경이 오늘 저렴하다면, 스멜이 어떻게 보이든 비용이 거의 들지 않는 것입니다.

4. 모든 발견 사항 확인

리뷰어의 발견은 사실이 아니라 단서이며, 자신의 첫인상도 마찬가지입니다. 어떤 것이 목록에 오르기 전에, 인용된 라인을 열고, 카운트를 다시 실행하고, 맞지 않는 것을 삭제하거나 수정하십시오. 보고서에서 무엇을 확인했는지 말하십시오. 잘못된 항목이 사용자를 리팩터링으로 이끌면, 확인하는 데 걸리는 1분보다 훨씬 더 많은 비용이 듭니다.

그런 다음 절제를 적용하십시오. 문제를 찾도록 요청받은 리뷰어는 항상 무언가를 찾을 것이며, 모든 발견 사항을 수정하면 발생할 수 없는 경우에 대한 추가 계층과 테스트가 남게 됩니다. 콜드한 것, "스멜이 아닌 경우" 조건이 면제하는 것, 워크스루가 저렴하다고 말하는 것은 제외하십시오.

5. 순위 결정

각 항목에 안정적인 ID를 부여하십시오. 사용자는 이 ID로 항목을 지칭할 것이기 때문입니다("R4부터 시작"). 대괄호 안의 이름은 체크리스트(6단계)에 있는 항목의 필드입니다; F, L 및 D 항목은 note에 필드를 표시합니다.

  • R1, R2… 리팩터링, 제안된 순서대로. 각각에 대해:
    • 누락된 아이디어를 결과로 명명하는 제목("CheckoutController 리팩터링"이 아닌 "체크아웃 상태 이름 지정") (title);
    • 숫자와 file:line을 포함한 스멜 이름 (smells);
    • 3단계에서 완화되는 변경 유형 (eases), 및 해당 변경의 비용 (cost: today 및 after);
    • 플레이북의 단계별 계획 (plan);
    • 파일, 크기(S, M 또는 L) 및 위험도(낮음, 중간 또는 높음) (files, size, risk);
    • 동작이 변경되지 않았는지 확인하는 방법 (verify).
  • F1… 작업 중 발견된 작은 수정 사항. 각각 자체 커밋.
  • L1… 나중에: 스멜이 있지만 콜드하거나, 근처 코드가 다음에 건드려질 때 수행할 가치가 있음.
  • D1… 사용자의 결정: 작업 방식의 변경(예: 모든 변경을 비싸게 만드는 문서 규칙). 결정이 아닌 트레이드오프를 제시하십시오.

위험 대비 이득으로 순위를 매기십시오. 이득은 변경이 미래 작업을 얼마나 완화하는지입니다: 히트맵과 워크스루가 증거입니다. 스멜의 우선순위는 어떤 종류가 중요한 경향이 있는지 말해줍니다. 길고 복잡한 코드, 그리고 파일에 걸쳐 퍼진 변경이 가장 중요합니다. Feature Envy 및 Middle Man과 같은 결합 스멜은 가장 덜 중요하며, 지나가면서 수정됩니다.

목록을 다음과 같이 정렬하십시오:

  • 차단 항목이 먼저 옵니다: 이후의 모든 단계가 무언가를 망가뜨린 것처럼 보이게 만드는 내부 구조에 묶인 테스트, 그리고 테스트가 적용되지 않는 코드(항목은 테스트 고정으로 시작하거나 확인할 수 없다고 명확히 말함, 7단계);
  • 복사-붙여넣기 경로를 제거하는 작은 항목이 다음으로 옵니다;
  • 대규모 이동은 그 후에 옵니다. 종종 먼저 이음매(seam)가 필요하기 때문입니다;
  • 직접 확인할 수 없는 항목(소리, 느낌 또는 모양)은 사용자가 확인해야 하므로 마지막에 옵니다.

6. 체크리스트 및 보고서 작성

오늘 날짜로 --page .refactor-pass/<YYYY-MM-DD>.html 옵션과 함께 1단계 명령을 다시 실행하십시오. assets/page.html에서 체크리스트를 만들고 히트맵을 heatmap 블록에 씁니다; 다시 실행하면 히트맵만 새로 고쳐집니다. 그 아래에 pass 블록을 작성하며, project, started, base 및 updated는 이미 채워져 있습니다:

  • summary: 전체 상태를 두세 문장으로;
  • items: 모든 항목, 5단계의 필드, status (todo, doing, waiting(사용자 대기 중), done, 또는 skipped(이유를 outcome에 포함)), 그리고 새 항목의 경우 빈 commits 및 outcome; 이월된 항목은 from을 유지합니다. 각 항목은 ID와 상태를 한 줄로 시작합니다, {"id": "R1", "status": "todo",, 그 뒤에 다른 필드가 오며, 블록 위의 주석에 전체 형식이 표시됩니다; JSON 예쁘게 출력하는 방식이 아닙니다. 7단계의 상태 업데이트가 해당 한 줄을 편집하기 때문입니다.
  • left_alone: 각각 what 및 why와 함께;
  • checks: 프로젝트의 지침에서 가져온 프로젝트의 검사 항목, 또는 검사 항목이 없으면 수동으로 확인할 항목;
  • earlier: 이전 패스 후, 해당 체크리스트와 결과, 완료된 각 리팩터링에 대해: id, title, eases 및 cost, 실제로 다음에 온 change와 실제로 건드린 actual 항목(아직 온 것이 없으면 둘 다 비어 있음), 그리고 note. 그렇지 않으면 null;
  • log: 각각 date와 text를 가지며, 최신순.

각 편집 후, python3 <이 스킬 디렉토리>/scripts/check_page.py <페이지>를 실행하십시오. 이 스크립트는 깨진 대괄호, 잘못 입력된 필드, 중복 ID 또는 한 줄로 시작하지 않는 항목을 잡아냅니다. 환경이 HTML 페이지를 게시하거나 미리 볼 수 있다면, ... (원문이 잘렸으나, 체크 도구를 실행하고 HTML 페이지를 게시/미리보기하는 내용으로 추정됨)

7. 협업하며 진행하기

첫 번째 항목을 시작할 때 "status": "doing"으로 변경하고 체크리스트에 메모를 추가하십시오. 항목을 완료하면 상태를 "done"으로 변경하고 commits에 커밋 해시를 추가한 후 outcome에 한 문장을 추가하십시오. 항목을 건너뛰기로 결정하면 "skipped"로 변경하고 outcome에 이유를 추가하십시오. 항목에 대한 사용자의 결정이 필요한 경우 "waiting"으로 설정하고 note에 설명을 추가하십시오. 진행 상황을 설명하기 위해 체크리스트를 업데이트할 때마다 log에 항목을 추가하십시오.

관련 스킬