GN⁺ 4시간전 | parent | ★ favorite | on: 그냥 빌어먹을 Go를 써라(blainsmith.com)
Lobste.rs 의견들
  • 전달자를 탓하려는 건 아니지만, 이런 블로그 문체는 피곤하고 유치함. 처음엔 웃겼을지 몰라도 반복될수록 짜증이 기하급수적으로 커짐
    그래도 Go는 좋음. 최근 TypeScript 프로젝트에서 Go 프로젝트로 옮겼는데 정신 건강과 업무 사기가 빠르게 좋아지고 있음
    if err != nil이 버그가 아니라 기능이라는 말은 받아들이지만, 여전히 Go의 가장 큰 흠이라고 봄. 합 타입(sum types) 이 있었다면 실행 시간 타입 단언에 의존하지 않고도 훨씬 인체공학적으로 만들 수 있었을 것임

    • 모든 것에 양다리 걸치고 실제 입장은 없는 AI 잡글보다는 이런 글이 낫다고 봄
    • 재미있게 읽었지만 이런 류의 글을 많이 보진 않았음. 그래도 “dipshit”보다는 “walnut”이라고 부르는 쪽이 더 웃겨서 좋음
      이런 식으로 쓸 거면 최소한 모욕도 좀 재치 있게 했으면 함
    • 동의함. 신고할 방법이 있나? 어떤 신고 분류에도 맞지 않음
  • 다른 댓글을 보면 인기 없는 생각 같고 거칠게 들리긴 싫지만, Go가 정말 싫음
    Go는 동시성에 효율적인 런타임 위에 그럭저럭 나쁘지 않은 문법을 얹고, Google의 힘으로 생태계를 밀어붙인 언어임. 그 외에는 끔찍하다고 봄
    가장 큰 문제는 수십 년간의 프로그래밍 언어 설계 연구나 심지어 실무 관행까지 의도적으로 무시하려고 설계된 것처럼 보인다는 점임. 수십 년 뒤에야 제네릭이 생기긴 했지만
    항상 의존 타입을 써야 한다는 뜻은 아니지만, 그래도 정도가 있음. Go에는 현대 언어라면 가져야 할 데이터 모델링, 불변 조건 모델링, 코드 구조화 기능이 거의 없음. Rust는 학습 곡선이 더 가파르지만 이런 면에서는 훨씬 낫고, 꼭 Rust만큼 정교한 타입 시스템이 아니어도 충분히 괜찮을 수 있음. 컴파일 시간이 걱정이라면 단순하지만 쓸 만한 기능만으로도 빠르고 표현력 있는 건전한 타입 시스템을 만들 수 있음
    그리고 if err != nil은 오류 처리 잡음으로 코드를 도배하는 최악의 방식이라고 봄. Go 쪽은 합 타입에 왜 그렇게 반감이 있는지 모르겠음. 이 점에서는 Java 예외조차 더 낫다. 현실은 언어에 오류를 더 잘 다룰 기능이 없으니, 사람들이 가능한 최악의 땜질을 기능으로 착각하게 된 것임
    애초에 원문이 잘난 체하지 않았다면 이런 댓글도 안 썼을 것임. “그냥 X 써라”는 멍청한 말임. 사용 사례에 맞고 편하고 생산적인 도구를 쓰면 됨. 그게 Go라면 Go를 쓰고, 아니면 다른 걸 고르면 됨

    • Go가 설계 공간에서 차지하는 위치는 대규모 코드베이스와 대규모 조직에서 일하는 신입 개발자들의 단순성을 거의 모든 것보다 우선하는 선택이라고 봄. 그래서 경험이 적은 개발자도 많은 맥락을 쌓지 않고 코드를 읽고 국소적으로 고치기 쉬운 편임
      특히 Google 같은 조직에서는 수천 명의 개발자가 있고, 특정 팀이나 회사에 머무는 기간이 짧을 수 있으니 도움이 됨
      이런 맥락에서는, 특히 미숙한 개발자에게 고급 타입 시스템의 부재가 어느 정도 장점이 되기도 함. 기본 타입이나 구조체 같은 아주 기본 개념을 넘어서 타입을 거의 생각하지 않아도 되기 때문임. 데이터를 모델링하는 도구는 거의 주지 않지만, 반대로 별생각 없이 많은 코드를 쓸 수 있음
      언어 수준의 정확성에는 별로 좋지 않다고 봄. 하지만 대규모 조직에서는 모노레포 분석, CI/CD, 카나리아 테스트, 관측 도구 같은 주변 인프라에 더 많이 의존하게 됨. 작은 조직보다 그 인프라가 훨씬 더 하중을 떠받침
      나도 비슷한 낮은 인지 부하 때문에 Go를 어느 정도 좋아함. 특정 프로젝트에 가끔만 코드를 쓰고, 현재 매일 장기 프로젝트에 깊게 관여하지 않기 때문임. 한 달 동안 안 본 코드베이스에 들어가 한 시간 미만으로 작업할 수 있다는 건 큰 장점임. 다만 복잡한 프로젝트의 전업 개발자였다면 덜 좋아했을 것 같음
    • Dart도 수십 년 연구를 무시하는 것처럼 보이지 않는 Google 언어지만, Flutter 밖에서는 아무도 안 씀. Go는 괜찮음
    • 이 글은 공격적이고 잘난 체하는 밈 형식을 베낀 것임. 그래서 사람들을 자극할 게 뻔하고, 글의 요지는 불꽃싸움이 아니라 제대로 된 대화를 할 가치가 있다고 봐서 별로임
      Go 개발자들은 기본기를 제대로 잡는 데 집중했다고 봄. 지금까지의 언어들과 프로그래밍 언어 이론 연구 커뮤니티가 기본기를 무시해왔기 때문임. 사람들은 가장 포괄적인 타입 시스템에 집착하지만, 타입 시스템이 복잡하고 표현력 있어질수록 수익은 줄어들고, 타입 시스템에 아무리 힘을 쏟아도 끔찍한 패키지 관리, 팀이 새 DSL을 배워야 하는 빌드 도구, 타입 정보나 서드파티 패키지 문서 링크를 자동 생성하지 않는 문서 시스템, 빈약한 표준 라이브러리, 심각한 성능 문제, 정적 컴파일 전략 부재, 고통스러운 빌드 시간, 가파른 학습 곡선, 징벌적인 타입 시스템, 읽기 어려운 문법, 형편없는 편집기 통합을 보상할 수는 없음
      Go에 데이터 모델링 기능이 전혀 없다는 말은 명백히 틀림. 어떤 언어에서도 데이터와 불변 조건은 모델링할 수 있고, Go도 그 모델을 강제할 타입 시스템을 꽤 제공함
      Rust는 훌륭하고, 반복 속도가 중요하지 않거나 베어메탈에 배포하거나 정확성·성능 요구가 아주 강하면 좋은 선택임. 하지만 범용 애플리케이션 개발, 특히 팀 단위 개발의 기본값으로는 좋지 않음. if err != nil을 많이 치긴 하지만, 초당 키 입력 수가 병목인 사람은 없다고 봄
    • Rust 말고는 이런 기능을 가진 현대 언어가 거의 없음. Gleam이나 Swift로 프로그래밍하고 싶다면 몰라도, 그 정도로 틈새라면 차라리 Haskell을 쓰는 셈임
  • if err != nil이 버그가 아니라 기능이고, 문제가 생길 수 있는 모든 지점을 보게 만든다는 말은 틀림
    실제로는 강제하지 않음. 직접 확인하지 않으면 오류를 무시하는 게 더 쉬움
    오류를 처리하거나 전파하는 방식에서는 Rust가 여전히 빛나는 사례임

    • 맞음. errcheck 같은 게 없으면 오류는 너무 쉽게 무시되며, 그건 그냥 어리석음. 적어도 오류를 명시적으로 버리도록 강제해야 함
      다행히 최근 몇 년간 작업한 모든 Go 프로젝트는 Go의 빈약한 내장 정적 검사 위에 golangci-lint를 얹어 썼음. 솔직히 모든 Go 프로젝트에 필수여야 함
    • 이 부분은 Swift가 더 좋음. 기능적으로는 같은 모델이지만, Swift 쪽은 서로 다른 라이브러리의 오류 전파가 더 쉬워짐. 다만 장단점과 설계 선택이 다른 것일 뿐, 더 좋거나 나쁘다고 할 문제는 아님
  • 이런 글쓰기 유행은 정말 싫지만, 글이 말하려는 요지에는 동의함
    “Volkswagen 크기의 node_modules가 없다”는 말은 맞지만, 프로젝트 로컬 node_modules가 아니라 ~/go에 있는 전역 패키지 캐시일 뿐임

    • 게다가 홈 디렉터리를 더럽힘. 앞에 점조차 없음. 이게 어떻게 용인되는지 모르겠음
    • 다른 언어 생태계의 의존성 트리 크기를 욕하기 전에 wc -l go.sum부터 해보라고 늘 말하고 싶음
  • 페이지 열자마자 “Hey, dipshit.”이 보여서 바로 닫음

  • 프로그래밍 언어를 추켜세우는 글 대부분과 같은 문제를 가짐. 현재 언어가 얼마나 훌륭한지보다, 이전에 쓰던 언어가 얼마나 끔찍했는지에 더 집중함
    필자는 Ruby와 TypeScript, 어쩌면 Python에서 심각한 고통을 겪었고 Go가 그걸 해결해준 듯함. 하지만 나는 Ruby나 TypeScript를 쓰지 않아서 글이 별로 와닿지 않았음
    수년간 이런 변주를 수십 번 읽은 느낌임. Python과 JavaScript와 달리 정적 타입이 있으니 Haskell을 써라. Perl과 Erlang과 달리 단일 바이너리로 배포할 수 있으니 Rust를 써라. Ruby와 Tcl과 달리 제대로 된 동시성과 채널이 있으니 Elixir를 써라
    필자가 자신에게 맞는 언어를 찾은 건 기쁘지만, 그 조언을 따르지는 않을 것임

    • 여기에는 Go를 Lobsters 독자에게 팔아야 한다고 생각하는 사람이 꽤 있는 듯함. 어떤 사람들에게는 오히려 역효과가 날 수 있음
  • Go의 제로값은 늘 흠이라고 느꼈음. 사용자가 기본값을 명시하도록 강제하는 편이 더 낫다고 봄. 그 외에는 OCaml이 아니라는 점을 감안하면 꽤 좋은 언어임

    • 제로값을 좋아하고 꽤 영리하다고 생각함. 하지만 기본값 설정 기능은 정말 아쉬움. 예를 들어 누락된 booltrue 값을 가져야 하는 JSON 객체를 마샬링하기가 매우 어려움
  • 배포와 컴파일 경험은 훌륭하지만, 언어 자체를 작성하는 건 정말 싫음. 쓸 때마다 나쁜 경험이 됨. Go처럼 제약이 심하지 않으면서도 배포 경험이 좋은 다른 언어가 있나?
    내가 Go에서 뭔가 놓치고 있는 건가?
    최근 작은 Rails 애플리케이션을 배포해보니 설정이 너무 많이 필요해서 Go의 장점은 확실히 appreciate하게 됨

    • 최근 Rust 프로젝트를 x86_64-unknown-linux-musl로 컴파일하기 시작했음. 이렇게 하면 모든 64비트 Linux 머신에서 그냥 실행되는 정적 바이너리가 나옴. 그다음 scp로 옮겨서 실행함
      아직 포트를 할당하고 수동으로 시작해야 하는 문제는 남아 있지만, 약간의 systemd 마법으로 해결할 계획임
    • 배포 경험 측면에서는 회사에서 nix bundler로 의외로 큰 성공을 거뒀음. 맥락을 말하자면 Qt6 GUI 앱을 만들고 있음
      bundler를 쓰면 단일 실행 파일을 만들 수 있고, 다른 배포판의 Linux 머신에 떨어뜨려도, 심지어 Qt가 설치되어 있지 않아도 사용자가 실행 파일만 실행하면 전체 GUI가 동작함
      다만 OpenGL 드라이버에서는 문제가 있다는 단서가 있음. 여전히 가능하긴 하지만 “복사해서 실행”보다 복잡해짐
  • Go가 동시성을 위해 설계됐다고 주장하면서도, 실수로 쉽게 공유할 수 있는 원시 포인터가 내장되어 있다는 게 가장 큰 문제임

  • 지루함 자체는 괜찮지만, Go는 실제로 지루한 언어가 되는 데 유난히 실패한다고 봄
    “데코레이터가 없다”고 하지만 struct tags와 리플렉션은 있음. 실행해보기 전까지 이것들이 어떻게 상호작용하는지 파악하기 어려움
    구조적 인터페이스와 리플렉션은 멀리 떨어진 곳에서 행동이 바뀌는 무서운 원천임. 구조체에 잘못된 메서드 하나를 추가했을 뿐인데 라이브러리 동작이 완전히 바뀔 수 있음
    문서화 관점에서도 이상함. 타입이 어떤 인터페이스를 만족하도록 의도됐는지 명확히 드러내고 싶지 않을 이유가 있나?
    고루틴은 왜 그냥 스레드라고 부르지 않는지 모르겠음
    채널은 왜 언어 기능이어야 하나? 제네릭이 세 가지쯤 되는 타입 말고도 유용하다는 걸 인정하는 데 10년 늦었기 때문이라고 봄

    • 고루틴은 스레드가 아니라, 스레드 풀 위에서 동작하는 더 가벼운 추상화임. 그래서 수천 개의 고루틴을 쉽게 만들 수 있음
      채널이 런타임 일부인 건 고루틴 스케줄러가 채널을 알아야, 채널이 비어 있지 않게 됐을 때 받는 쪽 고루틴을 더 쉽게 깨울 수 있기 때문이라고 봄. 아마 이 방식이 더 쉬웠을 것임
    • 고루틴은 여러 부가 도구가 붙은 그린 스레드이기 때문임