Shopify에서 Sorbet은 현재 75,000개의 파일 중 99%를 검사하고 있으며, 150만 개 메서드의 71%에 타입 시그니처가 적용되어 생산 환경에서의 오류를 크게 줄이고 있습니다. 그럼에도 불구하고, sig {}
및 T.let
과 같은 Sorbet의 DSL 문법은 복잡성을 추가하고 sorbet-runtime
gem의 의존성으로 인해 런타임 성능 오버헤드가 발생하여 프로덕션 환경에서는 런타임 타입 검사를 비활성화하는 등의 한계가 있었습니다. 이러한 배경에서 Ruby 3.0의 RBS가 대안으로 부상했습니다. RBS는 .rbs
파일을 통해 타입 정의를 코드와 분리하여 간결하게 표현할 수 있지만, 75,000개에 달하는 Shopify의 파일에서 .rbs
파일을 별도로 관리하는 것은 또 다른 중복 관리 문제를 야기했습니다. 또한, 초기 RBS는 지역 변수 타이핑을 직접 지원하지 않고 대규모 코드베이스를 위한 확장 가능한 타입 체커가 부족했습니다.
Shopify 팀은 이러한 RBS의 장점과 Sorbet의 강력한 기능을 결합하여 Ruby 파일 내에 직접 타입 주석을 달 수 있는 간결한 문법을 구현하는 비전을 세웠습니다. 그 결과, #: (Array[String]) -> String
과 같은 주석 기반의 RBS 문법을 개발하여 코드 가독성을 높이고 런타임 의존성을 제거했습니다. 이 기능은 Sorbet의 기존 타입 검사 파이프라인에 새로운 단계를 추가하여 구현되었습니다. 파서(parser)와 디슈가러(desugarer) 사이에 RBS 주석을 찾아 해당 AST(Abstract Syntax Tree) 노드와 연결하고, RBS 내용을 파싱하여 동등한 Sorbet sig
및 T.let
구조로 AST 내에 직접 삽입하는 방식입니다. 이를 통해 Sorbet의 나머지 파이프라인은 변경 없이 원활하게 작동할 수 있습니다. 현재 Sorbet은 인스턴스 및 싱글톤 메서드에 대한 RBS 시그니처 주석(#: ...
), 여러 줄에 걸친 시그니처(#|:
), 속성 타이핑(#: String? attr_reader :nickname
), 지역/전역/인스턴스/클래스 변수 및 상수에 대한 타입 지정(#: String
), 타입 캐스팅(#: as String
), 그리고 T.must
에 해당하는 #: as !nil
과 같은 다양한 RBS 주석 기능을 지원합니다. 이러한 주석은 표현식 중간에서도 타입 단언(type assertion)으로 활용될 수 있어 유연성을 제공합니다.