AI 시대의 병목은 코드가 아니라 spec이다
코드가 싸지면 애매함이 비싸진다. Notion의 spec-first workflow가 보여주는 것은 AI 코딩 시대의 진짜 병목이 구현 속도가 아니라 명세와 검증이라는 점이다.
코드가 싸졌다.
이게 AI 코딩 시대의 출발점이다. 예전에는 아이디어를 실제 코드로 옮기는 데 시간이 많이 들었다. 화면을 만들고, controller를 붙이고, 모델을 고치고, 테스트를 추가하고, PR을 정리하는 일이 무거웠다.
이제 그 무게가 빠르게 줄고 있다. Codex든 Claude Code든 Cursor든, agent는 코드 초안을 아주 빨리 만든다. 틀릴 때도 많지만, 적어도 "아무것도 없는 상태에서 첫 구현을 만드는 비용"은 분명히 낮아졌다.
그런데 코드가 싸지면 다른 것이 비싸진다.
애매함이다.
무엇을 만들지 애매한 상태. 어디까지 만들고 멈출지 정하지 않은 상태. 실패 케이스를 말로만 넘긴 상태. 권한과 데이터 상태와 완료 기준이 흐릿한 상태. 이런 애매함은 예전에도 문제였지만, AI agent가 붙으면 더 빨리 비용이 된다.
사람 개발자는 애매하면 중간에 멈추거나, 눈치껏 묻거나, 조직의 맥락으로 보정한다. agent도 질문할 수는 있지만 기본값은 다르다. agent는 주어진 말을 해석해서 앞으로 간다. 그리고 앞으로 가는 속도가 빠르다.
그래서 AI 시대의 병목은 점점 코드 작성이 아니라 무엇이 맞는지 적는 일로 이동한다.
나는 이걸 spec의 문제라고 본다.
Notion 사례에서 봐야 할 것
Ryan Nystrom이 Lenny's Newsletter의 Spec-driven development: The AI engineering workflow at Notion에서 이야기한 Notion의 내부 workflow는 꽤 화려하다.
Notion comment에서 Codex를 호출한다. agent가 VM에서 작업한다. PR을 만든다. screenshot도 붙인다. 내부적으로는 Boxy 같은 시스템이 있고, CI 시간을 줄이기 위한 Project Afterburner도 돈다.
이런 부분은 당연히 눈에 띈다. "Notion은 agent로 PR까지 뽑는구나" 하고 보게 된다.
하지만 내가 더 중요하게 본 건 그 앞단이다.
아이디어를 말로 남긴다. 받아쓴다. 그것을 spec으로 정리한다. 그 spec을 repo에 넣는다. agent는 그 다음에 움직인다.
순서가 중요하다.
채팅창에서 "이 기능 만들어줘"라고 던지는 게 먼저가 아니다. agent를 부르기 전에 기능의 기준을 repo 안에 넣는다. 무엇을 만들지, 무엇을 만들지 않을지, 어떤 동작이 맞는지, 어떤 검증을 통과해야 하는지를 먼저 적는다.
즉 Notion 사례의 핵심은 "Codex가 코드를 잘 쓴다"가 아니다.
핵심은 코드보다 먼저 spec이 version control 안으로 들어간다는 점이다.
이건 개발 문화의 중심이 prompt에서 spec으로 옮겨가는 신호에 가깝다.
Spec은 PRD가 아니다
여기서 말하는 spec은 두꺼운 PRD가 아니다. PM이 회의 끝나고 쓰는 제품 문서도 아니고, 나중에 감사용으로 남기는 설명서도 아니다.
AI coding workflow에서 spec은 더 실행적인 문서다.
agent가 읽고 움직일 수 있어야 한다. 리뷰어가 보고 판단할 수 있어야 한다. 테스트와 연결되어야 한다. 다음 세션의 나나 다른 agent가 다시 읽어도 "왜 이렇게 만들었는지" 복원할 수 있어야 한다.
좋은 spec은 적어도 이 네 가지를 담는다.
- 왜 하는가
- 어디까지 하는가
- 어떤 동작이 맞는가
- 무엇으로 검증하는가
이 정도만 있어도 agent의 움직임은 달라진다.
반대로 이 네 가지가 없으면 agent는 빠르게 추측한다. 그리고 그 추측은 꽤 그럴듯한 코드로 나타난다. 그래서 더 위험하다. 이상한 코드보다 그럴듯한 코드가 더 늦게 발견된다.
BDD는 사라지지 않는다
Spec 이야기를 하면 자연스럽게 BDD가 떠오른다.
"그럼 BDD랑 뭐가 다른가?"
내 답은 이렇다.
BDD는 spec의 경쟁자가 아니라 spec 안에 들어가는 재료다.
BDD는 사용자의 행동을 예시로 고정한다. 어떤 상태에서, 어떤 행동을 하면, 시스템이 어떻게 반응해야 하는지 적는다.
Given 로그인한 작성자가 있고
When front matter가 있는 Markdown 파일을 업로드하면
Then 제목과 본문과 태그가 draft post로 저장된다이건 여전히 좋다. 오히려 agent 시대에는 더 좋아진다. agent에게 "이런 행동을 만족시켜야 한다"고 말해주는 명확한 예시이기 때문이다.
하지만 BDD example만으로는 부족한 순간이 많다.
왜 이 기능을 만드는가. 이번 범위에서 제외할 것은 무엇인가. slug 충돌은 어떻게 처리할 것인가. writer 권한이면 published가 아니라 submitted가 되어야 하는가. 세션이 만료되면 어떤 메시지를 보여줄 것인가. 어떤 테스트 명령을 통과해야 완료라고 볼 것인가.
이 질문들은 Given/When/Then 하나에 다 들어가지 않는다.
BDD는 behavior를 선명하게 만든다. Spec은 그 behavior가 놓일 작업의 울타리를 만든다.
그러니까 "spec이냐 BDD냐"가 아니다.
Spec 안에 BDD가 들어간다.
AI가 틀리는 방식
AI agent가 틀릴 때 가장 짜증나는 지점은 단순히 문법을 틀리는 게 아니다. 그건 비교적 빨리 잡힌다.
진짜 문제는 의도를 조금씩 틀리게 해석하는 것이다.
관리자 전용이어야 하는 기능을 public API처럼 열어둔다. draft까지만 만들면 되는데 publish까지 해버린다. 실패하면 아무것도 만들지 말아야 하는데 빈 record를 남긴다. 권한 체크는 있는데 scope가 tenant 기준이 아니라 user 기준으로만 걸린다. 테스트는 통과하지만 실제 사용 흐름에서는 이상한 상태가 된다.
이런 오류는 "코딩 실력"만의 문제가 아니다.
작업 정의가 약했기 때문에 생긴다.
사람끼리 일할 때도 그랬다. 다만 사람은 맥락을 더 많이 들고 있었다. agent는 그렇지 않다. agent가 읽는 것은 지금 주어진 문장과 파일과 테스트다. 그러니 그 입력이 약하면 결과도 약하다.
결국 좋은 agent workflow의 핵심 질문은 이것이다.
agent가 지금 무엇을 읽고 판단하고 있는가?
채팅창의 흐릿한 지시만 읽고 있다면 결과도 흐릿해진다. repo 안의 spec, behavior example, verification command를 읽고 있다면 훨씬 나아진다.
"완료했습니다"를 믿지 않기
AI agent는 완료 보고를 잘한다. 너무 잘한다.
"구현했습니다."
"테스트도 추가했습니다."
"이제 동작합니다."
문제는 이 말들이 실제 완료를 보장하지 않는다는 점이다. 사람도 마찬가지지만, agent는 특히 그럴듯한 완료감을 만든다. 코드가 생겼고, 파일이 바뀌었고, 테스트 이름도 있다. 얼핏 보면 끝난 것 같다.
그래서 spec에는 완료 기준이 들어가야 한다.
Verification:
- Markdown import service test
- admin publish controller test
- expired session regression test
- public post URL smoke check이런 식으로 적어두면 "완료"가 감상이 아니라 증거가 된다.
PR 리뷰도 달라진다. 코드 스타일만 보는 게 아니라, spec의 각 항목이 실제로 충족됐는지 본다. 테스트가 그것을 고정하는지 본다. 실패 케이스가 빠졌는지 본다.
AI 시대의 code review는 점점 이렇게 바뀔 것이다.
코드가 예쁜가?
보다 먼저,
이 변경은 적어둔 spec을 만족하는가?
Spec은 changelog가 된다
Spec을 repo에 넣는 또 다른 이유는 기록이다.
채팅창에서 agent에게 지시하면 순간 속도는 빠르다. 하지만 며칠 지나면 이유가 사라진다. 왜 이 범위를 제외했는지, 왜 이 예외는 막았는지, 왜 이 테스트를 완료 기준으로 봤는지 흩어진다.
repo 안의 spec은 다르다.
기능이 바뀌면 spec도 바뀐다. non-goal이 goal로 승격된다. behavior가 늘어난다. verification이 추가된다. 그러면 spec은 단순한 사전 문서가 아니라 기능의 changelog가 된다.
코드가 어떻게 바뀌었는지만 남는 게 아니다. 기능을 이해하는 방식이 어떻게 바뀌었는지도 남는다.
agent와 일할수록 이게 중요해진다.
agent는 과거 회의 분위기를 기억하지 못한다. 이전 세션의 감각도 안정적으로 보존하지 못한다. 설령 기억한다고 해도 그걸 그대로 믿으면 안 된다. 다음 agent, 다음 주의 나, 다른 리뷰어가 같은 지점에서 다시 출발하려면 정본이 필요하다.
그 정본이 spec이다.
CI가 느리면 agent도 느리다
Ryan의 인터뷰에서 CI 이야기가 같이 나오는 것도 우연이 아니다. Notion은 CI 시간을 줄이는 Project Afterburner를 진행하고 있다고 한다.
AI coding에서는 CI 속도가 더 중요해진다.
사람이 하루에 PR 하나를 만들던 시절에는 CI가 느려도 어느 정도 버틸 수 있었다. 답답하지만 기다리면 됐다. 그런데 agent가 작은 PR을 계속 만들고, 리뷰 반영을 반복하고, test failure를 보고 다시 고치는 흐름에서는 느린 CI가 곧 전체 처리량의 병목이 된다.
AI engineering throughput은 모델 성능만으로 결정되지 않는다.
Spec이 선명해야 한다.
테스트가 빨라야 한다.
CI가 빨라야 한다.
실패 로그를 agent가 읽고 다시 고칠 수 있어야 한다.
리뷰 기준이 자동화되어 있어야 한다.
Spec이 없으면 agent는 방향 없이 빠르다. Test가 없으면 agent는 그럴듯하게 빠르다. CI가 느리면 agent는 기다리다가 멈춘다.
셋이 같이 있어야 진짜 속도가 난다.
혼자 만들수록 더 필요하다
이 이야기는 Notion 같은 큰 회사에만 해당하지 않는다. 오히려 혼자 제품을 만드는 사람에게 더 절실하다.
큰 회사에는 PM, designer, engineer, QA, EM, staff engineer가 따로 있다. 요구사항이 흐릿하면 누군가 잡아줄 가능성이 있다. 물론 그만큼 회의도 많지만, 역할이 분산되어 있다.
솔로 빌더는 다르다.
혼자 아이디어를 낸다. 혼자 구현한다. 혼자 테스트한다. 혼자 배포한다. 혼자 고객 반응을 본다.
여기에 agent를 붙이면 생산성이 올라간다. 동시에 혼란도 빨리 쌓인다. 애매한 상태에서 agent에게 일을 맡기면, 혼자 감당해야 할 이상한 코드와 이상한 제품 결정이 빠르게 늘어난다.
그래서 솔로 빌더에게 spec은 팀 문서가 아니다.
자기 자신과 agent를 통제하는 운영 장치다.
오늘의 내가 내린 결정을 내일의 나와 agent가 다시 읽을 수 있게 만드는 장치다. "왜 이 기능을 만들었지?", "왜 이건 안 만들기로 했지?", "무엇을 보면 안심할 수 있지?"를 repo 안에 남기는 일이다.
한 페이지면 충분하다
Spec이라고 해서 거창할 필요는 없다. 작은 기능은 한 페이지면 충분하다.
예를 들면 이런 정도다.
# Draft URL Import
Why:
- 글감 URL을 admin에서 바로 draft로 바꿔 큐레이션 속도를 높인다.
Goal:
- URL 하나를 넣으면 제목, 요약, OG 이미지 후보를 가져와 draft post로 만든다.
Non-goals:
- 자동 발행은 하지 않는다.
- 로그인 없는 public import는 만들지 않는다.
Behavior:
- admin이 URL을 입력하면 OG metadata를 fetch한다.
- title이 비어 있으면 URL hostname을 fallback으로 쓴다.
- fetch 실패 시 draft는 만들지 않고 오류를 보여준다.
Verification:
- OG fetcher unit test
- admin import controller test
- 실패 응답 regression test여기에 BDD example을 붙이면 더 좋다.
Given editor가 admin에 로그인해 있고
When 유효한 URL을 import하면
Then draft post가 생성되고 public으로 발행되지는 않는다이 정도만 있어도 agent는 훨씬 덜 헤맨다.
중요한 건 문서 양이 아니다. 작업의 경계가 보이는가다.
나쁜 spec의 냄새
나쁜 spec은 보통 멋있다.
"사용자가 더 쉽게 글을 쓸 수 있게 한다."
"AI로 운영을 자동화한다."
"관리자 경험을 개선한다."
다 맞는 말이다. 하지만 agent가 읽고 움직이기에는 약하다. 방향은 있지만 끝 조건이 없다.
좋은 spec은 조금 더 건조하다.
누가, 어디에서, 무엇을 입력하고, 어떤 상태가 만들어지고, 어떤 경우에는 막히고, 무엇을 보면 완료라고 판단하는가.
이게 있어야 한다.
또 하나의 냄새는 non-goal이 없는 spec이다. agent에게 "이걸 만들어줘"라고만 하면 agent는 옆 기능까지 만들고 싶어 한다. 친절하게 하려고 설정 화면을 붙이고, 추상화를 만들고, 아직 필요 없는 옵션을 넣는다.
그래서 spec에는 "이번에 하지 않을 것"이 꼭 들어가야 한다.
Non-goal은 게으름의 표시가 아니다.
작업을 끝내기 위한 울타리다.
결론
Spec-driven development는 새 유행어처럼 보일 수 있다. 하지만 나는 이것을 꽤 실용적인 변화로 본다.
AI가 코드를 더 많이 쓰게 되면 사람의 일은 사라지지 않는다. 앞쪽과 뒤쪽으로 이동한다.
앞쪽에서는 문제를 정확히 자른다.
뒤쪽에서는 결과를 검증한다.
그 사이에서 agent가 구현한다.
이 구조에서 spec은 사람과 agent 사이의 인터페이스다. BDD는 그 spec 안에서 behavior를 선명하게 만드는 도구다. 테스트와 CI는 spec이 지켜졌는지 확인하는 회로다.
그러니까 질문은 "spec이냐 BDD냐"가 아니다.
질문은 이거다.
내 agent는 지금 무엇을 읽고 있는가?
앞으로 좋은 AI 엔지니어링은 프롬프트를 잘 쓰는 사람만의 일이 아닐 것이다. 좋은 spec을 남기고, 그 spec을 테스트로 고정하고, agent가 실패했을 때 다시 읽을 수 있는 작업 구조를 만드는 사람의 일이 될 것이다.
코드는 여전히 중요하다.
하지만 코드보다 먼저, 무엇이 맞는지 적는 일이 더 중요해지고 있다.
코드가 싸졌기 때문이다.
그리고 코드가 싸질수록, 애매함은 더 비싸진다.
참고: