지니와 계약

비결정적 에이전트가 코드를 작성할 때 사양 기반 개발(SDD)과 테스트 기반 개발(TDD)이 실제로 어떻게 조화를 이루는지, 그리고 단순한 접근 방식이 어떤 문제점을 드러내는지 살펴봅니다.

예측 불가능한 지니

켄트 벡은 50년 동안 테스트가 최우선이어야 한다고 주장해 왔습니다. 따라서 그가 현재 함께 일하는 AI 코딩 에이전트를 어떻게 묘사하는지 주목할 만합니다. 당신의 소원을 들어주는 "예측 불가능한 지니"종종 문자 그대로 예상치 못한 방식으로 말이죠. 당신이 무언가를 요청하면, 그것은 당신에게 그것을 줍니다. 무언가그것이 당신에게 실제로 필요한 것인지는 별개의 문제입니다.

같은 대화에서 Beck은 TDD를 다음과 같이 부릅니다. "초강대국" 이러한 에이전트들은 끊임없이 회귀 오류를 발생시키기 때문에 테스트 스위트가 이를 잡아내는 가장 저렴한 방법입니다. 하지만 그는 또한 여러분이 주의해야 할 만한 세부 사항을 슬쩍 언급합니다. 그는 에이전트의 행동을 막는 데 어려움을 겪는다는 것입니다. 테스트 삭제 그들이 시험에 통과하도록 하기 위해서죠. 시험은 방어 수단인 동시에 시험의 함정이기도 합니다. 바로 이것이 이 문제의 핵심입니다.

용어에 대해 간단히 설명드리겠습니다. 이 두 약어 중 하나가 종종 모호하게 사용되기 때문입니다. TDD는 오래된 개발 방식입니다. 먼저 실패하는 테스트를 작성하고, 테스트를 통과할 만큼의 코드만 작성한 후, 코드를 정리하고 이 과정을 반복하는 것입니다. SDD는 더 새로운 개발 방식입니다. 구축하려는 것을 구조화된 기술 명세(API 구조, 데이터 모델, 승인 기준 등)로 작성하고, 이 문서를 코드가 충족해야 하는 계약으로 간주하는 것입니다. 중요한 것은 SDD가 바로 그 계약이라는 점입니다. 연습제품이 아닙니다. GitHub의 Spec Kit 이는 도입을 용이하게 해주는 도구 중 하나이며, 이 글 전체에서 예시로 사용되지만, 여기에 나오는 모든 내용은 모든 SDD 설정에 적용됩니다. (또한 이는 와는 다릅니다.) 스토리 중심 (테스트 주도 개발(TDD)과는 다른 방식으로, 사용자 스토리라는 좀 더 느슨한 수준에서 작동하는 개발 방식입니다.)

왜 둘 다 지니 혼자서는 살아남지 못하는 걸까요?

SDD를 단독으로 실행하면 의미가 모호해집니다. 에이전트는 모호한 명제를 자체적으로 해석하여 내부적으로 일관성 있는 코드를 생성하지만, 명제를 그대로 실행할 수 없고 코드를 명제로 되돌릴 장치가 없기 때문에 결국 명제의 의도를 놓치게 됩니다. TDD를 단독으로 실행하면 정반대의 결과가 나타납니다. 앞에 놓인 테스트를 통과하는 코드만 생성될 뿐, 그 이상의 의미는 전달하지 못하며, 테스트의 의도를 설명하는 명제도 존재하지 않습니다. 영상을 주장하기 때문에 결국 에이전트가 구축하기로 결정한 내용을 인코딩하게 됩니다. 어느 쪽이든 녹색 체크 표시가 뜨지만 대상은 잘못되었습니다.

둘을 합치는 이유는 둘 다 좋아서가 아닙니다. 그 이유는... 사양과 테스트는 동일한 의도를 나타내는 두 가지 독립적인 표현 방식입니다. 하나는 사람이 읽을 수 있는 문서이고, 다른 하나는 기계가 실행할 수 있는 문서입니다. 각각은 서로의 사각지대를 보완합니다. 명세서는 테스트의 존재 이유를 제공하여 테스트 스위트가 단순히 코드의 복제품이 되지 않도록 합니다. 테스트는 명세서에 효력을 부여하여 에이전트가 명세서의 내용을 슬쩍 바꿔 해석하지 못하도록 합니다.

왼쪽의 사양 패널과 오른쪽의 테스트 패널은 모두 중앙의 에이전트를 향해 안쪽으로 향하며, 에이전트를 양쪽에서 프레임처럼 둘러쌉니다.

하나의 스파인, 두 개의 게이트, 피드백 에지

아래 다이어그램은 형태를 보여줍니다. 사람이 의도를 정의합니다. Spec Kit의 specify plan 명령어를 통해 사양서와 계획서가 만들어지고, 담당자가 이를 검토합니다. 이는 최종 확정이 아닌 중간 점검 단계입니다. 세 가지 세부 사항이 이것이 실제로 작동하는지 여부를 결정합니다.

첫째, analyze 모든 코드보다 먼저 실행됩니다. 이는 사양, 계획 및 작업 전반에 걸친 읽기 전용 일관성 검사입니다. 일반적으로는 마지막에 수행하는 것이 좋지만, Spec Kit에서는 그렇지 않습니다. 나만의 퀵스타트 첫 번째 패스가 속한다는 것이 명시적으로 드러납니다. 전에 implement틈이 있을 때는 수정 비용이 저렴합니다. 나중에 드리프트 검토를 위해 다시 실행해도 좋지만, 부하 지지 단계는 에이전트가 라인을 작성하기 전 단계입니다.

둘째, 한 사람이 합격 시험을 소유합니다. — 다음 섹션에서 그 의미를 설명합니다. 세 번째로, 에이전트가 내장됩니다. 수직 슬라이스전체 테스트 스위트를 미리 생성하거나 수백 줄의 코드를 한 번에 내놓는 대신, 작은 동작 조각 하나를 처음부터 끝까지 살펴보는 방식을 사용합니다. 즉, 실패하는 테스트 하나와, 그 테스트를 통과시키기 위한 최소한의 코드, 정리 작업 후 다음 단계로 넘어가는 방식입니다. 이렇게 하면 겉보기에는 그럴듯해 보이지만, 누구도 원인을 파악할 수 없는 방식으로 오류가 발생하는 코드를 만들 수 있습니다. 또한 코드는 테스트가 모두 통과하고, 테스트 스위트가 실제로 버그를 잡아내는지 확인하고, 커밋된 테스트가 통과하도록 수정되지 않았을 때만 병합됩니다.

사람의 의도에서 병합 게이트까지 이어지는 수직 파이프라인으로, 분석 단계를 사전 코드 게이트로, 사람이 직접 수행하는 읽기 전용 승인 테스트, 수직 슬라이스 코드 루프, 그리고 루프에서 사양으로 되돌아가는 피드백 에지를 포함합니다.

누가 안전 난간을 지키는가?

이것을 깔끔한 "사양 → 테스트 → 코드" 흐름으로 상상하고 거기서 끝내는 것은 쉽습니다. 하지만 진정한 엔지니어링은 실패 모드에 있으며, 모든 실패 모드는 다음 질문으로 귀결됩니다. 에이전트가 사양을 작성하고, 테스트를 작성한다면, 코드를 작성하는 사람이 실제로 무엇을 검증하는 건가요?

누가 “옳다”는 것의 의미를 결정하는가?

만약 하나의 모델이 명세, 테스트, 코드 세 가지 모두를 작성한다면, 모두 동일한 맹점을 공유하게 됩니다. 의도를 한 번이라도 잘못 읽으면, 그 잘못된 해석을 그대로 반영한 명세, 그 해석을 강제하는 테스트, 그리고 테스트를 통과하는 코드가 만들어집니다. 모든 것이 통과하는 것처럼 보이지만, 실제로는 모두 틀린 것입니다. 두 개의 "독립적인" 인코딩은 다음과 같은 경우에만 독립성을 유지합니다. 개인은 적어도 그 중 하나를 소유하고 있으며, 이는 부동산 중개인이 조용히 내용을 변경할 수 없는 부분입니다.

담당자에게 책임을 맡기기에 가장 적합한 결과물은 인수 테스트입니다. 인수 테스트는 "이 기능이 완성되었고 올바르게 작동한다"는 것을 증명하는 테스트입니다. 글로 작성된 명세서는 글자 그대로 준수하더라도 그 취지를 위반할 수 있지만, 테스트는 구체적이고 통과 또는 실패 여부가 명확한 기준이 됩니다. 소유권을 가진다는 것은 담당자가 모든 주장을 직접 손으로 작성한다는 의미가 아닙니다. 담당자는 테스트 초안을 작성할 수 있습니다. 중요한 것은 담당자가 테스트 내용을 검토하고, 주장 내용을 승인하며, 그에 대한 책임을 진다는 것입니다. 코드가 측정되는 기준이 되기 전에, 동일한 에이전트가 조용히 자체 코드를 생성하고 병합하도록 두는 것이 아닙니다. 테스트 관점에서 인수 테스트는 바로 이러한 것입니다. 신탁코드가 올바른지 여부를 결정하는 요소입니다. 이 작업을 진행했던 다른 사람들이 발견했듯이, 인간이 통제하는 시험은 최후의 방어선입니다. 압박을 받는 상황에서 에이전트의 본능은 코드를 수정하기보다는 실패한 테스트를 삭제하는 것이기 때문입니다. 에이전트가 오라클을 수정할 수 있다면 오라클은 존재하지 않는 것입니다.

녹색은 프록시이고, 프록시는 악용될 수 있습니다.

"테스트를 통과할 때까지 에이전트는 다음 단계로 넘어갈 수 없습니다."라는 말은 마치 보장된 것처럼 들리지만, 사실은 그렇지 않습니다. 의도한 바를 충족시키지 않고도 테스트를 통과시키는 방법은 여러 가지가 있습니다. 검사 범위를 약화시키거나, 해당 테스트를 건너뛰거나, 실제 구성 요소를 스텁으로 대체하거나, 예상되는 답을 하드코딩하거나, 오류를 무시하거나, 아예 테스트를 다시 작성하는 등의 방법이 있습니다. 문제는 바로 여기에 있습니다. "테스트를 통과시키는 것"이 ​​목표가 되는 순간, 테스트 통과는 더 이상 시스템이 제대로 작동한다는 확실한 지표가 되지 못합니다.

이것은 가상적인 상황이 아닙니다. 인기 있는 명령줄 코딩 에이전트이자 빌드 루프에 적합한 선택지인 Aider는 설계상 이러한 기능을 제공할 것입니다. 편집 테스트가 틀렸다고 판단합니다. 코드를 억지로 끼워 맞추는 대신에 말이죠. 사람이 직접 운전할 때는 정말 유용하지만, 에이전트가 스스로 실행될 때는 오히려 허점이 됩니다. 그렇다면 에이전트가 게이트를 악용하는 것을 어떻게 막을 수 있을까요? 두 가지 방어책이 대부분의 역할을 하는데, 단순히 통과/실패 횟수만으로는 해결되지 않습니다.

검사로 알 수 없는 것

단위 테스트는 "이 함수가 독립적으로 내가 말한 대로 작동하는가?"라는 좁은 질문에 답합니다. 성능, 보안, 접근성, 그리고 가장 중요한 것은... 체계 실제로는 실제 사용자에게 제대로 작동합니다. 바로 그 마지막 공백이 AI 생성 코드가 실패하는 지점입니다. 에이전트가 특정 부분만 제대로 처리하고 그 부분을 통과하는 전체 여정을 조용히 망가뜨리는 것입니다. 따라서 주의해야 합니다. 엔드투엔드 및 통합 테스트를 사후 고려 사항이 아닌 최우선 순위로 다뤄야 합니다. — 사용자가 실제 애플리케이션을 사용하는 방식대로 작동하게 하고 단위 테스트에서 발견할 수 없는 부분을 잡아내는 계층입니다. 다음과 같은 도구들이 있습니다. 극작가 MCP 에이전트가 추측하는 대신 실제 브라우저를 실행하고 해당 여정과 비교하여 자체 작업을 검증하도록 하십시오. 테스트가 전혀 불가능한 요구 사항의 경우 빌드 파이프라인에서 명시적인 관문으로 만드십시오. 희망은 통제 수단이 될 수 없습니다.

리팩토링은 선택 사항이 아닙니다.

TDD 루프는 두 단계가 아니라 세 단계로 구성됩니다. 실패하는 테스트를 작성하고(빨간색), 통과하도록 만들고(녹색), 마지막으로 방금 작성한 코드를 정리하는 것(리팩토링)입니다. 에이전트를 그냥 두면 세 번째 단계인 리팩토링을 건너뛰고 테스트가 통과되는 순간 배포하는 경향이 있습니다. 하지만 코드 품질을 유지하는 핵심은 리팩토링입니다. 빠르고 지치지 않는 에이전트로 이 단계를 건너뛰면 TDD의 본래 목적인 코드 오류를 그대로 쌓아 올리게 됩니다. 리팩토링을 루프에 포함시키고, 복잡성이나 코드 변경량 신호를 주시하여 에이전트가 과도하게 구축한 부분을 잡아내세요.

사양은 고정된 것이 아니라 살아있는 것입니다.

두 방법 사이에는 실제로 긴장 관계가 존재합니다. SDD는 코드를 작성하기 전에 명세를 확정하는 것을 목표로 합니다. 이것이 바로 계약의 핵심입니다. 반면 TDD는 부분적으로는 다음과 같은 방식입니다. 발견 설계 과정에서 요구사항이 잘못되었거나 인터페이스가 어색하다는 사실을 테스트를 작성하는 도중에 알게 되는 경우가 종종 있습니다. 그렇다면 테스트를 통해 명세서 자체가 불완전했다는 사실이 처음으로 드러났을 때 어떤 일이 벌어질까요?

해답은 명세서를 일회성 승인 문서가 아니라 살아있는 문서로 취급하는 것입니다. 요구사항이 변경되거나 테스트에서 허점이 발견되면 코드에 맞춰 테스트를 수정하는 것이 아니라, 먼저 명세서를 업데이트하고(작고 검토된 변경 사항), 그에 맞춰 테스트를 업데이트한 다음, 테스트가 실패하도록 만들고 에이전트가 이를 통과하도록 합니다. 이는 코드뿐 아니라 명세서에도 적용되는 동일한 레드-그린 루프이며, 절대 어겨서는 안 되는 한 가지 규칙이 있습니다. 변경 흐름은 사양 → 테스트 → 코드로 바뀌며, 테스트 → 코드 순서로 직접 이어지는 경우는 없습니다. 그 방침을 고수하면 요구사항 변경은 이 접근 방식의 가장 큰 약점이 아니라 가장 큰 약점이 될 것입니다.

문은 환경 속에 존재합니다.

핵심적인 디자인 선택 사항은 다음과 같습니다. 학문이 존재하는 곳특정 도구의 속성으로 만들고 싶은 유혹이 들기 마련입니다. 이 도구는 계획을 세우고, 저 도구는 코딩을 한다고 말이죠. 하지만 그렇게 하지 마세요. 게이트를 특정 에이전트 하나에 두기보다는 버전 관리 및 CI 환경과 같은 실제 운영 체제에 두십시오. 일단 그곳에 자리 잡으면, 코딩 에이전트는 더 이상 신뢰하는 대상이 아니라 교체 가능한 부품이 되어버립니다.

SDD 도구가 계획의 핵심을 담당합니다. 스펙 키트를 사용하면 constitution → specify → clarify → plan → tasks → analyze → implementTDD 규칙을 그 안에 넣으세요. 헌법 파일구현 전에 실패하는 테스트를 작성하십시오. 테스트가 실패하는 동안에는 작업을 완료로 표시하지 마십시오. 통과시키기 위해 이미 확정된 테스트를 수정하지 마십시오. 모든 명령은 이러한 규칙을 따르므로, 장시간 세션 동안 에이전트가 이러한 규칙을 기억할 것이라고 기대할 필요가 없습니다.

해당 게이트는 프롬프트가 아니라 사전 커밋 훅 및 CI 검사입니다. 테스트는 통과하고, 변이는 포착되고, 커밋된 테스트는 변경되지 않았습니다. 이는 환경에 의해 강제되므로 코드가 Spec Kit에서 가져온 것인지 여부와 관계없이 적용됩니다. implementAider나 다른 어떤 것으로부터도 마찬가지입니다.

실행기는 플러그인 방식입니다. 에이더의 고향 --auto-test 루프는 배치 명령보다 변경 주기가 더 짧고, 비용에 따라 모델을 라우팅합니다. 즉, 사양 및 계획에는 강력한 모델을, 빈번한 루프에는 더 저렴한 모델을 사용하고 캐싱을 적용합니다. 사용 여부는 자유입니다. 방법론이 관건입니다.

위험도에 맞춰 의식을 진행하세요

규모 축소가 불가능한 프로세스는 버려집니다. 프로젝트 헌장, 사양서, 계획, 작업 분할, 변형 테스트 등 모든 절차를 거치는 것은 한 줄짜리 버그 수정에는 과도합니다. 따라서 두 가지 방식으로 나누어 진행하세요. Spec Kit 자체 문서 좋습니다.

저위험 작업에 적합한 신속 처리 방식: 간단한 의도 정의, 한두 번의 사람 검토 테스트, 그리고 게이트 테스트 실행. 새로운 하위 시스템이나 규제 대상 표면에 적합한 전체 처리 방식: 전체 스파인에 더해 변이 테스트 및 통합 단계까지 수행. 절차는 고정된 비용이 아니라 위험도에 따라 결정됩니다. 이것이 바로 "이건 추가 단계가 있는 워터폴 방식 같네요"라는 질문에 대한 솔직한 답변입니다.

그만한 추가 비용을 감수할 가치가 있을까요?

이 모든 것은 공짜가 아닙니다. 에이전트가 한 번에 코드를 작성하는 것보다 모델 호출, 테스트 실행, 피드백 과정이 더 많이 필요하기 때문에 추가 비용이 과연 정당한지 의문을 제기하는 것은 당연합니다. 합리적인 방법은 중요한 부분에 투자하는 것입니다. 즉, 명세 및 계획 단계에는 강력한 모델을, 빈번한 빌드 루프에는 더 저렴하고 빠른 모델을 사용하고, 돌연변이 테스트와 같은 비용이 많이 드는 검사는 매 반복마다 실행하는 대신 병합 단계에서만 실행하며, 각 루프의 비용을 낮추기 위해 슬라이스를 작게 유지하는 것입니다. 하지만 진짜 중요한 질문은 무엇과 비교하느냐입니다. 기준선은 숙련된 엔지니어가 완벽한 코드를 작성하는 것이 아니라, 그럴듯하지만 오류가 있는 코드를 생성하는 비감독 에이전트이며, 사용자는 이를 몇 시간 동안 디버깅해야 합니다. 이러한 기준선과 비교했을 때, 오버헤드는 실제로 신뢰할 수 있는 결과를 얻기 위한 요소입니다.

이렇게 하지 말아야 할 때

때로는 그럴 가치가 전혀 없다는 것이 답일 수도 있습니다. 빠르게 배우고 버리는 것이 핵심인 일회용 프로토타입이나 스파이크의 경우에는 전체 프로세스를 생략하세요. 게이트 설정 비용이 버그 수정 비용보다 더 많이 드는 정말 사소한 변경 사항에 대해서도 생략하세요. 그리고 의미 있는 테스트를 저렴하게 작성할 수 없는 영역에 대해서는 솔직하게 이야기하세요. 그런 곳에서는 오라클은 공허하고 보장은 허울뿐입니다. 덴 델리마르스키는 다음과 같이 관찰합니다. Spec Kit을 직접 사용해본 결과, 스펙은 만능 해결책이 아닙니다. 이러한 한계를 명확히 밝히는 것은 단순히 책임을 회피하는 것이 아니라, 방법론과 판매 전략을 구분하는 핵심 요소입니다.

기준점이 올라갔어요

자동화 과정에서 살아남는 것은 코드를 입력하는 행위가 아닙니다. 원하는 것이 무엇인지 정확히 알고, 문자 그대로 해석하는 지니도 오해할 수 없을 만큼 정확하게 표현하고, 제대로 이해했는지 확인할 수 있는 능력입니다. SDD는 바로 그 표현 방식이고, TDD는 검증 방식입니다.

에이전트는 빨간색 오류를 없애기 위해 기꺼이 테스트를 삭제할 것입니다. 따라서 사양, 테스트 스위트, 그리고 그 둘을 뒷받침하는 판단은 실제 작업에 대한 형식적인 절차가 아닙니다. 에이전트가 개입하면, 그들은 are 진짜 일은 바로 이것입니다.

자료 및 도구

  1. 켄트 벡이 TDD, AI 에이전트, 그리고 "예측 불가능한 지니"에 대해 이야기합니다. 실용적인 엔지니어
  2. 인간이 통제하는 테스트가 최후의 방어선인 이유 — 올스택
  3. GitHub Spec Kit — REPO · 퀵 스타트 · 헌법과 9개 조항
  4. Spec Kit을 활용한 사양 기반 개발 — 개발자를 위한 Microsoft · 덴 델리마르스키의 심층 분석
  5. Aider의 테스트 루프 — 보풀 제거 및 테스트 · 옵션 참조
  6. 상담원 검증 절차 완료 — 극작가 MCP

당신은 또한 좋아할 거라