
26시간 동안 네 번의 릴리스: 언어 설정 배관 공사와 "브리지 릴리스"
GeekBye v1.7.6부터 v1.8.1까지: 노트북 밖으로 한 번도 나가 본 적 없던 언어 설정, 진짜 키바인딩을 보여주는 Cmd+/ 오버레이, 코드 변경이 하나도 없는 버전 — 그리고 자동 업데이트 피드를 옮길 때의 닭과 달걀 문제.
2026년 2월의 어느 목요일 아침부터 금요일 오후 사이, GeekBye는 네 번의 릴리스를 출시했습니다: v1.7.6, v1.7.7, v1.8.0, 그리고 v1.8.1 — 총 16개 커밋, 첫 태그부터 마지막 태그까지 약 26시간. 어느 하나도 헤드라인감은 아닙니다. 하지만 합쳐 놓고 보면, 작은 릴리스가 이어지는 한 주에 실제로 무엇이 담기는지를 보여주는 초상화입니다: 마침내 여정을 끝낸 기능, 삶의 질을 높이는 오버레이, 코드가 하나도 없는 버전 번호, 그리고 진짜로 교훈적인 릴리스 엔지니어링 한 편.
노트북 밖으로 나가지 못한 언어
GeekBye에는 2025년 9월부터 트랜스크립트 언어 설정이 있었습니다. 잘 작동했습니다 — 로컬에서는요. 스페인어를 고르면 온디바이스 음성 인식 파이프라인은 충실하게 스페인어를 받아 적었습니다. 하지만 v1.7.5 코드베이스를 그 설정으로 grep해 보면 정확히 세 곳에서만 나옵니다: 오디오 핸들러, Settings UI, 그리고 설정 훅. API 클라이언트에는 없습니다. 세션 트래커에도 없습니다. 4개월 동안 머신을 떠나는 모든 요청은 언어를 몰랐습니다: 백엔드 세션 기록은 자기가 어떤 언어를 담고 있는지 몰랐고, AI 채팅은 스페인어라는 힌트가 전혀 없는 스페인어 트랜스크립트를 받았습니다.
v1.7.6은 이 설정이 여정을 끝낸 릴리스입니다 — 커밋 두 개, 하루. 오디오 세션 시작은 이제 페이로드에 transcriptLanguage를 싣고, AI 스트림 채팅 요청에도 같은 필드가 생겼습니다. 설정을 읽고 auto이거나 미설정일 때는 의도적으로 undefined를 반환하는 작은 헬퍼를 통해 연결되어 있어서, 오래된 백엔드는 모르는 필드에 막히는 대신 우아하게 성능이 저하됩니다. diff 안에는 조용한 보너스 수정도 숨어 있습니다: 언어를 전달하려면 세션 시작이 언어 결정 뒤로 옮겨져야 했는데 — 우리가 읽기로는, 이것이 동시에 세션이 시작되고(한도에 카운트되고) 잠시 뒤 언어 결정이 실패하면 고아가 되던 에러 경로도 닫았습니다.
교훈은 일반화됩니다: 설정은 UI와 효과를 가진 시점에 출시된 것이 아닙니다. 그것을 알아야 하는 모든 소비자에게 도달한 시점에 출시된 것입니다. 우리 설정은 네트워크 경계에서 4개월을 멈춰 있었고, 아무도 눈치채지 못했습니다. 로컬 동작 — 눈에 보이는 동작 — 이 내내 올바랐기 때문입니다. (기록을 위해 말해 두자면, 이건 언어 처리의 투박한 첫 시도였습니다; AI가 대화의 언어로 실제로 답하도록 가르치는 것은 이 이야기의 훨씬 나중 장입니다.)
Cmd+/ — 키바인딩에 대해 거짓말하지 않는 오버레이
v1.8.0의 사용자 대상 기능은 Cmd+/로 여는 키보드 단축키 오버레이입니다 — 새 컴포넌트 193줄, 그리고 훔쳐 갈 만한 설계 결정 하나: 하드코딩된 치트 시트를 렌더링하지 않는다는 것. 라이브 단축키 설정을 가져와 customKeys || defaultKeys를 렌더링하기 때문에, 키를 리매핑한 사용자는 자신의 키를 봅니다. 키를 바꾼 사람에게 기본값을 보여주는 단축키 오버레이는 문서가 아닙니다; 예쁜 타이포그래피를 걸친 오보입니다.
나머지는 작은 정확성 손질입니다: 오버레이는 화면의 어느 쪽에 공간이 있는지에 따라 앱 카드의 왼쪽이나 오른쪽에 자리 잡고, Escape로, 다시 한 번의 Cmd+/로, 또는 바깥 클릭으로 닫힙니다. Cmd+/ 자체도 다른 모든 단축키와 같은 리매핑 가능한 프리셋 시스템에 합류합니다 — 이건 중요합니다. Cmd+/는 대부분의 에디터에서 "주석 토글"이고, 개발자를 위한 도구가 옮길 수도 없게 에디터 키를 깔고 앉아서는 안 되기 때문입니다.
브리지 릴리스
흥미로운 엔지니어링은 아무도 두 번 쳐다보지 않을 버전 번호들 안에서 일어났습니다. v1.8.0은 프로젝트의 릴리스를 새 GitHub 조직으로 옮기기도 했습니다 — 겉보기엔 org 정리 정돈이지만, 데스크톱 자동 업데이트에는 한 가지 속성이 있습니다: 피드 URL이 바이너리에 구워져 있다는 것. 모든 v1.8.0 설치본은 새 조직의 릴리스를 폴링합니다. 그리고 세상에 나가 있는 모든 설치본 — 전부 v1.7.7 이하 — 은 거기에 뭔가 나타나지 않는 한, 영원히 옛것을 폴링합니다.
그게 닭과 달걀입니다: 다음 릴리스를 새 저장소에만 게시하면 기존에 배포된 전체가 그것을 영영 보지 못합니다. v1.8.0 태그 16분 뒤, 릴리스 스크립트에 새 플래그가 착지했습니다: --publish-owner. electron-builder가 아티팩트를 업로드하는 위치를 빌드 이후에 다시 쓰는 플래그입니다 — 코드 주석의 표현을 빌리면, 명시적으로 "컴파일된 앱 코드에는 영향을 주지 않으면서". 트릭은 그게 전부이고, 그것이 정확히 올바른 이음새입니다: 바이너리에 구워진 피드와 아티팩트의 업로드 목적지는 서로 다른 결정이며, 마이그레이션에는 딱 한 번의 릴리스 동안 그 둘을 분리하는 것이 필요합니다. v1.8.1을 새 피드를 가리키도록 빌드하고, 옛 저장소에 업로드합니다; 옛 클라이언트들은 거기서 업데이트를 발견해 설치하고, 깨어나 보면 새 조직을 폴링하고 있습니다. 커밋 메시지는 이것을 "브리지 릴리스(bridge release)"라고 부르는데, 딱 맞는 이름입니다. v1.8.1은 v1.8.0의 2시간 20분 뒤에 태그됐습니다. (이 프로젝트가 자동 업데이트를 공포에 가까운 경외심으로 대하게 된 기원이 궁금하다면, 우리 앱을 죽일 수 없게 만든 자동 업데이트를 보세요.)
그럼 v1.7.7은? 커밋 하나. 버전 범프 그 자체입니다. 수정도, 되돌리기도, 히스토리 속 설명도 없습니다 — 우리의 가장 그럴듯한 해석은 다시 실행해야 했던 publish입니다. 이야기를 지어내느니 그렇게 인정하는 편이 낫습니다. 버전 번호는 때때로, 소프트웨어를 릴리스하는 일이 소스 트리의 순수 함수가 아니라 현실 세계의 딸꾹질이 있는 현실 세계의 프로세스이기 때문에 존재합니다. 애초에 왜 1.7이 1.8이 되었는가: 어떤 커밋도 말해 주지 않지만, 피드 이전이야말로 의미 있는 호환성 경계입니다 — 모든 1.8.x 바이너리는 모든 1.7.x 바이너리와 다른 조직을 바라봅니다 — 그리고 그것은 어떤 기능보다도 마이너 범프에 값합니다.
이번 주가 우리에게 가르쳐 준 세 가지
- 설정을 모든 소비자까지 추적하라. 언어 설정은 4개월 동안 "완성"이었지만 백엔드는 아무것도 몰랐다. 눈에 보이는 로컬 동작이 올바랐다는 사실이 바로 그 틈을 살아남게 했다 — 감사 질문은 "이 값을 또 누가 알아야 하는가?"이며, 모든 경계에서 물어야 한다.
- 문서가 아니라 라이브 설정을 렌더링하라. 단축키 오버레이는 단축키 시스템이 쓰는 것과 같은 단일 진실 공급원을 읽는다. 그 외의 방식은 사용자가 처음 커스터마이즈하는 순간부터 어긋난다.
- 피드 이전에는 두 세계에 동시에 사는 릴리스 하나가 필요하다. 바이너리에는 새 피드, 업로드는 옛 저장소로. 릴리스 도구가 그 두 대상을 분리하지 못한다면, 필요해지기 전에 플래그를 추가하라 — 우리 플래그는 필요해진 지 16분 뒤에 도착했는데, 그 정도면 세이프로 쳐 줄 만하다.
v1 이야기의 이전 장은 로그인 페이지가 곧 데모다(v1.7.5)를 보시고; 전체 궤적은 소프트웨어를 완벽하게 출시하는 것의 해부학을 보세요.