AI가 프로그래밍의 쉬운 부분을 해결했다. 이제 어려운 부분은 더 알아보기 힘들어졌다.
요약
Fred Brooks의 'No Silver Bullet' 이론을 바탕으로 AI가 소프트웨어의 우연적 복잡성을 제거하는 방식을 분석합니다. AI가 상용구 코드를 대신 작성하며 생산성을 높이지만, 도메인 지식에 기반한 본질적 복잡성은 여전히 해결해야 할 과제로 남습니다.
핵심 포인트
- AI는 구문, 설정 등 우연적 복잡성을 제거하는 데 탁월함
- 비즈니스 로직과 데이터 관계 등 본질적 복잡성은 AI가 해결하기 어려움
- 개발자의 업무 비중이 상용구 코드에서 본질적 로직으로 이동함
- AI가 생성한 코드의 '올바름'을 판단하는 도메인 지식의 중요성 증대
✓ 인간이 작성한 분석; AI는 서식 지정 및 교정용으로 사용됨.
1986년, Fred Brooks는 "No Silver Bullet — Essence and Accident in Software Engineering"을 발표했습니다. 그의 주장은 다음과 같습니다: 소프트웨어 복잡성(complexity)에는 두 가지 종류가 있습니다. 우연적 복잡성 (Accidental complexity)은 도구에 의해 부과되는 어려움입니다. 즉, 구문 규칙 (syntax rules), 상용구 코드 (boilerplate code), 빌드 설정 (build configurations), 배포 스크립트 (deployment scripts) 등을 의미합니다. 본질적 복잡성 (Essential complexity)은 문제 자체에 의해 부과되는 어려움입니다. 비즈니스 규칙 (business rules), 데이터 관계 (data relationships), 그리고 현실 세계가 데이터 모델에 깔끔하게 들어맞지 않는 예외 케이스 (edge cases) 등이 이에 해당합니다.
Brooks는 우리가 모든 우연적 복잡성을 제거하더라도 소프트웨어는 여전히 어려울 것이라고 예측했습니다. 본질적 복잡성은 줄일 수 없습니다. 그것은 도구가 아니라 도메인 (domain)에서 비롯됩니다. 어떤 도구도 이를 제거할 수 없는데, 왜냐하면 그것은 도구의 속성이 아니라 문제의 속성이기 때문입니다.
AI는 우연적 복잡성을 제거하기 위해 만들어진 역대 가장 효과적인 도구입니다. AI는 Brooks가 예측했던 방식대로 그의 예견이 옳았음을 증명하고 있습니다.
AI가 제거하는 것
AI는 상용구 코드 (boilerplate)를 즉각적으로 생성합니다. HTTP 핸들러 (HTTP handlers), 데이터베이스 CRUD 작업 (database CRUD operations), 직렬화 코드 (serialization code), 테스트 스캐폴딩 (test scaffolding), 설정 파일 (configuration files), Dockerfile 템플릿 (Dockerfile templates), CI 파이프라인 정의 (CI pipeline definitions) 등이 그 예입니다. 개발자들이 작성하는 데 수 시간을 소비하는 모든 우연적 복잡성 조각들을 AI는 단 몇 초 만에 만들어냅니다.
시간의 40%를 상용구 코드에, 60%를 본질적 로직 (essential logic)에 쓰던 개발자는 이제 상용구 코드에 5%를 쓰고, 95%의 시간을 본질적 로직을 위해 사용할 수 있게 되었습니다. 시간 절약과 생산성 향상은 실재합니다. 우연적 부분에 있어서 '10배의 생산성(10x)'을 느끼는 것은 실재하는 경험입니다.
AI는 우연적 복잡성을 제거합니다. 문제는 우연적 복잡성이 사라진 후에 발생합니다.
우연적 요소가 제거된 후 남는 것
보일러플레이트 (Boilerplate) 코드가 몇 초 만에 생성될 때, 개발자는 본질적 복잡성 (Essential complexity)에 더 빠르게 도달합니다. 비즈니스 로직 (Business logic), 도메인 규칙 (Domain rules), 엣지 케이스 (Edge cases) 등이 그것입니다. 데이터베이스 테이블에 명확하게 매핑되지 않는 엔티티 (Entities) 간의 관계, 특정 순서에서만 나타나는 레이스 컨디션 (Race conditions), 조직 계층 구조, 위임 체인, 그리고 시간적 상태 (Temporal state)에 따라 달라지는 권한 부여 규칙 등이 포함됩니다.
이것이 바로 브룩스 (Brooks)가 줄일 수 없다고 말했던 복잡성입니다. AI는 이를 제거할 수 없는데, 이는 이것이 구문 (Syntax)의 문제가 아니기 때문입니다. 이것은 소프트웨어와 현실 세계 사이의 대응 관계에 관한 문제입니다. AI는 비즈니스 규칙을 처리하는 함수를 생성할 수 있습니다. 하지만 그 비즈니스 규칙이 올바른지는 알 수 없습니다. 왜냐하면 '올바름'은 도메인 (Domain)에 의해 정의되기 때문입니다.
과거의 워크플로우 (Workflow)에는 자연스러운 속도 조절 메커니즘이 있었습니다. 보일러플레이트를 작성하는 데 소비된 시간은 개발자가 무의식적으로 도메인에 대해 생각하는 시간이었습니다. 구문과 씨름하는 과정은 개발자에게 "잠깐, 이 비즈니스 규칙은 말이 안 되는데"라고 알아차릴 시간을 주었습니다. 보일러플레이트는 짜증스러운 일이었지만, 동시에 개발자가 충분히 생각할 수 있도록 속도를 늦춰주는 마찰 (Friction) 역할도 했습니다.
AI는 그 마찰을 제거했습니다. 개발자는 프롬프트 (Prompt)를 입력한 지 몇 분 만에 작동하는 코드에 도달합니다. 본질적 복잡성은 여전히 그 자리에 있습니다. 하지만 개발자는 그것을 볼 수 있을 만큼 충분히 속도를 늦추지 않았기 때문에 이를 알아차리지 못했습니다.
완료의 환상
가장 위험한 결과는 AI가 생성한 코드가 완성된 것처럼 보인다는 점입니다. 보일러플레이트와 에러 핸들링 (Error handling)이 갖춰져 있습니다. 테스트도 통과합니다. 개발자는 이를 검토하고 "다 됐다"라고 생각합니다.
하지만 '다 됐다'는 것은 우연적 복잡성 (Accidental complexity)이 해결되었음을 의미합니다. 본질적 복잡성 — 즉 도메인 로직, 엣지 케이스, 불변량 (Invariants) — 은 전혀 검토되지 않았습니다. 코드는 일반적인 케이스를 올바르게 처리하지만 (AI가 일반적인 패턴으로 학습되었기 때문에), 이 특정 도메인을 독특하게 만드는 특수한 케이스에서는 실패합니다 (그러한 케이스들이 학습 데이터에 없었기 때문입니다).
이러한 실패는 고객이 AI가 전혀 고려하지 못한 엣지 케이스 (edge case)를 마주하는 몇 주 또는 몇 달 후에 표면화됩니다. 문제를 디버깅하는 개발자는 자신이 작성하지 않은 코드, 자신이 설계하지 않은 비즈니스 규칙을 구현하는 코드, 그리고 자신이 생각하지 못한 케이스를 처리하는 코드를 발견하게 됩니다. 그들은 본질적 복잡성 (essential complexity)을 이해하지 못하기 때문에 이를 수정할 수 없습니다. AI는 완벽해 보이는 우연한 해결 (accidental resolution)이라는 층 아래에 이를 숨겨버렸습니다.
리뷰어의 딜레마 (The reviewer's dilemma)
이는 문제를 심화시키는 비대칭성을 만듭니다. 직접 50줄의 코드를 작성하는 것보다 AI가 생성한 100줄의 코드를 리뷰하는 것이 더 어렵습니다.
개발자가 코드를 작성할 때, 그들은 진행 과정에서 도메인 (domain)에 대한 정신적 모델 (mental model)을 구축합니다. 구현 과정에서 엣지 케이스 (edge case)를 마주하고, 이를 어떻게 처리할지에 대한 결정을 내리며, 그 추론 과정을 계속 이어 나갑니다. 코드와 이해는 함께 구축됩니다.
반면 개발자가 AI가 생성한 코드를 리뷰할 때는, 코드가 추론의 흔적 없이 이미 완성된 형태로 전달됩니다. 리뷰어는 구현 결과물로부터 도메인 모델을 역공학 (reverse-engineer)해야 합니다 — 어떤 엣지 케이스가 처리되었는지, 어떤 것이 누락되었는지, 그리고 처리된 것들이 올바르게 처리되었는지를 파악해야 합니다. 이는 모델을 직접 구축하는 것보다 더 어렵습니다. 왜냐하면 리뷰어는 의도 (intent)가 담겨 있지 않은 결과물로부터 의도를 재구성해야 하기 때문입니다.
그 결과: 배포된 코드 라인 수나 병합된 풀 리퀘스트 (pull requests)로 생산성을 측정하는 팀은 엄청난 처리량 (throughput) 증가를 목격합니다. 반면 방지된 결함이나 검증된 도메인 커버리지로 생산성을 측정하는 팀은 동일한 본질적 복잡성 병목 현상을 목격하며, 이제 그 병목은 더 빠르고 더 큰 배치 (batch) 단위로 찾아옵니다.
처리량 (Throughput)은 진보가 아닙니다.
본질적 복잡성이 존재하는 곳 (Where the essential complexity lives)
브룩스 (Brooks)는 본질적 복잡성 (essential complexity)을 줄일 수 없게 만드는 네 가지 속성을 식별했습니다. AI는 각 속성과 서로 다르게 상호작용합니다.
복잡성 (Complexity). 시스템이 가질 수 있는 상태 (states)의 순수한 숫자입니다. 상호작용하는 10개의 컴포넌트 (components)를 가진 시스템은 개발자가 열거할 수 있는 것보다 더 많은 가능한 상태를 가집니다. 브룩스는 이를 근본적인 속성(fundamental property)이라고 불렀습니다 — 다른 모든 속성을 악화시키는 속성입니다.
AI는 이 속성을 직접적으로 증폭시킵니다. 코드를 직접 작성하는 개발자는 하루에 수백 줄의 코드를 생성합니다. 각 줄은 그들이 의식적으로 내린 결정 지점(decision point)입니다. AI의 도움을 받는 개발자는 하루에 수천 줄을 배포합니다. 각 줄은 누군가가 이해해야 할 결정 지점이지만, 아무도 의식적으로 결정하지 않은 지점입니다. 시스템의 상태(states) 수는 코드와 함께 증가하지만, 그 상태들을 머릿속에 매핑하는 개발자의 능력은 일정하게 유지됩니다. 더 많은 코드, 동일한 뇌. 상태 공간(state space)은 확장되지만, 개발자의 모델은 확장되지 않습니다.
적합성 (Conformity). 소프트웨어는 현실 세계 — 즉 규제, 기존 시스템, 인간의 기대치 — 에 적합해야 합니다. 이러한 적합성 요구사항은 코드 패턴으로부터 도출될 수 없습니다. 반드시 도메인(domain)으로부터 이해되어야 합니다.
적합성은 브룩스(Brooks)가 말한 네 가지 속성 중 가장 어려운 것이며, 클라우드는 이를 가시화합니다. 클라우드는 적합성 엔진입니다. 당신은 모델을 강제하는 모든 서비스에 적합해야 합니다. AWS의 IAM 평가 로직, GCP의 네트워킹 모델, Azure의 RBAC 계층 구조 등이 그러합니다. AI는 이러한 API의 구문(syntax)에는 능숙하지만, 그것들이 당신의 내부 컴플라이언스(compliance) 규칙과 상호작용할 때 발생하는 보안적 함의(security implications)에 대해서는 침묵합니다.
학습 패턴을 기반으로 IAM 정책을 생성하는 AI는 IAM 평가 의미론(semantics)을 이해하지 못합니다. AI는 학습 데이터에 나타난 정책들을 패턴 매칭(pattern-matches)할 뿐입니다. 두 정책이 학습 데이터에서 결합된 적 없는 방식으로 상호작용할 때, 새로운 액션 네임스페이스(action namespace)가 기존 와일드카드(wildcard)의 의미를 조용히 변경할 때, 혹은 특정 컴플라이언스 요구사항이 두 규제 해석 사이에 걸쳐 있을 때, 이 차이는 결정적인 문제가 됩니다.
변경 가능성 (Changeability). 요구사항은 변합니다. 현실 세계가 변하기 때문입니다. 지난 분기에 정확했던 가격 모델이 이번 분기에는 틀릴 수 있습니다. 지난주에는 안전했던 IAM 정책이, 제공업체가 네임스페이스 (namespace)에 새로운 액션 (actions)을 추가함에 따라 이번 주에는 OAuth 에이전트 권한을 부여하게 될 수도 있습니다. AI가 생성한 코드는 지난 분기의 모델을 완벽하게 구현합니다. 이번 분기 모델에 맞춰 이를 업데이트해야 하는 개발자는 이전 모델을 이해하지 못하며 (본인이 작성한 것이 아니므로), 이를 안전하게 수정할 수도 없습니다 (구현에 어떤 가정이 내재되어 있는지 알 수 없기 때문입니다).
비가시성 (Invisibility). 소프트웨어는 보이지 않습니다. 전체 구조를 보여주는 공간적 표현이 없습니다. 개발자는 구조를 머릿속에 담고 있어야 합니다. 피터 나우르 (Peter Naur)가 "이론 구축으로서의 프로그래밍 (Programming as Theory Building)"에서 주장했듯이, 개발자는 프로그램에 대한 하나의 extit{이론 (theory)}을 구축해야 합니다. 즉, 솔루션이 문제에 어떻게 매핑되는지, 각 부분이 무엇을 하고 왜 그러한지, 각 부분들이 서로 및 도메인 (domain)과 어떻게 연관되는지에 대한 정신적 모델 (mental model)을 구축해야 합니다.
나우르의 통찰은 브룩스 (Brooks)의 통찰을 심화시킵니다. 만약 프로그래밍이 개발자의 머릿속에 이론을 구축하는 것이라면, AI가 생성한 코드는 이론이 없는 (theory-less) 코드입니다. 결과물은 존재하지만 그 뒤에 숨겨진 이론은 결코 구축되지 않았습니다. 왜 코드가 지금과 같은 형태를 갖추고 있는지, 어떤 도메인 제약 조건 (domain constraints)을 충족하는지, 혹은 요구사항이 변경될 때 어떤 가정이 깨질지에 대한 정신적 모델을 가진 사람이 아무도 없습니다. 코드는 이중으로 비가시적입니다. 개발자는 이론을 가지고 있지 않으며, 코드의 구조는 도메인의 패턴이 아닌 학습 데이터 (training data)의 패턴을 따르기 때문입니다. 코드의 구조는 문제의 구조와 일치하지 않습니다. 대신 AI가 본 유사한 코드의 구조와 일치합니다.
이로 인해 AI 생성 코드는 변경 시 유독 취약해집니다. 사람이 작성한 코드는 이를 이해하던 개발자가 팀을 떠날 때 퇴보합니다 — 즉, 이론이 문 밖으로 걸어 나가는 것입니다. 반면 AI 생성 코드는 이론 없이 시작됩니다. 이론이 나갈 문조차 없습니다. 이해라는 것이 애초에 존재하지 않았기 때문입니다.
이것이 AI 지원 개발에 의미하는 바
Brooks의 통찰은 DORA의 2025년 데이터가 측정한 바를 예측합니다: 조직들은 프로덕션(production)에 실제로 배포되는 코드 양에서 약 10%의 개선을 목격했습니다. 더 높은 불안정성(instability)과 함께 더 높은 처리량(throughput)이 나타났습니다 — 즉, 더 많은 코드가 배포되었지만, 더 많은 롤백(rollback)이 발생했습니다.
우연한 복잡성(accidental complexity)은 제거되었습니다 (막대한 시간 절약). 하지만 본질적 복잡성(essential complexity)은 그대로 남아 있었습니다 (도메인 버그의 수는 동일함). 순 개선 효과는 미미했습니다. 시간은 언제나 본질적 복잡성에 소비될 운명이었으며, AI는 이를 줄여주지 않기 때문입니다.
성공할 팀은 우연한 복잡성에서 절약한 시간을 본질적 복잡성에 더 많이 투자하는 팀입니다 — 즉, 도메인 모델링(domain modeling), 속성 정의(property definition), 불변식 명세(invariant specification), 그리고 AI의 출력이 정확한지 검증할 수 있을 만큼 비즈니스 규칙을 깊이 이해하는 데 시간을 쓰는 팀입니다.
실패할 팀은 절약된 시간을 도메인을 이해하는 데 투자하지 않고 더 많은 코드, 기능(features), 엔드포인트(endpoints), 서비스(services)를 생성하는 데 사용하는 팀입니다. 이들은 나중에 더 커진 코드베이스, 더 적은 이해도, 그리고 본질적 복잡성 실패가 발생할 수 있는 더 넓은 표면적(surface area)을 가진 채 Brooks의 벽에 부딪히게 될 것입니다.
본질적 복잡성 관리로서의 명세(Specification)
명세(Specifications) — 타입 시그니처(type signatures), API 계약(API contracts), 데이터베이스 스키마(database schemas), 속성 정의(property definitions)는 본질적 복잡성을 관리하기 위한 도구입니다. 이들은 도메인 규칙을 코드 내에 암시적(implicit)으로 남겨두는 대신 명시적(explicit)으로 만듭니다.
func ProcessOrder(order Order) (Receipt, error)라고 말하는 타입 시그니처는 본질적 복잡성을 포착합니다: 주문은 영수증(receipt) 또는 에러(error)를 생성합니다. '어쩌면 영수증일 수도 있는 것(maybe-receipt)', 침묵하는 실패(silent failure), 또는 부분적인 결과가 아닙니다. 타입 시스템(type system)은 구현이 인간에 의해 작성되었든 AI에 의해 작성되었든 상관없이 컴파일 타임(compile time)에 이를 강제합니다.
"할인이 적용된 후 어떤 주문도 음수의 총액을 가질 수 없다"라고 명시하는 속성은 도메인 불변량 (domain invariant)을 포착합니다. AI는 특정 할인 조합에 대해 음수의 총액을 생성하는 할인 로직을 만들 수도 있습니다. 이 속성은 모든 변경 사항에 대해 기계적이고 결정론적으로 이를 잡아냅니다. 본질적 복잡성 (essential complexity)이 검증 가능한 문장으로 명시되었기 때문입니다.
AI는 우연적 복잡성 (accidental complexity)을 제거합니다. 명세 (specifications)는 본질적 복잡성을 관리합니다. 두 가지 모두 필요합니다. 명세 없는 AI는 올바르게 보이지만 검토되지 않은 도메인 가정을 내포하는 코드를 생성합니다. AI 없는 명세는 인간이 모든 상용구 코드 (boilerplate)를 작성해야 합니다. 이들이 결합하면, AI는 구문 (syntax)을 처리하고, 명세는 의미론 (semantics)을 처리합니다.
브룩스 (Brooks)는 은탄환 (silver bullet)은 없다고 말했습니다. 그는 옳았습니다. AI는 결코 어려운 부분이 아니었던 우연적 복잡성을 제거합니다. 어려운 부분은 여전히 남아 있습니다. 줄일 수 없는 본질적인 상태로, 이제는 완성된 것처럼 보이는 완벽하게 포맷팅된 코드의 층 아래에 숨겨져 있습니다.
클라우드 보안에서 본질적 복잡성은 도메인 불변량입니다: "어떠한 역할 (roles), 정책 (policies), 신뢰 관계 (trust relationships)의 조합을 통해서도 권한 상승 (privilege escalation) 경로가 존재하지 않는다." 우연적 복잡성은 검사 로직을 작성하는 것, 즉 YAML, CEL 술어 (predicates), 테스트 픽스처 (test fixtures)를 작성하는 것입니다. Stave는 우연적 복잡성을 처리합니다 (3,000개 이상의 제어 항목을 기계적으로 평가). 어떤 속성을 검증해야 하는지 아는 본질적 복잡성은 어떤 AI도 대체할 수 없는 인간의 판단으로 남아 있습니다. 40년이 지난 지금도 브룩스는 여전히 옳습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기