dev.to

The Epistemology of Quality

품질을 어떻게 알 수 있는가

AI가 작성한 코드에 사람의 리뷰를 의무화하는 관행을 비판적으로 살펴봅니다. 코드 리뷰는 결함을 찾는 약한 증거일 수 있으며, 서로 다른 방식과 기준으로 검증하는 독립적인 검사들을 조합하고 사람이 요구사항과 정답의 기준을 정하는 일이 품질 보증에 더 중요하다고 주장합니다.

에디터 노트

검사 수를 늘리는 것보다 각 검사가 같은 요구사항을 잘못 이해할 가능성을 줄이는 일이 더 중요합니다. 글은 코드와 테스트가 같은 모델에서 나올 때 통과 결과가 독립된 증거가 아닐 수 있다고 설명하고, 사람이 정답의 기준을 정해야 한다고 주장합니다. 이 구분은 리뷰 승인과 책임을 동일시하는 관행을 짚는 데 설득력이 있습니다. 댓글에서는 독립성 주장을 변이 테스트로 측정하자는 제안이 나왔고, 인수 기준이 사용자 결정인지 구현 중 추론인지 PR에 드러내자는 의견도 있습니다. 이런 제안은 글의 주장을 실제 검사 설계로 옮기는 보완점입니다. 다만 변이 테스트는 의도하지 않은 코드 결함을 확인할 뿐, 잘못된 요구사항을 검증하는 문제까지 해결하지 못한다는 점도 댓글이 짚습니다. 글이 제안한 품질 검사의 조합은 정답 기준을 세우는 사람의 판단과 함께 평가해야 합니다.

AI 요약

AI가 작성한 코드를 사람이 리뷰해야 하는지를 묻는 데서 출발해, 소프트웨어가 올바른지 판단할 때 각 검사가 어떤 증거를 제공하는지 따져봅니다. 현실의 시스템은 요구사항이 완전히 명세되지 않고 의존성, 동시성, 예상하지 못한 실패도 얽혀 있어 정확성을 증명하기 어렵습니다. 따라서 단일 검사 결과를 증명으로 여기는 대신 여러 종류의 증거를 모아 판단해야 한다는 것이 글의 출발점입니다.

코드 리뷰가 바뀌며 얻은 것과 잃은 것

저자는 보험사에서 일하던 시절, 코드를 출력해 개발자들이 한자리에 모여 빨간 펜으로 검토한 경험을 설명합니다. 이 방식은 코딩 표준과 문법을 확인하고 여러 개발자가 코드를 함께 보는 데는 도움이 됐지만, 요구사항을 충족하는지 확인하거나 테스트하는 과정은 별도 테스트 팀의 몫이었습니다. 리뷰 분위기는 가르침보다 표준 위반을 지적하는 심문에 가까웠다고 합니다.

이후 개발자에게 품질 책임을 맡기면서 단위 테스트, 커버리지 분석, 동료 리뷰를 도입했습니다. 리뷰에서는 기능이 실행되는 모습을 보고 테스트 결과와 커버리지를 확인한 다음 코드를 읽었습니다. 저자는 여러 종류의 증거를 결합한 이 방식이 코드만 읽는 리뷰보다 강하다고 봅니다. 특히 테스트는 실행 결과를, 리뷰는 구조에 대한 판단을 보탤 수 있지만 결함을 찾는 데는 코드 읽기가 가장 약한 신호였다고 설명합니다.

오픈소스 개발과 분산된 팀에서는 Pull Request(PR)가 코드 변경을 검토하는 기본 단위가 됐습니다. PR은 변경분을 보여주지만, 저자는 diff만 훑는 방식이 코드 실행 여부를 확인하거나 구현 방식을 함께 논의하던 기회를 줄였다고 지적합니다. CI/CD의 빌드, 단위 테스트, 커버리지 보고서, 정적 분석 도구가 검증의 상당 부분을 맡게 됐으며, 사람이 코드를 눈으로 읽는 일은 개발자 교육과 협업에는 쓸모가 있어도 결함 탐지에는 약하다는 주장입니다.

검사 결과는 증거이지 증명이 아닙니다

AI 코딩에서 특히 문제가 되는 현상은 테스트 과적합입니다. 모델이 테스트를 통과하는 코드를 만들면서 테스트가 의도한 요구사항을 놓칠 수 있습니다. 테스트 열 개가 모두 통과해도 실제 사용자는 기능이 명백히 잘못됐다고 느낄 수 있습니다. 글은 이를 줄이기 위해 앱을 직접 실행해 사용해보고, 같은 코드 경로를 여러 입력값으로 검사하는 방법을 제안합니다.

코드와 테스트를 같은 모델이 만들면 요구사항을 잘못 이해한 내용이 둘에 함께 반영될 수 있습니다. 테스트가 통과해도 독립된 검증이 아니라 같은 오해를 두 번 표현한 결과일 수 있다는 설명입니다. 그래서 글은 검사 간 독립성을 강조합니다. 독립성은 작성자, 검사 방법, 정답의 기준인 오라클(oracle) 세 측면에서 따질 수 있으며, 특히 오라클의 독립성이 중요하다고 봅니다. 잘못된 요구사항을 기준으로 삼으면 독립적인 테스트도 잘못된 것을 검사하기 때문입니다.

글은 빌드와 타입 검사, 단위 테스트, 커버리지 분석, 기능 및 통합 테스트, 앱 수동 실행, 정적 분석, 의존성 및 복잡도 분석, 보안 검사, 부하 테스트, AI 코드 리뷰와 AI 인수 테스트, 프로덕션 관측을 각각 다른 증거로 나열합니다. 단위 테스트는 지정된 사례의 동작을 확인하고, 커버리지는 테스트가 실행한 코드 영역을 보여주지만 검증의 의미까지 보장하지 않습니다. 정적 분석과 보안 검사는 알려진 유형의 문제를 찾고, 부하 테스트는 다른 검사로 확인하지 못하는 스트레스 상황을 다룹니다. 프로덕션 관측은 배포 이후에만 얻을 수 있는 증거도 있음을 보여줍니다.

사람의 책임을 리뷰 승인에서 기준 설정으로

저자는 AI가 만든 코드에 사람의 리뷰를 의무화하는 관행이 품질 보증에 충분한지 의문을 제기합니다. 그럴듯하고 빌드와 테스트를 통과한 코드의 개념적 오류는 GitHub에서 diff를 빠르게 읽는 방식으로 찾기 어렵고, 제대로 검토하려면 요구사항과 전체 설계, 구현 세부사항을 이해해야 하므로 코드 작성에 가까운 시간이 들 수 있다고 주장합니다. AI가 코드를 빠르게 늘리면 사람의 리뷰는 병목이 되고, 승인자가 결함을 책임지게 하는 방식도 실제로 확인할 수 있는 범위를 넘어선 책임을 지운다고 봅니다.

대신 사람은 무엇을 올바른 동작으로 볼지 요구사항과 인수 기준을 정하고, 조직은 독립적인 검사 체계를 설계하고 유지해야 한다고 말합니다. 린터, 테스트, 의존성 분석과 보안 검사를 AI 에이전트가 작업하는 동안 자주 실행하고, PR은 각 검사의 결과를 정리하는 최종 관문으로 두자는 제안입니다. 품질을 변경 용이성으로 평가할 때는 새 요구사항을 처음 보는 에이전트에게 맡겨 올바른 변경에 든 노력, 회귀 발생률, 드러난 결합도를 비교할 수 있다고 덧붙입니다.

dev.to 반응

  • @wiseai — 공유해줘서 고맙습니다. AI가 쓴 코드를 사람이 리뷰해야 하느냐고요? 물론 그래야 하며, 경우에 따라 다릅니다. 저처럼 AI가 쓴 코드를 검토하는 시스템을 만들고, 그 검토자를 다시 검토하는 계층을 더해 소프트웨어 구축과 관리를 이어가는 사람도 있습니다. 게다가 왜 AI가 쓴 코드를 사람의 코드와 다르게 취급하나요? 사실상 차이가 없습니다. AI나 직접 만든 LLM 시스템을 원하는 방식으로 말하고 행동하도록 조정할 수 있고, 어느 정도 오차는 감수하면 됩니다. 우리는 산업 시대에 살고 있습니다. AI가 만든 코드와 사람이 만든 코드를 다르게 취급하는 건 진보 측면에서 큰 손해입니다. 코드가 누가 썼는지와 상관없이 제대로 실행된다면 작성자보다 결과에 초점을 맞춰야 합니다. 물론 이렇게 하면 사회에서 사람들과 어울리며 무언가를 만드는 즐거움은 사라집니다. 하지만 한 사람이 조직이나 회사 전체를 만드는 방향으로 가고 있습니다. 코드가 올바른지 어떻게 아느냐고요? 단위 테스트와 통합 테스트면 충분합니다. AI가 내부 코드의 논리와 작동 방식을 추상화하므로 블랙박스 테스트가 길입니다. 소프트웨어에 입력을 주고 출력을 관찰한 다음, 허용 오차가 있는 비결정적 시스템이라면 출력이 맞아질 때까지, 결정적 시스템이라면 같은 출력이 나올 때까지 AI가 스스로 수정하게 하면 됩니다. AI 시대에 사람의 리뷰가 왜 그렇게 중요하냐고요? LLM이 기본 설정에서 비결정적이기 때문이지만, 매개변수를 조절해 원하는 대로 결정적으로 만들 수 있습니다. 신뢰가 이유 중 하나라는 말은 맞습니다. LLM은 환각을 일으키며 내용을 지어냅니다. 제 생각에는 환각은 버그가 아니라 기능입니다. 환각이 많을수록 토큰을 더 쓰고 LLM 제공업체가 더 많은 돈을 버는 사업 모델의 기능입니다. 환각은 소프트웨어의 새 기능을 제안하기도 합니다. 그중에는 앞으로 만들 만한 그럴듯한 기능도 있습니다. 주관적인 리뷰보다 객관적인 품질 측정이 필요하다는 데 동의합니다. 코드 작성자가 누구인지에 너무 집중하지 않아도 됩니다. 사람이 코드를 검토할 수는 있습니다. 다만 의무적인 사람 리뷰가 품질을 보장하는 체계적으로 타당한 방법인지가 질문입니다. 시스템 종류에 따라 다릅니다. 결정적인 시스템이라면 사람 리뷰가 필수입니다. 그 정도의 정확성이 필요하지 않은 생성형 시스템이라면 AI로 그 계층을 추상화할 수 있습니다. 결국 회사가 제품 품질을 신경 쓴다면 사람을 고용해 소프트웨어를 만들고, 품질을 신경 쓰지 않는다면 사람의 개입 없이 모든 과정을 AI에 맡기면 됩니다. 계속 잘하세요. 또 봐요.
  • @debtdraft — 좋은 글입니다.
  • @izgorodin — 오라클의 독립성이 가장 중요하다는 점이 글에서 가장 날카로운 주장입니다. 독립적인 검사가 늘어날수록 신뢰가 빠르게 커진다는 주장도 가정하지 않고 측정할 수 있습니다. 최근 변경 몇 개를 골라 바뀐 코드 줄에 작은 결함을 심어보세요. 비교 연산을 뒤집거나 반복문 경계를 바꾸거나 널 검사를 지우는 식입니다. 단위 테스트, 정적 분석, AI 리뷰, 브라우저를 조작하는 에이전트를 각각 실행해 결함을 찾는지 확인합니다. 검사들이 독립적이라면 모두가 놓치는 결함의 비율은 각 검사의 실패율을 곱한 값에 가까워야 합니다. 가장 실패율이 낮은 검사 하나의 비율에 가깝다면 검사들이 같은 맹점을 공유한다는 뜻이고 같은 종류의 검사를 더해도 얻는 것이 적습니다. 같은 표를 이용하면 사람이 읽는 시간당 자동 검사가 모두 놓친 결함을 리뷰어가 얼마나 잡는지도 측정할 수 있습니다. 변이 테스트는 의도하지 않은 결함만 살핍니다. 글에서 가장 우려하는 실패, 즉 코드와 테스트가 함께 요구사항을 잘못 이해하는 문제는 변이 테스트가 아니므로 오라클에도 결함을 심어야 합니다. 요구사항에 미묘하게 잘못된 인수 기준을 주고 어떤 검사가 이의를 제기하는지 보면 됩니다. 그 기준을 그대로 통과한 검사는 마지막 부분의 주장을 측정 가능한 형태로 보여줍니다.
  • @neilblaze — 좋은 글입니다.
  • @ldrscke — 증거 유형에 mutation testing도 추가하겠습니다.
  • @jabinpou — 독립적인 작성자와 독립적인 오라클의 차이가 핵심이라고 봅니다. 두 번째 모델이 같은 오해를 한 요구사항을 자신 있게 승인할 수도 있습니다. PR 요약에 실제 사용자 결정에서 나온 인수 기준과 구현 과정에서 추론한 기준을 구분해 보여주면 좋겠습니다.
  • @abhijithshaji17 — 유익합니다.

원문: dev.to / 번역·요약: Trawling