2P by GN⁺ | ★ favorite | 댓글 1개
  • 오랜 개발 경험에서 반복되는 가장 큰 교훈은 복잡성이 코드베이스의 “영원한 적”이며, 작은 변경이 엉뚱한 곳을 깨뜨리는 상태를 만든다는 점임
  • 복잡성을 줄이는 가장 강한 도구는 기능과 추상화에 “no”라고 말하는 것이지만, 현실적 타협이 필요할 때는 80/20 해법으로 대부분의 가치를 적은 코드로 전달함
  • 설계와 테스트는 너무 이른 추상화보다 시스템의 형태가 드러난 뒤 좋은 경계점을 찾는 쪽에 무게를 두며, 단위 테스트보다 오래 유지되는 통합 테스트를 중시함
  • 도구, 타입 시스템, 로깅, 디버거, API 설계는 개발자의 인지 부담을 줄여야 하며, 타입 시스템의 큰 실용 가치는 IDE 자동완성에 있음
  • DRY, 관심사 분리, 마이크로서비스, SPA, 제네릭, 과한 리팩터링처럼 좋은 이름의 기법도 코드 이해와 유지보수를 어렵게 만들면 복잡성의 통로가 됨

복잡성은 코드베이스의 최상위 위험

  • 복잡성은 처음에는 이해 가능하던 시스템을 갑자기 다루기 어렵게 만들고, 한 곳의 변경이 관련 없어 보이는 다른 곳을 깨뜨림
  • 좋은 의도를 가진 개발자나 프로젝트 매니저를 통해 복잡성이 들어올 수 있으며, 때로는 자신이 들여온 복잡성이 문제의 원인이 됨
  • 복잡성을 해결하는 만능 “클럽”은 없고, 가장 좋은 대응은 처음부터 들이지 않는 것임

“no”와 “ok”: 거절과 현실적 타협

  • 복잡성을 막는 가장 좋은 무기는 “no”임
    • “그 기능은 만들지 않음”
    • “그 추상화는 만들지 않음”
  • 다만 “no”는 좋은 엔지니어링 조언이어도, 더 많은 보상이나 승진에는 불리할 수 있음
  • 현실적으로 타협해야 할 때는 “ok”라고 답한 뒤 80/20 solution을 찾음
    • 원하는 가치의 80%를 코드의 20%로 구현하는 방식
    • 모든 장식 기능을 갖추지 못하고 조금 못생길 수 있지만, 대부분의 가치를 전달하면서 복잡성을 낮게 유지함
  • 프로젝트 매니저가 세부 요구를 잊거나 이동하거나 퇴사하는 경우도 있으므로, 때로는 80/20 방식이 더 나은 선택이 됨

설계와 코드 분해는 너무 이르면 위험함

  • 프로젝트 초반에는 시스템의 형태가 아직 “물”처럼 불명확하므로 너무 이른 분해를 피함
  • 시간이 지나면 좋은 경계점이 드러남
    • 나머지 시스템과의 인터페이스가 좁음
    • 적은 수의 함수나 추상화로 내부 복잡성을 감춤
  • 추상화를 너무 일찍 만들면 잘못된 추상화가 생기기 쉬워서, 기다리는 쪽에 더 무게를 둠
  • 초반에 큰 추상화를 만들려는 개발자에게는 UML 같은 버릴 수 있는 산출물을 주거나, 내일 동작하는 데모를 요구하는 방식이 도움이 됨
  • 초기 프로토타입은 실제로 동작하는 코드와 현실적인 제약을 더 빨리 보여줌

테스트는 “나중에 몰아서”가 아니라 “형태가 잡히며 함께”

  • 테스트는 많은 문제를 막아주지만, 도메인도 이해하기 전부터 test first를 강제하는 방식에는 반대함
  • 프로토타입 이후 코드가 단단해지기 시작할 때 대부분의 테스트를 작성하는 편을 선호함
  • “내 기계에서는 됨”이라며 테스트를 건너뛰는 태도는 매우 나쁨
  • 테스트 유형별 선호는 뚜렷함
    • unit tests: 프로젝트 초반에는 유용하지만 구현 변경에 쉽게 깨지고 리팩터링을 어렵게 만들 수 있음
    • end to end: 전체 시스템 동작을 보여주지만, 깨졌을 때 원인을 이해하기 어렵고 자주 깨지면 무시되기 쉬움
    • integration tests: 시스템 정확성을 볼 만큼 높고, 디버거로 원인을 찾을 만큼 낮아 가장 좋은 지점에 가까움
  • 경계점 API가 안정되면 그 주변에 통합 테스트를 강하게 투자함
  • end-to-end 테스트는 가장 흔한 UI 기능과 중요한 일부 edge case에 집중해 작고 잘 관리되는 모음으로 유지함
  • mocking은 꼭 필요할 때만, 가능하면 큰 경계점 단위로만 사용함
  • 버그가 발견된 경우에는 예외적으로 먼저 회귀 테스트로 재현한 뒤 수정하는 방식을 선호함

Agile, 리팩터링, Chesterton's Fence

  • Agile 자체는 최악은 아니며 개발 조직 방식 중 하나로 쓸 수 있지만, 실패의 면책 도구처럼 쓰는 “agile shaman”은 경계해야 함
  • 프로토타입, 좋은 도구, 좋은 개발자 채용이 소프트웨어 성공에 더 중요하며, 어떤 프로세스도 모든 문제를 해결하는 silver bullet은 아님
  • 리팩터링은 특히 프로젝트 후반 코드가 단단해졌을 때 유용하지만, 큰 리팩터링일수록 실패 가능성이 커짐
    • 작게 나누어 진행함
    • 시스템이 가능한 한 계속 동작하는 상태를 유지함
    • 각 단계가 끝난 뒤 다음 단계로 넘어감
  • 과한 추상화는 리팩터링 실패와 시스템 실패로 이어질 수 있음
    • J2EE는 많은 프로젝트에 해를 끼친 사례임
    • OSGi는 복잡성을 가두려다 더 강하게 만들었고, 여러 man-year의 재작업이 필요했음
  • Chesterton's Fence는 보기 싫은 코드라도 왜 존재하는지 이해하기 전에는 제거하지 말라는 태도와 연결됨
  • 동작 중인 코드는 완벽하지 않아도 존중해야 하며, 큰 시스템일수록 먼저 이해하는 시간이 필요함

마이크로서비스와 도구

  • 마이크로서비스는 시스템을 제대로 나누는 어려운 문제에 네트워크 호출까지 추가함
  • 도구는 개발자의 생산성과 이해력을 크게 높임
    • 새로운 환경에서는 도구를 배우는 데 시간을 쓰는 편이 좋음
    • 2주간 도구를 익히면 개발 속도가 2배 빨라질 수 있음
    • 문서가 없으면 다른 개발자에게 물어보고 파고들 필요가 있음
  • IDE의 코드 완성은 API를 모두 기억하지 않아도 되게 해주며, Java 개발에는 거의 필수에 가까움
  • 좋은 디버거는 매우 중요함
    • 조건부 중단점
    • 표현식 평가
    • 스택 탐색
  • 새 개발자는 사용 가능한 디버거를 깊이 배워야 하며, 이는 대학 수업보다 컴퓨터에 대해 더 많이 가르칠 수 있음

타입 시스템, 표현식, DRY

  • 타입 시스템의 가장 큰 가치는 점을 찍었을 때 가능한 동작 목록이 나타나는 도구 지원에 있음
  • 타입 정확성도 좋지만, 코드 완성과 탐색이 더 큰 실용적 가치를 가짐
  • 타입 시스템을 지나치게 추상적으로 사용하는 개발자는 경계 대상임
    • 제네릭은 특히 위험할 수 있음
    • 대부분의 경우 컨테이너 클래스 정도로 제한하는 편을 선호함
  • 짧은 조건식을 한 줄에 몰아 쓰기보다, 중간 변수에 의미 있는 이름을 붙여 나누는 편이 디버깅과 이해에 좋음
  • DRY는 좋은 조언이지만 균형이 필요함
    • 단순하고 명확한 중복 코드는 때로 복잡한 콜백, 클로저, 객체 모델보다 나음
    • 중복 제거 자체가 복잡성을 키우면 손해가 될 수 있음

관심사 분리, 클로저, 로깅

  • Separation of Concern은 강력한 아이디어지만, 실제로는 여러 파일을 오가며 동작을 이해해야 해 시간을 낭비하게 만들 수 있음
  • 대안으로 locality of behavior를 선호함
    • “동작하는 것” 가까이에 코드를 두면, 그 대상을 볼 때 무엇을 하는지 바로 알 수 있음
  • 클로저는 컬렉션 연산을 추상화하는 데 적합하지만, 소금처럼 적은 양으로 충분함
  • JavaScript의 “callback hell”은 클로저를 과하게 사용한 복잡성의 예임
  • 로깅은 특히 클라우드 배포 환경에서 매우 중요함
    • 주요 논리 분기마다 로그를 남김
    • 여러 머신을 지나는 요청에는 request ID를 포함해 로그를 묶을 수 있게 함
    • 가능하면 로그 레벨을 동적으로 조절함
    • 가능하면 사용자별 로그 레벨을 조절해 특정 사용자 문제를 디버깅함
  • 로깅 라이브러리는 복잡할 수 있지만, 로깅 인프라를 제대로 만드는 투자는 나중에 큰 보상을 줌

동시성, 최적화, API

  • 동시성은 두려워할 만한 영역이며, 가능한 한 단순한 모델을 선호함
    • 상태 없는 웹 요청 핸들러
    • 서로 의존하지 않는 원격 작업 큐
    • 단순한 API
  • 웹에서는 optimistic concurrency가 잘 맞을 수 있음
  • Java의 ConcurrentHashMap 같은 동시성 자료구조도 조심해서 써야 함
  • 최적화는 실제 성능 프로파일로 구체적 문제가 확인된 뒤 시작해야 함
    • CPU만 보지 말아야 함
    • 네트워크 접근은 많은 CPU 사이클에 해당하므로 가능하면 줄여야 함
    • 중첩 루프를 보고 바로 O(n^2) 제거에 들어가면 복잡성이 커질 수 있음
  • 좋은 API는 개발자가 많이 생각하지 않아도 되게 해야 함
    • API 작성자가 구현 관점이나 도메인 관점에 갇히면 사용하기 어려워짐
    • 단순한 경우에는 단순한 API를 제공하고, 복잡한 경우에는 더 복잡한 API를 따로 제공하는 계층화가 좋음
    • 객체지향이라면 동작은 가능한 한 그 대상 위에 있어야 함
  • Java의 stream/collector 예시는 흔한 작업을 불필요하게 우회하게 만드는 API로 비판받음

파싱, Visitor Pattern, 프런트엔드

  • 파서는 recursive descent 방식이 재미있고 아름답다고 봄
  • parser generator는 생성 코드의 이해와 디버깅이 어렵고, 문법의 재귀적 성질을 숨김
  • 실제 프로덕션 파서는 대부분 recursive descent이며, Bob Nystrom의 Crafting Interpreters를 추천함
  • Visitor pattern은 “bad”로 짧게 정리됨
  • 프런트엔드와 백엔드를 분리하고 SPA 라이브러리와 GraphQL JSON API를 HTTP로 연결하는 방식은 복잡성의 둥지를 두 개 만드는 일에 가까움
  • 단순한 웹사이트나 폼을 데이터베이스에 넣는 작업에도 큰 프런트엔드 라이브러리를 쓰는 흐름을 경계함
  • 복잡성을 낮추기 위해 htmxhyperscript를 만들었음
    • 단순한 HTML을 유지함
    • 많은 JavaScript를 피함
  • React는 일자리와 일부 애플리케이션 유형에는 더 낫지만, 프런트엔드 복잡성의 길로 들어서는 선택으로 봄

유행, FOLD, impostor syndrome

  • 개발에는 유행이 많고, 특히 프런트엔드에서 더 두드러짐
  • 혁명적이라는 새 접근에는 의심을 가져야 함
    • 컴퓨터 분야에서는 이미 많은 아이디어가 한 번쯤 시도되었음
    • 나쁜 아이디어가 새 이름으로 돌아올 수 있음
  • senior 개발자가 공개적으로 “이건 너무 복잡해서 이해하기 어렵다”고 말하는 것은 좋음
  • FOLD는 Fear Of Looking Dumb의 약자로, 멍청해 보일까 봐 복잡함을 인정하지 못하는 상태임
    • senior가 복잡하다고 말하면 junior도 이해하지 못함을 인정하기 쉬워짐
    • FOLD는 특히 젊은 개발자에게 복잡성이 힘을 얻는 주요 원천임
  • 개발자는 자신이 모든 것을 지배하는 느낌과 아무것도 모르는 느낌 사이를 오가며, impostor syndrome은 흔함
  • 모두가 impostor처럼 느낀다면 누구도 impostor가 아니며, 젊은 개발자는 좌절과 걱정이 있어도 커리어를 이어갈 수 있음

추천 읽을거리와 결론

댓글과 토론

Hacker News 의견들
  • 똑똑한 사람들이 불나방처럼 복잡성에 끌리는 게 이상함
    과잉 설계하고 싶은 충동과 싸우는 법을 배우는 데 몇 년이 걸림
    한 번 보이기 시작하면 무시하기 어려워지고, 이제는 코드가 과잉 설계됐는지 즉시 알 수 있음
    안타깝게도 코드의 99%쯤은 과잉 설계된 것처럼 보이며, 개발자가 자기 대체 불가능성과 청구 시간을 극대화하려는 유인이 강하게 작동함
    완전히 기계적이고 자아가 없어 보이는 개발자도 과잉 설계를 자주 저지르고, 이는 무의식적으로 작동함
    많은 개발자가 자신이 쓰는 코드 한 줄 한 줄을 의식적으로 생각하지 않고, 목표를 정한 뒤 그 목표에 조금 가까워지는 첫 번째 코드를 뽑아낼 뿐이라서, 매 단계마다 더 나은 대안이 있었다는 걸 깨닫지 못함

    • 개발자의 대체 불가능성과 청구 시간을 늘리려는 유인이 과잉 설계의 원인인 경우는 내 경험상 거의 없었음
    • 이 문제와 계속 씨름하고 있음
      첫째, 재미있는 퍼즐을 좋아함. 특히 상업용 코드는 제대로 만들면 꽤 지루한 경우가 많아서, 무의식적으로 구현이 재미있는 기능을 밀어붙이게 됨. 여기서 재미있다는 건 결국 모든 걸 과하게 복잡하게 만드는 기능이라는 뜻임
      둘째, 뭔가를 프로그래밍하는 중에는 내가 내리는 모든 선택이 당연하고 필요해 보임. 나중에 코드에서 떨어져 있다가 새 눈으로 다시 이해하려고 할 때야 내가 얼마나 엉망으로 만들었는지 보임
      대부분의 문제에서 가장 눈에 띄는 해법은 꽤 복잡한 경우가 많고, 단순한 해법을 어디서 찾아야 하는지는 지혜·기술·도메인 지식이 있어야 알 수 있음. 단순하고 깔끔한 해법은 거의 명백하지 않음
      “우리가 반쯤 만든 메시지 큐를 서서히 구현하고 있네. 그냥 표준 메시지 큐를 쓰자”, “버그 많은 커스텀 바이너리 프레이밍 프로토콜을 만들고 있네. 그냥 protobuf/msgpack을 쓰자”, “데이터를 가져오려고 커스텀 RPC 프로토콜을 만들지 말고 HTTP 위의 REST를 쓰자. 그러면 중간에 nginx를 넣어 백엔드 응답을 캐시하고 커스텀 캐시도 버릴 수 있음”
    • 어떤 작가의 편지에서 나온 “짧게 쓸 시간이 없어서 긴 편지가 됐다”는 식의 문장이 떠오름
      결국 시간이 있고 그렇게 하도록 보상받는다면 완벽하고 단순하며 반복 없는 코드를 쓸 수 있음. 하지만 대부분은 무언가를 출시하고, 그걸 출시하는 데 들어간 작업량·지성·설계 능력으로 기술적 역량을 보여주는 쪽이 보상받음
      자연스러운 결론은 모든 것이 부풀고 과잉 설계된 임시방편 덩어리가 되며, 3~7년마다 다시 작성되는 구조임
      자료를 본 건 아니지만 “재작성 반감기”는 이직률이나 평균 재직 기간과 상관이 있을 것 같음. 사람들이 과잉 설계하지 않으려 더 노력해도 어차피 다시 작성될 가능성이 큼
      완벽주의자로서는 정말 거슬리지만, 최고의 단순한 해법을 더 오래 고민하는 것보다 복잡하고 과잉 설계된 것을 만드는 쪽이 보상을 더 잘 받는 경우가 있음. 더 나은 조직도 있겠지만 9년간 소프트웨어 엔지니어로 일하면서 아직 찾지 못함
      오픈소스 프로젝트가 떠오르는 가장 좋은 반례지만, 좋은 라이브러리도 관리자가 바뀌면 재작성되거나 새 버전이 나오곤 함. 오픈소스와 HN의 많은 전업 소프트웨어 엔지니어가 보는 기업용 잡동사니 사이에는 금전적 유인이 매우 다름. 잘 다듬은 학술 논문과 대충 쓴 업무 이메일을 비교하는 것과 비슷함
    • 내 경우 과잉 설계는 보통 반대에서 나옴. 코드를 쓸 때 너무 많이 생각하고, 코드베이스가 어떤 모습이어야 한다는 선입견을 갖는 데서 생김
      Handmade Hero의 Casey가 자기 프로그래밍 스타일을 “압축 기반”이라고 불렀던 것으로 기억함. 일단 코드를 쓰고, 함께 속하는 것을 점진적으로 분리하는 방식임
      반복될 때 추상화하지, 의식적으로 설계해서 추상화하지 않음. 점점 이 방식을 더 많이 쓰고 있음
    • “코드의 99%가 과잉 설계된 것 같다”는 결론이 99% 같은 답을 내놓는다면, 그동안 배운 점이 타당하더라도 자신이 만든 사고 모델에 문제가 있을 수 있다고 보지 않나?
      그 자체로 반드시 틀렸다는 뜻은 아니지만, 다른 사람의 작업 품질을 평가하는 일이라면 강한 편향이 들어 있을 가능성이 커 보임
  • 이 사이트를 정말 좋아하고 늘 웃게 됨. 특히 제일 좋아하는 부분은 마이크로서비스
    “grug은 큰 뇌가 가장 어려운 문제인 시스템을 올바르게 나누는 일을 가져와서 거기에 네트워크 호출까지 넣는 이유를 궁금해함. grug에게는 매우 혼란스러워 보임”

    • 이 사이트를 좋아해서 자주 읽음. 내가 아는 사람들은 grug brain이라는 소프트웨어 개발 선언문에 이미 질렸을 것 같음
      “복잡성은 매우, 매우 나쁨”
      “복잡성과 티라노사우루스와의 1:1 대결 중 고르라면 grug은 티라노사우루스를 고름. 적어도 grug은 티라노사우루스를 볼 수 있음”
  • 타입 부분에는 불만이 있음
    grug은 타입 시스템의 가치가 키보드에서 점을 눌렀을 때 할 수 있는 목록이 마법처럼 뜨는 데 90% 이상 있다고 하지만, 우리 회사 주니어들은 프로덕션에서 널 접근 때문에 깨지는 코드를 자주 배포하고 Sentry가 그걸 알려줌
    집중 개발 기간에는 주니어 개발자 1명당 하루에 감지된 널 접근 버그가 하나쯤 나옴
    정적 검사가 있는 제대로 된 타입 시스템을 쓰면 IDE에서 “이거 null일 수 있는데 확실해?”라고 알려줘서 엄청 도움이 될 것 같음
    그리고 정적 타입이 없어도 자동 완성은 가능함
    “큰 뇌 타입 시스템 주술사들은 타입 정확성이 타입 시스템의 핵심이라고 하지만, grug은 그런 주술사들이 코드를 자주 배포하지 않는다는 걸 봄. 배포되지 않은 코드는 어떤 의미에서는 올바르겠지만, grug이 말하는 올바름은 아님”이라는 부분은 무례하고 불필요함
    C, PHP, Python, Haskell, 타입이 붙은 Python으로 주로 코드를 배포해 왔는데, 타입 언어에서는 프로덕션까지 가는 버그 발생률이 훨씬 낮음. 그게 타입을 좋아하는 이유 중 하나임
    또한 리팩터링이 훨씬 쉬워짐. 널리 쓰이는 것을 바꿀 때 전체 코드베이스에서 깨진 호출부를 몇 초 안에 신뢰성 있게 확인할 수 있고, 커지는 코드베이스를 반복 개선하는 데 큰 도움이 됨

    • 그 말이 틀렸나? 직접 말한 경험을 봐도 형식 증명을 강제하는 타입 시스템까지는 받아들이지 않는 것 같음
      대신 일부 문제를 잡아내고, 무엇보다 자동 완성 마법을 제공하면서도 완전한 정확성 증명에 필요한 모든 세부사항에 발목 잡히지 않는 절충적 타입 시스템을 받아들이는 셈임
    • 그냥 농담임. 아마 내가 놀림받는 쪽에 속할 가능성이 높지만, 너무 예민하게 받아들일 필요는 없다고 봄
      그는 C나 Java 수준의 정적 타입은 괜찮거나 좋다고 보고, Rust나 Scala에서 볼 수 있는 더 화려한 것들은 시간 낭비라고 보는 듯함. 나는 그가 완전히 틀렸다고 생각하지만, 삶이 그런 것임
    • 원문은 타입 언어에 찬성하는 글 아닌가? 결국 같은 입장인 것 같아 혼란스러움
    • 여기서 말하는 “주술사”는 당신 같은 사람을 가리키는 게 아닌 것 같음
      “큰 뇌 타입 시스템 주술사”는 반드시 코드를 쓰는 사람이 아니라, 예컨대 그 주제로 강의를 팔고 사람들을 들뜨게 만드는 사람을 뜻한다고 이해했음
      글 앞부분에서도 Agile을 말할 때 “주술사”라는 표현을 쓰는데, 실제 소프트웨어 개발보다는 강의 판매에 가까운 일을 하는 사람이 꽤 많음
    • 예전에 “큰 뇌 타입 시스템 주술사”와 함께 일해 봤음. 오히려 이 글은 매우 친절하고 관대한 분석에 가까움
      타입 계층에 집착하는 사람들과 일하는 건 참기 힘들고, 정당화는 대개 “만약에”와 “하지만 그건 sound하지 않다”의 세계에 머묾
      타입 시스템이 유발하는 많은 품질 저하는 타입을 충분히 많이 넣으면 버그를 완전히 막을 수 있다고 믿는 사람들에게서 나오는데, 그건 사실이 아님
  • 앞으로 몇 달 안에 모두가 HTMX 열차에 올라탈 것 같고, 많은 젊은 개발자들의 머리를 뒤흔들며 전 세계 에너지를 크게 아끼고 많은 사람을 행복하게 만들 것 같음
    그러고 나서 이 호기심 많은 젊은 사람들이 htmx.org의 링크를 계속 누르다가 hyperscript를 발견하면, Dusk till Dawn에서 뱀파이어들이 튀어나오는 순간 같아질 것임

    • 모두가 HTMX 열차에 올라탄다는 건 JavaScript에서는 무한 반복
      JavaScript 복잡성에 질린 누군가가 단순한 JavaScript 라이브러리를 만들고, 그 단순함에 사람들이 몰리고, 모든 웹 기능을 지원해야 해서 그 라이브러리가 복잡해지고, 다시 그 복잡성에 질린 누군가가 단순한 JavaScript 라이브러리를 만듦
      순수 JavaScript로 시작해 IE5/6과 몇 년 싸웠고, jQuery가 모두를 구했으며, AngularJS는 별로라 건너뛰었고, React 베타가 나오자 함수형 프로그래밍 이상 때문에 좋아했음. 이제는 트랜스파일링, 핫 리로드, Typescript, 무거운 React 라이브러리 더미 속을 헤치고 있음
      Intercooler와 HTMX로도 프로젝트를 몇 개 만들었음. HTMX는 전반적으로 탄탄하지만, 이 마을에 처음 지나가는 열차는 아님
    • 아, Carson Gross가 grug brained developer와 htmx의 저자였구나
      htmx에 대한 진짜 비판을 보고 싶음
      개인적으로 웹의 큰 지속 과제는 페이지를 잘 갱신하는 방법을 찾는 일이었고, 우리는 온갖 시도를 계속해 왔다고 봄. 그래서 피로가 생기고, 어떤 아이디어는 과격하거나 시간이 지나며 잡동사니가 됨
      Htmx는 grug식으로 더 적게, YAGNI를 고집하는 반작용처럼 보임
      미래는 불확실하지만 signals 같은 작업은 여전히 성스러운 일처럼 보임. 여러 방향에서 많은 시도가 이뤄지고 있고, 우리는 이 전선과 다른 전선들을 아직 개척 중임
      MobX와 Svelte 등도 이미 긴 여정을 해 왔음. 여정이 아직 끝나지 않았고 복잡성이라는 괴물과 계속 얽히는 것이 약함의 신호라기보다는, 보수적이거나 grug식 사고가 그렇게 볼 수 있다 해도, 단순하기만 한 접근이 아니라 단순하면서 좋은 것에 도달하기 위한 마땅한 노력이라고 생각하고 싶음
    • 그 문장을 보고 웃은 뒤 실제로 확인해 봤음
      _="on load wait 5s then transition opacity to 0 then remove me"
      아, 안 돼
      _="on htmx:error(errorInfo) fetch /errors {method:'POST', body:{errorInfo:errorInfo} as JSON} "
      아, 안 돼!
  • 개발자로 30년 일했는데, 초창기에는 오만했고 내가 다른 사람보다 똑똑하다고 생각했음을 인정함. 그때의 나는 모든 복잡성 악마를 좋아하는 “큰 뇌” 쪽이었음
    10년 뒤에는 “Grug brain” 개발자에 더 가까워졌고, 지금은 완벽하지 않을 수 있음을 알면서도 작동 가능한 가장 단순한 해법에 집중함. 그래도 괜찮은 이유는 그것이 올바른 것에 더 가까워지게 하고 반복 개선을 가능하게 하기 때문임
    개발자로서 할 수 있는 최고의 일은 코드 삭제임. 지금 우리에게 2년 동안 유지해 온 요구사항이 갑자기 더 이상 요구사항이 아니게 됐고, 많은 코드를 뜯어낼 생각에 정말 신남. 모든 것이 더 단순해지기 때문임

    • 코드를 지울 때 머리 뒤쪽에서 형언하기 어려운 즐거운 감각이 있음
      마치 공간이 해방되는 걸 몸으로 느끼는 것 같음
  • Grug은 일주일 전 Philosophy of Software Design 글의 댓글에서도 나왔고, 그때 논의가 꽤 좋았다고 봄. https://news.ycombinator.com/item?id=38011938
    “grug에 대해 나도 정확히 같은 느낌임. 사람들이 무엇이 단순한지 실제로 합의한다고 보지 않아서, 자기 ‘단순함’이 원시인도 동의할 명백한 것인 척하는 건 허세처럼 느껴짐”
    단순함은 발견하기 가장 복잡한 것 중 하나인 경우가 많음. 자기 주변의 모든 것, 단 자신이 하는 일만 빼고, 복잡하니 단순함으로 구부려야 한다고 확신하는 사람을 많이 봄. 권위에 매달리는 위험하게 약한 태도처럼 느껴짐

    • 좋은 지점임. 다만 핵심은 결국 나쁜 철학이 복잡성을 부추긴다는 데 있다고 봄
      grug brain 철학은 절대 피할 수 없을 때를 제외하면 어떤 대가를 치르더라도 단순함을 택하는 것임
      grug이 보는 큰 뇌 철학은 절대 피할 수 없을 때를 제외하면 어떤 대가를 치르더라도 재사용성을 택하는 것임
      이 철학의 문제는 1세대 설계 선택을 고착시키고 반복 개선을 더 어렵게 만든다는 데 있음
      단순함을 찾기 어렵다는 건 맞지만, 단순함을 찾는 열쇠는 반복 개선
    • Grug 글에서 특히 좋은 점은 뉘앙스와 겸손이 가득하다는 것임. 허세가 아님
    • 내 생각에는 코드 줄 수를 재면 됨
      지역 최솟값은 만들고 있는 것의 복잡성에 따라 움직이므로, 꽤 똑똑한 여러 견해가 동시에 맞을 여지가 있음
      완벽한 지표는 아니지만 방향성은 맞고, 로그 스케일에서는 아주 잘 작동함
  • 체스터턴의 울타리 개념을 수없이 써 왔고 설명해 왔는데, 그 이름을 알게 되어 좋음
    신입 개발자들과 일할 때 정말 자주 생기는 일임. 이들은 “낡은 레거시 쓰레기”를 보면 첫 반응으로 뜯어내거나 전부 버리고 처음부터 다시 만들고 싶어함
    어떤 경우에는 직접 시도하게 놔두는 것도 배움이 될 수 있지만, 우리보다 앞서 있던 사람들이 전부 완전한 바보는 아니었고 대체로 그렇게 한 이유가 있었다는 점을 기억하는 게 좋음
    때로는 정말 교체해야 할 지저분한 오래된 코드이기도 하지만, 그 경우에도 그 도메인에서 무언가를 만들기 위해 이해하고 처리해야 하는 모든 구석과 경계 사례에 대한 힘들게 얻은 기록이 담겨 있는 경우가 많음

  • 테스트 부분이 정말 좋음. 몇 년 동안 배워 온 것과 정확히 맞아떨어짐
    버그를 찾는 데는 통합 테스트가 적절한 지점임
    모의 객체는 일을 과하게 복잡하게 만드는 경향이 있고, 아직도 가끔 쓰긴 하지만 체계적으로 쓰는 건 피함
    단위 테스트는 리팩터링 앞에서 너무 깨지기 쉬운 반면, 통합 테스트는 리팩터링에 도움이 됨

    • 내가 좋아하는 꼼수는 데모 시나리오를 따라가는 통합 테스트를 만들어 두고, 데모 직전에 실행하는 것임. 가능하면 두 번 실행함
    • 강하게 반대함. 통합 테스트는 어느 정도 규모나 복잡도에 도달하기 전까지는 훌륭하게 작동하지만, 그 선을 넘으면 정말 나빠짐
      단위 테스트는 작성과 유지보수가 더 어렵지만, 실패했을 때 이해하고 디버깅하기 훨씬 쉬워 장기적으로 훨씬 더 잘 버팀
      최악의 테스트는 다른 통합 테스트가 먼저 실행됐다는 사실에 몰래 의존하는 통합 테스트임. 99%는 맞다가, 어떤 변경 때문에 순서가 바뀌는 순간 깨짐
    • 테스트에 관해 내가 내린 결론은 이럼
      팀에 단순한 코드가 있다면 테스트가 큰 도움이 됨. 하지만 팀에 단순한 코드가 없다면, 보통은 없는데, 테스트를 쓰는 시간보다 코드를 단순하게 만드는 데 시간을 쓰는 편이 낫음
    • 많은 부분은 무엇을 “단위”라고 생각하느냐에 달려 있다고 봄
      잎 함수 하나를 단위로 삼으면 너무 낮은 수준인 경우가 많지만, 비교적 안정적인 인터페이스를 가진 더 큰 모듈은 테스트하기 생산적인 좋은 단위가 될 수 있음
  • “복잡성은 매우 매우 나쁘다”
    “복잡성 영혼 악마에 맞서는 최고의 무기는 마법의 단어 ‘아니오’다”
    “슬프지만 사실: ‘예’를 배운 뒤 실패하면 다른 grug을 탓하는 법을 배우는 것이 이상적인 커리어 조언이다”
    grug의 복잡한 지혜임

  • 당시에도 논의됐음
    The Grug Brained Developer - https://news.ycombinator.com/item?id=31840331 - 2022년 6월, 댓글 374개

    • 복잡성은 여전히 매우 나쁨