


컴포넌트가 많은 대규모 코드베이스를 읽기 쉽고 망가뜨리지 않게 고칠 수 있도록 유지하기
여러 컴포넌트로 이루어진 큰 코드베이스에서 AI에게 코드를 맡길 때 사용합니다. 코드 작성법, 파일과 변경의 크기, 코드 리뷰에서 볼 점, 테스트, API 이름 짓기, 빌드 진행 방법의 규칙을 주어 변경을 작고 읽기 쉽게 유지하고 다른 부분을 망가뜨리지 않게 합니다.
이 AI 규칙을 사용하려면
## true/false만 넘기는 인수를 피해 호출만 봐도 뜻이 통하게 하기
호출 지점에서의 명료함을 영리함보다 우선한다.
- 변수는 위치 인자로 전달하지 말고 문자열 포맷 플레이스홀더에 직접 인라인으로 삽입한다.
- 언어가 허용한다면 사소한 클로저보다 메서드 참조를 선호한다.
- 읽는 사람이 의미를 추측해야 하는 불투명한 위치 인자(단순 불리언, null/none, 숫자 리터럴 등, 예: `foo(false)`)를 피한다. 열거형, 명명된 메서드, newtype, 그 밖의 자기 설명적인 API 형태를 선호한다.
- 자기 설명적인 형태가 불가능해서 위치 리터럴을 그대로 남겨야 할 경우, 호출 대상의 매개변수 이름과 정확히 일치하는 인라인 `/*param_name*/` 주석을 붙인다. 문자열/문자 리터럴은 주석이 정말로 명료함을 더하지 않는 한 주석을 달지 않는다.
- 분기를 전수적으로(catch-all 와일드카드 분기 없이) 작성하여, 새로 추가되는 케이스가 컴파일 시점에 드러나게 한다.
- 새로 추가하는 추상화(trait/interface)에는 그 역할과 구현자가 어떻게 사용해야 하는지를 설명하는 문서 주석을 단다.
- 파일 로컬 관례를 따른다. 명확한 가독성 또는 기능상의 이득 없이 동등한 형태 사이에서 코드를 갈아엎지 않으며, 단순히 축약형을 만족시키기 위해 타입 주석을 추가하지 않는다.
- 한 곳에서만 참조되는 작은 헬퍼 함수를 만들지 않는다.
## 핵심 컴포넌트를 키우지 않고 파일과 변경을 작게 나누기
단위를 작게 유지하여 서로 무관한 변경들이 충돌하지 않도록 한다.
### 모듈 크기
- 기존 모듈을 키우기보다 새 모듈을 추가하는 것을 선호한다. 테스트 코드를 제외하고 대략 500줄 이하를 목표로 하며, 파일이 약 800줄에 근접하면 명확하고 문서화된 이유가 없는 한 새 기능은 새 모듈에 넣는다.
- 이미 무관한 수정들을 끌어들이고 있는, 트래픽이 많고 중심적인 오케스트레이션 파일에 이 원칙을 가장 엄격하게 적용한다. 그런 파일은 독립형 헬퍼들을 쌓아 두지 않고 오케스트레이션에 집중하도록 유지한다.
- 큰 모듈에서 코드를 분리할 때는, 불변 조건이 그것을 소유하는 코드 곁에 남아 있도록 테스트와 타입/모듈 문서를 새 구현과 함께 옮긴다.
### 가장 큰 컴포넌트가 비대해지지 않도록 저항한다
- 가장 크거나 가장 중심적인 컴포넌트는 새 코드가 가장 쉽게 들어가는 경로이며, 바로 그 때문에 계속 커진다. 여기에 코드를 추가하는 것에 저항한다. 먼저 기존 컴포넌트가 더 나은 자리인지, 아니면 새 컴포넌트를 도입할 때인지 물어보고, 필요하면 리팩터링한다. 리뷰에서는 중심 컴포넌트를 불필요하게 비대하게 만드는 변경에 반대 의견을 낸다.
### 변경 규모
- 기계적인 변경이 아닌 한, 전체 변경 줄 수를 대략 800줄 미만으로 유지한다. 복잡한 로직의 경우 500줄 미만으로 유지한다.
- 변경이 더 크다면, 실제 diff, 의존 관계, 영향을 받는 호출 지점을 바탕으로 리뷰 가능한 단계들로 나누고, 가장 작은 일관된 단계를 먼저 반영한다.
## 코드 리뷰에서 호환성을 깨는 변경과 끝없이 커지는 컨텍스트 찾기
나중에 발견하면 비용이 큰 위험을 중심으로 리뷰한다.
### 공개 표면을 작게 유지한다
각 컴포넌트의 공개/내보내진 API를 가능한 한 작게 유지한다. 명시적으로 내보낸 공개 표면을 갖춘 비공개 내부 구현을 선호하고, 테스트 전용 헬퍼가 프로덕션 코드에 늘어나지 않게 한다.
### 모델/추론 컨텍스트를 보호한다
시스템이 추론 요청마다 모델로 보내는 컨텍스트를 유지한다면 이를 다음과 같이 보호한다.
- 컨텍스트를 점진적으로 쌓아 올린다. 이력을 절대 다시 쓰지 않는다.
- 캐싱을 무효화하는 빈번한 컨텍스트 변경을 피한다.
- 주입되는 모든 항목은 상한 크기와 엄격한 상한선을 가져야 한다. 무제한 항목은 없어야 하며, 어떤 단일 항목도 고정된 토큰 상한을 넘지 않아야 한다.
- 개별적으로 큰 크기 임계값을 넘을 수 있는 새 항목은 추가 수동 리뷰 대상으로 표시한다.
- 임시방편적인 삽입 대신, 주입되는 모든 조각을 단일한 타입이 지정된 추상화를 통해 전달한다.
### 파괴적 변경을 감지한다
공개 API, (실험적인 것을 포함한) 이벤트 페이로드, 커맨드라인 매개변수, 설정 로딩, 그리고 기존 세션이나 영속된 상태를 재개할 수 있는 능력 등 외부 통합 표면에서 파괴적 변경이 없는지 능동적으로 찾는다.
## 통합 테스트와 스냅샷 테스트로 실제 동작 확인하기
행동을 테스트하고, 테스트를 정직하게 유지하며, 테스트를 프로덕션 코드 밖에 둔다.
- 핵심 동작 변경에는 좁은 범위의 단위 테스트보다 실제 인스턴스를 구동하는 통합 테스트를 선호한다. 해당 동작에 대한 모든 변경은 반드시 통합 테스트를 추가해야 하며, 커버해야 할 동작들을 열거한다.
- 단위 테스트는 구현 파일에 인라인으로 넣지 말고 전용 형제 테스트 파일에 두며, 파일에는 설명적인 이름을 붙인다. 테스트 전용 함수를 프로덕션 모듈에 추가하지 않으며, 이 관례를 따르기 위해서만 기존의 인라인 테스트 모듈을 다시 쓰지 않는다.
- 사용자에게 보이는 렌더링 결과물에 대한 모든 변경에는 스냅샷 커버리지를 포함해야 한다. 스냅샷을 추가하거나 갱신하고, 변경의 일부로 그 diff를 리뷰하여 UI 영향이 계속 눈에 보이게 한다.
- 필드를 하나씩 비교하지 말고, 명확한 diff를 만들어 주는 어서션 라이브러리를 사용하여 전체 객체를 깊은 동등성으로 검증한다.
- 테스트 내부에서 전역 프로세스 상태(예: 환경 변수)를 변경하지 않는다. 의존성과 플래그는 위에서부터 전달한다.
- 정적으로 정의된 상수를 테스트하지 않으며, 제거된 로직에 대한 부정 테스트를 추가하지 않는다.
- 테스트를 간결하고 읽기 쉽게 유지하기 위해, 새 설정 코드를 작성하기 전에 기존 테스트 헬퍼가 있는지 확인한다.
## API 이름과 필드 형식을 어디서나 같게 맞추기
네트워크 또는 언어 간 API를 발전시킬 때, 와이어 경계에서의 일관성은 조용한 파손을 막아준다.
- 활발한 개발은 현재 API 버전에 집중한다. 지원 종료(deprecated) 버전에 새 표면을 추가하지 않는다.
- 페이로드는 역할별로 일관되게 명명한다(요청 매개변수용 접미사, 응답용 접미사, 알림용 접미사를 각각 둔다). 메서드는 리소스 이름 뒤에 동작 이름을 붙인 형태로 노출하며, 리소스의 단수/복수 관례를 일관되게 유지한다.
- 필드와 열거값에 대해 하나의 와이어 케이싱 관례를 정하고 어디에나 적용하며, 명시적인 호환성 요구가 있을 때만 예외를 둔다. 직렬화기와 타입 생성 어노테이션을 서로 일치시켜서 양쪽이 합의하게 하며, 한쪽이 명시적 이름 변경을 쓰면 다른 쪽도 맞춰야 한다.
- 구분된 유니온(discriminated union)에는 모든 직렬화기에서 명시적 태깅을 사용한다.
- 경계에서는 정수가 아닌 일반 문자열 ID를 선호하고 내부에서 변환한다. 타임스탬프는 일관된 `*_at` 명명 규칙을 갖는 정수를 사용한다.
- 목록 엔드포인트에는 기본적으로 커서 기반 페이지네이션을 구현한다(입력으로 커서와 limit, 출력으로 데이터 배열과 다음 커서).
- 실험적 표면은 명시적인 마커 뒤에 게이트를 두고, API 형태가 바뀔 때마다 문서, 예제, 스키마 픽스처를 재생성한다.
## 포매터와 범위를 좁힌 테스트를 먼저 돌리고 샌드박스 가드는 건드리지 않기
이미 존재하는 툴체인과 제약을 존중한다.
### 환경 및 샌드박스 가드
- 신중한 의도 없이 샌드박스나 환경 기능 플래그를 관리하는 코드를 추가하거나 수정하지 않는다. 이런 가드는 코드와 테스트가 특정 런타임 제약(예: 네트워크 접근이 불가능할 때 조기 종료하는 테스트)을 전제로 작성되었기 때문에 존재한다. 가드를 조용히 제거하거나 바꾸면 그 전제가 깨진다.
### 빌드 및 툴체인 위생
- 코드 변경을 마친 뒤에는 포매터를 자동으로 실행한다. 포매팅에 승인을 요청하지 않는다.
- 변경된 컴포넌트로 범위를 좁혀 가장 좁은 관련 테스트를 먼저 실행한다. 공유되거나 중심적인 코드가 변경됐을 때만 전체 스위트로 범위를 넓히고, 긴 전체 스위트 실행을 시작하기 전에는 확인을 받는다.
- 스키마, 락파일, 생성된 타입 등 생성된 산출물에 입력이 되는 것들을 변경할 때는, 같은 변경 안에서 그것들을 재생성한다. 드리프트는 CI에서 검증된다.
- 빌드 도구가 컴파일 타임 접근을 위해 소스 파일 선언을 필요로 한다면, 그런 읽기를 추가할 때마다 해당 선언을 갱신한다.
- 느린 빌드와 테스트 명령에는 인내심을 가진다. 예상되는 락 경합 때문에 느려질 수 있다. 프로세스 ID로 강제 종료하지 않는다.
### 플랫폼 지원
- 기능이 명시적으로 특정 OS 전용이 아닌 한, 테스트와 기능은 공식적으로 지원되는 모든 운영체제를 지원해야 한다. 시스템이 컴포넌트를 서로 다른 운영체제에서 실행할 수 있게 하는 곳에서는, 새 테스트가 그런 OS 간 구성들을 실제로 검증하도록 한다.
