당신의 AI 코딩 설정은 복잡한 도메인 도구입니다. 당신의 코드베이스는 복잡합니다.
요약
AI 코딩 에이전트가 해결할 수 있는 문제의 범위를 Cynefin 프레임워크를 통해 분석합니다. 단순한 코드 작성을 넘어 시스템의 진화와 상호작용에서 발생하는 복잡한 문제를 다루는 데 있어 현재 AI 도구의 한계와 접근 방식을 설명합니다.
핵심 포인트
- AI 코딩 도구는 주로 분석 가능한 '복잡(Complicated)' 영역에 머물러 있음
- 시스템의 진화와 상호작용은 사후에만 파악 가능한 '복잡(Complex)' 영역의 문제임
- 에이전트 기술(Agent Skills)은 제한된 작업을 위한 절차적 지식의 패키징임
- 컨텍스트 윈도우만으로는 시스템의 창발적 문제를 모두 해결할 수 없음
✓ 사람이 작성한 분석; AI는 서식 지정 및 교정용으로 사용됨.
AI 코딩 에이전트(AI coding agents)를 더 신뢰할 수 있게 만드는 도구의 물결이 일고 있습니다: 에이전트 기술(Agent Skills), 슈퍼파워(Superpowers), Get Shit Done, 명세 기반 개발 키트(spec-driven development kits) 등이 그것입니다. 이들은 효과가 있습니다. 에이전트는 갈팡질팡하는 것을 멈추고 프로세스를 따르기 시작합니다. 하지만 사람들이 이를 논하는 방식에는 혼란이 있습니다. 비용이 발생하는데, 그것은 바로 더 나은 작업당 설정(per-task setup)이 모여 일관된 시스템을 구축한다는 믿음입니다. 그렇지 않습니다. Dave Snowden의 Cynefin 프레임워크가 이를 분석할 틀을 제공합니다.
우리는 가시성(visibility, AI가 볼 수 있는 것)과 창발성(emergence, 시스템이 런타임에 수행하는 것)을 구분할 것입니다. 무한한 컨텍스트 윈도우(context window)라 할지라도 특정 운영 부하 상황에서만 발생하는 레이스 컨디션(race condition)을 추론할 수는 없습니다. 그것은 여전히 복잡(Complex) 도메인의 속성으로 남아 있습니다.
기존 카테고리(CMS)를 구축하는 것과 새로운 카테고리를 구축하는 것의 차이는, 왜 어떤 팀은 AI 도구가 기적적이라고 느끼는 반면 다른 팀은 사용하기 답답하다고 느끼는지 설명해 줍니다. 시스템이 성장함에 따라 상호작용의 수는 기하급수적으로 증가하며, 결국 어떤 컨텍스트 윈도우나 탐색(probe)하기보다 분석하려는 인간의 능력을 초과하게 됩니다.
두 개의 도메인, 하나의 코드베이스
Cynefin은 원인과 결과의 성격에 따라 문제를 분류합니다.
**복잡(Complicated)**한 문제는 알 수 있는 정답이 있습니다. 정답을 찾는 데에는 기술이나 분석이 필요합니다. 그것은 눈앞에 있는 것들로부터 연역할 수 있습니다. 일단 찾아내면 반복 가능합니다. 이 스키마를 검증하는 함수를 작성하세요. 이 모듈을 리포지토리 패턴(repository pattern)을 사용하도록 리팩터링하세요. 이러한 것들은 전문가(또는 잘 유도된 에이전트)가 만들어낼 수 있는 정답을 가지고 있습니다. 방법은 분석한 다음 실행하는 것입니다.
복잡한 (Complex) 문제는 사전에 연역할 수 있는 정답이 없습니다. 시스템은 상호작용하는 부분들의 웹이며, 결과는 사후에야 비로소 일관성을 갖게 됩니다. 이 40개의 병합된 변경 사항들이 6개월 동안 일관되게 유지될까요? 이번 분기의 기능들이 추가된 후에도 아키텍처가 여전히 잘 유지될까요? 에이전트 A의 합리적인 지역적 선택이 에이전트 B의 선택과 조용히 모순되지는 않았을까요? 이러한 것들은 단일 아티팩트(artifact)로부터 연역할 수 없습니다. 그것들은 상호작용으로부터 창발(emerge)합니다. 방법은 탐색하고, 감지하고, 대응하는 것입니다. 시도하고, 관찰하고, 조정하십시오.
코드를 작성하는 것은 대부분 복잡한 (Complicated) 영역에 속합니다. 시스템을 구축하고 진화시키는 것은 복잡한 (Complex) 영역입니다. 거의 모든 새로운 AI 개발 도구들은 정확히 말하면 복잡한 (Complicated) 영역에 머물러 있습니다. 이것이 바로 AI 도구가 복잡한 (Complex) 영역의 문제를 해결할 것이라고 기대하는 것이 실수인 이유입니다.
복잡한 (Complicated) 영역의 작업 vs 시스템의 일관성 확보
에이전트 기술 (Agent Skills) (공개된 SKILL.md 형식)은 절차적 지식과 컨텍스트를 휴대 가능하고 버전 관리가 가능한 폴더로 패키징하며, 에이전트는 점진적 공개 (progressive disclosure)를 통해 필요할 때 이를 로드합니다. 발견하고, 활성화하고, 실행하십시오. 기술(skill)이란 _제한된 작업 (bounded task)_을 위해 포착된 전문 지식입니다. 예를 들어 프레젠테이션을 구성하는 방법이나 특정 분석 파이프라인을 실행하는 방법 등이 있습니다. 이것은 가장 순수한 형태의 복잡한 (Complicated) 영역 도구입니다. 이는 정의된 일을 수행하는 '알 수 있는 올바른 방법'을 인코딩하며, 이를 반복 가능하게 만듭니다.
슈퍼파워 (Superpowers) (Jesse Vincent의 프레임워크)는 결합 가능한 기술을 기반으로 구축된 코딩 에이전트를 위한 개발 _방법론 (methodology)_입니다. 이는 규율 있는 순서를 강제합니다. 설계를 브레인스토밍하고, 사양(spec)을 도출하며, 상세한 계획을 작성한 다음, 작업 사이에 코드 리뷰 게이트를 두는 Red-Green-Refactor 테스트 주도 개발 (TDD)을 수행합니다. 심지어 테스트 이전에 작성된 코드를 삭제하기도 합니다. 이것은 _작업 단위 (unit of work)_에 적용된 프로세스입니다. 이는 에이전트가 수행하는 각 작업을 더 엄격하게 만들고 덜 즉흥적으로 만듭니다. 다시 말하지만, 이는 복잡한 (Complicated) 영역에 해당합니다. 즉, 알 수 있는 일을 정확하고 검증 가능한 방식으로 수행하는 것에 관한 것입니다.
Get Shit Done은 동일한 계열의 메타 프롬프팅 (meta-prompting), 컨텍스트 엔지니어링 (context-engineering), 그리고 스펙 주도 (spec-driven) 시스템입니다. 즉, 스펙을 확정하고, 컨텍스트를 설계하며, 그에 따라 실행하는 것입니다. 스펙 주도 개발 (Spec-driven development)이 일반적으로 가장 명확한 사례입니다. 스펙 (specification)은 바로 이 복잡한 (Complicated) 영역의 도구입니다. 스펙은 연역 가능한 (deducible) 정답을 고정하여 에이전트가 이를 정확히 타격할 수 있게 합니다.
기술 (Skills)은 작업을 수행하는 올바른 방법을 포착합니다. 슈퍼파워 (Superpowers)는 작업에 대한 엄격한 프로세스를 강제합니다. 스펙 키트 (Spec kits)는 작업이 반드시 산출해야 하는 것을 확정합니다. 이 세 가지 모두가 단위 (unit) — 즉 작업, 함수, 경계가 지정된 변경 사항 (bounded change) — 를 신뢰할 수 있고, 반복 가능하며, 연역 가능한 상태에 가깝게 만듭니다. 이것은 가치 있는 일이며, 복잡한 (Complicated) 영역의 작업입니다. 이는 시스템을 일관되게 (coherent) 만드는 것과는 다른 문제입니다.
왜 "더 신뢰할 수 있는 단위"가 "일관된 시스템"과 같지 않은가
복잡성 (Complexity)은 상호작용 (interactions)의 속성입니다. 에이전트가 수행하는 모든 개별 작업을 결점 없이 만들 수 있습니다. 완벽한 스펙 준수, 완벽한 TDD, 완벽한 기술 실행을 수행하더라도 여전히 일관성 없는 시스템으로 끝날 수 있습니다. 왜냐하면 불일치는 조각들이 결합하는 방식에 존재하기 때문입니다.
필드 이름을 userId로 지정하는 완벽하게 실행된 작업과, user_id를 기대하는 또 다른 완벽하게 실행된 작업은 각각 고립된 상태에서는 옳지만, 함께 작동할 때는 깨집니다. 재시도 정책 (retry policy)과 타임아웃 정책 (timeout policy)은 각각 모든 테스트를 통과할 수 있지만, 결합되었을 때 시스템 과부하를 일으킬 수 있습니다. 공유 유틸리티에 대한 두 번의 리팩터링 (refactor)이 각각 엄격하고 스펙을 준수하더라도, 서로를 덮어쓸 수 있습니다. 작업별 규율 (Per-task discipline)은 이러한 문제를 잡아내지 못하는데, 왜냐하면 각 작업이 "올바르게" 수행되었기 때문입니다. 실패는 창발적 (emergent)입니다. 즉, 복잡한 (Complicated) 것이 아니라 난해한 (Complex) 문제입니다.
이것이 바로 더 나은 SKILL.md나 더 엄격한 TDD 게이트가 단위를 아무리 개선하더라도, 그 자체만으로는 시스템의 일관성을 만들어낼 수 없는 이유입니다. 그것은 복잡한 (Complicated) 문제를 잘 해결하고 있을 뿐, 난해한 (Complex) 문제는 그대로 방치하고 있는 것입니다. 두 영역은 서로 다른 방법론을 요구합니다. 단위에는 연역 (deduction)과 프로세스 (process)가 필요하고, 시스템에는 탐색-감지-반응 (probe-sense-respond)이 필요합니다.
"그냥 통합 테스트 (integration tests)를 작성하면 됩니다"라고 말하기는 쉽습니다. 복잡한 (Complex) 도메인에서는 무엇이 망가질지 미리 알 수 없기 때문입니다. 만약 미리 알 수 있다면, 특정 부분을 겨냥한 테스트를 작성할 수 있을 것이고 그것은 복잡한 (Complicated) 영역이 될 것입니다. 따라서 진정한 탐색 (probe)은 미리 작성해 둔 테스트가 아닙니다. 그것은 통합 (integration) 행위 그 자체입니다. 즉, 변경 사항을 병합하고, 조립된 시스템을 배포하며, 실제 부하 (load)를 가하는 것입니다. 그러면 감지 (sense) 결과가 돌아옵니다. CI 신호, 아무도 예측하지 못한 실패, 프로덕션 텔레메트리 (production telemetry) 등이 그것입니다. 반응 (response)은 당신이 관찰한 것에 맞춰 조정하는 것입니다. 통합 테스트와 속성 기반 테스트 (property-based tests)는 여전히 그 자리를 차지합니다. 하지만 이는 감지를 더 날카롭게 만드는 계측 도구 (instrumentation)로서 존재합니다. 이들은 조각들을 결합하고 그 결합이 어떻게 작동하는지 지켜보는 행위의 대체재가 아니라, 탐색할 때 당신이 알아차릴 수 있는 범위를 넓혀줍니다. 당신이 작성한 테스트는 어떤 상호작용을 확인해야 할지 이미 알고 있다는 것을 전제로 합니다. 정작 고통을 주는 모순은 당신이 찾아봐야 할 줄 몰랐던 것들입니다.
유혹적인 과장 (The seductive overclaim)
함정은 "이 사양 (spec)/기술/방법론을 채택하면 당신의 AI 개발은 해결된 것입니다"라는 생각입니다. Cynefin 프레임워크는 그렇지 않다고 말합니다.
사양 (spec)이 복잡한 (Complex) 문제를 복잡한 (Complicated) 도메인으로 옮겨주지는 않습니다. 사양은 _단위 (unit)_를 복잡한 (Complicated) 상태로 만듭니다. 즉, 경계가 있고, 연역 가능하며, 확인 가능한 상태로 만듭니다. 하지만 시스템의 창발적 행동 (emergent behavior)은 당신의 사양이 아무리 훌륭하더라도 복잡한 (Complex) 상태로 남습니다. 왜냐하면 독립적으로 올바른 조각들이 상호작용할 모든 방식을 미리 열거할 수 없기 때문입니다. 사양과 기술은 복잡성의 표면적을 줄여줍니다. 즉, 제약이 없는 단위를 줄이고, 무언가 잘못될 수 있는 자유도 (degrees of freedom)를 줄입니다. 이는 좋은 울타리가 군중이 잘못 행동할 수 있는 방식을 줄이는 것과 같습니다. 이것은 복잡성을 없애는 것과는 다르며, "우리는 사양이 있으니 시스템은 통제 하에 있다"라고 믿는 팀은 도메인 오류 (domain error)를 범한 것이며, 이는 결국 누구의 책임도 아니었던 통합 실패 (integration failure)로 나타나게 될 것입니다.
이러한 도구들은 다음과 같이 주장할 수 있습니다: 우리는 AI가 생성한 작업의 각 단위를 더 신뢰할 수 있고, 반복 가능하며, 검증 가능하게 만듭니다. 이는 사실이며 가치 있는 일이고, 복잡한 (Complicated) 도메인은 이를 보상합니다. 하지만 과장된 주장인 — 우리는 당신의 시스템을 올바르게 만듭니다 — 는 도구(tooling)가 넘지 못하는 도메인 경계를 가로지릅니다.
이와 관련된 또 다른 기대 역시 동일한 정밀한 검토가 필요합니다: 즉, 점점 더 커지는 컨텍스트 윈도우 (context windows)가 에이전트(agent)로 하여금 시스템 전체를 한 번에 보게 함으로써 문제를 해소할 것이라는 기대입니다. 컨텍스트 윈도우는 문제의 한 조각을 침식할 뿐입니다. 어떤 비일관성은 에이전트가 좁은 윈도우 내에서 작업하느라 코드베이스의 관련 부분을 볼 수 없었기 때문에 발생합니다. userId와 user_id의 충돌이 부분적으로 그러한 사례입니다. 윈도우를 두 가지를 모두 담을 수 있을 만큼 충분히 넓히면, 이러한 가시성 제한적 (visibility-limited) 부류의 모순은 포착 가능해집니다. 이는 "아무도 볼 수 없어서 나타나는 (emergent)" 상태에서 "이제는 볼 수 있으므로 추론 가능한 (deducible)" 상태로 이동합니다. 하지만 이를 더 밀어붙인다면 이는 두 가지 서로 다른 것을 혼동하는 것입니다: 시스템을 더 많이 보는 것이 시스템이 어떻게 동작하는지 추론할 수 있는 것과 같지는 않습니다. 전체 분산 시스템의 코드를 컨텍스트에 넣을 수는 있지만, 그것을 읽는 것만으로 운영 환경의 부하(production load) 하에서 데드락 (deadlock)이 발생하는지 여부를 여전히 추론할 수 없을 수도 있습니다. 그것은 정적 분석(static analysis)이 밝혀낼 수 있는 정적 속성이 아니라, 창발적인 (emergent) 런타임 속성입니다. 더 큰 컨텍스트는 "닥쳐올 것을 미리 보는" 종류의 복잡성은 줄여줍니다. 하지만 창발적인 종류의 복잡성은 그대로 둡니다. 시스템이 당면한 작업의 활성 컨텍스트 (active context)보다 커지는 순간, 가시성 조각의 문제 또한 다시 나타납니다.
제품이 지도에 진입하는 지점 — 그리고 경험이 오도할 수 있는 이유
도구(tooling)에 관한 논의는 보통 이전의 질문 하나를 건너뜁니다. 모든 제품이 동일한 도메인에서 시작되는 것은 아니라는 점입니다. 당신이 Cynefin 지도의 어디에서 _진입(enter)_하느냐는, 단순히 당신에게 얼마나 알려져 있느냐가 아니라 당신의 카테고리가 세상에 이미 얼마나 알려져 있는지에 의해 결정됩니다. CMS나 이커머스 플랫폼은 대부분 Complicated(복잡) 영역에서 시작하며, 때로는 Clear(명확) 영역에서 시작하기도 합니다. 이 영역에서는 수십 년간의 참조 아키텍처(reference architectures), 연구할 수 있는 경쟁사, 그리고 정립된 사용자 기대치가 존재하므로 문제가 해결되어 있습니다. 당신의 미지의 영역은 로컬(당신의 스택, 당신의 니치)적인 것이지, 카테고리적인 것이 아닙니다. 그곳에서는 사양 중심 방법론(spec-driven methodology)과 기술 프레임워크가 첫날부터 적합합니다. 왜냐하면 정답은 선행 기술(prior art)로부터 연역할 수 있으며, 이를 조기에 확정 짓는 것이 적절하기 때문입니다. 반면 새로운 카테고리는 Complex(복잡계)에서 시작하며, 때로는 Chaotic(혼돈) 영역에 맞닿아 있기도 합니다. 복제할 카테고리 자체가 아직 존재하지 않기 때문에, 아무도 제품이나 솔루션의 형태를 알지 못합니다. 이때 첫날부터 자신 있게 사양(spec)을 작성하는 것은 도메인 오류를 반대로 범하는 것과 같습니다. 즉, 충분히 학습하지 않은 것을 사양화하고, 현실과 접촉하여 검증되지 않은 가설을 고착화하는 것입니다.
그러한 Complex한 시작 단계에서 방법론은 프로토타입(prototype)과 실험으로 표현되는 '탐색-감지-대응(probe-sense-respond)'입니다. 기술 창업자들에게는 이곳에 특정한 위험이 도사리고 있습니다. 코드를 작성할 때만 진전이 있다고 느끼는 감각은 Complicated 도메인의 본능입니다. 이는 구축해야 할 알려진 대상이 존재하며, 할 일은 그것을 만드는 것이라고 가정합니다. 새로운 카테고리에서 할 일은 _학습(learning)_이며, 학습의 결과물은 종아 필요한 정보를 알려주고 나면 버리게 될 프로토타입인 경우가 많습니다. 불확실성 자체가 목적이었기 때문입니다. 실제 리스크가 "이 카테고리가 존재하는가"인 도메인에서, 커밋된 코드 라인 수로 진척도를 측정하는 창업자는 그 리스크가 전혀 다뤄지지 않는 동안에도 스스로 생산적이라고 느낄 것입니다. 잘못된 도메인에서의 생산성은 가장 비용이 많이 드는 종류의 바쁨입니다. 이것이 바로 '코드는 부채(code-is-a-liability)'라는 지점입니다. Complex 도메인에서 유지된 코드는 마이너스(-) 진전이 될 수 있습니다. 왜냐하면 여전히 버릴 자유가 있어야 할 가설들에 당신을 묶어버리기 때문입니다.
그 아래에는 함정이 있습니다. 경험이 상황을 더 악화시킬 수 있다는 점입니다. 깊은 전문 지식은 최선(best)과 양호(good)한 관행을 숙달하는 것, 즉 알려진 해결책을 빠르게 패턴 인식(pattern-recognition)하는 것을 의미합니다. 엔지니어가 숙련될수록 그들은 더욱 반사적으로 그러한 패턴에 의존하게 됩니다. 복잡한(Complicated) 도메인에서 그러한 반사 신경은 초능력입니다. 그것이 바로 "시니어(senior)"가 의미하는 대부분의 핵심입니다. 하지만 복잡한(Complex) 도메인에서 그것은 부채(liability)가 됩니다. 왜냐하면 정직한 답이 "아직 정해진 정답이 없으니 탐색(probe)해야 한다"임에도 불구하고, 그 반사 신경은 "여기서 알려진 올바른 방법은 무엇인가?"라고 묻기 때문입니다. 알려진 카테고리를 구축할 때 전문가를 탁월하게 만드는 바로 그 유창함이, 새로운 카테고리에서는 그들을 눈멀게 할 수 있습니다. 즉, 상황은 그 기능이 존재해야 하는지조차 멈춰서 질문하기를 요구하고 있음에도 불구하고, 그들은 잘못된 것을 만들어 버립니다. 새로운 카테고리 구축이 요구하는 규율은 알려진 카테고리에서 당신을 능숙하게 만드는 규율과 거의 정반대입니다.
코드를 버리는 것은 어렵습니다. 기술과 노력이 투입되었기 때문에, 그 코드가 당신에게 무엇을 가르쳐 주었는지와는 별개로 그 결과물(artifact) 자체를 가치 있게 느끼게 만들기 때문입니다. 이러한 매몰 비용(sunk-cost)의 유혹은 탐색(probe)을 성급한 제품(premature product)으로 변질시킵니다. 필요한 규율은 초기 코드를 탐색으로 취급하고, 배움은 유지하되, 결과물은 버리는 것입니다. 복잡한(Complex) 도메인에서 복잡한(Complicated) 도메인으로의 이행은 부분적이며 결코 완전할 수 없습니다. 프로토타이핑(Prototyping)은 당신이 제거할 수 있는 불확실성을 제거해 줍니다. 하지만 환원 불가능한 창발적(emergent) 유형의 불확실성에는 손을 대지 못합니다. 즉, 조립되어 실행 중인 시스템에서만 나타나는 런타임 동작(runtime behavior) 같은 것들 말입니다. 그리고 모든 새로운 기능은 이미 안정화된 코드베이스에 다시 복잡성(Complexity)을 주입합니다. 프로토타이핑을 통해 완전히 연역 가능한(deducible) 시스템으로 나아갈 수는 없습니다. 당신은 이행이 완료된 부분에 대해서는 복잡한(Complicated) 도메인의 도구를 사용할 권리를 얻게 되지만, 결코 이행되지 않을 부분에 대해서는 탐색-감지-반응(probe-sense-respond)의 감각을 계속 유지해야 합니다.
더 어려운 삭제: 작동하지만 본질을 희석하는 코드
탐색(probe)을 폐기하는 것은 두 가지 삭제 규율 중 더 쉬운 쪽입니다. 왜냐하면 그 코드는 처음부터 일회용이었기 때문입니다. 그 코드의 역할은 당신에게 무언가를 가르쳐 주는 것이었습니다. 여기에는 더 드물고 훨씬 더 어려운 두 번째 규율이 있습니다. 바로 작동하고, 헌신적으로 작성되었으며, "좋은" 코드임에도 불구하고 제품의 본질을 희석하기 때문에 이를 삭제하는 것입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기