두 개의 화면과 하나의 버튼으로 구현된 기능에서 발견된 아홉 가지 장애물
요약
이 글은 실제 운영 제품에 적용된 '문서 복원' 기능 개발 과정에서 발견된 아홉 가지 장애물과 그 공통점을 분석합니다. 이 버그들은 단순한 코딩 실수가 아니라, 두 코드 조각 사이의 접합부(join)나 계획과 기존 시스템 사이에 존재하는 구조적 결함입니다. 이러한 결함은 AI 지원 작업이 실패하는 주요 형태이며, 아무도 검토하지 않은 기반 위에 구축된 자신감 있고 잘 테스트된 결과물에서 발생합니다.
핵심 포인트
- 버그는 코딩 실수가 아닌 '접합부(join)'에 존재한다.
- AI가 놓치는 오류는 눈앞의 파일에는 보이지 않는 곳에 있다.
- 시스템은 완벽하게 테스트되고 구조화되어 있어 발견하기 어렵다.
- 진정한 결함은 계획과 기존 시스템 간의 불일치에서 발생한다.
Halfcycle은 사용자의 Claude Code 내부에서 5개 레이어의 빌드 방식을 실행하는 모듈입니다 (npx halfcycle).
대부분의 프로세스 주장들은 검증될 수 없습니다. 왜냐하면 아무도 발생하지 않은 버그들을 기록으로 남기지 않기 때문입니다. 하지만 이것은 가능합니다.
실제 운영 제품에 적용된 기능이 있습니다: 사용자가 편집했던 문서의 이전 버전으로 복원할 수 있게 하는 것입니다. 어떤 기준으로 보아도 작은 규모였습니다. 두 개의 화면 작업과 하나의 버튼만 필요했습니다. 이 기능은 누군가에게 빌드를 시작하도록 허용되기 전에 다섯 번이나 검토되었습니다.
각 라운드마다 발견된 장애물: 6개, 다음으로 2개, 그다음 1개, 그리고 없음, 그리고 없음. 총 아홉 개였습니다.
여기에 그중 여덟 가지를 소개합니다. 처음 네 가지는 실제 사용자에게 도달했을 수도 있었습니다.
복원 기능이 잠겨 있어야 하는 파일을 덮어쓸 수 있었습니다. 이 규칙은 일반적인 저장 경로에서는 강제되었지만, 아무도 이를 새로운 복원 경로에 복사하지 않았습니다. 새 코드를 테스트하는 모든 과정은 통과합니다. 실패하는 단언(assertion)을 찾을 수 없는데, 잘못된 것은 존재하지 않는 규칙이기 때문입니다. 누군가가 부재를 알아차려야 합니다.
실행 취소(undo) 기능이 보호하기 위해 존재하는 백업 데이터를 파괴할 수 있었습니다. 백업 저장, 검색 인덱스 업데이트, 커밋 등 모든 것이 한 단계로 이루어졌습니다. 만약 인덱스 업데이트에 실패하면, 백업 데이터는 폐기되고 새로운 콘텐츠는 이미 작성된 상태였습니다. 이 창은 작고 손실은 완전합니다.
이 버튼은 누구에게도 나타나지 않았을 것입니다. 이는 시스템이 해당 기능이 처리하기를 거부하는 레코드에만 정확하게 첨부하는 특정 데이터 조각에 의존하고 있었습니다. 코드는 고립된 상태에서는 올바릅니다. 아무도 사용하지 않게 배포되며, 모든 테스트는 녹색(green)입니다.
문서를 복원할 때 레이블이 조용히 삭제될 수 있었습니다. 공유 헬퍼가 모든 레이블을 지우고 전달받은 대로 다시 넣는데, 설계 과정에서 불완전한 목록이 전달되었습니다. 아무것도 실패하지 않습니다. 아무것도 기록되지 않습니다. 손실은 몇 주 후에 다른 곳에서 발생하며, 완전히 다른 버그처럼 보입니다.
계획 자체가 존재하지 않는 버그를 설명했다. 이는 코드베이스 검색에서 비롯되었으며, 발견한 내용으로부터 잘못된 결론을 도출했다. 이 계획에 의존하는 모든 하위 작업은 애초에 존재하지 않던 문제를 해결하고 있었을 것이다.
설계가 구현될 수 없는 곳이었다. 계획이 새 화면을 배치하기로 한 위치는 대체해야 할 대상의 형제(sibling)였는데, 형제는 아무것도 대체할 수 없다. 올바른 설계지만 잘못된 주소였다.
최종 체크리스트는 아무도 기록한 적 없는 주소를 지적했다. 이것은 네 번의 검토를 거치면서 살아남았다. 코드를 알 필요 없이 잡아낼 수 있는, 경로가 존재하는지 확인하는 기계적인 점검이었지만, 결국 값비싼 인간 독자의 시간을 소모했다.
모두가 가장 먼저 읽는 요약본은 여전히 세 차례나 폐기된 계획을 설명하고 있었다. 이 문서는 수정되었었다. 하지만 상단의 초록(abstract) 부분은 그렇지 않았고, 그 초록이 사람들이 실제로 읽는 부분이었다.
공통점
그들 중 어느 것도 코딩 실수는 아니었다.
모두가 접합부(join)에 존재한다: 두 코드 조각 사이, 두 사람의 작업 사이, 또는 계획과 이미 존재하는 것 사이에 있다. 그 앞에 파일을 작성하는 에이전트(agent)는 이들을 아무것도 볼 수 없다. 왜냐하면 어느 것도 눈앞의 파일에는 보이지 않기 때문이다. 마찬가지로 변경 사항을 독립적으로 읽는 검토자 역시 같은 이유로 볼 수 없다.
이것이 현재 AI 지원 작업이 실제로 실패하는 형태이다. 나쁜 코드가 아니다. 아무도 검토하지 않은 것에 기반하여 구축된, 자신감 있고 잘 테스트되고 구조화된 작업이다.
그리고 어떤 도구가 각각을 잡아낼 수 있었는지 주목하라. 린터(linter): 없음. 타입 체커(type checker): 없음. 테스트 스위트(test suite): 없음. 그리고 '없음'보다 더 나쁜 것은, 이 네 가지가 녹색으로 빌드되고 그 녹색이 증거로 받아들여진다는 점이다.
솔직히 말하자면
그렇게 작은 기능에 대해 다섯 번의 검토를 거치는 것은 부담스러우며, 우리는 이를 변호하지 않을 것이다.
첫 번째 라운드만으로도 여섯 개의 장애물이 발견되었습니다. 두 번째부터 네 번째 라운드에서는 다섯 개의 결함이 더 발견되었는데, 이 중 거의 모든 것이 이전 라운드의 수정 작업에서 발생했습니다. 즉, 스스로의 발견이며 불편한 사실입니다: 수정된 부분이 작업 과정에서 가장 위험도가 높은 영역입니다. 변경을 요청하는 검토자는 그 변경으로 인해 무엇이 망가졌는지 파악하기 어려운데, 이것이 바로 다음 라운드가 마지막 라운드를 본 적 없는 세션으로 진행되어야 하는 이유입니다.
이러한 특성 때문에 방법론은 네 군데에서 수정되었습니다. 어떤 범위 정의 문서(scoping document)는 전달되기 전에 검증되지 않은 것으로 표시해야 합니다. 어떤 디자인은 가정하는 것이 아니라 확인해야 합니다. 값싼 기계적 통과 과정(mechanical pass)을 비싼 리더가 사용하기 전에, 기계적 통과 과정이 할 수 있는 일에 대해 지출합니다—존재하지 않는 주소를 가리키는 체크리스트는 애초에 인간에게 도달해서는 안 됩니다. 그리고 모두가 가장 먼저 읽게 되는 요약본은 마지막에 작성되도록 허용되어야 합니다.
이것이 순환 과정(loop)으로 작동하는 방식이며, 동시에 논거이기도 합니다. 한 프로젝트에서 발견된 결함은 그 이후의 모든 프로젝트의 단계가 됩니다. 그리고 이것이 사실이기 위해 아무것도 설치할 필요가 없습니다.
비용 분석
사용자에게 도달했을 네 가지 항목은 네 개의 버그 리포트와는 같지 않습니다.
레이블 삭제는 지연되고 잘못 귀속된 증상을 동반하는 데이터 손실입니다—일주일 동안 다른 문제로 조사되는 종류의 것입니다. 백업 파괴는 이길 수 없는 지원(support) 대화입니다. 아무도 보지 않는 버튼은 출시되어 발표되지만 사용량이 전혀 발생하지 않아, 누군가 코드를 열기 전에 분석에 스프린트를 낭비하는 기능입니다.
그것과 비교하여: 두 개의 화면 작업에 대한 다섯 라운드의 검토 과정이었는데, 당시에는 과도하게 느껴졌고 지금도 그렇습니다.
우리는 그 교환 가치에 대한 숫자를 가지고 있지 않으며 만들지도 않을 것입니다. 우리가 가진 것은 위에 나열된 목록이며, 이는 확인 가능하고, 이 목록의 모든 발견 사항이 이제 프로세스에서 한 단계 더 앞서게 되었다는 점입니다.
시작하기
npx halfcycle
작업하려는 폴더에서 실행한 후, Claude Code로 열고 무엇을 만들고 있는지 말하세요. Node 20 이상이 필요합니다. 먼저 배울 것이 없고 기억할 다음 단계도 없습니다. 다음에 할 일이 있을 때 그것이 무엇인지 알려줍니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기