


기존 동작을 깨뜨리지 않고 대규모 다중 언어 런타임 코드베이스를 수정한다
네이티브 코드와 스크립트로 이루어지고 패치된 의존성을 벤더링한 대규모 런타임 프로젝트에서 쓴다. 가장 가까운 가이드를 먼저 읽고, 프로젝트의 빌드·테스트 단축 명령과 테스트 타깃을 쓰며, 자체 타입과 금지 사항을 지키고, 동작 변경은 호환성 플래그 뒤에 넣는다.
이 AI 규칙을 사용하려면
## 코드에 가장 가까운 가이드를 읽고 상위 프로젝트가 있는지 확인한다
이런 유형의 저장소에서 가이드는 계층적으로 구성되어 있다. 루트 가이드는 상위 수준의 방향성을 제공하고, 하위 디렉터리 안의 가이드는 핵심 클래스, 어디를 봐야 하는지에 대한 표, 로컬 컨벤션, 안티패턴 같은 컴포넌트별 맥락을 담고 있다. 루트 가이드뿐 아니라 지금 손대려는 코드에 가장 가까운 가이드도 항상 함께 읽는다.
- 저장소가 단독으로 체크아웃된 것인지, 아니면 더 큰 상위 프로젝트의 컴포넌트로 중첩되어 있는지 항상 먼저 확인한다. 중첩되어 있다면 중요한 문서가 상위 루트에 있어 여기서는 보이지 않을 수 있다. 무언가를 가정하기 전에 상위 readme, 상위 가이드, 그 외 최상위 문서를 먼저 읽는다.
- 자유롭게 조사하고 읽되, 편집을 제안하거나 실행하기 전에 이런 맥락부터 확보한다.
- 가이드에 빠져 있는 중요한 상위 수준 정보를 발견했다면, 대화 안에만 남겨 두지 말고 가이드에 추가할 것을 제안한다.
## 임시 명령이 아니라 프로젝트의 단축 명령으로 빌드하고 테스트한다
이 프로젝트는 하나의 공식 빌드 시스템과 그 위에 얇게 얹힌 단축 명령(태스크 러너) 계층을 가지고 있다. 일상적인 작업에는 단축 명령을 사용하고, 단축 명령이 필요한 옵션을 제공하지 않을 때만 하위 빌드 도구로 내려간다. 즉흥적인 컴파일 명령을 만들어 내지 않는다.
찾아서 재사용해야 할 대표적인 단축 명령: 빌드, 전체 테스트 실행, 포맷, 스크립팅 소스 린트, 네이티브 소스 린트, 시스템 언어 패키지 린트, 디버깅용 테스트 출력 스트리밍, 단일 호환성/적합성 스위트 실행, 새 테스트 스캐폴딩, 타입 정의 생성, 에디터 인덱스 데이터베이스 생성, 어드레스 새니타이저를 사용한 빌드 및 테스트, 커버리지 리포트 생성, 소스 디렉터리를 감시하다가 변경 시 명령을 재실행하는 것.
## 올바른 테스트 타깃과 자동 생성되는 변형을 찾는다
여러 테스트 계열이 공존하며 각각 고유한 진입점을 가진다.
- 설정 기반 통합 테스트: 임베디드 스크립트 모듈, 호환성 날짜, 기능 플래그, 그리고 서비스 바인딩·구조화 데이터·키-값 저장소·상태 저장 오브젝트 네임스페이스 같은 바인딩을 선언하는 직렬화된 설정 파일 하나로 서비스를 정의한다.
- 전용 매크로로 빌드되는 네이티브 유닛 테스트. 파일명에는 하이픈이 붙은 테스트 접미사가 붙는다.
- 이름으로 각각 실행되는 런타임 호환성 스위트와 웹 표준 적합성 스위트.
- 경로로 실행되는 벤치마크.
빌드 명령과 테스트 명령 모두 명시적 타깃을 받을 수 있으며, 지정하지 않으면 전체 트리를 대상으로 한다. 타깃 이름에는 반드시 붙어야 하는 접미사 마커가 있고, 빌드 매크로는 테스트마다 여러 변형을 자동으로 생성한다. 가장 오래된 호환성 기준선에 고정된 기본 변형, 모든 플래그를 켠 최신 날짜 변형, 모든 임시 게이트를 켠 변형이 그것이다. 어떤 파일에 맞는 타깃을 찾으려면 같은 디렉터리 안의 빌드 파일을 읽거나, 빌드 시스템에 그 패키지 안의 모든 타깃을 물어본다.
## 주요 코드 영역을 파악하고 비슷한 기존 구현을 따른다
편집하기 전에 먼저 머릿속에 지도를 그리고, 새 작업을 할 때는 비슷한 기존 구현을 찾아 그 패턴을 따른다.
- 런타임 API: 스크립트에 노출되는 인터페이스의 네이티브 구현, 표준 라이브러리 호환 계층, 그리고 그 테스트들.
- I/O 서브시스템: 상태 저장 오브젝트 스토리지와 캐싱, 요청 추적, 리소스 제한, 스레딩, 워커 생명주기. 요청별 컨텍스트 오브젝트가 하나의 요청을 하나로 묶는다.
- 글루 계층: 엔진 바인딩, 타입 래퍼, 프라미스 처리, 메모리 관리, 모듈 시스템.
- 서버 계층: 바이너리 진입점과 설정 처리. 스키마 파일로 기술되는 역량(capability) 기반 보안 모델을 사용한다.
- 호환 계층, 대체 스크립팅 런타임, 시스템 언어 통합 컴포넌트를 위한 언어별 트리.
호환 계층은 이중 구조다. 네이티브 절반은 한 트리에, 스크립팅 절반은 다른 트리에 있으며, 새 모듈은 반드시 지정된 모듈 테이블에 등록해야 한다. 타입 정의는 네이티브 타입 정보와 수기로 작성된 선언을 조합해 생성되며, 재생성 대상일 뿐 손으로 직접 편집해서는 안 된다.
## 하우스 스타일을 따르고, 커밋은 작게 나눠 각각 테스트를 통과시킨다
참조하는 사내 스타일 가이드를 그 안에 문서화된 예외 사항까지 포함해 따른다. 프로젝트가 그 가이드에서 의도적으로 벗어난 부분이 있다면, 그 벗어난 방식이 곧 규칙이다.
- 고정된 언어 표준, 프로젝트 고유의 특이한 소스 파일 확장자, 하이픈이 붙은 테스트 파일 접미사를 정확히 맞춘다.
- 하나의 포맷 명령이 모든 언어에 걸쳐 모든 포매터를 실행한다. 변경 사항을 제출하기 전에 이 명령과 전체 테스트 명령을 실행한다.
- 프리커밋 체크가 스테이징된 코드 안의 디버그 프린트 매크로를 차단하고 포맷 검사를 재실행한다. 이를 우회하지 않는다.
- 변경 사항은 작은 단위의 커밋으로 나눈다. 각 커밋은 그 자체로 컴파일되고 테스트를 통과해야 한다. 후속 수정용(fixup) 커밋을 남겨 두지 않는다.
- 스크립팅 쪽 소스는 엄격하게 타입이 지정된다. 정확한 옵셔널 프로퍼티 시맨틱, private 필드 문법, 명시적 반환 타입이 강제된다.
## 표준 라이브러리 대신 프로젝트 자체 기반 라이브러리의 타입을 쓴다
이 코드베이스는 대부분의 타입에 대해 의도적으로 언어 표준 라이브러리 대신 자체 베이스 라이브러리를 사용한다. 표준 컨테이너에 손을 뻗기보다 주변 파일의 방식에 맞춘다.
적용할 대체 목록: 표준 문자열 대신 소유 문자열과 비소유 문자열 뷰, 표준 벡터 대신 고정 크기 배열과 확장 가능한 벡터, 표준 스마트 포인터 대신 단독 소유·참조 카운트·스레드 안전 참조 카운트·약한 소유 핸들, 옵셔널 대신 메이비(maybe) 타입, 표준 함수 오브젝트 대신 콜러블 래퍼, variant 대신 태그된 유니언, 원시 스팬 대신 배열 슬라이스, 라이브러리 전용 예외 타입, 표준 퓨처 대신 라이브러리 프라미스와 포크된 프라미스.
안전을 위해 원시 포인터보다 배열 슬라이스를, 참조가 소유자의 수명에 종속될 때는 맨(bare) 참조보다 수명에 종속된 핸들을 우선한다. 에러 처리에는 메이비 값을 언랩할 때 전용 매크로를 사용하고, 실패가 적절한 예외 타입을 갖춘 스크립트 예외로 드러나야 할 때는 스크립트 대상 요구사항 매크로를 사용하며, 내부 불변 조건에는 네이티브 assertion 및 precondition 매크로를 사용한다.
## 누수와 숨은 손상을 일으키는 금지된 패턴을 피한다
다음은 절대적인 규칙이며, 위반하면 미묘한 데이터 손상, 메모리 누수, 호환성 붕괴로 이어진다.
- 엔진 로컬 또는 엔진 글로벌 핸들을 값 타입 구조체 선언 안에 절대 두지 않는다. 제공된 참조 래퍼를 사용한다.
- 엔진 핸들을 프라미스 타입 안에 절대 두지 않으며, 엔진 락을 비동기 코루틴으로 절대 넘기지 않는다.
- 소유 래퍼 없이 I/O 오브젝트에 대한 스크립트 힙 참조를 절대 보유하지 않는다. 스코프 가드가 이를 강제한다.
- 인스턴스 프로퍼티보다 프로토타입 프로퍼티를 우선한다. 인스턴스 프로퍼티는 가비지 컬렉션 최적화를 무력화한다.
- 순환 값에 대해 안전하지 않으므로 사용자 제공 콘텐츠를 절대 딥 프리즈(deep-freeze)하지 않는다.
- 알 수 없는 이름을 원격으로 디스패치하는 인터페이스에 호환성 플래그로 게이팅하지 않은 메서드를 절대 추가하지 않으며, 직렬화된 enum 값을 절대 변경하지 않는다.
- 원시 태스크 리스트 접근자보다 추가형(additive) 태스크 등록 호출을 우선하고, 불리언 파라미터 대신 강한 불리언 타입을 사용한다.
- 스크립트 힙에서 도달 가능한 네이티브 오브젝트에 저장된 참조는 반드시 가비지 컬렉션 방문(visitation)을 구현해야 한다. 네이티브 참조 사이클은 절대 수집되지 않는다. 전용 린트 체크가 이를 검증한다.
- 임베디드 데이터베이스 오용 에러는 항상 던져지며 절대 억제되지 않는다. 상태 저장 오브젝트 데이터베이스에서는 트랜잭션이 허용되지 않는다.
- 모듈 평가는 요청 컨텍스트 안에서 절대 실행되어서는 안 되며, 글로벌 스코프에서의 비동기 I/O는 금지된다.
- 공유 타입 선언 파일에서는 제네릭한 앰비언트 타입 이름을 절대 사용하지 않는다. 항상 제품명이나 기능명을 접두사로 붙인다. 타입으로 맨 언타입 오브젝트를 절대 사용하지 않는다. 알려진 필드를 가진 제대로 된 인터페이스를 선언하고, 필요하면 향후 호환성을 위해 오픈 레코드를 확장한다.
- 생성된 스냅샷을 절대 손으로 편집하지 않는다. 소스 계층을 고쳐서 재생성한다.
## 동작 변경은 호환성 플래그나 임시 게이트 뒤에 넣는다
이 프로젝트는 강력한 하위 호환성을 약속한다. 한 번 배포된 동작은 제거하거나 조용히 바꿀 수 없다. 따라서 동작 변경은 반드시 게이트 뒤에 도입해야 하며, 게이트는 그 지속성에 따라 선택한다.
- 날짜 기반 호환성 플래그는 인스턴스별로 적용되고 영구적이며, 플래그 이름과 활성화 날짜를 어노테이션으로 담은 스키마에 정의된다. 모든 플래그는 활성화 날짜가 오기 전에 반드시 문서화되어야 한다.
- 임시 프로세스 수준 게이트는 설정 기반이며, 조건부 활성화를 동반하는 위험한 롤아웃을 위한 것이다. 열거형과 문자열 맵으로 정의되며, 둘은 항상 동기화 상태를 유지해야 한다.
비표준 인터페이스에 대해서는 기준을 높게 유지한다. 프로젝트 고유의 동작을 새로 만들기보다 이미 공개된 표준을 구현하는 쪽을 우선한다.
## 로컬 패치를 지키면서 벤더링된 의존성을 업그레이드한다
서드파티 의존성은 빌드 시스템의 모듈 메커니즘을 통해 외부 디렉터리에 벤더링된다. 통합은 모듈 매니페스트와 의존성 빌드 디렉터리 안에서 이루어진다. 레거시 워크스페이스 파일이 아직 남아 있을 수 있지만 더 이상 주된 메커니즘은 아니다. 헤더와 네임스페이스에 대한 질문은 추측하지 말고 벤더링된 베이스 라이브러리 소스를 직접 참조한다.
일부 의존성은 빌드 중 적용되는 로컬 관리 패치 세트를 가지고 있다. 이 패치들은 프로젝트 고유의 커스터마이징과 새 인터페이스를 도입하므로, 해당 의존성을 업그레이드할 때마다 반드시 검토하고 갱신한다. 또한 이 프로젝트는 기본 동작과 다른 대체 메모리 할당자를 사용한다는 점에 유의한다. 메모리 분석을 할 때는 이 점을 반드시 고려해야 한다.
주요 엔진 또는 의존성 업그레이드를 진행할 때는, 작업을 시작하기 전에 정확한 목표 버전과 보존해야 할 패치나 커스터마이징이 무엇인지 먼저 확인한다. 그런 다음 문서화된 동기화, 패치 적용, 리베이스, 패치 재생성, 버전 올리기 단계를 따른다. 병합 충돌은 흔히 발생하며, 프로젝트 고유의 변경 사항을 온전히 유지하려면 거의 항상 사람의 판단이 필요하다. 사람의 검토 없이 자동으로 충돌을 해결하지 않는다. 전체 테스트 스위트가 통과하는지 확인하고, 발견한 새로운 지원 중단(deprecation) 사항은 반드시 알린다.
## 흩어진 문서를 찾고, 틀린 내용은 수정을 제안한다
문서는 한곳에 집중되어 있지 않고 분산되어 있다. 문서 디렉터리는 환경 설정, 새 인터페이스 추가 가이드라인, 의존성 업그레이드 절차 같은 여러 영역을 아우르는 주제를 다루며, 개별 소스 디렉터리들도 종종 자체 readme나 서브시스템 세부 가이드를 가지고 있다. 낯선 영역에서 작업할 때는 추측하거나 질문하기 전에 먼저 이런 문서들을 능동적으로 찾아본다.
문서가 없거나, 부정확하거나, 오래되었다면 침묵하지 말고 업데이트를 제안한다. 먼저 사실 관계를 확인하고, 관리자에게 정확성을 확인받지 않은 채로 문서 편집을 커밋하지 않는다.
