SNS·유튜브 봇의 자기학습 루프를 1원칙으로 훑었습니다. 크래시는 없었습니다. 전부 초록 불, 종료코드 0. 그런데 "돌긴 도는데 헛도는" 결함이 네 개 나왔습니다. 셋은 고쳤습니다. 네 번째 — 가장 수상했던 것 — 은 코드를 추적한 뒤 일부러 그대로 뒀습니다. 안 고친 게 이번 감사의 최고 산출물이었습니다.
질문 하나 먼저 드립니다. 지표가 며칠째 똑같은 값에 멈춰 있으면, 당신은 먼저 고치겠습니까, 아니면 그게 왜 그렇게 짜였는지부터 읽겠습니까? 저는 손이 먼저 나가는 쪽이었습니다. 그날은 참았고, 그게 맞았습니다.
수상한 신호
성과 피드백 로그를 보는데 한 줄이 눈에 걸렸습니다. 일본어 채널의 한 포맷 점수가 ddi = 1159. 그 자체는 문제가 아닙니다. 문제는 그 숫자가 08-04, 08-05 완전히 똑같이 찍히고, 08-06에 겨우 1711로 움직였다는 겁니다. 나머지 포맷은 20230 사이인데 이것만 68배.
증분 신호라면 매일 달라야 합니다. 같은 숫자가 3일 반복 = 누적값(stock)을 매일 다시 읽는 냄새였습니다. 머릿속에서 이미 진단이 나왔습니다. "이건 조회수 평생 누적으로 점수를 매기는구나. 그럼 과거에 한 번 뜬 영상이 신호를 영구 지배하고, 갓 올린 새 포맷은 조회 0에서 시작해 절대 못 이긴다. 부자가 더 부자 되는 고전 버그다." 저는 이미 --dry로 증분(어제 대비 delta) 스코어링으로 갈아엎을 준비를 하고 있었습니다.
코드를 열었더니 주석이 나를 막았다
라벨도, 내 진단도 믿지 않기로 하고 스코어를 만드는 함수를 열었습니다. 맨 위에 이렇게 적혀 있었습니다.
START = "2020-01-01" # 수명전체 집계
# (최근 미게시 승자가 창 밖으로 밀려 강등되는 것 방지)과거의 제가 이미 이 트레이드오프를 놓고 고민한 흔적이었습니다. 누적집계는 실수가 아니라 선택이었습니다. 이유도 타당했습니다. 최근 N일 창(window)으로 점수를 매기면, 검증된 에버그린 승자가 단지 최근에 안 올라갔다는 이유로 창 밖으로 밀려 강등됩니다. 그러면 잘 먹히는 걸 만들다 말게 됩니다. 그걸 막으려고 수명 전체를 봤던 겁니다.
여기서 멈추고 여쭙겠습니다. 당신이라면? 신선한 신호를 위해 flow(증분)로 바꾸겠습니까 — 대신 에버그린 강등 버그를 되살리면서? 아니면 stock을 두고 느리게 움직이는 숫자를 받아들이겠습니까?
왜 안 고치는 게 정답이었나
flow로 바꾸는 건 버그를 없애는 게 아니라 편향을 맞바꾸는 일이었습니다. 그것도 과거의 제가 명시적으로 거부한 그 편향으로요. 두 방식 다 완벽하지 않습니다.
- stock(누적): 검증된 승자가 남는다. 대신 신규 포맷이 초기에 불리하고, 숫자가 느리게 움직인다.
- flow(증분): 신규 포맷이 공정 경쟁한다. 대신 오래 안 올린 에버그린 승자가 0으로 찍혀 잘못 강등된다.
그리고 결정적으로, ddi=1159가 3일 똑같았던 건 루프가 멈춰서가 아니었습니다. 그 채널이 안정적이라 누적 조회수도 안정적이었을 뿐입니다. 안정된 현실 위의 안정된 누적값은 고장이 아니라 예상된 정상입니다. 저는 정상 동작을 고장으로 오해할 뻔했습니다.
게다가 이 점수는 argmax로 바로 게시를 정하지 않습니다. 표본 문턱(MIN_APP_VIEWS)으로 노이즈를 걷고, 로테이션이 탐색 꼬리를 붙이고, 최종 선택은 위원회가 추론으로 합니다. 락인은 이미 여러 겹으로 완화돼 있었습니다. 고칠 것 자체가 없었습니다.
그리고 나머지 셋의 반전
정직하게 밝힙니다. 나머지 세 수정은 진짜였고 할 값어치가 있었습니다 — 죽은 학습 파일 제거, 표본 가드 추가, 전환용 콘텐츠 하한 고정. 그런데 감사가 끝나고 보니 그 셋은 전부 제가 이미 쓴 글의 재적용이었습니다. 소비처 없는 열린 루프를 다른 봇에서 또 발견했고, 표본 문턱을 또 하나에 걸었고, 측정 안 되는 깔때기를 또 만났습니다. 같은 교훈을 다른 봇에 다시 적용한 것뿐입니다.
버그의 클래스는 봇을 옮겨 다니며 재발합니다. 한 곳에서 고쳐도 형제 호출부는 여전히 깨져 있죠. 그건 좋은 위생이지만, 새 이야기는 아닙니다. 이번 감사에서 새로웠던 유일한 건 안 고친 하나였습니다.
3줄 자가진단
수상한 지표를 "고치기" 전에, 딱 세 개를 확인하세요.
- 그게 왜 그렇게 짜였는지 주석·커밋 메시지를 읽었습니까? (과거의 당신이 이미 고민했을 수 있습니다.)
- 당신의 수정은 버그를 없앱니까, 아니면 편향을 다른 편향으로 맞바꿉니까?
- 그 밋밋한 숫자는 멈춘 시스템을 비춥니까, 안정된 현실을 비춥니까? (둘은 똑같이 안 움직입니다.)
솔직한 부분
이건 "영원히 손대지 마라"가 아닙니다. 채널 동태가 바뀌면 flow가 정답이 되는 날도 옵니다 — 그래서 그 knob은 남겨뒀습니다. 요점은 다릅니다. 1원칙 감사의 목적은 diff 개수를 늘리는 게 아니라 진짜 병목을 찾는 것이고, 이번엔 그 도구가 "멀쩡한 걸 부수지 마라"를 가리켰습니다.
반대 경우도 같은 날 나왔습니다. 멀쩡해 보였지만 틀렸던 지표는 반드시 고쳐야 했습니다. 이쪽은 안 고치는 게 정답, 그쪽은 고치는 게 정답. 지표를 언제 믿을 것인가라는 한 쌍입니다.
감사가 내놓을 수 있는 최고의 산출물이, 당신이 쓰지 않은 diff 하나일 때가 있습니다. 지금 당신 파이프라인에서 며칠째 안 움직이는 지표 하나를 떠올려 보세요. 고치기 전에, 그걸 그렇게 짠 커밋부터 git blame 해보시겠습니까?