


하나의 API를 여러 프로그래밍 언어에서 쓸 수 있게 하는 프레임워크를 개발한다
하나의 코드에서 여러 언어용 패키지를 만드는 프레임워크를 개발할 때 쓴다. 프로세스 사이의 JSON 통신, 내보낼 수 있는 타입, 코드 생성, 모든 언어에서 동작이 같은지 확인하는 테스트의 규칙을 정한다.
이 AI 규칙을 사용하려면
## 모든 언어에서 같은 동작과 하위 호환성을 지킨다
언어 간 프레임워크는 구현 언어에서 사용할 수 있는 모든 관용 표현이 아니라, 지원되는 모든 언어에서 이해 가능하고 동등하게 유지되는 동작을 기준으로 최적화해야 한다.
### 핵심 불변 조건
- 어셈블리 형식, 런타임 프로토콜, 공개 API의 하위 호환성을 유지한다.
- 지원되는 모든 대상 언어에서 동등한 관찰 가능 동작을 요구한다.
- 각 대상 언어의 일반적인 명명, 패키징, 타입 규칙을 따르는 코드를 생성한다.
- 모든 대상에서 충실하게 표현할 수 있는 원본 언어 기능만 허용한다.
- 대상 언어별 성능 지름길보다 정확성과 상호 운용성을 우선한다.
### 패키지 경계
- 프로토콜 커널, 호스트 런타임, 스키마 정의, 코드 생성기, 리플렉션 도구, 호환성 검사, 테스트 픽스처를 분리한다.
- 빌드 유틸리티와 준수 보고 기능은 런타임 패키지 밖에 둔다.
- 각 언어 런타임과 선택적 통합을 독립적으로 책임지는 패키지로 취급한다.
- 모든 컴파일러, 생성기, 런타임 테스트가 동일한 동작 사례를 사용할 수 있는 위치에 공유 픽스처를 둔다.
- 별도 저장소에서 유지 관리하는 구성 요소는 명시적으로 버전이 지정된 의존성을 통해 연결한다.
## 코드는 커널 프로세스에서 실행하고, 각 언어와 JSON으로 주고받는다
호스트 언어가 구현 언어를 직접 실행할 수 없을 때는 양측 런타임 모델을 사용한다. 호스트 프로세스는 표준 입력과 표준 출력으로 전달되는 JSON 메시지를 통해 커널 프로세스와 통신하며, 구현 언어의 모든 실행은 커널 안에서만 이루어진다.
### 프로세스 경계
- 모든 요청에 명시적인 작업 판별자를 부여한다.
- 어셈블리 로드, 객체 수명 주기, 메서드 호출, 속성 접근, 비동기 작업, 통계, 콜백을 서로 다른 프로토콜 작업으로 처리한다.
- 호스트 런타임이 핵심 프로세스에 필요한 파일 디스크립터를 보존할 수 없을 때만 추가 래퍼 프로세스를 사용한다.
- 진단 출력은 오류 스트림에 유지하여 프로토콜 스트림을 손상시키지 않도록 한다.
### 콜백 순서
구현 언어 코드가 호스트 언어에서 재정의한 메서드나 속성에 도달하면 다음과 같이 처리한다.
1. 커널은 원래 응답 대신 콜백 요청을 보낸다.
2. 호스트는 재정의된 동작을 실행하고 결과 또는 실패를 담은 완료 메시지를 반환한다.
3. 커널은 원래 작업을 재개하고 실제 응답을 반환한다.
### 프로토콜 발전
- 요청 이름, 필드 의미, 응답 순서, 콜백 순서를 호환성 계약으로 취급한다.
- 프로토콜이 변경되면 모든 호스트 런타임을 함께 업데이트한다.
- 메시지 형태나 수명 주기 규칙을 변경하기 전에 호환성 테스트를 추가한다.
## 언어 사이에서 값을 복사로 넘길지 참조로 넘길지 정한다
이식 가능한 각 값 범주에 대한 전송 표현을 정의하고 소유권 의미를 드러낸다. 복사되어 경계를 넘는 값과 커널이 계속 소유하는 객체를 혼동해서는 안 된다.
### 전송 표현
- 불리언, 숫자, 문자열은 각 JSON 표현을 사용하여 값으로 전송한다.
- 목록과 맵은 값으로 전송하며, 다른 객체와 구분해야 할 때는 태그가 명시된 감싸기 구조를 사용한다.
- 열거형은 완전 수식 멤버 식별자로 인코딩한다.
- 날짜는 태그가 지정된 모호하지 않은 타임스탬프 표현으로 인코딩한다.
- 데이터 전용 구조체는 완전 수식 타입과 필드 데이터로 인코딩한다.
- 클래스 인스턴스는 안정적인 객체 식별자를 사용하여 참조로 인코딩한다.
### 경계 규칙
- 언어 간에 UTF-8 문자열, 부동 소수점 동작, 널 허용 여부, 컬렉션 복사 의미를 보존한다.
- 사용자가 태그가 지정된 전송 키에 의존하기 시작하면 해당 키를 안정적으로 유지한다.
- 프로토콜이 원격 수명 주기 관리를 소유할 때는 객체 참조를 명시적으로 해제한다.
- 구현 언어 측만이 아니라 모든 호스트 런타임을 통한 왕복 변환을 테스트한다.
## 모든 언어가 표현할 수 있는 타입만 내보낸다
모듈의 주 진입점에서 내보낸 선언에는 이식성 제한을 적용하되, 내부 구현 코드는 더 폭넓은 언어 기능을 사용할 수 있게 한다.
### 이식 가능한 API 표면
- 모든 대상에서 건전한 표현을 제공할 수 있는 경우에만 기본 타입, 목록, 맵, 열거형, 날짜, 클래스, 인터페이스, 데이터 구조체, 프로미스, 불투명 값을 지원한다.
- 명시적인 명명 규칙이나 스키마 규칙으로 동작 인터페이스와 데이터 전용 구조체를 구분한다.
- 데이터 전용 구조체에는 읽기 전용 속성만 포함하고 메서드는 포함하지 않도록 요구한다.
- 메서드 반환 타입처럼 모든 대상에서 일관되게 노출할 수 있는 위치에서만 프로미스를 허용한다.
- 정적 타입 대상이 더 정확한 타입을 복원할 수 없을 때는 불투명 값을 암묵적으로 널 허용으로 취급한다.
- 클래스는 참조로 전달하고 값과 유사한 구조체는 복사본으로 전달한다.
### 제한 사항
- 튜플, 값이 존재하지 않는 타입, 임의 정밀도 정수, 심벌, 메서드 오버로드처럼 지원되지 않는 내보내기 구문을 거부한다.
- 재정의 메서드는 정확히 호환되는 시그니처를 유지해야 하며, 공변 반환 타입이나 반공변 매개변수에 의존해서는 안 된다.
- 정적 타입 대상에서 타입이 없는 객체로 축소되는 유니온 타입은 피한다.
- 동작 인터페이스와 데이터 전용 구조체가 서로 상속하지 못하게 한다.
## 같은 컴파일된 어셈블리에서 언어별 패키지를 만든다
각 대상 언어 패키지가 동일한 API 사실의 원천을 공유하도록, 같은 컴파일된 어셈블리에서 각 대상을 생성한다.
### 생성 파이프라인
1. 컴파일된 구현 언어 패키지에서 어셈블리를 읽는다.
2. 선택한 대상 언어에 관용적인 소스 코드를 생성한다.
3. 해당 언어의 도구 체인으로 생성된 소스 코드를 컴파일한다.
4. 대상 생태계의 표준 패키지 관리자로 결과물을 패키징한다.
### 대상 언어 설계
- 공유 순회 추상화를 기반으로 각 대상 언어에 전용 어댑터를 제공한다.
- 새로운 대상을 추가할 때는 성숙한 대상을 참고하되, 새 언어의 관용 표현을 보존한다.
- 런타임 아키텍처에서 요구하는 경우에만 원래 구현 산출물을 함께 묶는다.
- 하위 리플렉션 도구나 생성 도구에 필요한 어셈블리 메타데이터를 포함한다.
- 대상 간에 서식 지정, 들여쓰기, 파일 생성, 코드 블록 유틸리티를 공유한다.
- 대상이 변경될 때마다 생성 코드 스냅샷을 업데이트한다.
## 모노레포 안에서 버전이 고정된 도구로 빌드하고 테스트한다
저장소에 버전이 고정된 작업 공간 도구 체인을 통해 설치, 빌드, 테스트, 린팅, 스냅샷 업데이트, 준수 보고서 생성을 실행한다.
### 환경과 범위
- 전체 초기 설정과 저장소 전체 검증은 저장소 루트에서 시작한다.
- 범위를 좁혀 반복 작업할 때는 패키지 디렉터리에서 패키지 범위 스크립트를 실행한다.
- 선언된 패키지 관리자와 오케스트레이션 도구의 버전을 준수한다.
- 패키지 간 의존성 때문에 병렬 실행이 안전하지 않을 때는 빌드 동시 실행 수를 제한한다.
### 변경 작업 흐름
- 편집하거나 명령을 실행하기 전에 변경을 소유하는 패키지를 식별한다.
- 먼저 대상 패키지를 빌드하고 테스트한 다음, 공유 계약에 대해 더 넓은 검증을 실행한다.
- 생성된 번들이 아니라 소스를 편집하고 지정된 파이프라인을 통해 다시 빌드한다.
- 임시 런타임 재정의와 디버거 설정은 로컬에만 유지한다.
- 생성된 변경이 의도한 것임을 확인한 후에만 스냅샷을 업데이트한다.
## 스냅샷과 모든 언어가 통과해야 하는 공통 테스트로 동작을 확인한다
빠른 패키지 수준 테스트와 생성 출력 회귀 검사, 언어 간 준수 테스트 모음을 결합한다.
### 검증 계층
- 패키지 로직과 프로토콜 경계 사례에는 단위 테스트를 사용한다.
- 생성된 소스의 의도하지 않은 변경을 감지하는 데 스냅샷을 사용한다.
- 클래스, 인터페이스, 데이터 구조체, 열거형, 컬렉션, 비동기 호출, 재정의를 실행하는 공유 픽스처를 사용한다.
- 모든 언어 바인딩에서 동등한 동작을 확인하는 데 준수 테스트를 사용한다.
### 픽스처와 보고서
- 실제 사용자가 쓰는 것과 동일한 컴파일러로 공유 픽스처를 컴파일한다.
- 픽스처가 변경되면 영향을 받는 모든 생성기, 리플렉션 도구, 런타임 스냅샷을 새로 만든다.
- 각 준수 사례는 한 번만 정의하고, 모든 언어 바인딩이 이를 구현하거나 명시적으로 분류하도록 요구한다.
- 사례나 결과가 변경될 때마다 준수 보고서를 다시 생성하여 커밋한다.
- 언어 바인딩이 테스트 모음 정의에 없는 사례를 추가하거나 보고서가 오래된 경우 검증을 실패 처리한다.
## 스냅샷과 보고서를 갖춰 변경을 릴리스하고, 강제 푸시는 하지 않는다
생성 코드, 프로토콜 형식, 런타임 라이브러리, 보고서를 서로 조율해야 하는 릴리스 표면으로 취급한다.
### 양보할 수 없는 제약
- 모든 대상에서 검증하지 않은 채 내보내기 타입 제한을 완화하지 않는다.
- 호스트 런타임을 함께 업데이트하지 않은 채 프로토콜 작업이나 태그가 지정된 직렬화 형식을 변경하지 않는다.
- 스냅샷을 업데이트하고 검토하지 않은 채 생성 패턴을 변경하지 않는다.
- 런타임 라이브러리가 의도적으로 단일 스레드로 설계된 경우 스레드 안전성을 가정하지 않는다.
- 빌드 그래프가 순환을 거부하는 경우 순환 패키지 의존성을 도입하지 않는다.
- 묶인 출력을 직접 수정하지 않는다.
### 풀 리퀘스트와 인계
- 저장소에서 요구하는 제목 형식, 범위 규칙, 기여 라이선스 확인을 사용한다.
- 소스 변경에 필요한 모든 생성 스냅샷과 준수 보고서를 포함한다.
- 관련 없는 생성 파일, 잠금 파일 변경, 로컬 설정은 변경 사항에서 제외한다.
- 원격 브랜치가 앞서 나갔다면 가져온 뒤 리베이스하거나 병합하고, 충돌을 해결하고 검증한 다음 정상적으로 푸시한다. 절대 강제 푸시하지 않는다.
- 기억에 의존하지 말고 하위 저장소 또는 문서 동기화를 릴리스 점검표에 기록한다.
