# Spinel: Ruby AOT 네이티브 컴파일러

> Clean Markdown view of GeekNews topic #28871. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=28871](https://news.hada.io/topic?id=28871)
- GeekNews Markdown: [https://news.hada.io/topic/28871.md](https://news.hada.io/topic/28871.md)
- Type: GN+
- Author: [xguru](https://news.hada.io/@xguru)
- Published: 2026-04-25T12:44:29+09:00
- Updated: 2026-04-25T12:44:29+09:00
- Original source: [github.com/matz](https://github.com/matz/spinel)
- Points: 1
- Comments: 1

## Topic Body

- Ruby 코드를 **독립 실행형 네이티브 바이너리**로 바꾸고, 전체 프로그램 단위 **타입 추론**과 C 코드 생성을 통해 최신 CRuby `miniruby` 대비 기하평균 약 11.6배 빠른 실행을 노림
- 컴파일 파이프라인은 Prism 기반 파서로 Ruby를 **AST 텍스트**로 바꾼 뒤 self-hosting 백엔드가 타입 추론과 C 코드 생성을 수행하고, 표준 C 컴파일러로 standalone 바이너리를 만듦
- 컴파일러 백엔드는 Ruby로 작성된 **self-hosting 구조**를 갖고 있으며, 부트스트랩 과정을 거쳐 `gen2.c == gen3.c`가 성립해 자기 자신을 다시 컴파일하는 루프가 닫힘
- 문자열 연결 평탄화, value-type promotion, loop-invariant length hoisting, static symbol interning, bigint 자동 승격 같은 **컴파일 타임 최적화**를 넣고, 내장 regexp 엔진과 bigint, 단일 헤더 런타임으로 외부 런타임 의존성을 줄임
- `eval`, 메타프로그래밍, Thread, 일반적인 인코딩 처리는 지원하지 않지만, **Ruby 없이 실행되는 배포 형태**와 계산 집약적 워크로드에서의 큰 성능 차이로 Ruby AOT 컴파일의 실용성이 드러남

---

### 동작 방식
- 컴파일 파이프라인은 Ruby 파일을 파싱해 **AST 텍스트 파일**로 직렬화한 뒤, 타입 추론과 C 코드 생성을 거쳐 표준 C 컴파일러로 네이티브 바이너리를 만드는 흐름으로 이루어짐
- `spinel_parse`는 Prism과 libprism을 사용해 Ruby를 파싱하며, C 바이너리가 없을 때는 CRuby와 Prism gem을 사용하는 대체 경로를 사용함
- `spinel_codegen`은 self-hosted 네이티브 바이너리로 동작하며, AST를 받아 **타입 추론 + C 코드 생성**을 수행함
- 최종 단계는 `cc -O2 -Ilib -lm`으로 C 소스와 런타임 헤더를 함께 컴파일하며, 결과 바이너리는 **standalone** 형태로 만들어짐

### Self-Hosting
- 부트스트랩 체인은 `CRuby + spinel_parse.rb`로 AST를 만들고, `CRuby + spinel_codegen.rb`로 `gen1.c`와 `bin1`을 만든 뒤, 다시 생성된 바이너리로 `gen2.c`, `gen3.c`를 만드는 방식으로 닫힘
- `gen2.c == gen3.c`가 성립해 **bootstrap loop**가 닫혔음을 명시함
- 백엔드인 `spinel_codegen.rb`는 Spinel이 직접 컴파일 가능한 Ruby 부분집합으로 작성됨
  - classes, `def`, `attr_accessor`
  - `if`/`case`/`while`
  - `each`/`map`/`select`, `yield`
  - `begin`/`rescue`
  - String, Array, Hash 연산과 File I/O
- 백엔드에는 **metaprogramming**, `eval`, `require`를 넣지 않음

### 성능과 벤치마크
- 테스트는 **74개 통과**, 벤치마크는 **55개 통과** 상태임
- 28개 벤치마크 기준 기하평균은 최신 CRuby `miniruby` 대비 **약 11.6배 빠름**
- 비교 기준은 번들 gem이 없는 최신 CRuby `miniruby` 빌드이며, 시스템 `ruby` 3.2.3보다 더 빠른 기준과 비교했어도 계산 집약적 워크로드에서 우위가 큼
- ## 계산 성능
  - `life`는 20ms 대 1,733ms로 **86.7배** 빠름
  - `ackermann`은 5ms 대 374ms로 **74.8배** 빠름
  - `mandelbrot`는 25ms 대 1,453ms로 **58.1배** 빠름
  - `fib` 재귀 버전은 17ms 대 581ms로 **34.2배** 빠름
  - `nqueens`는 10ms 대 304ms로 **30.4배** 빠름
  - `tarai`는 16ms 대 461ms로 **28.8배** 빠름
  - `tak`는 22ms 대 532ms로 **24.2배** 빠름
  - `matmul`은 13ms 대 313ms로 **24.1배** 빠름
  - `sudoku`는 6ms 대 102ms로 **17.0배** 빠름
  - `partial_sums`는 93ms 대 1,498ms로 **16.1배** 빠름
  - `fannkuch`는 2ms 대 19ms로 **9.5배** 빠름
  - `sieve`는 39ms 대 332ms로 **8.5배** 빠름
  - `fasta`는 3ms 대 21ms로 **7.0배** 빠름
- ## 데이터 구조와 GC
  - `rbtree`는 24ms 대 543ms로 **22.6배** 빠름
  - `splay tree`는 14ms 대 195ms로 **13.9배** 빠름
  - `huffman`은 6ms 대 59ms로 **9.8배** 빠름
  - `so_lists`는 76ms 대 410ms로 **5.4배** 빠름
  - `binary_trees`는 11ms 대 40ms로 **3.6배** 빠름
  - `linked_list`는 136ms 대 388ms로 **2.9배** 빠름
  - `gcbench`는 1,845ms 대 3,641ms로 **2.0배** 빠름
- ## 실제 프로그램
  - `json_parse`는 39ms 대 394ms로 **10.1배** 빠름
  - `bigint_fib` 1000자리 계산은 2ms 대 16ms로 **8.0배** 빠름
  - `ao_render`는 417ms 대 3,334ms로 **8.0배** 빠름
  - `pidigits`는 2ms 대 13ms로 **6.5배** 빠름
  - `str_concat`는 2ms 대 13ms로 **6.5배** 빠름
  - `template engine`은 152ms 대 936ms로 **6.2배** 빠름
  - `csv_process`는 234ms 대 860ms로 **3.7배** 빠름
  - `io_wordcount`는 33ms 대 97ms로 **2.9배** 빠름

### 지원하는 Ruby 기능
- **Core** 기능으로 classes, inheritance, `super`, `include` 믹스인, `attr_accessor`, `Struct.new`, `alias`, module constants, 내장 타입에 대한 open classes를 지원함
- **Control Flow**로 `if`/`elsif`/`else`, `unless`, `case`/`when`, `case`/`in` 패턴 매칭, `while`, `until`, `loop`, `for..in`, `break`, `next`, `return`, `catch`/`throw`, `&.`를 지원함
- **Blocks**로 `yield`, `block_given?`, `&block`, `proc {}`, `Proc.new`, `-> x { }`, `method(:name)`를 지원하며, `each`, `map`, `select`, `reduce`, `sort_by`, `times`, `upto`, `downto` 같은 블록 메서드도 포함함
- **Exceptions**로 `begin`/`rescue`/`ensure`/`retry`, `raise`, 사용자 정의 예외 클래스를 지원함
- **Types**는 Integer, Float, String, Array, Hash, Range, Time, StringIO, File, Regexp, Bigint, Fiber를 포함함
  - 다형성 값은 **tagged unions**로 처리함
  - 자기 참조 데이터 구조를 위해 nullable object types `T?`를 둠
- **Global Variables**는 `$name`을 정적 C 변수로 컴파일하며, 타입 불일치는 컴파일 시점에 검출함
- **I/O**는 `puts`, `print`, `printf`, `p`, `gets`, `ARGV`, `ENV[]`, `File.read/write/open`, `system()`, 백틱을 지원함

### 문자열, 정규식, 심볼, Bigint, Fiber
- **Strings**는 불변과 가변 문자열을 모두 다루며, `<<`는 자동으로 가변 문자열 `sp_String`으로 승격되어 O(n) 제자리 append를 수행함
- `+`, interpolation, `tr`, `ljust`/`rjust`/`center`와 표준 메서드가 두 문자열 표현 모두에서 동작함
- `s[i] == "c"` 같은 비교는 char 배열에 직접 접근하도록 최적화되어 **할당 없이** 처리됨
- `a + b + c + d` 같은 연결은 `sp_str_concat4` 또는 `sp_str_concat_arr` 한 번으로 평탄화되어 **N-1개 적은 할당**으로 바뀜
- 루프 안의 `str.split(sep)`는 같은 `sp_StrArray`를 반복 재사용하며, `csv_process`에서 **400만 개 할당 제거**가 일어남
- **Regexp**는 외부 의존성 없는 내장 **NFA regexp engine**을 사용함
  - `=~`, `$1`-`$9`, `match?`, `gsub`, `sub`, `scan`, `split`를 지원함
- **Bigint**는 mruby-bigint 기반 임의 정밀도 정수를 사용함
  - `q = q * k` 같은 루프 곱셈 패턴에서 자동 승격됨
  - 정적 라이브러리로 링크되며 실제 사용할 때만 포함됨
- **Fiber**는 `ucontext_t` 기반 협력적 동시성을 제공함
  - `Fiber.new`, `Fiber#resume`, `Fiber.yield`와 값 전달을 지원함
  - 자유 변수는 heap-promoted cells로 캡처함
- **Symbols**는 문자열과 분리된 `sp_sym` 타입으로 구현됨
  - `:a != "a"`를 유지함
  - 심볼 리터럴은 컴파일 시점에 intern되어 `SPS_name` 상수가 됨
  - `String#to_sym`은 필요할 때만 동적 풀을 사용함
  - 심볼 키 해시는 `sp_SymIntHash`를 사용해 문자열 대신 정수 키를 직접 저장하므로 **strcmp와 동적 문자열 할당**이 사라짐

### 메모리 관리와 값 타입
- 메모리 관리는 **mark-and-sweep GC**를 사용하며, size-segregated free lists, non-recursive marking, sticky mark bits를 포함함
- 작고 단순한 클래스는 자동으로 **value types**로 승격되어 스택에 배치됨
  - 조건은 스칼라 필드 8개 이하
  - 상속 없음
  - 파라미터를 통한 변경 없음
- 필드 5개짜리 클래스 100만 회 할당은 85ms에서 **2ms**로 줄어듦
- 값 타입만 사용하는 프로그램은 **GC 런타임 자체를 전혀 내보내지 않음**

### 최적화
- 전체 프로그램 단위 타입 추론을 바탕으로 여러 **컴파일 타임 최적화**를 수행함
- **Value-type promotion**으로 작은 불변 클래스는 C struct 스택 객체가 되어 GC 오버헤드를 없앰
- **Constant propagation**으로 `N = 100` 같은 단순 리터럴 상수는 `cst_N` 조회 없이 사용 지점에 직접 인라인됨
- **Loop-invariant length hoisting**으로 `while i < arr.length`는 루프 전에 길이를 한 번만 계산함
  - 본문에서 `arr.push`처럼 수신 객체를 바꾸면 이 hoist는 비활성화됨
- **Method inlining**은 짧고 재귀가 아닌 메서드 3문장 이하에 `static inline`을 붙여 gcc 인라이닝을 유도함
- **String concat chain flattening**은 연결 체인을 단일 호출로 줄여 중간 문자열 생성을 없앰
- **Bigint auto-promotion**은 자기 참조 덧셈이나 반복 곱셈 패턴을 bigint로 자동 승격함
- **Bigint `to_s`** 는 mruby-bigint의 `mpz_get_str`을 써서 divide-and-conquer **O(n log²n)** 으로 처리함
- **Static symbol interning**은 `"literal".to_sym`을 컴파일 타임 `SPS_&lt;name&gt;` 상수로 바꾸며, 동적 interning을 쓸 때만 런타임 풀을 넣음
- `sub_range`에서 길이가 hoist된 문자열은 `sp_str_sub_range_len`을 사용해 내부 `strlen` 호출을 건너뜀
- 루프 내부 `line.split(",")`는 기존 `sp_StrArray`를 재사용함
- **Dead-code elimination**은 `-ffunction-sections -fdata-sections`와 `--gc-sections`를 사용해 쓰이지 않는 런타임 함수를 최종 바이너리에서 제거함
- **Iterative inference early exit**는 param, return, ivar의 서명 배열 3개가 더 이상 바뀌지 않으면 고정점 루프를 즉시 중단함
  - 대부분 프로그램은 4회 전체 반복 대신 1~2회에 수렴함
  - bootstrap 시간은 약 **14%** 줄어듦
- **`parse_id_list` byte walk**는 self-compile 동안 약 12만 회 호출되는 AST 필드 리스트 파서를 `s.split(",")` 대신 `s.bytes[i]` 수동 순회로 바꿔 호출당 할당을 N+1개에서 **2개**로 낮춤
- 생성된 C 코드는 기본 경고 수준에서 **warning-free build**를 유지하며, 하니스는 `-Werror`를 사용해 회귀를 즉시 드러내게 함

### 아키텍처
- 저장소 구조는 다음 요소로 나뉨
  - `spinel`: POSIX shell 기반 **원커맨드 래퍼 스크립트**
  - `spinel_parse.c`: libprism에서 텍스트 AST로 가는 C 프런트엔드 1,061줄
  - `spinel_codegen.rb`: AST에서 C 코드로 가는 컴파일러 백엔드 21,109줄
  - `lib/sp_runtime.h`: 런타임 라이브러리 헤더 581줄
  - `lib/sp_bigint.c`: 임의 정밀도 정수 5,394줄
  - `lib/regexp/`: 내장 regexp 엔진 1,759줄
  - `test/`: 기능 테스트 74개
  - `benchmark/`: 벤치마크 55개
  - `Makefile`: 빌드 자동화
- 런타임 `lib/sp_runtime.h`는 GC, array/hash/string 구현과 기타 런타임 지원을 **하나의 헤더 파일**에 담음
- 생성된 C 코드는 이 헤더를 include하며, 링커는 `libspinel_rt.a`에서 필요한 부분만 가져옴
  - bigint
  - regexp engine
- 파서는 두 구현을 가짐
  - `spinel_parse.c`는 libprism을 직접 링크해 **CRuby 없이** 동작함
  - `spinel_parse.rb`는 Prism gem을 사용하는 **CRuby fallback**임
- 두 파서는 동일한 AST 출력을 만들며, `spinel` 래퍼는 가능하면 C 바이너리를 우선 사용함
- `require_relative`는 파싱 시점에 해결되어 참조된 파일이 인라인됨

### 제약 사항
- **No eval**: `eval`, `instance_eval`, `class_eval`은 지원하지 않음
- **No metaprogramming**: `send`, `method_missing`, 동적 `define_method`는 지원하지 않음
- **No threads**: `Thread`, `Mutex`는 지원하지 않으며 Fiber만 지원함
- **No encoding**: UTF-8과 ASCII를 가정함
- **No general lambda calculus**: 깊게 중첩된 `-> x { }`와 `[]` 호출은 다루지 않음

### 의존성과 실행 모델
- 빌드 시 의존성은 [libprism](https://github.com/ruby/prism) C 라이브러리와 초기 부트스트랩용 CRuby임
- 런타임 의존성은 없으며, 생성된 바이너리는 **libc + libm**만 필요함
- 정규식은 내장 엔진을 써서 외부 라이브러리가 필요 없음
- Bigint는 내장되어 있지만 실제 사용할 때만 링크됨
- [Prism](https://github.com/ruby/prism)은 `spinel_parse`가 사용하는 Ruby 파서임
  - `make deps`는 rubygems.org의 prism gem tarball을 내려받아 C 소스를 `vendor/prism`에 풂
  - 이미 prism gem이 설치되어 있으면 자동 감지함
  - `PRISM_DIR=/path/to/prism`으로 사용자 경로를 지정할 수도 있음
- CRuby는 초기 bootstrap에만 필요하며, `make` 이후에는 전체 파이프라인이 **Ruby 없이** 실행됨

### 프로젝트 이력
- Spinel은 처음에 C로 구현되었고, 규모는 **18K lines**였으며 `c-version` 브랜치에 남아 있음
- 이후 Ruby로 다시 작성한 `ruby-v1` 브랜치를 거쳤음
- 현재 `master`는 self-hosting 가능한 Ruby 부분집합으로 다시 작성된 버전임

### 라이선스
- MIT License를 사용함
- [LICENSE](LICENSE) 파일을 따름

## Comments



### Comment 56280

- Author: neo
- Created: 2026-04-25T12:44:31+09:00
- Points: 1

###### [Hacker News 의견들](https://news.ycombinator.com/item?id=47887334) 
- Matz가 만든 거라면 **Ruby semantics**의 한계도 잘 알고 있을 테니 신뢰가 감  
  제 석사 논문도 **AOT JS compiler**였는데, 돌아가긴 했지만 입력 데이터 제약이 커서 결국 접었음  
  당시 JS 개발자들은 스스로 제약을 잘 지키는 쪽에 익숙하지 않았고, `JSON.parse`처럼 본질적으로 알 수 없는 입력이 걸림돌이었음  
  지금은 TypeScript 덕분에 그때보다 훨씬 현실적일 수도 있음  
  일반적인 **lambda calculus**만 봐도 타입 추론의 한계는 분명하고, Matt Might 쪽 논문이나 Shed-skin Python 작업에서도 비슷한 제약이 드러남  
  `eval`, `send`, `method_missing`, `define_method`가 실제 Ruby 코드에서 얼마나 흔한지 궁금하고, 타입 없는 파싱, 예를 들어 JSON 입력은 보통 어떻게 처리하는지도 궁금함
  - 이 설계는 꽤 **pragmatic**해 보임  
    Ruby 파싱은 번역 자체보다 더 어려울 정도라서 **Prism**을 쓰고, 결과는 C를 생성함  
    기본적인 Ruby semantics 자체는 구현이 그렇게까지 어렵지 않음  
    반대로 나는 순수 Ruby로 만든 오래된 **self-hosting AOT compiler**를 붙들고 있는데, 자체 파서를 쓰겠다고 고집해서 일부러 훨씬 힘든 길을 탔음  
    초반 80%는 대충 만들어도 Ruby 코드 상당수가 돌아간다는 걸 일찍 배웠고, 진짜 어려운 "두 번째 80%"는 Matz가 이번 프로젝트와 mruby에서 뺀 것들, 예를 들면 인코딩이나 온갖 주변 기능들에 몰려 있음  
    솔직히 Ruby에는 실제 코드에서 한 번도 못 본 기능도 꽤 있어서, 몇몇은 deprecated돼도 이상하지 않다고 봄  
    `send`, `method_missing`, `define_method`는 아주 흔함  
    제약은 mruby와 비슷하고, 그런 제약 아래에서도 쓸 곳은 있음  
    `send`, `method_missing`, `define_method` 지원은 비교적 쉬운 편임  
    반면 **eval()** 지원은 엄청 고통스러움  
    다만 Ruby에서 `eval()`의 큰 비율은 정적으로 **instance_eval의 block 버전**으로 환원 가능해서, 그런 경우엔 AOT 컴파일이 꽤 쉬워짐  
    예를 들어 `eval()`에 들어가는 문자열을 정적으로 알 수 있거나 분해할 수 있으면 해결 여지가 큼  
    실제로 많은 `eval()` 사용은 불필요하거나 단순한 introspection 우회에 가까워서, 정적 검사로 처리 가능함  
    내 컴파일러도 그게 병목이 되면 거기부터 손댈 생각임
  - 이런 기능이 꽤 많아야 **Rails식 magic**을 만들 수 있음  
    타입 없는 JSON ingestion도 아마 그런 메커니즘을 쓸 가능성이 큼  
    그걸 걷어내면 Crystal만큼 강한 타입은 아니지만 공식 Ruby만큼 메타프로그래밍에 기대지도 않는, 작고 읽기 쉬운 언어가 남음  
    그래서 잠재력은 꽤 커 보이지만, 결국 시간 지나야 판단 가능함
  - Ruby를 **Objective-C**로 컴파일하는 방식이라면 Ruby 기능을 전부 지원하면서도 인터프리터 Ruby보다 더 빠를 수 있을 것 같음
  - 나는 `eval`을 자주 쓰는 쪽임  
    안 쓸 수도 있겠지만, 내겐 그게 더 ergonomic함
  - 내 경험상 흥미로운 건 `eval`, `exec`, `define_method`, 그리고 `Class.new`, `Struct.new`로 새 클래스를 만드는 패턴임  
    이들 사용 대부분은 앱 **boot 시점**이나 파일을 require하는 동안 몰려 있고, 어떤 면에선 이미 컴파일 단계와 비슷함

- 이건 **RubyKaigi 2026**에서 Matz가 방금 발표한 것임  
  실험적이긴 하지만 Claude 도움을 받아 약 한 달 만에 만들었고, 라이브 데모도 성공했음  
  이름은 Matz의 새 고양이에서 따왔고, 그 고양이 이름은 Card Captor Sakura의 고양이 이름에서 왔으며, 거기서 또 Ruby라는 이름의 캐릭터와 짝을 이룸
  - 사람들이 AI가 프로그램을 처음부터 끝까지 다 만든다는 얘기를 많이 하지만, 더 현실적인 시나리오는 **10x programmer**를 **100x programmer**로 만들어 주는 쪽이라고 봄  
    Matz 같은 사람에겐 100x를 500x로 밀어 올리는 셈일 수도 있음
  - 내 머릿속 최신 Spinel은 Steven Universe 쪽이라서 **Spinel/Ruby (Moon) pun**은 전혀 못 알아봤는데, 알고 나니 하루가 즐거워졌음
  - 나는 당연히 **광물 spinel** 얘기인 줄 알았음 :)  
    [https://en.wikipedia.org/wiki/Spinel](<https://en.wikipedia.org/wiki/Spinel>)
  - 고맙다  
    영상은 아직 라이브가 아닌 것 같고, 여기 채널에 하나씩 올라오는 듯함  
    [https://www.youtube.com/@rubykaigi4884/videos](<https://www.youtube.com/@rubykaigi4884/videos>)
  - 그 고양이 이름 유래 얘기는 **Ruby Central drama**와 Spinel.coop 창립자들과의 관계를 생각하면 꽤 수상해 보임  
    프로젝트 이름도 감정적으로 지은 것처럼 느껴짐

- 분명 엄청 인상적이지만, **AI agent** 없이는 유지보수 불가능해 보임  
  `spinel_codegen.rb`가 2만 1천 줄이고, 어떤 메서드는 중첩이 15단계까지 감  
  원래 컴파일러 코드는 예쁘기 어렵지만, 이건 그 기준으로도 사람이 관리하기 매우 힘들어 보임
  - 컴파일러 코드는 시간만 있으면 충분히 **예쁘게** 만들 수 있음  
    컴파일러는 서브시스템 경계가 뚜렷하고 단계별 handoff도 명확해서, 오히려 가장 modular하게 만들기 쉬운 축에 듦  
    문제는 대개 일단 돌아가게 만든 뒤 리팩터링할 시간이 없다는 데 있고, 그러면 지저분함이 계속 불어남
  - `spinel_codegen.rb`는 거의 **eldritch horror** 수준임  
    Claude를 쓰면 나도 늘 이런 스파게티 코드가 나오는데, 내가 뭘 잘못하고 있나 싶었음  
    그런데 최고 수준 프로그래머라고 생각하는 사람이 만든 진짜 흥미로운 프로젝트에서도 코드 질이 군데군데 꽤 나쁜 걸 보니, 나만 그런 건 아니었음  
    예를 들어 `infer_comparison_type()`은 최악의 사례까진 아니고 읽기 어렵지도 않지만, 훨씬 더 단순하고 명확한 구현이 있는데도 Claude가 거기로 못 감  
    `Set`으로 비교 연산자를 모아두고 `include?`로 처리하면 더 짧고 빠르고 읽기 쉽고 유지보수도 쉬움  
    그런데 Claude는 늘 **if-return 연쇄**로 흘러가고, 심지어 if-else조차 낯설어하는 느낌임  
    내 Claude 코드베이스도 그런 패턴으로 가득한데, 이제 나만 그런 게 아니라는 걸 알겠음  
    반면 다른 파일들은 훨씬 낫고, 특히 `lib` 디렉터리는 메인 Ruby repo의 `ext` 디렉터리와 대응되는 듯하며 품질이 괜찮음  
    API도 MRI Ruby 영향을 분명히 받았고, 구현은 꽤 다르더라도 Matz가 원본 API 일부를 닮게 유도해서 출력이 더 정돈된 것 같음  
    [1] [https://github.com/matz/spinel/blob/98d1179670e4d6486bbd1547...](<https://github.com/matz/spinel/blob/98d1179670e4d6486bbd15473a68ecdb1c4309cb/spinel_codegen.rb#L1600>)
  - 지금 단계에선 사람이 손으로 유지보수 가능한지가 그렇게 중요하진 않다고 봄  
    테스트와 벤치마크만 통과하면 일단 만족함  
    다만 거대한 파일이 AI에게도 다루기 쉬운지는 의문임  
    나는 파일을 300줄 안쪽으로 제한하려고 하고, 사람이 이해하기 쉬운 코드가 **coding agents**에게도 쉬울 거라고 생각함

- 제약은 이렇다고 함  
  **No eval**: `eval`, `instance_eval`, `class_eval`  
  **No metaprogramming**: `send`, `method_missing`, `define_method`(동적)  
  **No threads**: `Thread`, `Mutex`(Fiber는 지원)  
  **No encoding**: UTF-8/ASCII 가정  
  **No general lambda calculus**: `[]` 호출을 동반한 깊은 중첩 `-> x { }`  
  UTF-8/ASCII 가정은 개인적으로 큰 제약이 아니지만, 나머지는 꽤 많은 프로그램에서 실제 제약이 될 듯함  
  그리고 이걸 다시 넣으려면 상당한 작업이 필요해 보임
  - 이러면 Ruby의 **magic** 상당 부분이 사라짐

- Ruby를 오래 써 왔고, 나열된 기능을 전부 활용해 본 입장에서 보면, 오히려 내가 진화 끝에 원하게 된 건 이런 버전의 **단순한 Ruby**임  
  더 단순하고 이해하기 쉬운데도 Ruby 특유의 미적 감각은 남아 있음  
  이제는 LLM 덕분에 코드 생성 생산성이 워낙 높아서, 예전처럼 개발자 생산성을 위해 메타프로그래밍으로 boilerplate를 줄일 필요가 덜함  
  개발자가 직접 코드를 쓰는 비중 자체가 줄고 있기 때문임
  - 그게 원하는 게 단지 **Ruby aesthetic**이라면 Crystal도 잘 맞을 수 있음  
    문법은 비슷하고 정적 타입 시스템이 있어서, 더 효율적인 컴파일 코드로 이어짐
  - `eval`이 없는 건 차라리 낫다고 보지만, **threads와 mutexes**까지 없는 건 아쉬움  
    `define_method` 부재는 용도를 생각하면 이해가 감  
    하지만 `send`와 `method_missing`은 기존 라이브러리에서 흔하고, 구현도 메모리 lookup table을 컴파일 시점에 구성하는 식으로 아주 어렵진 않을 것 같음  
    그래서 의도적으로 뺀 건지, 아직 거기까지 못 간 건지 모르겠음  
    내 바람은 후자지만, 최소한 당장은 호환성 때문에 실무 투입은 어려울 듯함
  - 메타프로그래밍의 장점은 원래 **코드를 덜 쓰는 것**이 아니었음  
    **읽어야 할 코드를 줄이는 것**이었음

- 이거 정말 멋지고, 나는 오랫동안 **Ruby용 AOT compiler**를 기다려 왔음  
  다만 `eval`이나 메타프로그래밍 fallback이 없는 건 아쉽고, 그래도 작은 고성능 subset에 집중하려고 그렇게 한 것 같음  
  이 AOT 컴파일러로 만든 gem이 MRI와 잘 상호작용하면 좋겠음  
  표준 Ruby와 gem을 패키징하거나 번들링하는 쪽은 여전히 tebako, kompo, ocran이 필요하고, 예전엔 ruby-packer, traveling ruby, jruby warbler 같은 프로젝트들도 있었음  
  선택지가 하나 더 생긴 건 좋지만, 더 나은 개발자 UX를 갖춘 **결정판**이 나오길 바라고 있음
  - 맞음, 나도 최근에 **warbler**를 포크해야 했음  
    너무 오랫동안 업데이트가 없었기 때문임

- 왜 **no threads**인지 궁금함  
  Ruby scheduler와 밑단의 pthread 구현은 C 영역에서도 잘 동작할 것 같은데, 혹시 zero dependency를 노린 건가 싶음  
  optional extension을 나중에 넣을 계획이거나 아직 뺀 상태가 아니라면, 이 선택은 좀 이상하게 느껴짐
  - 이걸 의도적으로 **지원 안 하기로 결정했다**는 근거는 아직 못 봤음  
    아마 그냥 아직 거기까지 못 간 것 아닐까 싶음  
    멀티스레딩은 원래 제대로 만들기 아주 어려움

- 한 달 조금 넘는 기간에 만들었다니 놀라움  
  AI에 대해 뭐라 하든, **실력 있는 개발자** 손에 들어가면 엄청난 속도 향상을 만들어 냄
  - 업계 전체는 agent harness, SOUL.md, 권한 설정, skills, MCPs, hooks, env 다 깔고 시작하는데  
    Matz는 그냥 `gem env|info`랑 `find`면 충분한 느낌임

- 이게 Matz가 만든 만큼, 앞으로 **Ruby core**의 일부가 될 가능성이 얼마나 현실적인지 궁금함  
  그리고 그렇게 되면 Crystal에 얼마나 위협적일지도 궁금함
  - Crystal은 명시적인 **static type system**이 있고, 언어 차원에서 AOT 컴파일에 최적화돼 있음  
    이런 특성은 큰 프로그램을 컴파일하고 유지하는 데 사실상 필수에 가까움  
    반면 이건 제한된 Ruby subset용이라서, 인기 있는 Ruby gem 대부분은 그대로는 돌아가지 않을 것임  
    C 컴파일을 지향하는 언어 subset이라는 점에서 **PreScheme**에 더 가까워 보임  
    지금 단계에선 둘이 같은 영역에서 직접 경쟁한다고 보지 않음  
    완전한 Ruby는 거의 확실히 JIT가 필요함  
    [1]: [https://prescheme.org/](<https://prescheme.org/>)
  - 다른 관점으로 보면, 결국은 LLM이 우리가 원하는 어떤 언어로든 **formal specification**을 쏟아내는 지점에 도달할 것 같음  
    Rational Unified Process, Enterprise Architect 같은 도구들의 복수전이 벌어지는 셈임  
    차이가 있다면 UML 다이어그램 대신 markdown 파일이 온다는 정도임

- 이건 **infrastructure tools** 쪽에서 유용할 것 같음  
  예를 들어 Ruby로 쓰였지만 정적으로 컴파일되는 bundler가 있어서, RVM 같은 Ruby 설치 도구 역할까지 같이 해 준다고 상상해 볼 수 있음  
  기존 Ruby buildpack은 Ruby로 작성돼 있지만 부트스트랩을 bash로 해야 해서 짜증나고 edge case도 생김  
  CNB는 그 문제를 피하려고 Rust로 쓰였고, **의존성 없는 단일 바이너리**를 배포할 수 있다는 발상은 정말 강력함
