null
vuild_
Nodes
Flows
Hubs
Login
MENU
GO
Notifications
Login
⌂
"AI 코딩 에이전트 길들이기 — Karpathy의 4원칙으로 Claude & Cursor 장악하기"
Structure
why-ai-coding-fails
•
"AI 코딩 도구는 왜 자꾸 이상한 짓을 하는가"
think-before-coding
•
"Think Before Coding — 섣부른 가정을 막는 법"
simplicity-first
•
"Simplicity First — 과설계와 코드 비대화를 막는 법"
surgical-changes
•
"Surgical Changes — AI가 건드리지 말아야 할 코드를 건드리는 문제"
goal-driven-execution
•
"Goal-Driven Execution — AI를 검증 루프에 가두는 법"
claude-md-in-practice
•
"CLAUDE.md 실전 적용 — Claude Code & Cursor에 Karpathy 원칙 심기"
build-your-own-guidelines
•
"나만의 AI 코딩 가이드라인 만들기 — 원칙 확장과 팀 적용"
Flow Structure
"Simplicity First — 과설계와 코드 비대화를 막는 법"
4 / 7
"Goal-Driven Execution — AI를 검증 루프에 가두는 법"
☆ Star
↗ Full
"Surgical Changes — AI가 건드리지 말아야 할 코드를 건드리는 문제"
@devpc
|
2026-04-27 06:24:25
|
GET /api/v1/flows/15/nodes/271?fv=1&nv=2
Context:
Flow v1
→
Node v2
0
Views
1
Calls
# Surgical Changes — AI가 건드리지 말아야 할 코드를 건드리는 문제 코드 리뷰를 하다 보면 가끔 이런 PR을 만난다. 제목은 "버튼 색상 변경"인데 diff에는 100줄이 넘는 변경이 있다. 변수명 수정, 주석 추가, 들여쓰기 정리, "개선된" 로직. AI 코딩 도구가 만드는 PR이 정확히 이렇다. 요청한 부분을 고치면서 근처에 있는 코드도 "개선"한다. 좋은 의도지만, 실제로는 여러 문제를 만든다. --- ## 1. Orthogonal Edit 문제 **Orthogonal(직교하는)** 변경이란 요청된 작업과 논리적으로 무관한 코드 수정을 말한다. ```diff // 요청: "fetchUser 함수에 캐싱 추가해줘" async function fetchUser(id: string): Promise<User> { + const cached = cache.get(id); + if (cached) return cached; const user = await db.users.findById(id); + cache.set(id, user); return user; } - // 기존 권한 체크 (왜 삭제됐는지 설명 없음) - async function checkUserPermission(userId: string, resource: string) { - const user = await fetchUser(userId); - return user.permissions.includes(resource); - } + // AI가 "개선"한 버전 + async function checkUserPermission(userId: string, resource: string) { + const user = await this.userService.getUser(userId); + return this.permissionService.check(user, resource); + } ``` 캐싱 추가를 요청했는데 권한 검사 함수까지 바뀌었다. 바꾼 이유도 없다. 이 변경이 기존 동작을 깨뜨렸을 수도 있다. --- ## 2. Surgical Changes 원칙 ```markdown ## Surgical Changes Touch only what you must. Clean up only your own mess. When editing existing code: - Don't "improve" adjacent code, comments, or formatting - Don't refactor things that aren't broken - Match existing style, even if you'd do it differently - If you notice unrelated dead code, mention it — don't delete it When your changes create orphans: - Remove imports/variables/functions that YOUR changes made unused - Don't remove pre-existing dead code unless asked The test: Every changed line should trace directly to the user's request. ``` 두 가지 원칙이 균형을 잡는다. 1. **요청과 무관한 코드 건드리지 않기** — 리팩터링, 스타일, 주석 수정 금지 2. **내가 만든 고아 코드는 정리하기** — 내 변경으로 생긴 unused import, 변수 제거 --- ## 3. The Traceability Test 원칙의 핵심 테스트다: > "Every changed line should trace directly to the user's request." 이 기준으로 diff를 보면 즉시 파악된다. ```diff // ✅ 추적 가능한 변경 (캐싱 추가 요청) + const cached = cache.get(id); + if (cached) return cached; const user = await db.users.findById(id); + cache.set(id, user, 3600); // ❌ 추적 불가능한 변경 (요청과 무관) - const userId = user.getId(); // 기존 스타일 + const userId = user.id; // AI가 "더 나은" 방식으로 바꿈 ``` --- ## 4. "언급하되 삭제하지 않기" 패턴 원칙의 섬세한 부분이다: > "If you notice unrelated dead code, mention it — don't delete it" AI가 dead code를 발견했을 때의 올바른 동작: ``` // ❌ 조용히 삭제 (아무 말 없이 관련 없는 unused 함수 제거) // ❌ 조용히 리팩터링 (요청 범위 밖 코드를 정리하며 커밋에 포함) // ✅ 언급하고 판단 맡기기 "참고로: formatLegacyDate() 함수가 현재 사용되지 않는 것 같습니다. 요청하신 캐싱 기능과 별개로, 나중에 정리가 필요할 수 있습니다. 지금 이 작업과 함께 삭제할까요?" ``` 결정 권한이 사람에게 있다. AI는 발견하고 알린다. --- ## 5. 기존 스타일 매칭 또 하나 중요한 규칙: "Match existing style, even if you'd do it differently." ```typescript // 기존 코드 스타일 (함수형, arrow function) const getFullName = (user: User) => `${user.firstName} ${user.lastName}`; const getEmail = (user: User) => user.email; // ❌ AI가 "더 나은" 스타일로 추가한 코드 (클래스형으로 전환) class UserFormatter { static formatPhone(user: User): string { return user.phone.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3'); } } // ✅ 기존 스타일에 맞춘 코드 const getPhone = (user: User) => user.phone.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3'); ``` 코드베이스의 일관성이 개별 파일의 "더 나은 패턴"보다 중요하다. --- ## 6. 내 변경이 만든 고아는 내가 치운다 Surgical Changes의 균형 잡힌 반대편이다. 내 수정으로 인해 생긴 dead code는 내가 정리해야 한다. ```typescript // 기존 코드 import { legacyFormat } from './utils/legacy'; function processUser(user: User) { return legacyFormat(user); // AI가 이 줄을 새 함수 호출로 교체함 } // AI가 수정한 코드 // ❌ import 남겨둠 (내 변경이 만든 고아인데 정리 안 함) import { legacyFormat } from './utils/legacy'; // 이제 unused function processUser(user: User) { return newFormat(user); } // ✅ 내가 만든 고아는 내가 치움 // import 제거 function processUser(user: User) { return newFormat(user); } ``` > 💡 **핵심**: AI의 "개선 본능"을 제어해야 한다. Surgical Changes는 모델에게 스케일펠을 쥐어주는 것이 아니라, 지정된 부위만 정확히 건드리도록 제한한다. diff의 모든 줄은 요청으로 추적될 수 있어야 한다. > 💡 **다음 챕터**: Goal-Driven Execution — "작동하는 것 같아요"를 검증 가능한 성공 기준으로 바꾸는 법.
"Simplicity First — 과설계와 코드 비대화를 막는 법"
"Goal-Driven Execution — AI를 검증 루프에 가두는 법"
// COMMENTS
Newest First
ON THIS PAGE
No content selected.