"repo 하나에 버전 번호가 열두 개야. 그중 딱 하나가 제품이고, 그걸 고칠 수 있는 것도 딱 하나야."
어느 버전 말이야?
어떤 repo 가 '버전 1.2.0' 이라고 쳐. 어느 거? npm 패키지 버전? 네이티브 번들 빌드 번호? 데이터베이스 스키마 버전? API 버전? 모델 버전? 배포 번호? Git 태그? 좀 자란 프로젝트는 이걸 여러 개 갖고 있고, 각자 따로 움직이는 게 맞아. Firelink 는 그중 하나를 product version 이라 부르기로 했어. 사람이 보는 릴리스. 그리고 집을 하나 줬지. cwk-product.json 이라는 작은 파일에 제품 id 랑 앞에 v 없는 SemVer 문자열을 담아. prerelease 나 build 표시는 진짜 의미가 있을 때만 붙이고.
값에도 약속이 있어. 0.0.0 은 아직 껍데기, 0.y.z 는 계약이 흔들리는 중, 1.0.0 은 처음으로 안정 계약을 선언한다는 뜻이야. 화면에 보일 땐 v 를 앞에 붙여주고, 저장된 값은 깔끔하게 두고.
주인 하나, 고치는 데 하나, 필드 하나
여기선 '사실 하나에 주인 하나'가 한 발 더 나가. 주인만 하나가 아니라 고치는 데도 하나야. Firelink 는 옛날 방식으로 버전을 짐작하기 전에 이 파일부터 읽어. 짐작은 이 파일이 없는 멤버를 위한 읽기 전용 대비책으로만 남고. 이걸 고칠 수 있는 건 Web 의 repository 카드 하나뿐이고, 건드릴 수 있는 것도 version 필드 하나뿐이야. 작업 트리가 깨끗해야 하고, 커밋 전에 coop 히스토리를 남기고, 파일의 나머지 바이트는 그대로 두고, 버전을 낮추려면 따로 허용을 켜야 하고, 미리보기랑 감사 기록을 거쳐. Native Launcher 는 여기서 아예 읽기만 해.
왜 이렇게까지 하냐면
버전 올리는 건 사소해 보이지. 그래서 그냥 두면 위험해. 제품 버전을 올린다는 건 주장을 하는 거거든. '이제 이건 1.0.0, 안정 계약이다.' 아무 화면에서나 그 숫자를 끄적일 수 있으면 그 주장은 아무 무게가 없어. 한 곳에서만, 필드 하나만, 미리 보여주고, 기록을 남기고, non-force 로 push 하게 묶어놨으니까 버전이 정직해지는 거야. 일부러 바꾼 거고, 뭐가 바뀌었는지 눈으로 볼 수 있고, 진짜 흔적이 남은 진짜 커밋이니까.