API 레이어에 숨어 있던 그 버그
테이블 뷰가 로드됐다. 캔버스가 렌더됐다. 테이블이 데이터베이스에 존재했다.
하지만 프런트엔드가 서버에 현재 테이블 상태를 요청했을 때 — 좌석, 스택, 누가 어디 앉아 있는지 — 서버는 404를 반환했다. 아무것도. 좌석도, 게임 데이터도, 렌더러가 작업할 수 있는 어떤 것도 없었다.
캔버스는 그릴 게 없어서 비어 있었다.
가능한 한 짧게 한 기술적 설명: table engine 앞의 API 게이트웨이는 모든 URL에서 접두사를 제거한 후 요청을 전달한다. /api/v1/tables/...는 table engine에 /tables/...로 도착한다. 빌드 세션이 작성한 table engine의 라우트들은 전체 접두사를 기대하면서 등록되었다 — 그래서 서버는 /api/v1/tables/...를 듣고 있었지만 /tables/...를 받고 있었다. 모든 요청이 빗나갔다.
로직 오류가 아니다. 깨진 알고리즘이 아니다. 잘못된 주소를 가진 라우팅 테이블이다. 전체 스택을 함께 테스트할 때만 표면에 드러나는 종류의 일이다.
찾는 데 얼마나 걸렸나?
시작부터 수정까지: 한 시간 미만.
빠르다. 이걸 빠르게 만든 것은: 디버깅 방법론.
이것에 대해 AI 에이전트와 작업한다는 것은 "뭐가 잘못됐어?"라고 묻고 답을 바라는 것이 아니다. 에이전트에게 올바른 컨텍스트를 동시에 주는 것을 의미한다 — 접두사를 처리하는 프록시 설정, table engine의 라우트 등록 파일, 404를 보여주는 요청 로그 — 그리고 불일치를 추적해 달라고 요청하는 것.
이 조각들이 함께 보일 때, 에이전트는 인간 엔지니어가 그저 탐색하는 데만 20분이 걸릴 표면 영역에 걸쳐 패턴 매칭을 할 수 있다. 에이전트는 약 3분 만에 프록시 rewrite 규칙과 table engine의 라우트 등록 사이의 불일치를 찾았다.
버그 뒤의 버그
라우팅 버그를 찾자 다음 버그가 드러났다.
엔드포인트는 이제 응답했다. 하지만 반환하는 데이터는 잘못된 형식이었다.
Table engine은 내부적으로 한 언어를 말한다: 좌석 번호, occupied 같은 상태 코드, 단순 정수로 된 원시 칩 수. 프런트엔드 렌더러는 다른 언어를 말한다: 좌석 인덱스, $235.50 같은 형식화된 통화 문자열, 작성된 타입 정의에서 계약상 기대하는 특정 필드 이름들.
아무도 변환 레이어를 작성하지 않았다. 빌드 세션은 엔드포인트를 작성했고 내부 형식을 반환했다. 렌더러는 인식하지 못하는 데이터를 받아 조용히 아무것도 만들지 않았다.
이것은 사전에 테스트를 작성하기 악명 높게 어려운 종류의 버그다. 두 시스템이 모두 돌아가고, 연결되어 있고, 서로 대화하려 하고 있어야 볼 수 있다. 두 시스템 사이의 격차는 어느 시스템도 자체적으로 버그가 없을 때 어떤 unit test도 커버하지 못한다.
이것이 빌드 방법론에 의미하는 것
테스팅 단계가 방법론이 실패했다는 의미는 아니다. 방법론이 우리를 테스팅까지 데려왔다는 의미다.
모든 소프트웨어 플랫폼에는 조각들이 실제 세계에서 서로 만나야 하는 통합 레이어가 있다. Spec-driven 개발은 그 레이어로 더 빠르게 데려간다. 그것을 건너뛰지는 않는다.
이 시리즈의 다음 글은 우리가 수정을 적용했을 때 일어난 일을 다룬다 — 그리고 브라우저는 여전히 같은 깨진 결과를 보여줬다.
우리가 어떻게 빌드하는지에 대한 엔지니어링 방법론 — spec, 세션, 세션 동안 에이전트가 하는 것과 아키텍트가 하는 것 — 전체 글은 The Salty Korean에 있다.
Stay salty.
The Salty Korean
Salty Poker Network의 창립자. 텍사스 포커, 플랫폼 구축, 온라인 포커의 미래에 대해 글을 씁니다. 자세한 내용은 여기에서 The Salty Korean.