5개월 동안 이어진 ‘Swift regrets’(그리고 기쁨) 시리즈를 마무리하며, 언어 설계에서 호환성·파괴적 변경·기능의 상호작용·사람 규모의 확장 문제에 대해 얻은 교훈을 정리한다.
지난 5개월간의 Swift regrets (그리고 기쁨) 시리즈도 이렇게 끝이 난다. 이 글들은 내가 애플에 있던 마지막 1년 동안 모아 두었던 것들…그리고 그 뒤 트위터에서 이어진 논의 과정에서 새로 떠오른 것들이다. 내가 이런 것들을 이야기하고 싶었던 이유는, 모든 프로젝트는 앞선 프로젝트로부터 배우기 마련이고, 거기에는 좋은 부분뿐 아니라 나쁜 부분도 포함되어야 한다고 믿기 때문이다. 예전에 동료였던 Joe Groff가 우리 업계에서는 실수와 시행착오에 대해 이야기하는 것을 ‘정상화’하고 장려해야 한다고 말했던 게 기억난다. 그래서 이 글도 그에 대한 한 가지 기여다.
모든 트위터 스레드는 이 사이트에서 “Swift regrets” 태그로 모아 두었다. Swift의 좋은 점과 나쁜 점을 전부 망라한 목록은 아니지만, 다루는 범위는 꽤 넓다. 개인적으로 가장 좋아하는 건 아마도 내가 처음 쓴 글들 중 하나인 Sequence일 것이다. 문제와 가능한 해결책을(물론 내 의견이지만) 잘 정리해 두었다.
그렇다면 이 시리즈에서 얻을 수 있는 교훈은 무엇일까? “<기능>은 실수였다. 당신의 언어에 비슷한 기능을 넣기 전에 한 번 더 생각하라” 같은 메시지 외에도, 몇 가지 상위 수준의 아이디어를 뽑아볼 수 있을 것 같다. 그중 가장 중요한 것은, 언어 작업에서는 어느 시점부터는 더 이상 바꿀 수 없게 된다는 점이다!
이 제약은 대부분의 소프트웨어 프로젝트에는 적용되지 않는다. (널리 채택된) 언어, ABI 안정성이 있는 라이브러리, 그리고 교환 포맷 같은 경우에만 해당한다. 그 밖의 대부분의 경우에는 새로운 메이저 버전을 도입해서 기존 것을 대체할 수 있고, 사람들은 옮겨 가거나 옮겨 가지 않을 뿐이다. 어려울 수는 있어도, 항상 _선택지_로는 남아 있다. 이는 많은 오픈소스 프로젝트나 순수 앱 프로젝트에서 온 사람들에게 문화적 충격일 수 있다. (Swift 1b1부터 Swift 3까지의 막대한 변화들을 알고 있는 상태에서 이런 말을 하는 거다. 그럼에도 여전히 고치지 못했거나 고칠 수 없었던 것들이 있었다니…)
이게 가장 중요하다. 그 밖에 ‘regrets’와 관련된 조언들을 더 적어보면:
역으로 하는 것보다, 능력을 추가하거나 제약을 제거하는 편이 더 쉽다. 제공된 기능이 있으면 사람들은 즉시 쓰기 시작한다. 특히 그게 어떤 일을 해내는 유일한 방법이라면 더더욱 그렇다. 나중에 모두가 칭찬하는 더 나은 기능을 도입하더라도, 기존 기능을 제거하는 순간 파괴적 변경(breaking change)이 된다.
파괴적 변경을 할 거라면, 과감하게 하라. 다시는 기회가 오지 않을 가능성이 크다.
가장 야심 찬 변경은 초기에 하라. 그래야 실제로 써 보고 조정할 수 있고…필요하다면 되돌릴 수도 있다.
그렇다고 해서, 실제 사용 사례를 들 수 없다면 “멋져 보이거나”, “영리해 보이거나”, 심지어 “당연해 보이는” 것이라도 추가하지 마라. 그런 것들은 함정일 수도 있고, 불필요한 복잡성일 수도 있고, 최소한 영원히 유지보수해야 할 무언가가 된다.
비슷한 기능을 가진 다른 언어에 그게 없다면, 아마도 이유가 있을 수 있다.
암시적(implicit)인 것은 편리할 수 있지만, 기본값으로는 나쁠 수도 있다.
일관성은 좋다. 구현할 것도 줄고, 배울 것도 줄어든다.
하지만 서로 무관해 보이는 기능들이 이상한 방식으로 상호작용할 수도 있다.
나중에 돌아가서 제대로 다시 만드는 기회는 드물다. 처음 버전이 ‘영원히 버틸 만큼’ 충분히 좋아야 한다. 나중에 고치고 싶어질 걸 알아도 말이다.
관련해서: 완벽(perfect)은 좋은(good)의 적이고, 좋은(good)은 완벽(perfect)의 적이다. 맞다, 양쪽 방향 모두에서 문제가 된다.
하지만 때로는 실제로 사용되는 모습을 보기 전까지는 잘못된 것을 만들었다는 사실조차 모른다. 그리고 때로는 명백한 정답이 없기도 하다.
“기쁨(delight)” 쪽은, 무엇이 전체를 묶는지 더 명확하지는 않지만, 몇 가지 생각을 적어보면:
Swift에서 내가 가장 좋아하는 것들 중 상당수는, 다른 언어에 이미 있던 것들을 조금씩 다듬은 것이다. 때로는 처음부터 새로 만드는 게 아니라, 다듬어서 접근 가능하게 만드는 것이 최선일 수 있다…혹은 그게 옳은 일이라면 남들이 한 것과 똑같이 하는 게 최선일 수도 있다. (매우 애플스러운 접근이다.)
비슷하게, 당신이 스스로는 호환성을 깨뜨릴 수 없더라도, 다른 사람들이 호환성을 깨뜨릴 수 있기를 바라는 모든 지점을 찾아서 그 방향으로 따라갈 수는 있다.
“쉬운 일은 쉬워야 하고 어려운 일은 가능해야 한다”, “API는 올바른 사용으로 유도해야 한다”, “간결함보다 명료함” 같은 흔한 금언들.
이 모든 걸 고려하면, “더 좋아진다면 호환성을 깨는 게 그만한 가치가 있지 않나?”라는 질문에 다시 답해야 할 것 같다. 프로그래밍 언어를 이야기할 때는, 옛 버전이 사라지지 않는다. 컴파일 시점에 구버전 라이브러리를 import할 수 있다면 그 영향은 두 배고, 애플 플랫폼에서 ABI 안정성까지 있으면 세 배다. 호환성을 깨뜨린다는 것은 사용자를 소외시키거나(혹은) 여러 버전을 앞으로 몇 년간 유지보수한다는 뜻이며, 더 현실적으로는 둘 다 하게 된다는 뜻이다. 그러니 그 변경은 반드시 그럴 만한 가치가 있어야 한다. (규모의 문제도 있다. 애플 플랫폼 개발자는 수백만 명이고, 그 결과 사용자는 아마 10억 명에 이를 테니까.)
또 한 번 강조하지만, 핵심 메시지는 “Swift는 실패작이다, 이렇게 잘못된 게 많잖아”가 아니다. 모든 언어에는 이런 목록이 있다. 내가 Swift에 대해 이 글들을 쓴 이유는, 내가 Swift를 만들었던 사람이었고, 어떻게 그리고 왜 이런 결과가 되었는지에 대한 통찰을 제공할 수 있기 때문이다. 다른 언어 설계자들이 무엇을 후회하고 무엇이 잘 버텼는지에 대한 글이나 스레드도 보고 싶다.
이 시리즈는 많은 좋은 논의를 만들어 냈고, 특히 Dave Zarzycki에게 감사 인사를 전하고 싶다. 그는 초기 Swift 작업에도 참여했고, 애플에서의 긴 경력 동안 다른 여러 프로젝트(원래의 launchd 포함)에도 참여했다. 우리끼리 나눈 비공개 대화 중 하나에서, 수년간 지속될 언어이자 대규모 협업 프로젝트에 적합한 언어를 이야기하며 그는 이런 말을 했다:
성공을 전제로 하고, 가능한 한 빨리 사람 규모의 확장 문제를 해결하라. 그게 전부다.
다만, 그건 엄청난 작업량을 의미한다. 수많은 기본값을 올바르게 정하고, 기능들이 서로 어떻게 상호작용하는지 깊이 생각하고, 점진적 공개(progressive disclosure)를 설계하고, API 진화와 복원력(resiliency)을 계획하는 등등, 등등.
더 나아가, 새 프로그래밍 언어들이 종종 유혹을 느끼는 ‘편의성의 함정’ 일부를 포기한다는 뜻이기도 하다(예: 무계획한 암시적 변환 같은 것).
그리고 내게는 전부 좋은 조언처럼 들린다.
언어 설계에 대한 더 많은 회고를 찾고 있다면, 마이크로소프트의 언어이자 운영체제 프로젝트였던 Midori에 대한 Joe Duffy의 시리즈를 추천한다. 이 프로젝트는 Swift보다 몇 년 앞서 시작됐다. 결국 중단되었고 개발자들은 마이크로소프트의 다른 조직으로 흩어졌지만, 목표는 Swift보다도 더 야심찼고, 눈에 띄는 유사점들도 있으며, 우리가 배울 수 있었던 점들도 있다.
이 5개월 동안 함께해 줘서 고맙다. 2022년에 보자.
이 글은 2021년 12월 31일에 게시되었고, Technical 분류에 속한다. 태그: Swift, Swift regrets, Programming languages