DEV Community

JustJinoIT
JustJinoIT

Posted on AI-assisted

AI 커밋이 2배 더 고쳐진다고? 비교군이 하루치였다

Ars Technica에서 하버드 워킹페이퍼(Fiona Chen, James Stratton) 기사를 읽었다. 700여 개 회사의 작업 기록 약 3억 건을 분석했더니, AI 코딩 에이전트를 도입한 뒤 PR은 23%, 코드 생산량은 30% 정도 늘었는데 이슈 해결률은 통계적으로 의미 있게 달라지지 않았다고 한다. 병목은 리뷰였다. PR이 머지되기까지 걸리는 시간이 49% 늘었고 수정 요청은 거의 두 배가 됐다.

내 레포도 그런지 재 봤다. 사이드 프로젝트 두 개(아래 레포 A, B)이고, 둘 다 6월 4일에 Claude와 함께 시작했다.

메시지에 Co-Authored-By: Claude 트레일러가 있으면 AI 커밋(245개), 없으면 "트레일러 없는 커밋"(65개)으로 나눴다. 지표는 제목이 fix나 revert로 시작하는 커밋의 비율이다.

AI 커밋 트레일러 없는 커밋
제목이 fix/revert로 시작 38% 17%

두 배가 넘는다. 연구와 같은 방향이다(같은 지표는 아니다). 이대로 글을 쓸 뻔했는데, 먼저 깨 보기로 했다.

확인 1: fix:는 누가 제목에 쓰나

AI 커밋의 69%가 feat:, fix: 같은 표준 형식 제목을 쓴다. 트레일러 없는 커밋은 18%다. 고칠 때마다 fix:를 붙이는 에이전트는 제목을 읽는 지표에서 더 많이 잡힌다. fix, bug, revert, 수정, 버그, 고침, 오류 중 하나가 제목 어디에든 들어간 커밋으로 넓히면 격차가 벌써 줄어든다. 43% 대 26%다.

확인 2: 트레일러 없는 커밋은 누구 것인가

이 커밋들을 사람 쪽 비교군으로 두고도 정작 열어 보지 않았다. 65개 중 57개가 6월 5일 하루에 몰려 있었다. 제목은 이런 식이다.

  • Phase 0-1: Add core modules (logging, pipeline, batch)
  • Phase A: Semantic cache + batch processing integration
  • Enhancement Level 2: Sentiment analysis & topic classification

뼈대를 세우던 날의 커밋으로 읽힌다. 두 레포 모두 6월 4일의 첫 커밋부터 AI 트레일러가 있어서, AI가 없던 시기는 비교 기준으로 쓸 수도 없다. 이 커밋을 누가 썼는지 나는 말할 수 없다. 그게 문제다. 내 비교군은 누가 만들었는지 모르는 하루치 작업이었다.

확인 3: 커밋 크기

AI 커밋은 한 번에 건드리는 파일이 더 많다. 평균 3.1개이고 다른 쪽은 1.7개다. "같은 파일이 72시간 안에 다시 수정됐나"를 커밋 단위로 세면 큰 커밋이 겹칠 기회가 더 많다. 커밋 단위로는 74% 대 68%였다. 파일 단위로 세서 커밋 크기를 뺐다.

측정 방식 AI 커밋 트레일러 없는 커밋
제목이 fix/revert로 시작 38% 17%
제목 어디든 fix·버그·수정 등 포함 43% 26%
건드린 파일이 72시간 안에 다시 수정됨 (파일 단위, 두 레포 합산) 46% 47%

제목과 커밋 크기를 둘 다 뺀 마지막 줄에서는 차이가 없다.

그래서 이 숫자는

두 레포만 가지고는 AI 커밋이 더 자주 고쳐지는지 덜 고쳐지는지 알 수 없다. 연구가 틀렸다는 말도 아니다. 표본이 작고 인과를 보여 주는 것도 아니다. 처음 나온 2배는 의도하지 않고 잰 세 가지에서 왔다. 제목 쓰는 습관, 하루치로 이뤄진 비교군, 커밋 크기.

다음에 AI와 비AI 작업의 격차를 믿기 전에 먼저 이걸 본다.

  • 비교군에 누가 들어 있고, 언제 커밋한 건가?
  • 내가 세는 라벨이 측정 대상의 습관에서 나온 건 아닌가?
  • 두 그룹이 같은 크기의 일을 하고 있나?

내 레포에서 재 보려면

표준 라이브러리만 쓴다. 첫 AI 커밋 이후의 커밋만 비교한다. 다른 도구를 쓴다면 co-authored-by: claude 문자열만 바꾸면 된다.

import subprocess, sys
from collections import Counter
from datetime import datetime

repo = sys.argv[1] if len(sys.argv) > 1 else "."
out = subprocess.run(
    ["git", "-C", repo, "log", "--no-merges", "--name-only",
     "--pretty=format:\x1e%ct\x1f%B\x1f"],
    capture_output=True, text=True).stdout

commits = []  # dict: 시각, AI 트레일러 여부, 제목, 건드린 파일
for chunk in out.split("\x1e")[1:]:
    t, msg, files = chunk.split("\x1f")
    commits.append(dict(t=int(t), ai="co-authored-by: claude" in msg.lower(),
                        subj=msg.strip().split("\n")[0], files=set(files.split())))
commits.sort(key=lambda c: c["t"])

if not any(c["ai"] for c in commits):
    sys.exit("AI 트레일러가 붙은 커밋이 없다. 메시지에 Co-Authored-By: Claude 가 있는지 확인할 것")
start = min(c["t"] for c in commits if c["ai"])   # 첫 AI 커밋 이후만 비교
commits = [c for c in commits if c["t"] >= start]

pairs = {True: [0, 0], False: [0, 0]}             # [파일 수, 72시간 안에 다시 수정된 파일 수]
for i, c in enumerate(commits):
    for f in c["files"]:
        pairs[c["ai"]][0] += 1
        if any(c["t"] < d["t"] <= c["t"] + 72 * 3600 and f in d["files"] for d in commits[i + 1:]):
            pairs[c["ai"]][1] += 1

for ai in (True, False):
    g = [c for c in commits if c["ai"] == ai]
    if not g:
        continue
    fix = sum(c["subj"].lower().startswith(("fix", "revert")) for c in g)
    day, k = Counter(datetime.fromtimestamp(c["t"]).date() for c in g).most_common(1)[0]
    n_f, hit = pairs[ai]
    print("AI       " if ai else "트레일러 없음", f"커밋 {len(g)}개 | 제목이 fix/revert로 시작 {fix / len(g):.0%}"
          f" | 커밋당 파일 {sum(len(c['files']) for c in g) / len(g):.1f}개"
          f" | 파일 단위 72시간 내 재수정 {hit / max(n_f, 1):.0%}"
          f" | 가장 몰린 날 {k / len(g):.0%}")
Enter fullscreen mode Exit fullscreen mode

내 두 레포에서는 이렇게 나왔다.

레포 그룹 커밋 fix/revert 제목 커밋당 파일 72시간 내 재수정(파일) 가장 몰린 날
레포 A AI 145 40% 2.9 47% 10%
트레일러 없음 16 25% 1.6 52% 62%
레포 B AI 100 36% 3.4 44% 23%
트레일러 없음 49 14% 1.8 46% 96%

마지막 열은 처음부터 찍어 봤어야 했다. 비교군의 커밋 대부분이 하루에 몰려 있다면, 어떤 배율이든 믿기 전에 그 하루부터 열어 보는 게 낫다.

돌려 보면 "가장 몰린 날" 열이 어떻게 나오는지 궁금하다.

Top comments (0)