나는 여전히 통제하고 있지만, 코딩을 하지는 않는다
요약
AI를 활용한 코딩 방식에서 상세한 명세서(spec) 작성보다 검증(verification)과 유연한 대응이 중요함을 강조합니다. 계획에 매몰되기보다 의도된 동작과 수락 기준을 중심으로 가볍게 접근하며 현실의 변화에 즉각 대응하는 방식을 제안합니다.
핵심 포인트
- 상세한 명세서는 현실의 변화에 취약하며 매몰 비용을 발생시킴
- 계획보다 중요한 것은 구현 결과에 대한 지속적인 검증
- 구현 경로를 예측하기보다 의도된 동작과 수락 기준에 집중
- 문서에 대한 확약 대신 유연하게 업데이트 가능한 가벼운 목록 유지
내가 생각하는 바이브 코딩 (vibe coding)은 엔지니어를 관리하는 것과 같다. 나보다 훨씬 빠르게 타이핑을 하고, 내가 결과물을 거절해도 기분 나빠하지 않으며, 하루에도 몇 번씩 가장 친절한 방식으로 나에게 약간의 거짓말을 하는 지치지 않는 엔지니어 말이다.
나는 더 이상 코드를 직접 작성하지 않는다. 한동안 그러지 않았다. 하지만 그 어느 때보다 코드에 대한 책임감을 느끼고 있으며, 바로 그 점을 기록하고 싶다. 왜냐하면 나의 작업 방식은 이 문제를 두고 논쟁하는 두 진영 중 어느 쪽과도 일치하지 않기 때문이다. 한 진영은 프롬프트 (prompt)를 입력하고 기도한다. 현재 담론을 주도하고 있는 다른 진영은 해결책이 사전 규율에 있다고 말한다. 즉, 명세서 (spec)를 작성하고, 계획을 세워 AI를 당신의 설계를 실행하는 타이피스트 (typist)로 만들라는 것이다. 나는 두 진영 모두 통제의 위치를 잘못 잡고 있다고 생각한다.
계획은 작업을 안내할 수는 있다. 하지만 내가 통제하고 있다는 것을 증명할 수는 없다. 검증 (Verification)만이 그것을 할 수 있다.
계획은 실전에서 살아남지 못한다
무언가를 출시해 본 사람이라면 누구나 이를 알고 있지만, 우리는 모두 그렇지 않은 척하며 살아간다. 작업을 계획하고 시작하면, 이틀째 되는 날 현실이 당신을 대신해 계획을 수정해 버린다. 의존성 (dependency)이 문서에 명시된 대로 작동하지 않는다. 어렵다고 생각했던 기능은 한 시간 만에 끝나버리고, 존재조차 몰랐던 무언가가 일주일 내내 발목을 잡는다. 이는 AI 이전에도 사실이었으며, 에이전트 (agent)라고 해서 이를 바꾸지는 못한다. 오히려 놀라운 일들이 더 빠르게 닥쳐오기 때문에 상황을 더 악화시킨다.
그래서 나는 계획 모드 (plan mode)를 실행하지 않으며, 시작하기 전에 명세서 (spec)를 작성하지도 않는다. 명세서는 항상 깨지기 마련인 바로 그 지점, 즉 길을 걷기 전에 경로를 예측하는 나의 능력에 거는 도박이다. AI의 혼돈에 대한 업계의 현재 해답은 명세서 기반 개발 (spec-driven development)인데, 이는 가장 빠르게 부식되는 산출물 (artifact)을 더 크고 상세하게 만든 버전에 불과하다.
대신 내가 하는 방식은 의도적으로 더 가볍다. 나는 해당 기능에 필요한 기능 목록을 유지하고, 그중 무엇을 먼저 구축할지 결정한다. 보통은 가장 단순한 것, 즉 나머지가 기반을 둘 수 있는 기준점 (baseline)을 선택한다. 그 목록이 유일한 사전 산출물이다. 그것은 내가 무엇을 원하는지는 말해주지만, 그곳에 도달하는 경로가 어떻게 될지에 대해서는 침묵한다. 왜냐하면 그것도 모르고, 나 또한 모르기 때문이다.
그 목록을 한 줄짜리 명세 (spec)라고 불러도 무방할 것이며, 나는 명세 (spec)를 지지하는 진영에 그 이상의 공로를 인정한다. 즉, 좋은 명세 (spec)는 구현 경로 (implementation path)를 예측하기보다는 의도된 동작 (intended behaviour)과 수락 기준 (acceptance criteria)을 기술하며, 저렴한 비용으로 수정될 수 있다는 점이다. 내가 중요하게 생각하는 차이점은 현실이 명세와 일치하지 않을 때, 그 산출물 (artifact)이 어떻게 취급되느냐 하는 것이다. 나의 목록은 즉시 패배한다. 나는 그것을 업데이트하고 다음으로 넘어간다. 왜냐하면 그것은 경로에 대해 결코 약속한 적이 없기 때문이다. 내가 피하고자 하는 실패 모드 (failure mode)는 문서 그 자체가 아니라, 그 문서에 대한 확약 (commitment)이다. 문서가 더 크고 상세할수록, 그것은 마치 확약인 것처럼 취급되도록 유도한다. 매몰 비용 (Sunk cost)은 코드에 적용되는 것과 동일하게 문서에도 적용된다. 사전 산출물 (upfront artifact)의 크기는 눈앞에 놓인 현실보다 그것을 신뢰하고 싶어지는 유혹의 크기와 같다.
그 지점부터 모든 단계는 원래 일어나기로 되어 있었던 일이 아니라, 지금까지 실제로 일어난 일을 바탕으로 결정된다. 에이전트 (agent)와 나는 진행하면서 상황을 파악하고, 기묘한 의존성 동작 (dependency behaviour)에 부딪히면 조정하며 계속 나아간다. 나는 전 과정에서 방향을 소유하며, 경로는 그 아래에서 발견된다.
로드맵이 아닌 로그 (A log, not a roadmap)
나는 무언가를 기록하긴 하지만, 그것이 사실이 된 후에만 기록한다. 마일스톤 (milestone)에 도달하고 테스트가 완료되면 기록에 남긴다. 작업 전에 작성된 로드맵 (roadmap)은 프로젝트 내내 거짓말을 하게 만드는 문서이지만, 로그 (log)는 실제로 일어난 일만을 기록한다. 로그는 여전히 불완전할 수 있고 내가 잘못 읽을 수도 있지만, 로드맵이 가진 특유의 독성, 즉 현실이 이미 부정해버린 미래에 대한 주장만큼은 결코 담고 있지 않다.
게이트: 앱을 제어하는 나의 손
이 모든 것을 실행하기에 충분히 안전하게 만드는 규칙은 다음과 같다. 내가 수동으로 테스트하기 전까지는 그 어떤 마일스톤 (milestone)도 완료된 것이 아니다. 에이전트 (agent)가 테스트를 통과했다고 말하는 것이 아니라, 직접 내가 그것을 클릭하며 확인해야 한다.
그리고 회귀 테스트 (regression) 또한 동일한 관문, 즉 재실행의 과정입니다. 새로운 마일스톤 (milestone)이 도달하면, 나는 다시 돌아가 이전 것들을 수동으로 재테스트합니다. 네, 이것은 느린 작업이며, 나는 이를 자동화하여 우회하지 못했습니다. 몇 주 전에 이미 검증했던 것들을 다시 클릭하며 확인해야 하고, 이는 실제 시간을 소모하게 합니다. 하지만 내가 기꺼이 그 대가를 치르는 이유는, 그것이 내가 실제로 어디에 와 있는지에 대한 정직한 그림을 제공하기 때문입니다. 나의 전체적인 방법론은 현재 상태로부터 조종 (steering)하는 것인데, 이는 내가 조종하고 있는 현재 상태가 실제일 때만 작동합니다.
여기서 언급할 만한 연구가 하나 있습니다. METR 통제 실험 (METR controlled experiment)에 따르면, AI 도구를 사용하는 숙련된 개발자들은 스스로 20% 더 빨라졌다고 믿었지만, 실제 작업에서는 19% 더 느렸습니다. 이는 나의 방식을 포함한 모든 AI 보조 워크플로우 (AI-assisted workflow)에 해당되는 문제이며, 이것이 바로 내가 이 루프 안에서 느낌이 아닌 단 한 가지 사실에 고정하는 이유입니다: 관문에서 포착된 버그 (bug)는 사실입니다. 나의 속도감이 보정되어 있다고 말할 수는 없습니다. 하지만 클릭이 무엇을 보여주었는지는 말할 수 있습니다. 범위를 공정하게 설정하자면, 그 관문은 취약점 (vulnerabilities)이 아닌 고장 난 동작을 잡아내는 것입니다. 아무도 인젝션 결함 (injection flaw)을 찾기 위해 수동으로 클릭하며 다니지는 않으니까요.
에이전트가 당신의 의견에 동의한다면, 그것은 틀리는 것보다 더 나쁘다
더 미묘한 문제이자, 내가 받아들이는 데 가장 오래 걸렸던 문제는 이것입니다: 모델은 나를 기쁘게 하고 싶어 한다는 점입니다. 내가 선호하는 답변이 드러나도록 질문을 구성하면, 모델은 내 쪽으로 기울어집니다. 이것은 가끔 발생하는 결함이 아니라 오류의 기본 방향이며, 이는 에이전트 (agent)의 동의가 증거로서 거의 가치가 없음을 의미합니다.
코드에 대한 거짓말은 마일스톤 관문에서 내 손에 의해 포착됩니다. 설계 (design)에 대한 거짓말은 더 나쁩니다. 왜냐하면 아키텍처 (architecture)에 대한 의견에 대해서는 실행할 수 있는 테스트가 없기 때문입니다. 그것은 몇 달 후, 프로덕션 (production) 환경에서 알게 됩니다.
따라서 중요한 것이라면 무엇이든, 나는 에이전트가 자발적으로 제시하지 않은 각도에서 그 아이디어를 공격합니다:
- "이 접근 방식의 문제점은 무엇인가?"
- "그것에 반대하는 논거를 제시하라."
- 동일한 질문을 반대되는 결론을 유도하도록 프레임(framing)을 바꾸어 다시 질문하기.
- 그리고 이제 내가 의도적으로 사용하는 한 가지 기술: 내가 틀렸다는 것을 알고 있는 반론을 제기하며 압박하기. 만약 모델이 바로 굴복한다면, 그 모델의 동의는 오늘 아무런 의미가 없으며, 모델이 승인한 중요한 모든 사항은 다시 재심리(re-litigated)되어야 합니다.
이 중 그 어떤 것도 문제를 근본적으로 해결해주지는 않으며, 이 점을 명확히 하고 싶습니다. 동의하기 쉬운 모델을 똑같이 동의하기 쉬운 모델로 심문하는 것은 완화(mitigation)일 뿐, 독립성(independence)은 아닙니다. 이는 잘못된 설계 결정의 비용을 "무료"에서 "교차 심문(cross-examination)을 견뎌내야 함"의 수준으로 높여주며, 이를 통해 진짜 쓰레기 같은 결과물은 걸러낼 수 있지만, 모델과 내가 공유하고 있는 실수는 잡아낼 수 없습니다. 나를 두렵게 만드는 아키텍처 결정에 대한 정직한 답변은 훨씬 더 느린 방법입니다: 하룻밤 자고 일어나거나, 나의 프레임(framing)을 보지 못한 새로운 모델로부터 신선한 의견을 듣는 것입니다. 이것 역시 진정한 독립성은 아니며 단지 오염(contamination)을 줄이는 것일 뿐이지만, 현재로서는 오염을 줄이는 것이 최선입니다.
에이전트와 매일 작업하면서 나는 "완료됨, 작동함, 테스트 통과"가 보고서가 아니라 하나의 기분(mood)이라는 것을 배웠습니다. 에이전트는 그것을 믿고, 코드는 때때로 그에 동의합니다. 마일스톤 게이트(milestone gate)가 존재하는 이유는, 실제 사용자들을 마주하는 실제 제품 환경에서 그 두 상태 사이의 간극을 프로덕션(production) 환경에서 발견하고 싶지 않기 때문입니다.
이것은 도구(tooling)에 반대하는 논거가 아닙니다
내가 말하지 않는 바를 명확히 해야 합니다. 왜냐하면 이 글을 "AI를 짧은 목줄로 통제하고 모든 것을 수동으로 하라"는 뜻으로 읽기 쉽기 때문입니다. 나는 헤비 머시너리(heavy machinery)를 사용합니다. Claude Code, 멀티 에이전트 워크플로우(multi-agent workflows), 하나의 작업을 병렬로 수행하는 에이전트 팀들: 이것은 나의 일상적인 설정이며, 당신에게 피하라고 경고하는 것이 아닙니다. 작업의 형태가 이미 알려져 있다면 에이전트 군단은 올바른 도구이며, 에이전트를 사용하면서 그렇지 않다고 주장한다면 그것은 이상한 일일 것입니다.
내가 지키는 선은 다릅니다. 내가 무엇을 위임하든, LLM이 실행하기 전에 무엇을 할 것인지 알아야 합니다. 어떤 파일을 건드릴지, 어떤 종류의 변경을 가하려 하는지, 그리고 계획의 경계에서 어떤 일이 발생하는지 말입니다. 그 경계에서의 규칙은 단순합니다. 우리 중 누구도 예측하지 못한 상황에 부딪히면, 즉흥적으로 행동하는 대신 멈추고 질문하는 것입니다. 모든 키 입력(keystroke)을 다 보는 것은 아니지만, 행동의 형태(shape)는 봅니다. 나는 변경 사항(diffs)이 적용되는 것을 계속 지켜보고, 때때로 코드베이스 자체를 직접 훑어봅니다. 그렇게 해서 모든 것을 잡아내기 때문이 아니라, 정신적 지도(mental map)가 있어야 다음 위임이 안전해지기 때문입니다. "그것이 무엇을 하려는 것인가"라는 질문에 답할 수 없는 순간, 나는 엔지니어를 감독하는 것이 아니라 램프를 문지르고 있는 것이며, 이 글의 모든 논지는 무너집니다. 왜냐하면 상태(state)가 어떻게 그곳에 도달했는지 이해하지 못한다면 현재 상태로부터 조종(steer)할 수 없기 때문입니다.
따라서 나의 워크플로우와 내가 논쟁하고 있는 워크플로우의 차이는 결코 수동(manual) 대 자동(automated)의 차이가 아니었습니다. 나는 도구가 허용하는 한 실행(doing)을 확장하지만, 모르는 상태(not-knowing)를 확장하는 것만은 단호히 거부합니다.
이것이 나를 어디로 이끄는가
Karpathy는 올해 자신의 용어를 은퇴시키고 이제 "에이전트 공학 (agentic engineering)"이라고 말합니다. 즉, 프로덕션 품질을 목표로 하는 에이전트와 감독(oversight)의 결합입니다. 저도 그 말에 동의합니다. 제가 이에 대해 쓰여진 대부분의 글과 다른 점은 강조점입니다. 담론들은 작업 전에 만드는 산출물(artifacts)에 에너지를 쏟지만, 저는 작업 끝의 관문(gate)에 에너지를 쏟습니다. 이미 많은 숙련된 사람들이 작은 증분(increments), 모델의 자기 보고(self-reports)에 대한 불신 등 이러한 방식의 일부를 실천하고 있으며, 제가 이것을 발명했다고 주장하는 것도 아닙니다. 제가 글로 자주 보지 못하는 것은 그 대가를 정직하게 명시하는 것입니다. 즉, 계획이 일회용 목록으로 축소되는 것과, 레버리지(leverage)의 비용으로서 실제 검증(verification)의 느림을 글로써 받아들이는 것입니다.
개발자들에게 AI 결과물을 신뢰하는지 물어보면 많은 사람이 '아니요'라고 답할 것입니다. 하지만 그들의 작업 흐름(workflow)을 보면, 하루 종일, 자신들을 대신하여 그것을 신뢰하고 있다는 것을 알 수 있습니다. 제가 설명했던 모든 느리고, 수동적이며, 반복적인 습관들은 저 자신이 제 불신을 충분히 심각하게 받아들여 행동으로 옮기기 때문에 존재합니다.
이것이 해결하지 못하는 것들
이는 제가 혼자서, 전체 표면(surface)을 머릿속에 담고 손으로 재검증할 수 있는 프로젝트에서는 효과적입니다. 저는 한계가 어디인지 모릅니다. 수동적인 회귀 테스트(manual regression)가 느리다는 것을 넘어 불가능해지는 프로젝트 규모 지점이 있는데, 저는 아직 그 지점에 도달하지 못했기 때문에 제가 거기서 무엇을 할 수 있을지 말씀드릴 수 없습니다.
또한 제가 대부분 건너뛴 중간 경로도 언급해야 합니다. 바로 진행하면서 작성하는 테스트(tests written as you go)인데, 많은 숙련된 사람들이 실제로 그렇게 하고 있습니다. 저의 망설임은 이 설정에 국한됩니다. 즉, 동일한 에이전트가 코드와 테스트를 모두 작성할 때, 테스트는 코드의 사각지대(blind spots)를 물려받게 됩니다. 이는 스스로 시험을 채점하는 것과 같습니다. 그래서 저는 테스트들이 적대적으로(adversarially) 작성되고 게이트(gates)라기보다는 스모크 알람(smoke alarms)처럼 취급되기를 원하지만, 이를 대규모로 실행해 본 적이 없습니다. 그리고 이 게시물의 핵심은 제가 테스트하지 않은 것은 공개하지 않는다는 것입니다.
또한 저는 여러분에게 이런 방식으로 일하라고 말하는 것이 아닙니다. '여전히 통제하고 코딩하지 않는다'는 것이 실제로 얼마나 비용이 드는지 말씀드리는 것입니다. 왜냐하면 수많은 글들이 항목별로 비용을 명시하지 않고 그 '통제'만을 판매하기 때문입니다. 그 비용은 다음과 같습니다:
- 의도적으로 속도가 느려집니다.
- 새로운 작업이 들어올 때마다 이전 작업을 재검증해야 하며, 이 비용은 프로젝트 규모에 따라 증가합니다.
- 자신과 동의하는 기계와 논쟁합니다.
- 당신의 계획은 현실과의 논쟁에서 승리할 것이라고 기대하는 목록입니다.
저는 계속해서 그 비용을 지불하고 있습니다. 대안은 자신감(confidence)에 의존하는 것인데, 자신감이야말로 에이전트가 무료로 만들어내는 유일한 것입니다.
다음 내용은 제가 실제로 사용하는 설정, 도구, 작업 흐름, 그리고 에이전트 팀들이 어떻게 연결되어 있는지에 대한 워크스루입니다. 이 내용이 올라오면 저를 팔로우해 주세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기