CI 실패를 해결할 때 Claude Code에게 실패 출력을 인용하게 하고, 가장 작은 수정과 반복 방지를 위한 회귀 테스트를 제안하게 하는 방법.

CI 실패는 보통 신비롭지 않습니다. 로그는 어디서 멈췄는지, 어떤 명령이 실패했는지, 에러 메시지를 알려줍니다. 좋은 로그라면 스택 트레이스, 파일과 라인 번호가 있는 컴파일러 오류, 또는 어떤 어설션이 실패했는지 보여 주는 테스트 보고서가 있습니다. 때로는 "expected X, got Y"처럼 diff 스타일의 단서나 "lint", "build", "migrate database" 같은 명백한 실패 스텝이 나오기도 합니다.
문제는 사람들이(그리고 AI도) 로그를 배경 소음처럼 취급한다는 점입니다. 긴 로그를 붙여넣고 "수정해줘"라고 하면 많은 모델이 마지막 의미 있는 라인을 읽기보다 익숙한 설명으로 바로 점프합니다. 오류가 흔해 보일수록(예: "module not found", "timeout", "permission denied") 추측은 더 심해집니다. 그 결과 실제 실패와 맞지 않는 대대적인 리팩터, 새 의존성 추가, 또는 "모두 업데이트해봐" 같은 답변이 나오곤 합니다.
목표는 "어떻게든 통과시키기"가 아닙니다. 더 단순합니다:
실무에서는 "최소 수정"이 보통 다음 중 하나입니다: 한 곳의 몇 줄짜리 코드 변경, 빠진 import나 잘못된 경로, CI 환경에서 명백히 잘못된 설정값, 또는 재설계 대신 우발적으로 도입된 깨진 변경을 되돌리는 것.
후속 테스트도 중요합니다. CI가 한 번 통과했다고 반복을 막는 것은 아닙니다. 실패가 엣지케이스(null 입력, 타임존, 반올림, 권한)에서 왔다면, 수정 전에는 실패하고 수정 후에는 통과하는 회귀 테스트를 추가하세요. 그렇게 하면 일회성 구조 복구가 아니라 방어선이 됩니다.
대부분의 나쁜 수정은 맥락 부족에서 시작합니다. 마지막 빨간 줄만 붙여넣으면 모델은 앞에서 무슨 일이 있었는지 추측해야 하고, 그 추측이 리팩터로 이어지기 쉽습니다.
실패를 처음 실제 오류부터 끝까지 따라갈 수 있을 만큼 충분한 세부사항을 제공하세요. 가능한 한 그대로 붙여넣는 항목:
go test ./..., npm test, flutter test, golangci-lint run).제약을 평문으로 추가하세요. 아주 작은 수정만 원하면 분명히 말하세요: 리팩터 금지, 동작 변경 금지(필요하지 않은 한), 패치는 실패한 영역으로 제한하라.
간단한 예: 의존성 업그레이드 후 lint 단계에서 CI가 실패한다면, 첫 경고부터 시작하는 lint 출력을 붙이고 CI가 사용한 명령과 단일 패키지 버전 변경만 적어 두세요. 그러면 한 줄의 설정 수정이나 작은 코드 변경을 제안하는 수준이면 충분합니다.
복사·붙여넣기 가능한 구조는 보통 충분합니다:
CI command:
Failing output (full):
Recent changes:
Constraints (smallest fix, no refactor):
Flaky? (runs attached):
모델이 CI 실패에서 빗나갈 때, 보통 프롬프트가 추측을 허용하기 때문입니다. 모델이 정확한 실패 출력을 사용해 작업 과정을 보여주고, 실패를 통과시키는 가장 작은 변경에 전념하게 만드세요.
증거와 작은 계획을 요구하세요. 좋은 프롬프트는 다섯 가지를 강제합니다:
불확실성은 괜찮지만, 숨겨진 불확실성은 시간을 낭비합니다.
질문 맨 위에 다음을 붙여넣으세요:
Use ONLY the evidence in the CI output below.
1) Quote the exact failing lines you are using.
2) Give ONE sentence: the most likely cause.
3) Propose the smallest fix: 1-3 edits, with file paths.
4) Do NOT do formatting/renames/refactors or "cleanup".
5) List uncertainties + the one extra detail that would confirm the diagnosis.
로그에 "expected 200, got 500"와 user_service.go:142로 들어가는 스택 트레이스가 있다면, 이 구조는 응답을 해당 함수와 작은 가드나 오류 처리 변경 쪽으로 밀어냅니다. 재설계가 아니라 작은 수정에 집중시키는 효과가 있습니다.
가장 빠른 승리는 로그 인용을 강제하고 제약을 지키며 정보가 부족할 때 멈추는 프롬프트에서 옵니다.
You are helping me fix a CI failure.
Repo context (short):
- Language/framework:
- Test/build command that failed: <PASTE THE EXACT COMMAND>
- CI environment (OS, Node/Go/Python versions, etc.):
Failing output (verbatim, include the first error and 20 lines above it):
<PASTE LOG>
Constraints:
- Propose the smallest possible code change that makes CI pass.
- Do NOT rewrite/refactor unrelated code.
- Do NOT touch files you do not need for the fix.
- If behavior changes, make it explicit and justify why it is correct.
Stop rule (no guessing):
- If the log is incomplete or you need more info (missing stack trace, config, versions, failing test name), STOP and ask only the minimum questions needed.
Your response format (follow exactly):
1) Evidence: Quote the exact log lines that matter.
2) Hypothesis: Explain the most likely cause in 2-4 sentences.
3) Smallest fix: Describe the minimal change and why it addresses the evidence.
4) Patch: Provide a unified diff.
5) Follow-up: Tell me the exact command(s) to rerun locally to confirm.
Then, write ONE regression test (or tweak an existing one) that would fail before this fix and pass after it, to prevent the same failure class.
- Keep the test focused. No broad test suites.
- If a test is not feasible, explain why and propose the next-best guardrail (lint rule, type check, assertion).
두 가지 세부사항이 불필요한 왕복을 줄입니다:
시간을 낭비하는 가장 빠른 방법은 한 번에 다섯 군데를 바꾸는 "정리" 패치를 받아들이는 것입니다. "최소"의 정의를 미리 하세요: 실패 스텝을 통과하게 하는 가장 작은 diff, 위험이 낮고 검증이 빠른 것.
간단한 규칙이 잘 작동합니다: 증상 먼저 고치고, 그다음에 더 넓은 리팩터가 필요한지 결정하세요. 로그가 한 파일, 한 함수, 빠진 import, 또는 하나의 엣지케이스를 가리키면 그곳을 목표로 하세요. "여기 있는 김에" 식의 변경은 피하세요.
정말로 대안을 원하면 두 개만 요구하세요: "가장 안전한 최소 수정" 대 "가장 빠른 최소 수정". 트레이드오프만 원하지 메뉴를 원하지 않습니다.
또한 로컬 검증이 CI와 동일하도록 요구하세요. 파이프라인이 실행한 동일한 명령(또는 가장 비슷한 명령)을 요구하면 몇 분 안에 확인할 수 있습니다:
# run the same unit test target CI runs
make test
# or the exact script used in CI
npm test
응답이 대규모 변경을 제안하면 다음과 같이 되물으세요: "관련 없는 포맷팅/이름 변경 없이 실패한 어설션을 고치는 가장 작은 패치를 보여달라."
먼저 실제 오류(최종 exit 1이 아닌)를 보세요.
로그를 읽었다는 증명을 요구하세요.
다음 같은 제약을 사용하세요:
실패 스텝을 성공하게 하는 가장 작은 패치를 기본으로 하세요.
보통 다음 중 하나입니다:
CI가 다시 통과할 때까지 정리(cleanup) 변경은 피하세요.
실패를 재현할 수 있을 만큼의 문맥을 붙이세요. 마지막 빨간 줄 하나만 붙이면 부족합니다.
포함할 것:
go test ./..., npm test, flutter test 등).네—제약을 평문으로 적고 반복하세요.
예시 제약:
이렇게 하면 응답이 집중되고 검토하기 쉬워집니다.
가장 이른 실제 실패를 먼저 고치세요.
모르겠다면 모델에게 로그에서 첫 실패 스텝을 식별하라고 시키고 그 부분만 고치게 하세요.
플래키함은 재시도(retry)를 추가할 신호가 아닙니다. 무작위를 제거하세요.
일반적인 안정화 방법:
결정성이 확보되면 ‘가장 작은 수정’이 명확해집니다.
CI가 실행한 정확한 명령을 물어보고, 그 명령을 로컬에서 그대로 실행하세요.
로컬 재현이 어렵다면 동일한 오류를 일으키는 최소한의 재현(target/test)을 리포에서 요청하세요.
수정 전에는 실패하고 수정 후에는 통과하는, 단 하나의 집중된 회귀 테스트를 작성하세요.
좋은 대상:
린트/빌드 실패라면 동일한 효과를 주는 규칙 강화나 체크 추가가 대안일 수 있습니다.
스냅샷/롤백을 이용해 실험을 되돌릴 수 있게 하세요.
실용적인 루프:
Koder.ai처럼 스냅샷을 제공하는 환경이면 실험 편집이 최종 패치에 섞이지 않도록 도와줍니다.