이제 코드 품질은 에이전트(agent)에 어떤 제약을 설정하느냐에 달려 있다.
오랫동안 코드 품질은 코드 리뷰를 통해 검증되어 왔다. 누군가가 작성된 코드를 직접 읽고, 그것이 깔끔하고 논리적이며, 성능이 좋고, 이해하기 쉽고, 테스트가 잘 갖춰져 있는지 확인하는 방식이었다. 하지만 에이전트 환경에서는 이 접근법이 잘 통하지 않는다. 사람이 일일이 읽기에는 코드 양이 너무 많다. 그 결과, 품질 검증의 상당 부분은 에이전트를 둘러싼 하네스(harness), 환경, 운영 체제 안에서 이루어져야 한다. 나도 여전히 코드를 직접 읽고 리뷰하지만, 어떤 제약이 검증 역할을 대신할 수 있는지 신중하게 따져가며 판단한다.
이제 소프트웨어 품질은 에이전트에 어떤 제약을 설정하느냐에 달려 있다.
제약이란 테스트와 결정론적 조건을 통해 에이전트가 제안한 내용의 허용 범위를 규정하는 것이다. 이러한 제약을 설정하고 유지함으로써, 에이전트가 하루에 수십만, 수백만 건의 변경을 만들어내는 환경에서도 안정적으로 고품질 프로덕션 소프트웨어를 제공하는 루프를 구축할 수 있다.
이러한 제약을 우리는 품질 게이트(quality gate)라고 부르며, 다양한 형태로 존재한다. 일반적인 단위 테스트, 속성 테스트, 인수 테스트가 여기에 해당한다. 변이 테스트(mutation testing)도 포함되는데, 코드의 변형을 생성한 뒤 동일한 테스트를 돌려 놓친 버그가 몰래 끼어들지 않았는지 확인하는 방식이다. 순환 복잡도(cyclomatic complexity)나 줄 길이처럼 코드 가독성을 유지하는 데 도움이 되는 품질 지표도 제약에 포함된다. 제약은 시스템이 어떤 제안을 코드 변경으로 수락하고 적용할지 결정하는 데에도 중요한 역할을 한다. 에이전트를 실행하는 인터프리터에서 에이전트 컨트롤러를 거쳐 프로덕션으로 나가기까지, 변경 제안은 충분한 검증을 거쳐야 비로소 안전하게 배포 가능하고, 그 변경의 영향 범위가 에이전트의 작업 범위 안에 있음을 확인할 수 있다.
에이전트는 무엇이든 제안할 수 있다. 그 제안이 팀과 함께 배포하기에 충분히 안전하고, 정확하며, 범위에 맞고, 유용한지는 제약이 결정한다.
이 모델이 제공하는 것은 많지만, 빠진 부분도 적지 않으며 이는 지금 당장 짚어볼 만한 문제다. 한 가지 문제는 자율성이다. 에이전트는 의도한 바를 잘 수행할 수 있지만, 정보가 부족하거나 수행해야 할 작업이 모호한 경우에는 실패하기 쉽다. 이는 작업 자체에 대해서도 그렇고, 하네스·환경·기타 구성 요소에 의해 매개변수화되는 방식에 대해서도 마찬가지다. 사람이 좋은 코드를 배포하지 못하는 많은 이유는 에이전트에도 동일하게 적용된다. 스크립트 기반 부하에 버티지 못하는 취약한 환경, 비결정론적 빌드, 권한 누락, 부실한 테스트가 그런 예다. 이런 문제들이 동기가 되어, 에이전트에게 신뢰할 수 있는 피드백을 제공하고, 실패하더라도 피해가 적으며, 점진적으로 성공을 쌓아갈 수 있는 더 나은 환경의 필요성이 높아진다.
우리가 지향하는 환경은 에이전트가 실질적인 작업을 수행하고, 신뢰할 수 있는 피드백을 받으며, 실패하더라도 큰 피해 없이 넘어갈 수 있는 환경이다.
또 다른 중요한 문제는 신뢰다. 아무리 뛰어나고 견고한 현대적 에이전트라 해도, 정확성을 검증하지 않은 채 의도를 그대로 위임할 수는 없다. 신뢰는 출발점이지만, 그것은 반드시 충분히 검증된 신뢰여야 한다.
어떤 제약은 작업이 시작되기 전에 방향을 잡아주고, 어떤 제약은 에이전트가 작업하는 동안 피드백을 제공한다. 그리고 어떤 제약은 그 결과물이 프로덕션 경계를 넘어갈 수 있는지 없는지를 최종 결정한다.
시스템 주변에 검증 구조를 구성하는 방법은 다양하다.
내 경험상, 단위 테스트에만 의존하기보다 폭넓게, 그러나 신중하게 선별된 검사 항목을 제약으로 갖추는 것이 효과적이다. 핵심은 각 검사가 타입 안전성과 성능에서 보안 스캐닝에 이르기까지 저마다 명확한 책임 영역을 갖는다는 점이다. 팀은 ESLint 같은 린팅 도구로 강제할 수 있는 아키텍처 규칙 등 자체적인 제약을 정의할 수도 있다. 이런 도구 중 상당수에는 문제가 발생했을 때 에이전트나 사람을 끌어들일 수 있는 내장 훅이 있다.
현재로서는, 에이전트 출력이 유용한 결과물이 되느냐 쓰레기가 되느냐는 여전히 루프를 운영하는 팀의 역량에 크게 달려 있다.
AI는 대규모 코드 생성과 빠른 속도를 가능하게 하지만, 그만큼 사람이 모든 변경 사항을 검토하기 어려워진다. 따라서 사람의 주의를 어디에 집중시킬지 의도적으로 설계해야 한다. 기계 속도로 돌아가는 시스템에 사람의 검토 단계를 끼워 넣으면 생산성이 저하되는 것은 당연한 결과다. 사람의 주의는 희소하고 소중한 자원이므로, 판단이 실제로 필요한 가장 미묘한 문제에 집중할 수 있도록 선제적으로 방향을 잡아줘야 한다. 자동화된 제약 가드레일이 깨졌을 때에만 담당자를 호출해야 한다.
미래의 "코드 리뷰"는 지금과는 전혀 다른 모습일 것이다
정확성은 중요한 품질 차원 중 하나지만, 유지보수성, 성능, 보안, 효율성, 가독성 역시 중요하게 다뤄야 할 요소다. 정확성이 여러 신호 유형으로 분해되듯, 나머지 품질 요소들도 마찬가지다. 제약의 수도 중요하지만, 그 제약이 품질 기준과 프로덕션 준비 요건을 충분히 충족시킬 만큼 엄격한지가 더 중요하다.
소프트웨어 품질은 단일 지표가 아니다. 팀마다 중요도가 다른 여러 신호들의 집합으로 봐야 한다.
역압(back-pressure)은 다양한 도구를 통해 구현할 수 있다. 컴파일러가 잘못된 코드를 거부하거나, 테스트가 실패하거나, 보안 정책이 나쁜 관행을 차단하거나, CI가 배포를 거절하는 방식이 그 예다. 이상적으로는 모든 작업이 끝난 뒤 단 한 번의 리뷰로 끝나는 것이 아니라, 루프 전체에 걸쳐 역압이 존재해야 한다.
제약과 역압은 에이전트가 문제가 되기 전에 잘못된 작업을 스스로 잡아낼 수 있게 한다
변경량이 도구가 처리할 수 있는 수준을 초과해 제약을 적용하지 못하는 상황이 되면 어떻게 될까? 결국 큐가 쌓이고, 사람의 속도로 움직이는 검증 시스템에 의존하게 된다. 확장성을 확보하려면, 마지막까지 기다리지 말고 검증 루프 전체에 걸쳐 최대한 많은 작업을 밀어 넣어야 한다. 자동화된 검사 안에서 확장이 가능하다면, 전체 배포 시스템의 속도와 처리량을 높일 수 있다. 검증 루프에 여유가 없어지면, 다음 중 하나를 선택해야 한다. 첫째, 검증 시스템을 확장하여 들어오는 변경에 제약을 가하고 피드백을 줄 수 있는 용량을 늘린다. 둘째, 에이전트의 변경 생성 속도를 줄여 검증이 작업량을 따라잡을 수 있게 한다. 셋째, 품질 기준을 낮춰 검증이 이전만큼 강하게 역압을 가하지 않도록 한다. 확장 관점에서는 이 모든 방법을 취할 수 있는 준비가 필요하다. 동시에, 일부 방향에서 제약을 푸는 것이 오히려 더 많은 것을 달성하게 해준다는 점도 놓치지 말아야 한다. 에이전트 개발자 군집이나 자동화된 소프트웨어 팩토리를 활용해 우리가 일일이 검토하지 않아도 변경을 만들어낼 수 있게 한다면, 에이전트 기반 변경 속도를 높일 수 있을지도 모른다.
어떤 영역에서는 에이전트에게 더 많은 자유를 주되, 다른 영역에서는 더 엄격한 제약을 유지하는 방식도 가능하다. 가장 중요한 곳에 엄격한 제약을 집중시키면, 품질을 희생하지 않고도 처리량을 극대화할 수 있다. 이 모든 결정에는 다양한 선택지가 있다. 가장 명확한 것은 품질의 여러 차원 사이에서 균형을 잡아야 한다는 점이다. 보안이 매우 중요하다는 사실은 분명하지만, 보안 확보와 제때 제품을 출시하는 것 사이에서도 균형을 맞춰야 했다. 혁신 중심에서 품질 중심까지 스펙트럼이 존재하고, 그 어딘가에서 우리 자신이 어디에 있고 싶은지를 선택해야 한다.
환경과 시스템에서 에이전트나 팀에게 명확한 피드백을 전달해야, 사람들이 취향, 의도, 아키텍처처럼 더 주관적인 문제에 집중할 수 있다. 사람들이 안전한 제약 범위 안에 머물 수 있도록 도우면, 무언가 잘못된 지점을 찾아 헤매는 수고를 줄일 수 있다.
소프트웨어 품질은 정확성 이상을 포함한다. 유지보수성, 성능, 보안, 효율성, 이해하기 쉬움 역시 소프트웨어 품질의 일부다. 이러한 기준을 충족하고 프로덕션 흐름을 유지하도록 돕는 모든 제약은 배포 파이프라인에 역압을 만들어낸다.
어디에 강한 제약을 적용하고 어디서 완화하거나 제거할지는 의도적으로 결정해야 한다. 두 가지 목표를 모두 충족하는 제약은 강하게 유지하고, 어느 쪽도 충족하지 못하는 제약은 지탱할 필요가 없다. 상황에 따라 기준을 올리거나 내릴 준비도 되어 있어야 한다. 그리고 소프트웨어 시스템 각 지점에 존재하는 이 제약들이 바로 소프트웨어 품질을 실제로 강제할 수 있게 만드는 요소임을 기억해야 한다.
이 이중 목적에 가장 잘 부합하는 곳에는 강한 제약을 적용하고, 어느 쪽 목적도 제대로 충족하지 못하는 제약은 완화하거나 제거하는 것을 고려해야 한다. 또한 필요에 따라 품질 기준을 올리거나 내릴 준비도 해야 한다. 결국, 소프트웨어 시스템 곳곳에 존재하는 이 제약들이 품질에 실질적인 강제력을 부여한다. 많은 경우, 새로운 도구를 도입하거나 기존 도구를 강화해 역압과 제약을 더 많이 만들어낼 수 있다. 이 모든 것은 대부분의 변경 요청에 반발력으로 작용할 수 있다. 파이프라인 전반에 걸쳐 이를 구축해야 하며, CI 시스템이 문제를 고치지 않으면 배포를 허용하지 않겠다고 알려주는 파이프라인 말단까지 기다려서는 안 된다. 가능한 모든 경로를 통해, 최대한 빨리 이 신호들을 활용해야 한다. 이 시스템에서 궁극적인 제약은 시스템을 구축하고 운영하면서 내린 결정과 행동에 스스로 책임을 지겠다는 의지다. 하지만 다른 모든 제약과 마찬가지로, 우리 자신의 판단이 얼마나 강하게 억제하고, 역압을 가하고, 최종 점검 역할을 할지에 대해서도 신중한 균형을 맞춰야 한다.
품질은 에이전트 주변에 우리가 설정하는 제약 안에 있다. 여러분 자신의 앱에서 품질을 고민하고 있다면, 이 문제 정의를 바탕으로 제약 중심의 계획을 직접 세워보길 바란다.
이 글은 Pangram 4에 의해 100% 인간 작성으로 평가되었습니다.
이 글은 원래 제 Substack에 게재된 글입니다.