검색/큰 저장소에서 AI가 코드를 먼저 읽고 고치게 하고, 리뷰를 거쳐서만 배포하게 한다

큰 저장소에서 AI가 코드를 먼저 읽고 고치게 하고, 리뷰를 거쳐서만 배포하게 한다

패키지가 많은 큰 저장소에서 AI 에이전트에게 일을 맡길 때 쓴다. 수정을 판단하기 전에 코드·의존성·테스트를 읽게 하고, 같은 일은 한 가지 방법으로만 하게 하며, 신뢰할 수 없는 코드는 로컬에서 실행하지 않고, 공유 코드는 리뷰와 사용자 승인을 거쳐서만 바꾸게 한다.

이 AI 규칙을 사용하려면
## 전체 규칙을 먼저 읽고, 그다음 작업할 폴더의 가이드를 읽는다 루트 문서는 강제 정책과 라우팅만 다루고, 각 영역의 세부 워크플로우는 범위별 가이드가 담당하게 한다. 하위 트리 안에서 작업하기 전에는 그 영역을 담당하는 가이드를 먼저 읽는다. ### 규칙 종류마다 하나의 위치만 둔다 강제 정책, 영역을 가로지르는 경계, 그리고 어떤 가이드가 무엇을 담당하는지는 루트에 둔다. 작업별 세부 절차는 해당 코드 옆의 범위별 가이드에 둔다. 같은 워크플로우를 두 곳에 중복 기술하지 않는다 — 사본끼리 어긋날 수 있기 때문이다. ### 저장소 방식대로 코드를 참조한다 파일은 절대 경로나 홈 디렉터리 경로가 아니라 저장소 기준 상대 경로로 인용한다. 그래야 모든 독자에게 참조가 동일한 의미를 갖는다. ## 직접 만들기 전에 쓸 만한 기존 라이브러리나 도구를 찾는다 커스텀 시스템, 기능, 도구, 통합, 자동화를 제안하거나 만들기 전에, 이미 충분히 잘 해결해주는 기존 라이브러리·플러그인·무료 플랫폼이 있는지 가볍게 확인한다. ### 검증된 옵션을 우선한다 기존 옵션이 충분하면 그것을 사용한다. 대안이 부적합하거나, 비용이 지나치게 크거나, 관리되지 않거나, 안전하지 않거나, 규정을 준수하지 않거나, 사용자가 명시적으로 커스텀 빌드를 요청한 경우에만 직접 만든다. ### 확인 과정은 가볍게 유지한다 이것은 짧은 사전 점검일 뿐, 리서치 과제가 아니다. 사용자가 지출을 승인하지 않은 한 유료 서비스를 추천하지 않는다. ## diff만 보지 말고 호출하는 쪽, 호출되는 쪽, 테스트까지 읽고 리뷰한다 수정, 트리아지, 리뷰는 보이는 변경 사항에서 나온 추측이 아니라, 실제로 읽어서 얻은 높은 확신을 바탕으로 해야 한다. diff만 보는 리뷰로는 부족하다. ### 판정을 내리기 전에 전체 표면을 읽는다 좋다, 나쁘다, 최선의 수정이다, 충분히 검증됐다고 말하기 전에, 변경된 함수나 모듈 전체는 물론 그것을 호출하는 곳, 그것이 호출하는 곳, 형제 구현, 인접한 테스트, 범위별 문서, 그리고 의존하는 모든 것의 계약까지 읽는다. ### 작은 근거 지도를 만든다 변경된 표면, 그 진입점, 소유 경계, 최소 하나의 호출자와 피호출자, 같은 불변조건을 공유하는 형제 표면, 기존 테스트, 현재 배포된 동작을 명시한다. 어느 항목이든 비어 있다면 결론을 내리는 대신 그 공백을 말한다. ### 이의가 제기되면 방어하기 전에 먼저 읽는다 판정에 이의가 제기되면 먼저 빠진 경로를 확인한다. 직접 확인하기 전까지는 이전 결론을 방어하지 않는다. ## 의존성은 실제 소스를 읽고, 외부 API는 직접 테스트한다 동작이 라이브러리나 외부 서비스에 의존할 때는, 기억·래퍼·생성된 스키마·다른 에이전트의 보고가 아니라 실제 대상을 기준으로 검증한다. ### 의존성의 소스를 직접 읽는다 의존성이 뒷받침하는 동작이라면 업스트림 소스, 문서, 타입을 직접 읽고 무엇을 확인했는지 인용한다. 대부분의 의존성은 오픈소스이므로 기본값, 오류, 타이밍을 추측할 핑계가 없다. ### 외부 서비스는 실제로 테스트한다 외부 API 작업에는 실제 테스트와 현재 시점의 인용 가능한 증거가 필요하다. 기억을 근거로 API에 대해 주장하지 않는다. 가능하면 항상 실제로 검증하고, 그 과정에서 비밀 정보를 절대 출력하지 않는다. ## 돌아가기만 하는 수정이 아니라 가장 좋은 수정인지 판단한다 리뷰 판정은 그 변경이 그저 작동하는 수정이 아니라 최선의 수정인지를 결정해야 한다. 결론을 내리기 전에 소유 경계, 호출자, 형제 요소, 테스트, 현재 동작을 비교한다. ### 한쪽만 고친 수정은 형제 요소에 대한 증거가 필요하다 한 표면만 건드리고 형제 표면은 건드리지 않은 수정은, 형제 표면이 영향받지 않는다는 증거나 그것을 다루는 명시적인 후속 작업이 필요하다. ### 취향을 지적 사항으로 올리지 않는다 변경된 코드가 범위별 가이드를 따르고 사용자에게 보이는 위험, 런타임 위험, 보안 위험, 유지보수자 위험이 없다면, 순전히 저장소 정책상의 취향 차이를 지적 사항으로 올리지 않는다. ## 설정을 읽는 방법은 하나로 하고, 옛 키는 버리지 말고 옮긴다 런타임은 오직 현재의 정본 형태의 설정과 데이터만 읽는다. 오래되거나 잘못된 형식의 키에 대한 암묵적 호환성은 없다. ### 기존 경로는 삭제한다 명시적으로 배포된 계약이나 태그가 붙은 업그레이드 경로가 인용되지 않는 한, 단일 정본 경로를 우선하고 기존 경로는 삭제한다. 두 번째 경로를 남긴 리팩터링은 대체로 실패한 것이다. ### 마이그레이션하되, 절대 조용히 버리지 않는다 변경으로 기존 설정이 무효화된다면, 전용 마이그레이션이나 복구 단계에 대응하는 마이그레이션을 추가한다. 키를 조용히 버리지 않는다 — 버려진 키는 숨겨진 동작 변화이기 때문이다. ### 설정 표면을 작게 유지한다 설정과 환경 변수 표면은 진입 기준이 높다. 옵션을 추가하기 전에 기존 동작으로 해결할 수 없음을 증명하고, 옵션을 제거하거나 통합하는 쪽을 우선한다. 문서화된 설정 흐름은 호환성에 민감한 것으로 다룬다 — 기존 스니펫을 깨뜨리기보다 추가적인 플래그를 우선한다. ## 모듈 전용 처리는 공유 코드가 아니라 그 모듈 안에 둔다 공유 코드와 코어 코드는 범용성을 유지하고, 소유자별 동작은 그것을 소유한 모듈 안에 둔다. 경계를 넘을 때는 반드시 문서화된 공개 접점을 통해서만 하고, 다른 모듈의 내부에 직접 손대지 않는다. ### 책임을 감싸지 말고 옮긴다 기본적인 수정 형태는 로직을 올바른 소유자에게 옮기고 낡은 추상화, 중복된 정책, 죽은 분기를 삭제하는 깔끔하고 범위가 명확한 리팩터링이지, 가능한 가장 작은 패치가 아니다. ### 정확하되 과설계하지 않는다 실제 입력과 상태에 대한 정확성은 필수다. 상상 속 입력을 위한 추가 레이어와 방어 코드는 엄밀함이 아니라 결함이다. 있음직하지 않은 엣지 케이스를 실제 단순함과 맞바꿀 때는 그 트레이드오프를 명시해서, 실수가 아니라 결정으로 남긴다. ## 실행 중 상태는 따로 파일을 만들지 말고 프로젝트의 정해진 저장소에 둔다 런타임 상태는 프로젝트의 정본 저장소에 보관한다. 저장소가 담당해야 할 상태, 캐시, 큐, 인덱스, 임시 데이터를 사이드카 파일로 흩어놓지 않는다. ### 마이그레이션이 예전 형태를 책임진다 레거시 파일 저장소와 폐기된 형태는 런타임 이전 단계인 마이그레이션이나 복구 코드에서만 정규화한다. 안정 상태의 런타임은 폴백 위치를 절대 읽지 않는다. ### 스키마 버전을 혼자 올리지 않는다 구버전 리더가 감당할 수 없는 스키마 버전 변경은 먼저 사용자와 명시적으로 논의하고 승인을 받아야 한다. 자율적으로 버전을 올리지 않는다. 구버전 빌드가 여전히 감당할 수 있는 순수한 추가 변경은 버전을 올리지 않는다. ## 한 번 알아낸 정보는 요청마다 다시 찾지 말고 재사용한다 핫 패스는 매 요청마다 다시 알아내는 대신, 이전에 이미 해결된 정보를 그대로 전달해야 한다. ### 한 번만 해결하고 재사용한다 식별자, 해결된 경로, 기능 정보를 앞으로 전달하고 준비된 객체를 재사용한다. 요청 시점마다 반복되는 탐색 문제를 캐시를 여기저기 흩뿌려 해결하지 않는다. 정본 정보를 더 이른 시점으로 옮기고 중복된 조회는 삭제한다. ### 루프 안에서 최신성을 폴링하지 않는다 설치 정보, 매니페스트, 해결된 경로처럼 프로세스 동안 안정적인 메타데이터는 재시작이나 명시적인 리로드에서만 바뀐다. 따라서 핫 패스는 다시 읽거나 다시 해싱해서 최신성을 재확인해서는 안 된다. 프로세스 로컬 캐시는 생명주기가 명확히 관리되고 범위가 한정된 경우에만 허용된다. ## 엄격한 타입으로 짧고 읽기 쉬운 코드를 쓴다 엄격한 타입 검사를 항상 켜두고 코드를 정직하게 유지한다. 목표는 가장 작은 diff가 아니라 작고 명확한 코드다. ### 경계에 타입을 부여하고 분기를 닫는다 모든 것을 받아주는 동적 타입은 피하고, 실제 타입과 좁은 어댑터를 우선한다. 외부 입력은 스키마로 검증한다. 자유 형식 문자열이나 센티널 값이 아니라 판별 유니온이나 닫힌 결과 코드로 분기하고, 불가능한 상태는 아예 표현할 수 없게 만든다. ### 읽기 쉽게 구조화한다 중첩된 피라미드보다 조기 반환을 우선하고, 작업을 수집·정규화·판단·실행 단계로 나눈다. 호출 지점은 지루하게 유지한다 — 복잡한 판단은 상위에서 이루어지고, 인자는 이름, 리터럴, 단순한 값 읽기로만 남는다. ### 자명하지 않은 부분에만 주석을 단다 인라인 주석은 자명하지 않은 불변조건, 순서 제약, 소유 경계, 정리 작업 간의 결합에서만 제 몫을 한다 — 그 분기가 왜 존재하는지, 제거하면 무엇이 깨지는지를 한두세 줄로 설명한다. 문법을 설명하는 주석은 달지 않는다. ## 리팩터링으로 코드가 길어지면 줄이거나 이유를 설명한다 리팩터링은 추가하는 만큼의 로컬 복잡성을 삭제해야 한다. 프로덕션 코드 줄 수가 늘어났다면 그것은 그냥 받아들일 기본값이 아니라 정당화해야 할 냄새다. ### 마무리 전에 크기를 확인한다 사소하지 않은 리팩터링을 마치기 전에 변경 크기를 비교한다. 테스트가 아닌 코드가 늘어났다면 줄이거나, 경로가 줄었는데도 왜 줄 수가 더 늘었는지를 명확히 설명한다. ### 새로 만든 표면은 제 몫을 하게 한다 새 헬퍼나 파일은 만들자마자 호출 경로, 개념, 중복 로직을 줄여야 한다. 일회성 호환성, 필드 이름 변경, 추측성 복원력을 위해 새로 만들지 않는다. 공개 인터페이스는 좁게 유지하고 유용한 최소한의 형태만 반환한다. ## 문구가 아니라 동작을 테스트하고, 테스트끼리 서로 독립시킨다 테스트는 동작을 증명하고 회귀를 막기 위한 것이지, 내부의 모든 분기마다 체크박스를 채우기 위한 것이 아니다. ### 문구가 아니라 동작을 테스트한다 문서나 정책 문자열을 grep하는 대신 동작 테스트를 우선한다. 운영자에게 주는 알림은 테스트 단언문이 아니라 문서에 둔다. 새 테스트는 머지 전에 검토하고, 가치가 낮은 테스트는 삭제한다 — 방금 제거한 경로에 대한 테스트도 포함해서다. ### 테스트를 독립적이고 정직하게 유지한다 타이머, 환경 변수, 전역 상태, 목, 임시 상태를 정리한다. 목은 실제 코드가 다루는 모든 바인딩을 내보내야 하며, 아이덴티티 검사에 쓰이는 에러 타입도 포함한다. 승인 없이 검사를 잠재우기 위해 베이스라인이나 스냅샷 파일을 수정하지 않는다. ### 실제 상태의 사본으로 테스트한다 현실적인 데이터가 중요할 때는 상태를 임시 위치로 복사해서 그 사본을 테스트한다. 명시적인 승인 없이는 운영 중인 서비스의 상태를 직접 변경하지 않는다. ## 신뢰할 수 없는 코드는 로컬에서 실행하지 않고, 무거운 빌드는 원격 러너로 보낸다 얼마나 검증할지 정하기 전에 소스의 신뢰도를 먼저 분류하고, 검증을 어디서 실행할지도 그 신뢰도에 따라 정한다. ### 신뢰할 수 없는 코드는 로컬에서 절대 실행하지 않는다 신뢰할 수 있는 소스는 의존성이 준비되어 있으면 로컬에서 몇 가지 집중된 테스트와 비용이 적은 정적 검사를 실행해도 된다. 신뢰할 수 없거나 외부에서 기여된 코드는 검증 규모와 무관하게 자신의 스크립트, 테스트, 훅, 설정을 로컬에서 절대 실행하지 않는다. 대신 격리되고 자격 증명이 없는 실행 환경을 사용한다. ### 무거운 작업은 원격 러너로 보낸다 전체 테스트 스위트, 빌드, 타입 검사, 패키징, 크로스 플랫폼 검증은 원격 러너에서 실행한다. 원격 러너는 첫 무거운 명령에서 지연 획득하고, 한 세션 동안 재사용하며, 인계 전에 반납한다. 원격 러너를 쓸 수 없다면 신뢰할 수 있는 작업은 로컬로 대체해도 되지만, 신뢰할 수 없는 작업은 절대 그렇게 하지 않는다. ### 머지 전에 새로운 리뷰를 통과시킨다 머지하기 전에 실행 가능한 지적 사항이 남지 않을 때까지 새로운 자동 리뷰를 실행한다. 범위가 사소하거나 사용자가 제외를 선택하지 않는 한, 초록불 지속적 통합만으로 머지하지 않는다. 검증이 막혀 있다면 무엇이 왜 빠져 있는지 정확히 말한다. ## 프로젝트 절차대로 이슈와 풀 리퀘스트를 열고, 릴리스 전에 승인을 받는다 공유 표면을 변경하는 것은 사회적 행위다. 프로젝트의 기여 절차를 따르고 되돌릴 수 없는 모든 단계마다 승인을 받는다. ### 리뷰와 머지 절차를 따른다 무언가를 열기 전에 기여 가이드, 이슈와 풀 리퀘스트 템플릿, 코드 소유자 파일을 읽는다. 버그, 사용자에게 보이는 기능, 아키텍처 결정은 먼저 이슈를 연다. 문제, 영향, 증거를 담은 실질적인 풀 리퀘스트 본문을 작성하고, 어떤 변경이든 하기 전에 플랫폼 자체 도구로 실제 상태를 확인한다. ### 요청받지 않은 되돌릴 수 없는 행동은 하지 않는다 요청받지 않은 채로 라벨을 달거나, 제목을 바꾸거나, 리베이스하거나, 머지하지 않는다. 사용자가 명시적으로 게시를 요청하지 않는 한 공개 댓글은 먼저 초안으로 보여주고, 게시된 모든 댓글은 링크와 함께 명시한다. 이슈와 풀 리퀘스트 작업은 항목의 전체 URL로 마무리한다. ### 커밋과 브랜치를 깔끔하게 유지한다 의도한 파일만 간결하고 그룹화된 메시지로 커밋하고, 푸시 전에 최신 메인라인 위로 리베이스하며, 의도하지 않은 파일을 삭제하거나 이름을 바꾸지 않는다. 같은 변경 안에서 문서를 동작과 함께 맞추고, 릴리스 변경 로그는 릴리스 절차에 맡긴다. ### 릴리스와 비밀 정보는 승인을 받는다 릴리스, 버전 업, 의존성이나 락파일 변경은 명시적인 승인이 필요하다. 실제 자격 증명이나 운영 설정을 절대 커밋하지 않고, 비밀 정보는 지정된 저장소에 보관한다.