변화는 스스로를 증명해야 한다: AI 생성 코드를 위한 5-R 리뷰
요약
AI가 생성한 코드가 단순히 작동하는 것을 넘어 프로덕션 환경에서 안전하게 운영되기 위한 '5-R 리뷰' 프레임워크를 제안합니다. 구현 자체보다 코드의 이유, 영향 범위, 가역성, 회복 탄력성, 검토 가능성을 검증하는 것이 중요함을 강조합니다.
핵심 포인트
- AI 생성 코드는 기술적 유효성만으로 프로덕션 준비가 완료된 것이 아님
- 5-R 리뷰: Reason, Reach, Reversibility, Resilience, Reviewability
- 분산 시스템에서 사소한 변경이 초래할 수 있는 광범위한 영향력을 고려해야 함
- AI를 통한 구현 속도 향상만큼 코드의 가정과 실패 동작에 대한 명시적 검토가 필수적임
AI가 생성한 코드는 종종 완성된 것처럼 보입니다.
컴파일이 되고, 저장소(repository)의 컨벤션을 따르며, 단위 테스트(unit tests)를 포함하고, 설계에 대한 자신감 넘치는 설명을 제시할 수도 있습니다. 하지만 AI가 자동으로 제공하지 못하는 것은 해당 변경 사항이 프로덕션(production) 환경에 적합하다는 증거입니다.
구현(implementation) 자체는 원래의 문제가 올바르게 정의되었는지 증명할 수 없습니다. 또한 그 변경 사항이 연결된 시스템들을 통해 얼마나 멀리 퍼져나갈지, 그 영향이 안전하게 되돌려질 수 있는지, 혹은 가정이 실패했을 때 어떻게 동작할지를 확립할 수 없습니다.
대규모 분산 시스템(distributed systems)에서 저는 겉보기에 사소해 보이는 변경 사항들—특히 재시도(retries), 스키마 업데이트(schema updates), 폴백 로직(fallback logic), 타임아웃 조정(timeout adjustments)—이 도입된 서비스의 범위를 훨씬 넘어서는 결과를 초래하는 것을 반복해서 목격했습니다.
따라서 프로덕션 변경 사항은 스스로를 방어해야 하는 기술적 주장(technical claim)으로 취급되어야 합니다. 왜 존재해야 하는지 설명해야 하고, 무엇에 영향을 미칠 수 있는지 식별해야 하며, 신뢰할 수 있는 되돌리기(reversal) 경로를 보존하고, 현실적인 실패를 견뎌내며, 이를 물려받을 엔지니어들이 이해할 수 있는 상태를 유지해야 합니다.
저는 이러한 평가를 다섯 가지 차원으로 구성합니다:
Reason(이유), Reach(영향 범위), Reversibility(가역성), Resilience(회복 탄력성), 그리고 Reviewability(검토 가능성).
이들은 함께 **5-R 리뷰(Five-R Review)**를 형성합니다.
코드를 생성하는 것이 쉬워지면서, 더 많은 코드를 생산하는 것은 더 이상 핵심 과제가 아닙니다. 더 어려운 문제는 다음을 결정하는 것입니다:
- 코드가 존재해야 하는가
- 설계가 주변 시스템에 적합한가
- 가정이 올바른가
- 변경 사항이 운영하기에 안전한가
- 팀이 6개월 후에도 이를 유지 관리할 수 있는가
AI는 구현 시간을 단축할 수 있지만, 프로덕션 변경 사항을 정당화해야 하는 의무를 없애주지는 않습니다. 구현이 더 빠르게 나타날수록, 그 가정과 영향 범위, 그리고 실패 동작을 더욱 명시적으로 검토해야 합니다.
작동하는 구현이 곧 프로덕션 준비가 된 것은 아니다
AI 어시스턴트는 해당 함수가 작동할 전체 환경을 이해하지 못한 채 기술적으로 유효한 함수를 생성할 수 있습니다.
겉보기에 간단해 보이는 요청을 가정해 봅시다:
다운스트림(downstream) 인벤토리 서비스가 타임아웃(timeout)될 때 자동 재시도(automatic retries) 기능을 추가하라.
구현 자체는 간단할 수 있습니다. 재시도 라이브러리를 추가하고, 지수 백오프(exponential backoff)를 설정하며, 실패를 로그로 남기고, 마지막 시도 후 에러를 반환하면 됩니다.
로컬 검증(local validation)을 통과했다고 해서 시스템 수준의 동작이 안전하다는 것이 입증되지는 않습니다. 바로 그 차이 때문에 구현 증거 수집(implementation evidence compilation), 테스트, 그리고 스타일 준수(style compliance)가 필요하지만, 그것만으로는 운영 환경 승인(production approval)에 충분하지 않은 것입니다.
하지만 숙련된 엔지니어는 즉시 다음과 같은 추가적인 질문들을 떠올립니다:
- 다운스트림 작업이 멱등성(idempotent)을 보장하는가?
- 재시도가 중복 예약을 생성할 수 있는가?
- 장애 발생 시 동시다발적인 재시도가 트래픽을 증폭시키지는 않는가?
- 타임아웃의 원인이 과부하(overload)인가, 데이터 손상(data corruption)인가, 아니면 네트워크 문제인가?
- 호출자(caller) 측에 자체적인 재시도 정책이 있는가?
- 메시지 브로커(message broker)가 요청을 다시 재생(replay)할 수 있는가?
- 첫 번째 호출은 성공했지만 응답이 유실된다면 어떻게 되는가?
- 지원 팀이 원래의 호출과 반복된 시도를 어떻게 구분할 것인가?
이 질문들 중 그 어느 것도 구문(syntax) 문제는 아닙니다.
이것들은 시스템 판단(system judgment) 문제입니다.
AI는 이러한 우려 사항들을 드러내는 데 도움을 줄 수 있지만, 조직이 이에 대해 올바른 답을 내놓았음을 자동으로 보장할 수는 없습니다. 결과물의 품질은 여전히 제공된 컨텍스트(context), 변경 사항을 둘러싼 아키텍처(architecture), 그리고 권장 사항을 평가하는 엔지니어에게 달려 있습니다.
로컬 수준에서 올바른 구현이라 할지라도 전역적으로는 잘못된 시스템을 만들 수 있습니다.
작동하는 코드와 방어 가능한 변경 사이의 증거 격차 (Evidence Gap)
AI 지원 개발은 팀이 해당 구현이 주변 시스템에 적절한지 판단하는 속도보다 더 빠르게 구현물을 만들어낼 수 있습니다.
이는 증거 격차(evidence gap)를 발생시킵니다. 즉, 코드가 통제된 조건 하에서 작동함을 보여주는 것과, 결과로 나타나는 변경 사항이 운영 환경(production)에서 안전하고, 필요하며, 운용 가능하고, 유지보수 가능한지를 보여주는 것 사이의 차이를 의미합니다.
모든 생성된 풀 리퀘스트 (pull request)는 여전히 가정 (assumptions), 의존성 (dependencies), 실패 동작 (failure behavior), 롤아웃 전략 (rollout strategy), 소유권 (ownership), 그리고 복구 (recovery)에 대한 의사결정을 필요로 합니다. 구현 (implementation)을 더 빠르게 생성한다고 해서 이러한 의사결정이 사라지는 것은 아닙니다. 단지 그 결정을 제대로 내릴 수 있는 가용 시간을 압축할 뿐입니다.
이제 팀은 더 많은 프로토타입 (prototypes), 더 많은 풀 리퀘스트 (pull requests), 더 많은 생성된 테스트 (generated tests), 그리고 더 많은 대안적 구현 (alternative implementations)을 만들어낼 수 있습니다. 하지만 생성된 모든 변경 사항은 여전히 다음과 같은 의사결정을 만들어냅니다:
- 수락 (Accept it)
- 거절 (Reject it)
- 수정 (Modify it)
- 추가 테스트 (Test it further)
- 재설계 (Redesign it)
- 신중한 배포 (Deploy it cautiously)
- 롤백 (Roll it back)
생성 속도가 증가함에 따라, 의사결정의 수는 팀이 이를 평가할 수 있는 능력보다 더 빠르게 증가할 수 있습니다.
생성된 변경 사항의 양이 증가함에 따라, 평가 부담은 다운스트림 (downstream)으로 이동합니다. 아키텍처 리뷰 (architecture reviews), 풀 리퀘스트 (pull requests), 테스트 환경 (testing environments), 배포 제어 (deployment controls), 그리고 운영 (production operations)은 구현 속도가 제거하지 못한 의사결정들을 흡수해야만 합니다.
팀은 더 많은 코드를 생성하기 때문에 더 빨라진 것처럼 보일 수 있습니다. 하지만 동시에, 그 코드를 리뷰 (reviewing), 통합 (integrating), 검증 (validating), 그리고 운영 (operating)하는 데 있어서는 더 느려질 수 있습니다.
병목 현상 (bottleneck)의 위치가 이동한 것입니다.
병목은 더 이상 에디터 (editor)에만 머물지 않습니다. 이제는 아키텍처 리뷰 (architecture reviews), 풀 리퀘스트 (pull requests), 테스트 환경 (test environments), 관측성 시스템 (observability systems), 배포 파이프라인 (deployment pipelines), 그리고 운영 (production operations) 전반에 걸쳐 분산되어 있습니다.
생성된 출력물은 활동의 증거일 뿐, 준비 상태의 증거가 아니다
AI 보조 환경에서는 전통적인 생산성 측정 방식이 위험해질 수 있습니다.
코드 라인 수 (Lines of code)는 공학적 가치를 측정하는 신뢰할 수 있는 지표였던 적이 없습니다. 수천 줄의 코드가 거의 즉각적으로 생성될 수 있는 환경에서는 그 의미가 더욱 퇴색됩니다.
동일한 문제가 다른 출력 중심의 지표들에도 적용됩니다:
- 풀 리퀘스트 (pull requests) 수
- 커밋 (commits) 수
- 완료된 스토리 포인트 (Story points completed)
- 생성된 기능 (Features created)
- 생성된 테스트 (Tests generated)
- 종료된 작업 (Tasks closed)
이러한 측정치들은 활동 (activity)을 설명합니다. 그것들이 반드시 개선 (improvement)을 설명하는 것은 아닙니다.
운영 복잡성 (operational complexity)을 증가시키는 생성된 기능이 자동으로 진보를 의미하는 것은 아닙니다. 모의된 동작 (mocked behavior)은 검증하지만 통합 실패 (integration failures)를 놓치는 대규모 테스트 스위트 (test suite)는 잘못된 확신을 심어줄 수 있습니다. 빠르게 생성된 추상화 (abstraction)는 한 명의 개발자를 더 빠르게 만들 수 있지만, 전체 시스템을 이해하기 더 어렵게 만들 수도 있습니다.
구현 (implementation)이 더 효율적으로 생성될수록, 팀은 완료된 코드와 방어 가능한 프로덕션 변경 (production change)을 더욱 신중하게 구분해야 합니다.
더 나은 질문은 다음과 같습니다:
팀이 얼마나 많은 코드를 생성했는가?가 아닙니다.
그것은 다음과 같습니다:
팀이 얼마나 많은 고품질의 엔지니어링 결정 (engineering decisions)을 신뢰할 수 있는 결과물로 성공적으로 전환했는가?
5-R 리뷰: 변경 사항이 프로덕션에서의 입지를 방어하는 방법
AI가 생성하거나 AI의 도움을 받은 변경 사항을 평가하기 위해, 저는 다섯 가지 차원을 사용합니다: 이유 (Reason), 범위 (Reach), 가역성 (Reversibility), 회복 탄력성 (Resilience), 그리고 검토 가능성 (Reviewability).
1. 이유 (Reason)
왜 이 변경 사항이 존재해야 합니까?
답변은 단순히 요청된 구현 (implementation)이 아니라, 문제를 설명해야 합니다.
취약한 이유:
성능 향상을 위해 Redis 캐싱 (caching)을 추가함.
더 강력한 이유:
제품 상세 (Product-detail) 요청이 고지연 (high-latency) 종속성 (dependency)으로부터 동일한 참조 데이터를 반복적으로 가져오고 있음. 해당 종속성은 응답 시간에 크게 기여하며, 데이터는 5분 정도의 신선도 저하 (staleness)를 허용할 수 있음.
더 강력한 설명은 제안된 솔루션을 테스트 가능하게 만듭니다. 또한 팀이 로컬 캐싱 (local caching), 요청 통합 (request consolidation), 사전 계산 (precomputation), 또는 종속성 최적화 (dependency optimization)와 같은 대안을 고려할 수 있게 합니다.
AI는 명확하게 정의된 솔루션을 구현하는 데 매우 효과적입니다. 하지만 근본적인 문제가 올바르게 정의되지 않았을 때는 신뢰도가 떨어집니다.
2. 범위 (Reach)
변경 사항이 시스템을 통해 얼마나 멀리 퍼질 수 있습니까?
다섯 줄의 설정 업데이트가 수십 개의 서비스에 영향을 미칠 수 있습니다. 스키마 (schema) 변경은 프로듀서 (producers), 컨슈머 (consumers), 분석 작업 (analytics jobs), 데이터 보관 정책 (data-retention policies), 그리고 장애 대응 절차 (incident-response procedures)에 영향을 줄 수 있습니다.
범위 (Reach)에는 다음이 포함됩니다:
- 직접 의존성 (Direct dependencies)
- 간접 소비자 (Indirect consumers)
- 공유 인프라 (Shared infrastructure)
- 데이터 계약 (Data contracts)
- 보안 경계 (Security boundaries)
- 운영 프로세스 (Operational processes)
- 고객 대면 동작 (Customer-facing behavior)
코드 변경의 크기가 폭발 반경 (blast radius)을 결정하지는 않습니다.
짧은 변경이라도 엄청난 범위 (reach)를 가질 수 있습니다.
3. 가역성 (Reversibility)
변경 사항을 안전하게 되돌릴 수 있습니까?
팀들은 배포 (deployment)에 대해서는 자주 논의하지만, 롤백 (rollback)을 설계하는 데에는 시간을 덜 할애하곤 합니다.
가역적인 변경을 위해서는 다음이 필요할 수 있습니다:
- 기능 플래그 (Feature flags)
- 하위 호환 가능한 스키마 (Backward-compatible schemas)
- 버전 관리된 API (Versioned APIs)
- 이중 읽기 또는 쓰기 (Dual reads or writes)
- 점진적 트래픽 마이그레이션 (Gradual traffic migration)
- 데이터 복구 절차 (Data-repair procedures)
- 명시적인 롤백 검증 (Explicit rollback validation)
롤백은 이전 상태를 마법처럼 복구하는 버튼으로 취급되어서는 안 됩니다. 데이터가 변경되고, 이벤트가 방출되거나, 고객이 조치를 취한 후에는 이전 소프트웨어 버전으로 돌아가는 것이 이전의 시스템 상태를 복구하지 못할 수도 있습니다.
가장 안전한 변경은 배포가 시작되기 전에 되돌릴 수 있도록 설계된 것입니다.
4. 회복 탄력성 (Resilience)
가정(assumptions)이 실패했을 때 변경 사항이 어떻게 동작합니까?
해피 패스 (Happy-path) 구현은 생성하기가 점점 더 쉬워지고 있습니다. 프로덕션 엔지니어링 (Production engineering)은 주로 해피 패스 이외의 모든 것에 관한 것입니다.
다음과 같은 상황에서 어떤 일이 발생하는지 질문해 보십시오:
- 의존성 (dependency)이 느려질 때
- 응답이 두 번 도착할 때
- 메시지가 순서대로 처리되지 않을 때
- 배포가 부분적으로만 완료되었을 때
- 데이터가 오래되었을 때 (stale)
- 자격 증명 (credentials)이 만료될 때
- 특정 리전 (region)을 사용할 수 없게 될 때
- 요청은 성공했지만 승인 (acknowledgment)이 실패할 때
- 운영자가 수동으로 개입할 때
AI가 생성한 솔루션에는 재시도 (retries), 폴백 (fallbacks) 또는 예외 처리 (exception handling)가 포함될 수 있습니다. 그것이 해당 메커니즘이 비즈니스 운영에 적합하다는 것을 의미하지는 않습니다.
회복 탄력성 (Resilience)은 방어적인 코드의 존재 여부가 아닙니다. 그것은 장애 상황에서도 시스템이 수용 가능한 동작을 유지할 수 있는 능력입니다.
5. 검토 가능성 (Reviewability)
다른 엔지니어가 변경 사항을 이해하고 검증할 수 있습니까?
생성된 코드는 종종 세련되어 보입니다. 가독성 (readability)을 정확성 (correctness)으로 오해할 수 있기 때문에, 이는 코드를 더 위험하게 만들 수 있습니다.
검토 가능한 변경 사항은 그 의도와 가정을 명확히 드러내야 합니다.
다음 질문들에 답할 수 있어야 합니다:
- 어떤 문제를 해결하고 있는가?
- 어떤 대안들이 고려되었는가?
- 어떤 서비스와 데이터 흐름 (data flows)이 영향을 받는가?
- 무엇이 실패할 수 있는가?
- 변경 사항을 어떻게 모니터링할 것인가?
- 성공을 입증할 증거는 무엇인가?
- 가정이 틀렸을 경우 팀은 어떻게 복구할 것인가?
목표는 모든 작은 변경 사항에 대해 더 많은 문서를 만드는 것이 아닙니다. 목표는 설명의 복잡성이 결정의 위험도와 일치하도록 보장하는 것입니다.
Pull Request는 변경 사항에 대한 근거를 제시해야 합니다
AI가 구현 (implementation)에 더 많이 기여함에 따라, 풀 리퀘스트 (pull request)는 변경 사항 뒤에 숨겨진 엔지니어링 근거를 더욱 명시적으로 나타내야 합니다. 검토자 (reviewers)가 생성된 코드만으로 목적, 가정, 영향 범위 (blast radius), 그리고 복구 전략을 재구성해야 해서는 안 됩니다.
유용한 AI 지원 풀 리퀘스트에는 다음과 같은 구조화된 정보가 포함될 수 있습니다:
change_intent:
problem: "참조 데이터 검색 시 발생하는 반복적인 지연 시간 (latency)"
expected_outcome: "신선도 (freshness) 요구 사항에 영향을 주지 않으면서 p95 응답 시간 단축"
...
이것이 모든 저장소 (repository)에 대해 의무적인 관료주의가 될 필요는 없습니다.
하지만 중요한 운영 환경 (production) 변경 사항의 경우, 이러한 정보는 검토자가 생성된 코드로부터 의도를 재구성하는 데 모든 시간을 소비하는 대신, 결정을 평가하는 데 도움을 줍니다.
숙련된 엔지니어가 방어 가능한 변경 사항에 필요한 요건을 정의할 것입니다
AI가 숙련된 엔지니어를 무용지물로 만들지는 않습니다. AI는 그들의 경험이 가치를 창출하는 방식을 변화시킵니다.
시니어 엔지니어들은 점점 더 다음과 같은 사항들을 정의해야 할 것입니다:
- 아키텍처 제약 사항 (Architectural constraints)
- 허용 가능한 구현 패턴 (Acceptable implementation patterns)
- 데이터 소유권 (Data ownership)
- 보안 경계 (Security boundaries)
- 신뢰성 기대치 (Reliability expectations)
- 배포 정책 (Deployment policies)
- 평가 기준 (Evaluation criteria)
- 에스컬레이션 조건 (Escalation conditions)
다시 말해, 시니어 엔지니어들은 모든 함수를 정확히 어떻게 작성해야 하는지 설명하는 데 쓰는 시간을 줄이는 대신, 구현이 안전하게 이루어질 수 있는 환경을 정의하는 데 더 많은 시간을 할애하게 될 것입니다.
이는 코드를 위한 지침(instructions)을 작성하는 것에서 시스템을 위한 정책(policies)을 수립하는 것으로의 전환입니다.
예를 들어, 조직은 모든 재시도(retry) 구현을 개별적으로 검토하는 대신 다음과 같은 규칙을 수립할 수 있습니다:
- 멱등성(idempotent)이 보장되는 작업만 자동으로 재시도할 수 있음
- 재시도 예산(Retry budgets)은 호출 체인(call chains) 전체에서 공유되어야 함
- 백오프(Backoff)와 지터(jitter) 적용이 필수임
- 특정 과부하(overload) 조건에서는 재시도를 중단해야 함
- 모든 재시도 가능한 작업은 시도 수준의 메트릭(attempt-level metrics)을 노출해야 함
- 재무적 영향이 있는 비즈니스 작업은 중복 제거(deduplication) 제어가 필요함
그러면 AI 도구는 이러한 경계 내에서 코드를 생성할 수 있습니다. 이러한 경계를 설정하는 목적은 AI 보조 개발(AI-assisted development)을 제한하려는 것이 아닙니다. 승인에 필요한 증거를 예측 가능하고 반복 가능하게 만들기 위함입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기