3P by GN⁺ | ★ favorite | 댓글 1개
  • 원래는 사소하거나 임시로 보이던 구조·코드도 시간이 지나며 시스템을 떠받치는 역할을 맡을 수 있어, 변경 전 현재 의존 관계까지 확인해야 함
  • Chesterton's Fence는 제거 전에 설치 이유를 이해하라는 유용한 원칙이지만, 최초 의도만 보면 나중에 생긴 역할을 놓칠 수 있음
  • 집 욕실 공사 중 방해물처럼 보였던 수직 스터드는 옷장 칸막이 목적이 사라진 뒤에도 잘못된 구조 변경 때문에 2층 하중을 일부 지탱하고 있었음
  • 복잡한 컴퓨터 시스템에서도 변경 이력과 설계 문서는 출발점일 뿐이며, 컴포넌트가 현재 시스템에 어떻게 통합되어 있는지 함께 봐야 함
  • 오래된 컴포넌트를 제거·수정할 때는 과거의 설계 이유와 현재의 숨은 역할을 동시에 확인하지 않으면 예상 밖 장애를 만들 수 있음

Chesterton's Fence가 놓칠 수 있는 것

  • Chesterton's Fence는 어떤 대상을 바꾸거나 제거하기 전에 그것이 왜 만들어졌는지 먼저 알아야 한다는 생각임
  • 울타리나 문처럼 사람이 만든 것은 대체로 누군가에게 유익하다고 판단된 이유가 있었을 가능성이 큼
  • 완전히 무의미해 보이는 설계라도, 변경하려는 사람이 문제의 한 측면을 놓쳤을 수 있음
  • 다만 이 관점은 원래 만든 사람이 의도한 역할에만 집중하게 만들어, 이후에 생긴 새로운 의존성을 간과할 수 있음

집 수리에서 발견한 우연한 하중 지지

  • 몇 년 전 집 욕실을 다시 만들 때 작업을 방해하는 수직 스터드가 있었음
  • 그 스터드는 원래 옷장 칸막이의 일부였고, 옷장 칸막이 역할은 더 이상 필요 없어 보였음
  • Chesterton's Fence 관점만 따르면 제거해도 괜찮아 보였지만, 실제로는 시간이 지나며 하중을 지탱하는 구조물이 되어 있었음
    • 다른 잘못된 구조 변경을 거치며 이 스터드가 집의 2층을 떠받치는 데 기여하고 있었음
  • 어떤 것이 처음에 왜 만들어졌는지뿐 아니라, 이후 어떤 추가 역할을 맡게 되었는지도 확인해야 함

복잡한 컴퓨터 시스템에 적용되는 교훈

  • 복잡한 컴퓨터 시스템을 변경할 때도 같은 문제가 반복됨
  • 변경 이력을 살펴보고, 원래 설계 문서를 읽고, 특정 컴포넌트가 왜 그렇게 만들어졌는지 이해하는 일은 여전히 유용함
  • 그러나 현재 그 컴포넌트가 시스템 안에서 어떤 방식으로 연결되고 사용되는지까지 살펴야 안전함
  • 컴포넌트는 시간이 지나며 원래 목적과 다른 부가 역할을 맡기 쉬움
  • 안전한 변경은 과거의 설계 의도와 현재의 실제 역할을 함께 확인하는 데서 시작됨

댓글과 토론

Hacker News 의견들
  • 드디어 이걸 부를 이름이 생겼다는 느낌
    제어 시스템 지원 일을 많이 하는데, 어떤 물리 장비를 특이하게 다루는 PLC 코드가 의도치 않은 문제를 만드는 경우가 드물지 않음
    “소프트웨어로 전기/기계 문제를 고칠 때마다 그렘린이 태어난다”는 말을 자주 되풀이하게 됨
    버그의 근본 원인이나 제거하고 싶은 프로그래밍된 제한을 발견해도, 그 코드가 왜 있는지 알기 전까지는 항상 거절함. 아무 이유 없이 들어간 코드는 없으니 타이머나 재정의가 왜 필요한지 먼저 알아야 함
    종종 이미 사라진 문제를 해결하던 코드라면 좋은 일이지만, 어떤 사고를 막기 위해 넣어둔 코드인데 인력 교체로 원래 목적이 사라진 경우도 많음. 문서가 없으면 남의 작업을 바로 되돌리는 데 매우 조심스러워짐
    동료를 신뢰한다는 점도 있음. 보통은 아무 이유 없이 뭔가를 하지 않으니, 코드가 있다면 목적이 있다고 보고 처음에 충분히 검토했으리라 믿어야 함. 그 신뢰가 무너지면 모든 의사결정이 어려워짐

    • 그래서 아주 명백하지 않은 코드 줄에는 항상 왜 그런지를 주석으로 남김
      코드베이스 밖의 운영체제, 파일시스템, 데이터베이스, HTTP 엔드포인트, 하드웨어 같은 것과 상호작용하는 이유라면, 단순한 API나 라이브러리 호출이 아닌 한 100% 주석을 달아둠
      다른 서비스의 속도 제한 때문에 sleep을 넣었다면 누가 요구하는지, 당시 아는 한도 내의 제한값, 정확히 모르면 “추정이지만 동작하는 것 같다”는 점, 초과 시 시스템이 어떻게 보일 수 있는지를 적음
      파일시스템으로도 될 법한 사소한 일에 데이터베이스를 쓰지만, 실제로는 그 환경에서 필요한 방식으로 파일시스템에 접근하면 부하 시 폭주하는 시스템 호출 때문에 리소스가 고갈된다면 주석을 남김
      Ubuntu가 LTS 릴리스에서 고치지 않는 널리 쓰이는 라이브러리 버그의 우회책도 주석 대상임. “이게 별로인 건 알지만, 이유는 이렇다”는 주석을 정말 많이 써왔음
      당장 확장성 있게 만들기 귀찮고 현재 예상으로는 필요도 없지만, 큰 규모에서는 잘 안 될 코드를 쓸 때도 주석을 남김. 어쩌면 자존심 보호에 가깝지만, “전체 파일을 메모리에 읽는 건 알지만, 드물고 예측 가능한 배치 작업이고 파일도 작을 예정이니 괜찮다. 메모리가 부족하면 여기부터 보라. 온디맨드 호출로 바꿀 거면 다시 작성하라” 같은 식임
    • 그건 그냥 일반적인 체스터턴의 울타리 논리 아닌가 싶음
      글에서 지적하려는 건 그 자체로는 충분하지 않다는 점임. 그 코드가 있다는 가정 위에 다른 무엇이 지어졌는지도 알아야 하기 때문임
    • Allen Bradley PLC 깊숙한 곳에서 본 가장 오싹한 주석은 이거였음
      “왜 이 렁이 필요한지는 모르겠지만, 지워보고 직접 확인해 보라”
      괜히 건드리지 않았고, 확인해 보지도 않았음
    • 이 중 일부는 문화적인 면도 있음
      전기공학자와 기계공학자는 역사적으로 소프트웨어를 전기·기계 시스템보다 덜 진지하게 봐왔고, 그 결과 EE/ME가 지배하는 엔지니어링 문화에서는 엉망인 코드가 나오기 쉬움
      겉으로는 Professional Engineer인 사람들 사이에서도 용납하기 어려울 정도로 미숙한 소프트웨어 엔지니어링이 여전히 흔함
    • PLC 일은 다시는 안 할 것 같음
      문서 없는 코드는 둘째 치고, 대개 30년 전 맞춤 제작된 하드웨어라 작업 중인 장비의 회로도조차 없음
  • 비슷한 일이 있었음
    몇 년 전 오래된 집을 샀는데, 이전 집주인들이 1960년대부터 대부분의 작업을 직접 해왔음
    아연 홈통이 아마 수십 년 동안 새면서 지붕 구조 일부를 망가뜨렸고, 지붕은 70년대에 안쪽을 덮으려고 붙인 목재 패널이 받치고 있었음. 즉 그 목재 패널이 실제로 하중을 받고 있었던 셈임
    이 집에서는 훨씬 더 많은 걸 발견했음. 예를 들어 지붕 끝부분에서 기와 폭이 부족하자 추가 기와를 사는 대신 시멘트와 깨진 세라믹 화분 조각으로 메워놨음

    • 1900년대 초반으로 가면 외장재를 대각선으로 설치해서 건물이 뒤틀리는 걸 크게 막아줬음
      지금은 지진과 폭풍에서 그 역할을 석고보드와 합판에 기대고 있음
      집을 뼈대만 남기고 재건하는 걸 보면 보강재가 몇 개 추가된 걸 보게 될 것임. 벽이 무너지지 않게 하려는 게 아니라, 벽을 다시 세울 때까지 직각과 평면을 유지하려는 용도임
    • 내 차고도 딱 그런 느낌임
      처음 보면 누가 차고문을 들이받아 심하게 찌그러뜨린 것 같지만, 자세히 보니 지붕이 문이 달린 레일에 간신히 버티고 있고 거의 끝장나기 직전임
      처음엔 서까래 끝을 이어 붙이고, 예전에 반대쪽도 누군가 그렇게 했으니 작동하면 된다는 식으로 차고문만 교체하려 했는데, 이제는 지붕 전체를 새로 해야 할 것 같음
      진짜 걱정되는 건 지하실 전체에 걸쳐 있는 수상한 배선임. 비교적 새 전선, 오래된 천 피복 전선, 그리고 그걸 이어 붙인 전기테이프가 뒤섞여 있음. 다행히 전선 중 하중을 받는 건 없어 보임
    • 하중을 받는 페인트까지 몇 단계 안 남았음
    • 흰개미 피해가 아주 심한 집이 있었는데, 시공업자가 그걸 구조용 스투코라고 불렀음
  • 이 글과 거의 모든 댓글이 진짜 문제를 놓치는 것 같음. 핵심은 테스트 부족
    소프트웨어는 다른 모든 생산 수단과 달리, 현실에 반영하기 전에 변경 사항을 실제로 테스트할 수 있음
    좋은 테스트가 있다면 의도가 무엇이었는지, 그 기능에 새 용도나 새 사용자가 생겼는지는 상관없음. 고치고 테스트를 돌리면 그 수정이 좋은지 알려줌
    좋은 테스트가 있으면 소프트웨어 고고학, 모든 균열을 아는 노련한 베테랑, 복잡한 시스템을 머릿속에 모델링하는 신동, 포괄적인 요구사항 문서, 일부 사용자 집단을 실험쥐로 만드는 조심스러운 배포 시스템이 필요 없음
    좋은 테스트가 있다면 시스템을 무작위로 바꾸다가 개선이 나올 때 멈출 수도 있음. Google이 AI가 정렬 개선을 “개발했다”고 보고한 방식과 정확히 같음
    그런데도 테스트 개발자는 절반 이하의 보수를 받고, 테스트 부서는 상대적으로 작고, QA는 고정되고 제한된 일정에 밀려 있으며, QA 출신의 기술 영웅은 거의 없음. 파생적이고 반응적인 일로 보이기 때문인가 싶음

    • 테스트가 코드가 설계된 동작을 검증하더라도, 다른 시스템이 코드가 실제로 하는 동작에 의존하게 된 경우가 있음
      사용하지 않는 코드와 그 테스트를 제거했는데 사실 여전히 쓰이고 있을 수도 있음
      변경 후 테스트가 실패했지만 테스트가 취약해서 새 상황에 맞게 테스트를 고쳤는데, 알고 보니 무언가가 예전 동작에 의존하고 있었을 수도 있음
      테스트는 훌륭하고, 충분히 자기완결적인 시스템에서는 그것만으로 충분할 수 있음. 하지만 더 큰 시스템에서는 때로 텔레메트리나 단계적 배포도 필요함
    • 테스트에는 특정 범위가 있음. 코드가 책임져야 하는 범위이지, 몇 달이나 몇 년 동안 사용되면서 이제 기대받게 된 모든 일을 덮지는 못할 가능성이 큼
      원래 코드는 구매 목록의 VAT를 계산하는 용도였지만, 점차 제품 카테고리별 VAT 캐시를 강제 갱신하는 수단이 되고 처음 예상하지 못한 맥락에서 호출될 수 있음
      주석도 마찬가지임. 원래 의도와 부작용은 다루지만, 훨씬 나중에 그 메서드나 클래스가 어디에 쓰이는지, 실제로 무엇을 하게 됐는지는 다루지 못함
      이상적인 세상에서는 주변 세계가 변할 때 주석도 갱신되지만, 실제로는 내부 코드가 함께 바뀌지 않는 한 거의 그렇게 하지 않음
    • 아주 오래 지속되는 프로젝트에서 일해보면, 테스트나 충분한 예산의 QA가 조직적 문제를 막아주지는 못함
      보통 테스트는 썩어감. 테스트에도 유통기한이 있는 것처럼 보이고, 결국 일부 테스트가 죽기 시작함
      의존성 문제, API 기대 변화, 보안 업데이트, 계정과 자격 증명 만료, 머신 엔드포인트와 상태 변화 등이 섞여서 테스트 결과가 더 이상 프로그램의 올바름을 가리키지 못하게 됨
      개별 깨진 테스트 하나를 고치는 한계 사업가치는 보통 매우 낮아서, 아예 꺼버리거나 오류여야 하는데도 “통과”하도록 강제하는 일이 많음
      10년, 20년 반복되면 금방 “실제로 신뢰하는 테스트”와 “너무 바빠서 고치거나 정리하지 못하는 테스트”가 나뉨
      어떤 테스트가 좋은지 나쁜지는 직무와 역할 변경으로 사라지는 부족 지식이 되고, 어느 순간 “작동한다고 거짓말하는 테스트”와 “실패가 진실인지 더는 따지지 않는 테스트”의 덩어리 자체가 우연히 하중을 받게 됨
    • 앞부분은 테스트 주도 개발식 낙관론을 반복하는 것처럼 보였는데, 갑자기 테스트 부서 얘기로 넘어가서 일관성이 떨어짐
      차라리 프로그래머가 테스트를 작성하고, 코드와 함께 보관하며, 빌드 과정에서 자동 실행해야 한다고 말하는 편이 나음
      하지만 제대로 된 테스트 주도 개발도 좋은 설계와 좋은 관행을 대체할 수는 없다고 봄. 아주 단순한 명세조차 테스트로 대체할 수 없음
      f(S)가 문자열을 자기 자신과 이어 붙여 반환한다고만 명세되어 있다면, f를 블랙박스로 다루는 명백한 테스트만으로 f가 맞다는 걸 검증하기 어렵다. 형식 명세도 중요함
      몇 군데 찍어볼 수는 있지만, 마법처럼 잘못된 값 하나가 치명적인 상황이라면 테스트가 그걸 보여주지 못함
      소프트웨어 고고학, 노련한 베테랑, 시스템을 머릿속에 모델링하는 신동, 포괄적 요구사항 문서, 일부 사용자를 실험쥐로 삼는 배포 시스템은 풍자할 수 있겠지만, 모두 소프트웨어가 어렵기 때문에 생긴 대응임. 그리고 소프트웨어는 정말 어렵다
    • 테스트 개발자, 테스트 부서, QA 팀이라는 구분 자체가 대다수 소프트웨어 조직에는 사치에 가까움
      보통 소프트웨어 팀은 자기 작업의 품질에 직접 책임을 져야 하고, 조직도 너머로 문제를 넘길 수 없음
  • 중요하지 않던 스터드가 나중에 하중을 받게 된다는 건 이해되지만, 내 경험상 게으른 설계의 신호처럼 보임
    적어도 소프트웨어를 만들 때는 장식용 스터드로 집 일부를 받치려 한다는 걸 알 수 있고, 더 나은 새 구조를 만들지 않고 그냥 그렇게 하기로 선택하면 나중에 개발팀이 꽤 우울해짐
    글에는 동의하지만, 이런 발견을 자주 하지 않을 거라고 기대할 수 있는 곳에서 일하는 편이 훨씬 좋음

    • “당신”이 만든 것에서는 “당신”이 알 수 있겠지만, 내 경력에서는 남이 만든 것을 다루고 다시 만드는 일이 훨씬 많았음
      이 글의 요점은 장식용 스터드를 하중 지지 요소로 쓰지 말라는 게 아니라, 당신이 오기 전에 누군가 그렇게 했을 수 있음을 인식하라는 것에 가까움
      이는 기본적인 체스터턴의 울타리 해석보다도 더 보수적인 입장이고, 그 기본 해석조차 많은 사람이 지나치게 제한적이라고 밀어냄
      내게는 이 글이 와닿음. 프로그램적으로 말해 “장식용” 몰딩을 제거했다가 천장이 머리 위로 무너진 일을 실제로 겪어봤음
    • 항상 나쁜 의미의 게으름일까? 소프트웨어에는 “하중을 받도록 만든 것”과 “석고보드를 붙이려고 만든 것” 사이에 날카로운 구분이 없음
      시스템이 견고한지, 위험하게 확장 불가능한지는 맥락에 달려 있음
      “영업팀이 두 배로 커지고 시장 100%를 차지할 때까지 가능한 한 빠르게 고객을 팔고 온보딩하면?” 같은 사고 실험은 언제나 할 수 있고, 그런 조건에서도 데이터베이스를 메시지 큐처럼 쓰는 게 괜찮을 수도 있음
      그 결과 개발팀이 힘들어졌다면 그건 실수인 경우임. 유지보수가 어렵거나 운영 지옥이 된 것임
      하지만 소프트웨어의 장식용 스터드를 하중 지지 요소로 쓴다고 해서 반드시 그런 결과가 나오지는 않음. 제대로 된 해법을 만들 몇 달을 아끼면서, 보이지 않게 행복하게 제 역할을 하는 시스템도 많음
    • 방어적으로 코드를 짠다고 해보자. 함수에 잘못된 입력 처리를 추가함
      코드베이스의 나머지 부분은 절대 잘못된 입력을 보내지 않으니 그 분기는 죽은 코드이고 하중을 받지 않음
      그러다 어느 시점에 버그가 들어와 잘못된 입력을 보내고, 그 분기가 성실히 처리하고 복구함. 그 순간 그 분기는 하중 지지 분기가 됨
    • 중요하지 않은 서비스를 잘 돌리고 있다고 생각했는데, 장애가 났을 때 원래는 아무 일도 아니어야 했던 상황에서 다른 팀이 그걸 업무 핵심 기능에 의존하기 시작했다는 걸 발견한 적이 있음
    • 더 자주 본 건, 실제로는 그걸 모르기 때문에 이런 일이 생긴다는 쪽임
  • 내가 본 “우연히 하중을 받는” 산출물 중 가장 좋아하는 건 잘못 설정된 sudo였음
    find 명령에 비밀번호 없는 sudo를 허용해서 -exec로 루트 권한 임의 코드 실행이 쉽게 가능했는데, 제품의 중요한 지원 스크립트 여러 개가 그걸 쓰도록 작성되어 있었음
    하중을 받는 권한 상승인 셈임

  • 몇 년 전 주방을 리모델링했음
    예전 주방 한쪽 끝에는 큰 보가 지나가고 있었는데, 우리가 집을 사기 전 어떤 리모델링에서 2층을 올리려고 추가된 것이었음. 주방을 확장하려면 이걸 없애야 했음
    천장을 뜯어보니 그 보가 위층 벽을 받쳐야 할 위치보다 약 2피트 오른쪽에 있었음
    결국 고쳐서 보를 위층 벽 안으로 옮겼고 잘 해결됐지만, 원래 위치에 대해 물어보니 시공업자가 대략 이렇게 말했음
    “제대로 하려는 사람 하나와 신경 쓰지 않는 사람 하나가 있었던 겁니다. 품질은 결국 가장 낮은 설정값으로 맞춰지죠”

  • 소프트웨어가 물리 시스템보다 나은 점 하나는, 의도를 더 명확하게 하도록 코드 안에 주석과 타입으로 쉽게 문서화할 수 있다는 것임
    특히 Python 같은 동적 언어에서는 완벽하지 않지만, 큰 도움이 됨
    하중을 받는 스터드에 해당하는 비유는 프로덕션에 갈 줄 몰랐던 해커톤 프로젝트일 수 있음
    실제로 우리가 하는 많은 일은 겨우 돌아갈 때까지 뭔가를 해킹하고, 다음 일로 넘어가는 것임

    • 맞음. 이런 정확한 문제들 때문에 항공우주와 방위 분야에서 시스템 공학이 도입됐음
      유지보수 계획은 각 교체 가능 부품이나 조립체가 어떤 “하중”을 받는지 알아야 하기 때문임
      오늘날의 시스템 공학은 안타깝게도 원래 목표에서 아주 멀리 벗어났지만, 원래 구상은 그랬음
      요즘 시스템 공학 부서가 상대적으로 힘이 약해진 이유 중 하나는 유지보수 계획에 재무가 들어왔기 때문임. 재고 감가상각은 잔혹하고, “무엇을 예비품으로 둘 것인가”는 적어도 내 경험상 이제 시스템 공학의 판단인 경우가 드묾
      결과는 예측 가능하지만, 항공우주 정비 인력의 기준이 매우 높다는 점이 일부 상쇄함. 이들은 예컨대 세탁기 수리공과 비교하면 상당히 뛰어난 편임
      물론 재무는 그 기준도 몇 단계 낮추고 싶어함
    • 의도적인 경우라면 그렇게 하기 쉬움
      그런데 “장식적”으로 보이는 상류 구성요소가 사실상 속도 제한을 걸고 있어서, 제거하면 나머지가 폭주하는 시스템을 얼마나 자주 보는지 늘 놀라움
  • 사용자가 소프트웨어의 버그를 자신도 모르게 활용해서 정상 업무 흐름에 통합해버리는 경우가 떠오름
    그 결과 버그를 고치면 업무 흐름이 끊기고 불만이 생김

  • “왜 거기 있는지는 쉽게 알 수 있었다. 벽장 칸막이의 일부였다”라고 했지만, 시간이 지나며 우연히 하중을 받게 됐고, 잘못된 다른 구조 변경을 통해 그 스터드가 이제 집 2층을 받치는 데 도움을 주고 있었다고 함
    하지만 명백히 왜 거기 있는지 쉽게 알 수 있었던 게 아님. 게다가 우연히 하중을 받게 됐다는 데도 설득되지 않음
    당신에게는 잘못된 이유처럼 보여도, 당시 사람들에게는 그렇지 않았던 이유로 의도적으로 하중을 받게 만들었을 가능성도 꽤 있어 보임

    • 거기 왜 있었는지는 알 수 있었음
      다만 처음에 왜 있었는지를 아는 것만으로는, 지금 무엇을 하고 있는지는 알려주지 않음
  • 예전에 같이 일하던 물리학 박사후연구원이 장비 구성 위에 가끔 이런 표지를 붙여두곤 했음
    “만지지 마시오. 숨겨진 위험이 있음”
    연구실에는 똑똑한 사람들이 가득했고, 이들은 뭔가를 보고 바꿔도 되는지 스스로 합리적으로 결론 내리는 데 익숙했음
    이 표지는 그런 판단을 너무 서두르지 말라는 경고였음