3P by GN⁺ | ★ favorite | 댓글 1개
  • Go는 2009년 11월 10일 오픈소스로 공개된 뒤 14년 동안, 새 언어 자체보다 프로덕션 소프트웨어를 쉽게 만드는 방식을 목표로 발전함
  • 형식 명세, 여러 컴파일러 구현, 쉬운 크로스 컴파일, Go 1.0 이후 호환성 보장, 표준 라이브러리와 gofmt가 채택을 뒷받침함
  • 서버 소프트웨어를 구조화하는 동시성은 강점이었지만, 초기에 병렬성과의 차이와 적합한 사용처를 충분히 설명하지 못해 혼란을 낳음
  • 인터페이스는 라이브러리, 테스트, 의존성 관리, 합성 중심 설계의 기반이 되었고, 제네릭 부재는 이후 10년 넘게 다형성 설계를 어렵게 만듦
  • 패키지 관리, 문서, 커뮤니티 협업에서는 시행착오가 컸지만, Go는 작성자와 시간이 달라도 비슷하게 보이고 계속 빌드되는 환경을 지향함

Go의 목표: 언어보다 소프트웨어 개발 방식

  • 2023년 11월 10일은 Go가 오픈소스 프로젝트로 공개된 지 14주년이 되는 날이었음
  • 2009년 공개 당시 Ken Thompson, Robert Griesemer, Russ Cox, Ian Taylor, Adam Langley, Jini Kim 등이 프로젝트 공개를 지켜봤음
  • Go의 원래 목표는 새 프로그래밍 언어 자체가 아니라, 더 나은 방식으로 고품질 소프트웨어를 작성하는 것이었음
  • 당시의 병목은 언어 기능 자체보다 Google에서 현대 서버 소프트웨어를 만드는 과정의 복잡성에 있었음
    • 의존성 제어
    • 큰 팀과 변하는 인력 구조
    • 유지보수 용이성
    • 효율적인 테스트
    • 멀티코어 CPU와 네트워킹 활용
  • Go는 지금도 프로덕션 소프트웨어 구축을 더 쉽고 생산적으로 만드는 프로젝트로 정의됨

Gopher 마스코트가 만든 공동체 정체성

  • Go gopher는 Go 성공의 초기 요인 중 하나였음
  • Renee French가 만든 gopher는 Go 프로그래머가 공유하는 상징이자 커뮤니티의 깃발 역할을 함
  • GopherCon이라는 이름의 컨퍼런스처럼, gopher는 기술적 탁월함과 재미가 함께 있는 프로젝트 분위기를 만들었음
  • Creative Commons Attribution 라이선스로 공개한 선택에는 장단점이 있었음
    • 리믹스와 변형을 장려해 커뮤니티 정신을 키움
    • attribution 조건 때문에 논쟁이 생기거나 Renee French가 만들지 않은 작업에 잘못된 크레딧이 붙는 문제도 있었음
  • 마스코트가 원래 정신을 유지하도록 관리하는 일은 여전히 어려운 과제로 남아 있음

Go가 잘한 기술적 선택들

  • 형식 명세를 먼저 작성해 컴파일러 구현의 동작을 고정하고, 여러 구현이 같은 동작에 합의할 수 있게 함
    • 컴파일러 하나만으로는 명세가 될 수 없다는 판단이 있었음
    • 첫 명세 초안은 Sydney Darling Harbour의 건물 18층에서 작성됨
  • 여러 컴파일러 구현은 언어와 명세를 다듬는 데 도움이 되었음
    • Ian Taylor는 2008년 6월 7일 Go 초안 명세를 읽고 gcc 프론트엔드를 만들었다고 메일을 보냄
    • 이후에도 다양한 호환 구현이 등장함
  • 크로스 컴파일을 쉽게 만든 덕분에 개발자는 원하는 플랫폼에서 작업하고 필요한 플랫폼으로 배포할 수 있었음
  • Go 1.0에서 언어를 정리한 뒤 호환성 보장을 도입한 선택은 Go 채택에 큰 영향을 줌
    • 강한 호환성을 유지하는 비용은 있지만, 새 Go 릴리스가 프로젝트를 깨뜨릴 걱정을 줄임
  • 표준 라이브러리는 21세기 서버 코드를 작성하는 데 필요한 대부분의 기능을 제공했고, 커뮤니티가 같은 도구 상자 위에서 일하게 만들었음
    • 초기에 Go 코드를 설치할 다른 장소가 없어 라이브러리가 커진 측면도 있었음
    • 변종 라이브러리가 난립하는 것을 줄이고 커뮤니티를 통합하는 데 도움이 됨

도구와 gofmt가 만든 개발 경험

  • Go는 파싱하기 쉬운 언어로 설계되어 도구 제작이 쉬웠음
  • 초기에는 IDE가 필요하다고 봤지만, 도구화가 쉬워지면서 시간이 지나 IDE들이 Go를 지원했고 gopls도 등장함
  • 컴파일러와 함께 자동 테스트, 커버리지, 코드 vetting 같은 보조 도구가 제공됨
  • go 명령은 전체 빌드 과정을 통합했고, 많은 프로젝트가 Go 코드 빌드와 유지보수에 필요한 대부분을 이 명령으로 해결할 수 있었음
  • gofmt는 Go뿐 아니라 프로그래밍 커뮤니티 전체에 영향을 준 도구로 자리 잡음
    • Robert Griesemer는 처음부터 gofmt를 만들 것을 주장함
    • gofmt 이전에는 자동 포매터 품질이 높지 않아 거의 쓰이지 않았음
    • 공백과 줄바꿈 논쟁에 쓰는 시간이 줄어듦
    • gofmt 내부가 라이브러리가 되면서 AST를 파싱·수정하고 사람이 읽을 수 있는 출력으로 다시 만들 수 있었음
    • simplifier, analyzer, 코드 커버리지 도구 같은 다른 도구도 가능해짐

동시성: 강력했지만 설명이 늦었던 기능

  • 2002년 당시 Google 소프트웨어는 스레드를 거의 피했고, John Ousterhout의 “threads were bad”라는 입장을 인용하며 사용을 막는 분위기가 있었음
  • Go는 pthread 같은 낮은 수준의 패키지보다 나은 방식으로 동시성을 쓸 수 있다는 경험에서 출발함
  • Go는 프로그래밍 세계가 동시성을 강력한 도구로 받아들이는 데 중요한 역할을 했음
    • 특히 멀티코어와 네트워크 환경에서 효과가 컸음
    • 오늘날 대부분의 주류 언어는 동시성 지원을 갖추고 있음
  • Go는 coroutine, task, thread 같은 이름을 쓰지 않고 goroutine이라는 새 용어를 만들었음
  • async/await는 pthread보다 큰 개선이지만, goroutine, channel, select와 비교하면 구현자에게는 더 쉽고 작아도 일부 복잡성을 프로그래머에게 돌려보낼 수 있음
    • Bob Nystrom의 “What Color is Your Function?” 문제가 예로 쓰임
    • Go의 CSP 모델은 절차적 언어에 잘 맞지만, 제대로 구현하려면 런타임 복잡성이 필요함
    • 하나의 환경에 여러 동시성 구현이 있으면 문제가 될 수 있어, Go는 이를 라이브러리가 아니라 언어에 넣음

동시성에서 잘못한 두 가지

  • Go 팀은 동시성을 어디에 써야 하는지 초기에 충분히 안내하지 못했음
    • 주된 사용처는 net/http 같은 핵심 라이브러리에서 서버 소프트웨어를 더 단순하게 만드는 것이었음
    • 모든 프로그램의 모든 곳에서 써야 하는 기능은 아니었음
  • 병렬성과 동시성의 차이를 늦게 설명한 것도 문제였음
    • 많은 개발자가 goroutine으로 코드를 병렬화해 더 빠르게 만들려 했지만, 오히려 느려져 당황함
    • 동시 코드는 문제 자체가 병렬적일 때, 예를 들어 HTTP 요청 처리 같은 경우에만 병렬화로 빨라질 수 있음
  • 이를 보완하기 위해 2012년 Waza에서 “Concurrency is not Parallelism” 발표가 진행됨
  • 이 설명은 더 일찍 이뤄졌어야 했음
  • 그럼에도 Go는 서버 소프트웨어를 구조화하는 방식으로 동시성을 대중화하는 데 기여함

인터페이스와 합성 중심 설계

  • 인터페이스는 동시성과 함께 Go를 구분하는 핵심 아이디어임
  • Go 인터페이스는 원래의 행동 중심 객체지향 설계에 대한 Go의 답에 가까움
  • 어떤 타입이 어떤 인터페이스를 구현하는지 미리 선언할 필요가 없는 동적 방식은 초기에 비판을 받았지만, Go 스타일 프로그래밍에 중요했음
  • 표준 라이브러리, 테스트, 의존성 관리는 인터페이스의 개방적인 성격에 크게 의존함
  • Go의 sort는 인터페이스 위에서 동작하는 함수로 구현됨
    • 많은 사람이 익숙했던 객체지향 스타일과 다르지만 강력한 아이디어였음
  • Reader, Writer, empty interface 같은 작은 인터페이스는 Go 라이브러리의 기반이 되었음
    • 이 유명한 세 인터페이스는 평균적으로 각각 2/3개의 메서드를 가짐
    • 작은 메서드 집합은 Go다운 관용구가 되었음

제네릭을 둘러싼 긴 우회

  • Go 팀은 제네릭 프로그래밍이 타입을 알고리듬보다 먼저 생각하게 만드는 경향을 봐왔음
    • 초기 추상화보다 유기적 설계를 선호함
    • 컨테이너보다 함수를 중시함
  • Go는 map, slice, array, channel 같은 제네릭 컨테이너를 언어에 포함했지만, 그 제네릭성을 사용자에게 직접 제공하지 않았음
  • 단순한 프로그래밍 작업 대부분은 이 타입들로 충분하다고 판단했지만, 그렇지 않은 경우도 있었고 이 경계가 일부 사용자에게 불편을 줌
  • 인터페이스는 Go 프로그래밍의 기반이었기 때문에, 새 다형성 모델은 반드시 인터페이스와 잘 맞아야 했음
    • 여러 시도와 중단된 구현이 있었음
    • 많은 논의 끝에 Phil Wadler가 이끄는 타입 이론가들의 도움도 받음
  • 최종적으로 Go는 인터페이스를 “메서드 집합”에서 “타입 집합”으로 일반화하는 방식으로 제네릭을 설계함
  • “generics”라는 용어는 데이터 구조 중심 다형성에서 온 말이라 Go가 제공하는 parametric polymorphism을 정확히 가리키지는 않지만, 실제로는 generics라는 말을 쓰게 됨

컴파일러: 처음엔 C, 나중엔 Go

  • 초기 Go 컴파일러가 C로 작성된 것은 언어 커뮤니티 일부를 불편하게 했음
    • LLVM 같은 툴킷을 쓰거나 Go 자체로 self-hosting해야 한다는 의견이 있었음
  • 처음 컴파일러를 C로 작성한 이유는 실용적이었음
    • 새 언어를 부트스트랩하려면 기존 언어가 필요했음
    • Ken Thompson이 이미 C 컴파일러를 작성했고, 그 내부 구조가 Go 컴파일러의 기반이 될 수 있었음
    • 언어를 설계하면서 동시에 그 언어로 컴파일러를 쓰면, 컴파일러 작성에 좋은 언어로 치우칠 수 있었음
  • 초기 컴파일러는 생성 코드가 평범하고 내부도 깔끔하지 않았지만, 작고 익숙하고 빠르게 바꾸기 쉬웠음
  • 자동으로 커지는 segmented stack 추가는 이 선택의 장점을 보여줌
    • LLVM 같은 툴킷을 썼다면 ABI와 가비지 컬렉터 지원 변경까지 포함해야 해 통합이 어려웠을 것임
  • 원래 Plan 9 컴파일러 제품군 방식에서 온 구조는 크로스 컴파일에도 잘 맞았음
  • Go 1.5에서 Russ Cox는 컴파일러를 C에서 Go로 반자동 번역하는 도구를 작성함
    • 이 시점에는 언어가 완성되어 컴파일러 중심 언어 설계 우려가 사라짐
    • Go로 옮긴 뒤 테스트, 도구, 자동 재작성, 성능 분석 같은 Go의 장점을 컴파일러 개발에도 적용할 수 있게 됨
  • 현재는 LLVM 기반 Go 컴파일러와 다른 여러 컴파일러도 존재함

오픈소스 프로젝트 운영과 커뮤니티

  • Go는 성공하려면 오픈소스 프로젝트여야 한다고 처음부터 판단했음
  • 다만 핵심 아이디어와 동작 구현을 정리할 때까지는 비공개로 개발하는 편이 더 생산적이라고 봄
    • 공개 전 첫 2년은 목표를 명확히 하는 데 중요했음
  • 오픈소스로 전환한 뒤 커뮤니티 입력은 압도적이었고, 특히 Ian Taylor는 질문에 답하는 데 많은 시간을 썼음
  • Windows 포트는 Alex Brainman의 안내 아래 커뮤니티가 전적으로 수행함
  • Go 팀은 커뮤니티와 협업하는 최선의 방식을 이해하는 데 시간이 오래 걸렸음
    • 소통을 잘하고 있다고 생각했지만, 오해와 기대 불일치로 시간이 낭비됨
  • Go 프로젝트는 코드를 빨리 받아들인 뒤 정리하는 방식이 아니라, 병합 전에 높은 코드 품질을 확보하는 방식을 택함
    • 필수 코드 리뷰
    • 세부 사항에 대한 철저한 주의
  • 이 방식은 커뮤니티에 더 많은 부담을 주므로, 참여자가 그 가치를 이해하지 못하면 환영받지 못한다고 느낄 수 있음
  • Go 프로젝트의 저장소 기록은 SVN, Perforce, Mercurial, Git을 거쳤지만, Russ Cox가 이력을 유지해 현재 Git 저장소에도 초기 SVN 변경 기록이 남아 있음
  • Google은 Go를 지원하지만 Go 팀의 의제를 정하지 않음
    • Google 내부의 큰 Go 코드베이스는 릴리스를 테스트하고 검증하는 데 쓰임
    • 흐름은 공개 저장소에서 Google로 가져오는 방식이지 반대가 아님
    • 핵심 Go 팀은 Google이 급여를 지급하지만 독립적으로 움직임

패키지 관리에서의 시행착오

  • Go 언어의 package 설계 자체는 훌륭했지만, 패키지 관리 개발 과정은 잘 진행되지 않았음
  • import 문에서 경로를 평범한 문자열로 지정한 것은 중요하고 유연한 선택이었음
  • 문제는 표준 라이브러리만 있던 상태에서 웹에서 코드를 가져오는 방식으로 넘어가는 전환이 험했다는 점임
  • 초기 Go 팀은 Google의 모노레포와 head에서 빌드하는 방식에 익숙했음
    • 여러 버전의 패키지를 가진 패키지 매니저 사용 경험이 충분하지 않았음
    • 의존성 그래프를 해결하는 기술적 복잡성을 처음부터 다루지 못함
  • deps.dev 작업은 이 문제에 대한 일종의 보상처럼 다뤄짐
  • 커뮤니티와 함께 의존성 관리 문제를 풀려는 시도는 선의였지만, 최종 설계가 나왔을 때 많은 커뮤니티 구성원이 소외감을 느낌
  • 이 경험은 커뮤니티 참여가 실제로 어떻게 이뤄져야 하는지에 대한 교훈이 되었고, 이후 많이 개선됨
  • 현재는 설계가 안정되었고, 기술적으로 훌륭하며 대부분 사용자에게 잘 작동하는 것으로 보임

문서와 실행 가능한 예제

  • 초기 문서는 많이 작성됐지만, 커뮤니티가 원하는 수준과 달랐음
  • 가장 부족했던 부분은 가장 단순한 함수에도 사용 예제가 필요하다는 점이었음
    • 무엇을 하는지 말하는 것보다 어떻게 쓰는지 보여주는 일이 더 가치 있었음
  • 현재 문서에는 많은 예제가 있으며, 대부분 오픈소스 기여자가 제공함
  • Go는 초기에 예제를 웹에서 실행 가능하게 만들었음
    • 2012년 Google I/O 발표에서 동시성을 보여주는 코드 조각을 브라우저에서 실행할 수 있게 함
    • Andrew Gerrand가 이를 가능하게 하는 웹 기술을 작성함
    • 이후 블로그와 온라인 패키지 문서에도 배포됨
  • The Go playground는 사람들이 자유롭게 코드를 시험하고 개발할 수 있는 공개 샌드박스로 제공됨

14년 뒤 Go가 도달한 위치

  • Go는 언어 설계와 개발 과정에서 내린 결정들 덕분에 단순한 프로그래밍 언어를 넘어 소프트웨어 작성 방식으로 자리 잡았음
  • Go가 도달한 위치에는 다음 요소들이 기여함
    • 서버 코드에 필요한 기본 기능 대부분을 구현한 강한 표준 라이브러리
    • 언어의 일급 구성요소인 동시성
    • 상속보다 합성에 기반한 접근
    • 의존성 관리를 명확히 하는 패키징 모델
    • 통합된 빠른 빌드와 테스트 도구
    • 엄격하고 일관된 포매팅
    • 영리함보다 가독성을 중시하는 태도
    • 호환성 보장
  • Go 커뮤니티는 다양하고 적극적인 Gopher들로 구성되어 있으며, 프로젝트 성공에 핵심 역할을 함
  • 결과적으로 Go 코드는 누가 작성했든 비슷하게 보이고 동작하며, 서로 다른 언어 부분집합을 쓰는 파벌이 비교적 적고, 시간이 지나도 계속 컴파일되고 실행되도록 보장됨
  • 이런 특성은 주요 프로그래밍 언어에서 드문 결과로 볼 수 있음

댓글과 토론

Hacker News 의견들
  • 핵심 인물들이 시간을 내서 회고를 남기는 건 정말 고마운 일이고, 지금 진짜 변화를 만들고 싶은 사람들에게 큰 도움이 됨
    다만 Rob Pike가 Go가 잘한 점을 충분히 명확히 말하지는 않은 것 같음. Go는 언어 자체뿐 아니라 프로젝트에 작용하는 힘도 잘 관리했음: 대상 용도를 시스템 프로그래밍으로 제한했고, 언어와 원칙을 명확히 정의했으며, 배포 전에 문제를 고치는 품질 우선 태도를 유지했고, 언어·릴리스·핵심 메시지는 엄격히 통제하면서도 하위 영역에서는 커뮤니티가 이끌 여지를 줬음
    덜 평가받는 점은 Google 자체가 크게 간섭하지 않았다는 점임. Go가 Google의 목표에 실제로 기여했고 중요했기 때문일 수 있음. 오늘날 새 프로젝트에서도 그럴 수 있을지는 의문임
    Dart는 Flutter 밖에서는 거의 채택되지 않았는데, 애플리케이션 코드가 시스템 코드보다 훨씬 많은 현실과 비교하면 흥미로움. Go는 서버 쪽 소프트웨어를 Java의 비대한 환경에서 네이티브 컨테이너로 옮긴 핵심 기술이었고, 지난 10년 웹 애플리케이션 인프라 대부분의 기반이 됐음. 그 성장 속에서도 팀은 작게 유지되고 핵심 인물들도 남아 있었는데, 앞으로도 그럴지는 모르겠음

    • Go는 가비지 컬렉션이 있고 green thread를 포함한 무거운 런타임 때문에 C 상호운용이 번거롭고 느려짐
      그래서 설계상 시스템 프로그래밍 언어로는 적합하지 않음. 초기에 스스로 그렇게 불렀던 탓에 이상한 신화가 오래 남았지만, 그 문구는 몇 년 전에 제거된 것으로 보임
      Go는 C나 Rust의 경쟁자라기보다 Java 계열의 경쟁자에 가깝고, 실제 사용 영역을 보면 드러남. 다만 제한적인 타입 시스템과 뛰어난 동시성 지원 때문에 스택에서 조금 더 낮은 층에 자주 자리 잡음
    • 커뮤니티가 이끈다는 부분에는 동의하기 어려움. 여전히 Google이 결정하는 느낌이 강함
      특히 버전 관리가 그랬음. 처음에는 Go가 문제 자체를 인정하지 않다가, 커뮤니티 합의가 커지자 기존 논의는 잊고 이제는 modules로 간다고 정했음
      Cloudflare가 일광절약시간제 장애를 겪기 전까지 단조 증가 시간(monotonic time)을 공개 API로 만들지 않으려 했던 것도 기억남
    • Java 개발자였고 지금은 Go를 좋아하지만, 예전 Java 동료들이 Go를 좋아할지는 확신이 없음. Go는 새 언어였을 때조차 좀 지루한 언어였음
      Java 세계에서는 새로운 기능, 예를 들어 훌륭한 streams 같은 것을 써서 코드베이스를 리팩터링하는 재미가 있음. Go에서는 가장 단순하고 평범한 추상화를 찾는 데서 재미가 옴
    • Dart에서 일하고 있음. Dart가 Flutter 밖에서 채택되지 않는다는 비판이 그렇게 치명적이라고 보지는 않음
      모든 언어는 특정 영역에 맞는 라이브러리와 프레임워크가 필요함. Flutter는 Dart로 클라이언트 앱을 쓰기 위한 프레임워크이고, Flutter가 없다면 Dart 언어를 아무리 좋아해도 Android와 iOS에서 화면에 픽셀을 그리는 방법부터 엄청난 시간을 들여야 함
      애플리케이션을 쓰는 사람은 누구나 라이브러리와 프레임워크 스택 위에 빌드함. Go와 Flutter가 붙은 Dart의 차이는, 서버에 필요한 네트워킹·직렬화·암호화 같은 영역 라이브러리가 Go에서는 표준 라이브러리에 들어 있다는 정도임
      Dart는 컬렉션과 비동기 같은 기본 요소를 내장 라이브러리로 갖고 있지만, 영역별 기능은 Flutter 같은 외부 패키지에 의존함. 이는 Dart가 아주 초기부터 탄탄한 패키지 관리 방식을 갖고 있었기 때문이 큼. Dart 팀이 작성·관리하는 많은 핵심 라이브러리도 내장 대신 패키지 관리자로 배포되는데, 그 편이 발전시키기 훨씬 쉬움
      UI 프레임워크는 언어보다 수명이 짧은 경향이 있으므로 Flutter가 Dart 표준 라이브러리에 박혀 있지 않은 편이 낫다고 봄. Flutter는 훌륭하지만 20년 뒤 더 나은 무언가가 나와도 놀랍지 않고, 그때 Flutter가 내장돼 있지 않으면 Dart 사용자가 더 쉽게 갈아탈 수 있음
    • 정리해고 때 핵심 FOSS 팀들을 잘라냈고, 경영진은 진짜 FOSS를 싫어함. Ruth의 스프레드시트에는 좋아 보이지 않기 때문임
      Android 같은 가짜 FOSS는 전략적으로 용인하지만, 그 밖에는 광고 클릭으로 이어지지 않으면 쓸모없다고 보는 분위기임
  • 2012년에 Python 개발자로서 Modbus용 비트 조작을 해야 해서 Go를 잡았음. 그 코드는 배포하지 않았지만, 비트와 바이트를 건드리는 일이 너무 쉽게 그냥 동작해서 충격적이었음
    10년이 지나고 거의 전업에 가까운 Go 일을 몇 번 겪은 뒤에도, 대부분이 그냥 동작한다는 점은 여전히 놀라움
    Go 언어와 Go 커뮤니티를 좋아함. Rob, Ian, Russ 등 Go를 만드는 사람들이 하는 일에 감사하고, 이 발표·글이 커뮤니티와의 “험한 구간”을 솔직히 다룬 점도 고맙게 봄
    다만 그 시기를 겪어본 입장에서는, 특히 패키지 관리 혼란에서 자신들이 어떻게 행동하는지 정확히 몰랐다고 믿기는 매우 어려움. 그래도 지금은 좋은 해법에 도착했다는 글의 평가도 맞음
    앞으로 10년의 Go와, Zig·Deno 같은 영감을 받은 비슷한 언어들이 건강한 커뮤니티로 계속 성장하길 바람

    • Go 핵심 팀에서 가장 좋아하는 점은 온갖 요청에 “아니오”라고 말하고, 제네릭에는 “올바른 구현을 기다리자”고 할 줄 알았다는 점임
      프로그래머라기보다 계산생물학자라 Go 사용이 들쭉날쭉하지만, 다시 Go로 돌아오면 예전 코드가 컴파일되고 언어가 기대한 대로 동작함
      동시에 Rob Pike가 반짝이는 기능들을 모두 받아들이지는 않으면서도, 커뮤니티 참여를 배우는 과정에서의 실수를 인정한 점은 좋게 봄
    • Python에서 넘어온 입장임. 요즘은 Bard, GPT-4 같은 AI 도구로 오래된 Python 유틸리티를 Go로 바로 변환하고 있음
      몇 개는 바로 동작했고, 몇 개는 AI 모델이 API 정보를 조금 오래된 상태로 알고 있어서 약간 손봐야 했음
      그래도 프로그램을 서버에 scp로 복사하면 그냥 동작한다는 점은, Python에서 매번 “이번 유행 venv 시스템은 뭐지” 춤을 추던 것과 비교하면 놀라움
    • Zig와 Deno는 Golang과 아주 잘 비교되는 대상은 아닌 것 같음. 오히려 Elixir와 ReasonML이 더 비슷하고, Google 내부로 보면 Dart도 있음
  • 멋진 글이지만 꽤 자기축하적으로도 보임. 언어의 더 깊은 문제들에 대한 인정이 있을 줄 알았는데, 아마 핵심은 그들에게는 언어가 완벽하고 내가 문제라는 데 있는 듯함
    그래서 Go를 추천하기 어렵다. 타입 시스템, 오류 처리, 안전하지 않은 동시성, 지나치게 단순한 문법, nil, 기본 0값, 방치된 주류 패키지들이 많다는 문제가 있음
    지금은 Rust를 주 언어로 사용함. 생태계가 활발하고 Go가 그렇지 못한 여러 면에서 비전이 있음
    더 날카롭게 말하면, Go가 PHP와 백엔드 언어로 경쟁하던 시절에는 자기 시대가 있었던 것 같음

    • 이건 토론에 크게 기여하지 않는 것 같음. 구체적인 요점 없이 Rust 옹호처럼 읽힘
      첫 문단은 “싫다”, 둘째는 비꼼, 셋째도 “싫다”, 넷째는 “Rust가 낫다”, 다섯째는 또 비꼼으로 보임
    • Plan 9 계열 과학자들은 자기 브랜드를 너무 좋아함. 2012년에 Go를 쓰기 시작했지만 deps.dev를 없앤 뒤 포기했음
      몇 년 뒤 회사에서 일을 끝내려고 팀에 Go를 도입하려 했는데, 다른 엔지니어가 시간을 들여 언어를 살펴보고 왜 별로인지 이유를 나열했고, 그가 맞았음
      핵심은 단순하긴 하지만 오류 처리와 사용하지 않는 imports 같은 사소한 것들이 사용을 고통스럽게 만든다는 점이었음. 개인적으로 오류 처리는 좋아했지만 타입 시스템은 싫었음
    • Rust를 배우기 전 Go를 썼고, 둘의 차이를 보고 생각이 크게 바뀌었음
      예전에 OCaml을 썼기 때문에 try/catch나 nil보다 OptionResult 타입이 가진 가치를 알고 있었지만, 쉬워서 Go를 썼음. 하지만 그 쉬움에는 장기 유지보수 비용이 따름. 처음부터 제대로 맞추고 나중에 어려움을 마주하지 않는 편이 좋음
      가볍게 말하려는 건 아니지만, Go는 지난 50년간의 프로그래밍 언어 발전을 가져다가 버린 언어라는 설명을 자주 들었음
    • 프로그래밍 언어에 대한 감상에는 동의하지만, Go를 변호하자면 Go는 프로그래밍 언어 자체에 관심 없는 사람들을 위한, 그런 사람들이 만든 언어에 가까움
      나쁜 뜻은 아님. Rob Pike는 동시성이 안전하지 않다거나 nil10억 달러짜리 실수라는 점을 신경 쓰지 않고, Go 사용자 대부분도 그렇지 않음
      그렇다고 Go가 좋은 언어가 되느냐 하면 그건 아님. 하지만 그건 요점이 아님. Go는 사용하기 쉽게 만드는 설득력 있는 도구들과 결합된, 편리하고 충분히 괜찮은 언어임. 프로그래밍 언어계의 Crocs 같은 존재임
    • Rust를 배웠지만 Rust 사용자 커뮤니티 때문에 언어를 좋아하기가 불가능함. 몇 년이 지나도 바뀌지 않았고, 오히려 더 나빠졌다면 모를까 나아지진 않았음
      높은 무결성과 안전성이 필요하면 Ada나 형식 검증을 붙인 Ada/SPARK를 쓰겠음. 그 외에는 Go가 Rust보다 훨씬 더 높은 생산성을 줌
      개인적으로 Rust는 C++처럼 과잉공학의 대표 사례라고 봄
  • 짜게 들리겠지만, 10년 전 go-nuts에서 조롱당했고 Rob Pike에게도 무시하는 답을 받았음. go get과 네트워크를 통한 모듈 import 방식이 동작하고, 초보자 문서 전반에서 홍보되고, 커뮤니티 전체에서 그렇게 사용되는 방식이 결국 해롭고 근시안적이라고 감히 말했기 때문임

    • Go의 패키지 시스템, 특히 modules 이전 방식은 Google 모노레포 안에서만 쓰도록 설계된 더 제한적인 시스템 위에 억지로 외부 동작을 붙인 해킹처럼 느껴짐
      이상한 전역 이름공간 트리는 그 안에서는 말이 되고, 체크인된 코드 생성물을 강조하는 것도 Google이 모노레포에 빌드 산출물까지 포함한다는 점을 보면 이해됨
    • 그래도 Go가 만든 결과물이 다른 언어들의 것보다 낫다는 점은 흥미로움
      물론 v2 문제나 영원한 v0 문제가 있음. 그래도 npm을 쓰거나, 성호를 긋게 되는 Python의 무언가를 다룰 때보다는 나음
    • Rob Pike가 2000년 이후 발견된 프로그래밍 언어 이론에 얼마나 무지한지 세상에 드러낸 셈임
  • 이 글은 ACM에 실었던 글 [1]의 더 개인적인 버전처럼 보임. 두 글 모두 언어 명세 관점에서 훌륭한 새 프로그래밍 언어를 만든 게 아니라, 프로그래밍 언어 주변의 것들을 매우 잘 구축했고 그게 오히려 더 중요할 수 있음을 인정함
    제출된 글에서는 인터페이스 사용 접근법과 동시성 접근법을 발명했다고 말함. 하지만 goroutine은 Haskell thread와 같고, 인터페이스는 지금처럼 제네릭 인자를 지원하면 Haskell typeclass와 매우 비슷함. Haskell이 Go보다 앞섰다는 점을 보면, 절차형 프로그래머들이 함수형 프로그래밍 아이디어의 힘을 독립적으로 발견하는 모습이 흥미로움
    Go의 한 가지 언어 혁신은 인터페이스 구현이 자신이 구현하는 인터페이스를 선언하지 않아도 된다는 점임. 안전성 관점에서는 끔찍하지만 실제로는 문제가 거의 없고, Haskell과 Rust에서 겪는 끔찍한 순환 의존성 문제를 없애줌
    [1] https://cacm.acm.org/magazines/2022/5/260357-the-go-programm...

    • 인터페이스가 구조적 타입이라는 사실이 이 기능이 언어, 더 나아가 관용구와 생태계에 미친 전반적 효과에서 얼마나 중요한지 과소평가한 것 같음
      타입이 미리 자신이 구현하는 인터페이스를 선언해야 했다면 Go는 완전히 다른 언어였을 것임. 이는 Go의 주요 차별점 중 하나였음. 적어도 TypeScript가 다른 이유로 구조적 타입 인터페이스를 들고 나오기 전까지는 그랬음
    • 구조적 타입은 이미 25년도 더 된 개념이고, OCaml과 Scala 같은 언어에서도 이미 쓰였음
    • 그게 어떻게 혁신인지 모르겠음. Go보다 앞선 주류 언어 중 떠오르는 것들은 전부 그런 식으로 동작함
      인터페이스를 정의하면 해당 인터페이스 구현을 반드시 제공하도록 강제하는, 우리가 신경 쓸 만한 프로그래밍 언어를 하나라도 들 수 있나?
  • Go의 암호화 관련 문제는 아직 많이 언급되지 않은 것 같음. OpenSSH가 SHA1을 폐기한 뒤 Go 팀이 x/crypto/ssh에 SHA2 지원을 추가하는 데 1년이나 걸렸음 [1]. Gitea가 유명한 피해자였음 [2]
    게다가 암호화 유지보수자가 GnuPG를 공격하고 [3], Dan Bernstein의 신뢰를 깎으려 하는 모습 [4]은 신뢰를 주지 못함
    [1]: https://github.com/golang/go/issues/49269
    [2]: https://github.com/go-gitea/gitea/issues/17798
    [3]: https://twitter.com/FiloSottile/status/1127643698676797441
    [4]: https://twitter.com/FiloSottile/status/1555669786826244096

    • x/ 패키지들은 많이 방치되는 편이고, 새 기여자가 패치를 성공적으로 보내기 어렵게 만드는 개발 모델을 따른다는 게 문제임
      반면 표준 라이브러리의 crypto 패키지는 Go의 큰 성공 중 하나라고 봄. OpenSSL은 오랫동안 재앙이었고, Go와 특히 agl은 매우 널리 사용되는 대체 구현을 만들고 배포했으며 평균 품질도 높았음. 특히 공개 API에서 발목총이 훨씬 적다는 관점에서 그렇음
  • 이 글은 gofmt를 “잘한 것”으로 언급하는데, 특히 강조할 만함
    많은 언어 설계자와 옹호자에게는 사소해 보이지만, Go 프로젝트 첫날부터 흔한 자전거 창고 논쟁 하나를 완전히 없애므로 엄청난 가치를 줌
    더 새로운 언어들 중 여럿이 이를 의도적으로 베끼거나, Go에도 있으니 우리도 만들어야겠다고 생각하며 받아들이는 걸 봤음
    꽤 이상한 포매팅 규칙도 봤지만, 익숙해질 수 없는 규칙은 거의 없었음. 반대로 강제 포매팅 규칙이 없는 코드베이스에서는 그 결과 코드를 이해하기 더 어려웠던 적이 많음

    • 코드 포매팅을 만지작거리는 경향이 있는데, go fmt가 그걸 대부분 무의미하게 만들어줘서 좋음
  • “21세기 서버 코드를 쓰는 데 필요한 대부분을 갖춘 탄탄하고 잘 만들어진 라이브러리의 존재는 큰 자산이었다”는 말에 동의함
    Go는 Google에 문제가 있었기 때문에 Google이 자금을 댔음. 서버 쪽 애플리케이션 코드를 많이 써야 했고, Python은 너무 느리고 C++는 너무 취약했음. Go는 그 틈새에서 매우 잘 맞음
    큰 보너스는 Google 사람들이 그런 작업에 필요한 라이브러리를 작성했고 내부에서도 쓴다는 점이었음. 그래서 라이브러리를 쓰면 오류 경로까지도 많이 실제로 밟힌 코드였음
    언어에 대해 기술적 이견은 있음. 특히 초기에 모든 것에 큐를 쓰려던 강조, 심지어 큐로 뮤텍스를 만들던 부분이 그렇음. 하지만 사용 사례에 맞는 green thread는 제대로 잡았음. 함수 색깔 문제는 실제 문제이고, Rust에서 비동기와 스레드를 함께 쓰게 만드는 난해한 요령은 너무 과함. Go는 약간의 성능을 포기하고 큰 단순성을 얻음
    예전의 스레드 혐오가 흥미롭기도 함. UNIVAC 메인프레임으로 시작했는데, 1967년부터 스레드가 있었고 당시에는 “activities”라고 불렀음. 1972년에는 사용자 공간 스레드가 있었을 뿐 아니라 대칭형 다중 프로세서에서 돌았음. 비동기 입출력과 사용자 공간 완료 함수, 락을 위한 내장 명령, 내부적으로 스레드화된 운영체제도 있었음
    스레드와 다중 프로세서는 정상적인 것으로 여겼고, UNIX로 옮기면서 그것들을 잃은 느낌이었음. UNIX/Linux가 이 영역을 따라잡는 데 수십 년이 걸렸고, 여러 세대의 프로그래머는 공유 메모리 다중 프로세서를 쓰는 법을 몰랐음
    Dijkstra의 초기 동시성 이론이 다른 용어와 종종 더 나쁜 설계로 재발명됐음. Go 사람들은 Dijkstra의 bounded buffer와 길이 0 및 1인 bounded buffer가 왜 유용한지 이해했음. 그걸 다시 보는 건 좋았음. 올바른 원시 연산이 있으면 동시성은 그렇게 어렵지 않음. 고전적인 pthreads식 사고처럼 락을 여기저기 뿌려 해결하려 하면 제대로 동작하지 않음. UNIX/Linux 초기에 CPU 스케줄러가 정말 형편없어서 스레드 차단 해제가 끔찍하게 동작했던 것도 도움이 되지 않았음

    • 무작위 HN 평론가들이 “Go는 Google에 문제가 있어서 자금을 댔다” 같은 말을 자주 하는데, Google이 어떻게 돌아가는지나 얼마나 큰 회사인지 잘 몰라서 그런 것임
      Go는 Rob Pike에게 문제가 있었기 때문에 만들어졌음. 그는 C++, Java, Python을 좋아하지 않았고, Rob Pike였기 때문에 Google은 몇 년 동안 새 언어를 쓰게 허락했음. Larry나 Sergey, Sundar, Eric이 Rob에게 뭔가 해달라고 찾아간 게 아님
      대체로 Go는 Rob Pike가 지원을 요청했고, Google이 Rob Pike를 붙잡아두고 싶어 했고, 그가 거의 확실히 흥미로운 무언가를 할 거라고 봤기 때문에 자금이 댄 것임. Google Engineering에서 회사 돈이란 사실상 사람들이 일할 수 있게 허락하는 것에 가까움
      Rob Pike가 뛰어난 엔지니어였기 때문에 Go는 몇몇 면에서 꽤 괜찮게 나왔고, Google 운영 애플리케이션을 쓰려면 C++, Python, Java를 써야 했던 그의 초기 문제와 잘 맞았음. Google 내부 일부 사람들도 동의해서 Go를 쓰기 시작했고, 그중 영향력 있는 사람들이 Python 대신 Go를 쓰도록 다른 사람들을 “장려”했음
    • Go는 단순히 Google 프로젝트가 아니라, 사실상 Unix/Linux를 위한 Plan 9의 컴파일러 철학과 CSP임
    • 이건 그 시기에 Google에서 일했던 기억과 맞지 않음. “Go는 시스템 프로그래밍용이었다”는 밈처럼, 뜯어보면 말이 잘 안 되는 믿음에 가까움
      2009년 Google에서 Python은 YouTube 인수 사례를 제외하면 내부용 유틸리티 몇 개 밖의 서버에는 거의 쓰이지 않았으므로 그 생각은 바로 버릴 수 있음. Google이 직접 만든 가장 큰 Python 서버는 Guido van Rossum이 쓴 코드 리뷰 도구 Mondrian이었고, 나중에 Java로 쓴 Critique로 대체됐음
      당시 Google은 프로그래밍 언어 싸움을 없애기 위해 꽤 엄격한 세 언어 정책을 갖고 있었음. C++, Java, Python을 쓸 수 있었고, Python은 스크립트, C++는 인프라, Java는 웹 서버에 쓰였음
      겹치는 부분도 있었음. Java는 C++보다 늦게 도입됐고, 일부 웹 서버는 다시 쓰기 어려워 C++로 남아 있었음. Java로 점진 이식하려는 시도도 있었지만 크게 자리 잡지는 못했음. Megastore 같은 일부 인프라 서버는 Java로 작성됐고, 핵심 라이브러리는 대체로 C++에 JNI와 Python 바인딩이 붙어 있었음
      정책의 빈틈도 있어서 일부 팀은 내부 인프라용 자체 언어를 만들었고, 실제 코드베이스에는 커스텀 설정 언어와 Rob Pike가 만든 Sawzall도 쓰였음. 문법은 Go와 조금 비슷했음
      전반적으로 사람들은 선택의 자유가 컸고, 새 웹 서버는 주로 Java로, 새 데이터베이스 엔진 같은 것은 주로 C++로 작성됐음
      비동기 코드를 쓰는 일은 확실히 힘들었고, Go가 그 부분을 제대로 해결한 건 맞음. 하지만 Go는 Google의 문제를 해결하기 위해 고위 경영진이 시작한 프로젝트가 아니었음. 경영진이 그런 프로젝트를 두려워해서가 아니라, 개발자 고충을 직접 해결하기 위해 관련 팀들이 만들고 인력을 배정한 내부 인프라 프로젝트는 엄청나게 많았음. 당시 추진된 대규모 네트워킹 업그레이드 같은 것들이 그 예임. 그런 프로젝트들은 미리 예고됐음
      그 상태에 대해 큰 불만이 있었던 기억은 없음. 빌드 시간은 Bazel/Blaze가 원격 빌드 클러스터와 함께 동작하기 전까지 문제였고, 그 뒤에는 모든 것이 원격 캐시되므로 거대한 코드베이스도 몇 초 만에 컴파일할 수 있었음. 로컬 컴파일러 속도는 거의 중요하지 않게 됐고, javac도 매우 빨랐음
      Go 발표는 꽤 놀라운 일이었음. 정말 Google의 문제를 해결하려 개발됐다면 먼저 내부에 출시하고, 내부 사용자와 몇 년 동안 발전시킨 뒤 공개하는 게 일반적인 Google 방식이었을 것임. 하지만 Go는 공개가 먼저였음. 주변 사람들은 이게 무엇을 위한 것인지 궁금해했는데, 정말 내부 수요가 주도했다면 기대하기 어려운 반응임
  • Ken Thompson의 C 컴파일러를 LLVM 대신 쓰기로 한 결정에 대한 부분이 흥미로움. 사람들이 불평했고 특히 초기 버전에서는 생성 코드가 덜 최적화되는 결과도 있었음
    그 반대급부로 분할 스택을 빠르게 만들 수 있었음. LLVM에서 구현하고 LLVM ABI에 맞춰야 했다면 아예 하지 못했을 수도 있음
    글에서는 이 결정을 통한 유일한 이점이 아니라 이점의 한 예로 든 것임

    • 인터뷰의 LLVM 관련 내용은 정확하지 않음. Rust를 위해 LLVM으로 분할 스택을 구현했는데, 실제로는 꽤 쉬웠음
      X86FrameLowering에 이미 지원이 있었고, Go가 출시될 당시에도 존재했음. 스택 분할을 켜면 LLVM은 함수 프롤로그에 검사를 내보내고, 필요하면 __morestack을 호출해 스택을 더 할당함. Windows MSVC ABI도 _chkstk 지원을 위해 매우 비슷한 코드가 필요하므로, __morestack 지원과 자연스럽게 맞물림
      오히려 GDB가 분할 스택을 이해하게 만드는 일이 컴파일러 구현의 어떤 부분보다 어려웠음. 이는 백엔드와 무관함
      아마 작성자가 혼동한 것은 재배치 가능한 스택일 것임. 당시에는 정밀 가비지 컬렉션이 필요해서 구현이 어려웠지만, 지금은 Azul이 LLVM에 구현했음. 그때 정밀 가비지 컬렉션을 구현하는 가장 쉬운 방법은 함수 호출을 가로질러 모든 레지스터를 spill하는 것이었고, 이는 구현 노력이 더 들지만 과도한 수준은 아니었음. Plan 9 컴파일러는 어차피 그렇게 하는 것으로 보이므로 6g/8g 대비 성능 회귀도 아니었을 것임. 어쨌든 Azul의 가비지 컬렉션 지원은 이제 루트를 레지스터에 저장할 수 있는 제대로 된 구현을 갖고 있음
    • 결국 Go는 분할 스택에서 벗어나지 않았나? 2014년에 출시된 Go 1.3에서였음. “hot spot” 문제 때문임 [1]
      그래서 실험할 수 있는 능력이 가치 있었던 건 맞지만, 이 특정 예가 완벽한 사례는 아님
      [1]: https://go.dev/doc/go1.3#:~:text=Go%201.3%20has%20changed%20...
  • 정적 타입 취미 언어를 구상하면서 인터페이스와 제네릭을 함께 다루는 무언가를 시도 중인데, Rob의 말이 맞다는 걸 직접 느꼈음. 둘을 잘 맞물리게 만드는 건 정말 어려움
    그래도 할 가치가 있다고 봄. 개인적으로는 사용자가 직접 제네릭 타입을 정의할 수 없는 정적 타입 언어를 쓰는 일은 별로 보람 없을 것 같음
    예전에 BASIC에서 서브루틴용 GOSUB은 있었지만 인자를 넘기는 서브루틴을 작성할 방법이 없던 환경에서 프로그래밍했음. 그런 경험을 타입 시스템 수준에서 반복하고 싶지는 않음
    그래서 Go 팀이 좋은 설계를 찾는 데 오래 걸린 점은 이해됨. 좋은 언어를 설계하는 건 어렵고, 타입 시스템이 있는 좋은 언어를 설계하는 건 10배 어렵고, 제네릭이 있는 타입 시스템을 설계하는 건 그보다 또 10배 어렵음

    • 공감하지 않음. 제네릭 구현에서 가장 어려운 부분은 구현 상속을 지원할 때인데, Go는 그걸 지원하지 않음
      Go는 제네릭 구현에서 가장 쉬운 일을 맡은 셈임. 하지 않았던 이유는 기술적이지 않았음. 이념적이었고 순전히 무지에 기반했으며, Go 설계자 대부분이 90년대 후반 이후 프로그래밍 언어 이론 분야를 더 이상 따라가지 않았기 때문임
    • ML 계열과 함수형 언어들은 이 문제가 없어 보이고, 그중 다수는 Go보다 훨씬 더 정교하고 강력한 타입 시스템을 갖고 있음
    • Scala는 Golang보다 먼저 출시됐고, Golang이 나올 무렵 이미 인기를 얻고 있었음. 뒤에 GOOG 규모의 자원이 있었던 것도 아니었음