프로젝트

다섯 개의 개인 프로젝트와, 만들면서 무엇이 어떻게 깨졌고 어떻게 알아냈는지의 기록.

이름을 누르면 그 프로젝트의 소개 페이지로 간다. 아래 주제 색인은 다섯을 가로질러 같은 모양으로 깨진 것을 묶은 것이다 — 장르도 언어도 다른데 겹쳤다.

프로젝트 5 개발 노트 17 공통 주제 7

각 프로젝트 페이지에 무엇을 하는 앱인지 · 구조 · 주요 기능 · 개발 노트가 있다. 노트 본문은 저장소에 있고, 이 페이지들은 링크만 건다 — 복사하면 어느 쪽이 최신인지 알 수 없게 된다.

같은 모양이 다섯 프로젝트에서 나왔다

장르도 언어도 다른 다섯 프로젝트인데 — 실시간 전략 게임 · 방어 시뮬레이터 · 상주형 모니터링 · 전적 조회 앱 · 위성 추적 앱 — 깨진 모양이 겹쳤다. 사례가 하나였을 땐 그 프로젝트의 성질인 줄 알았다. 새 주제는 두 프로젝트 이상에서 나왔을 때만 만든다. 그래서 주제는 넷 → 여섯 → 일곱로 늘었다 — Argus(3번째)에서는 하나도 안 생겼고, 피파(4번째)에서 둘, RL3D(5번째)에서 하나. 세 번 다 「이미 한 프로젝트에서 두껍게 나왔던 것」이 두 번째 사례를 만나 조건을 채운 경우였고, 그 사이의 기다림이 이 규칙의 값이다.

통과는 증거가 아니다

아무것도 검사하지 않는 검사도 초록으로 뜬다. 그리고 그건 실패보다 알아채기 어렵다.

  • Domynion한 번도 발동한 적 없는 규칙 — 소스를 일부러 깨뜨렸는데 17개 테스트가 전부 통과했다
  • 방어 시뮬아무것도 안 보면서 통과한 검사들 — 순회 대상 0개, 골든에 없는 편성, 스캔 범위 밖. 다섯 건 다 PASS였다
  • 피파검사 도구가 실행 환경과 달라 아무것도 안 잡았다 — 봇 런타임은 Rhino(ES5)인데 node로 문법을 검사하니 최신 문법을 전부 통과시켰다. 폰에 올린 뒤에야 안다
  • RL3D공허하게 통과한 검사들 — 좌표가 전부 NaN인데 every()가 참이었다(별도 vm realm 에서 instanceof Date가 false). 그리고 경계 테스트가 기대값을 상수로 계산해 그 상수를 0으로 바꿔도 통과했다

기능이 아니라 측정이 틀렸다

“효과가 없다”는 결론을 내리기 전에, 그 결론을 만든 측정이 유효했는지를 먼저 묻는다. 측정 코드도 코드다.

한 번도 돌지 않은 것이 오래 살아남는다

돌지 않는 규칙은 아무것도 깨뜨리지 않아서, 깨뜨리는 것보다 훨씬 오래 방치된다.

조용히 실패한다

예외도 로그도 붉은 테스트도 없이 죽는다. 그리고 방어 코드가 실패의 증거를 지우기도 한다.

  • Argus신호가 0인 실패 — 자식의 디코딩이 reader 스레드에서 죽어 stderrNone이 되고, (x or "")가 그걸 빈 문자열로 바꿔 실패 원인이 통째로 사라졌다
  • Argus지문 갱신 스레드가 첫 틱에 죽어 몇 주 동안 “기동 시 1회”로 돌았다 — pythonw라 stderr가 없어 아무 신호도 없었다
  • 피파exe가 아무 말 없이 빈 DB를 새로 만들었다 — 실행 방식에 따라 데이터 폴더가 갈려, 그때까지 쌓은 105경기를 못 봤다
  • 피파웹 점검 페이지를 “랭킹 밖”으로 오인해 빈 값을 TTL 내내 캐시했다 — 실패를 성공으로 굳혔다

화면이 본문과 다른 말을 한다

계산은 맞는데 전달이 틀린다. 두 표현이 각자는 맞고 서로 어긋나면, 한쪽만 보는 테스트는 영원히 통과한다.

  • Argus리포트 본문은 “원인 프로세스는 특정할 수 없습니다”라고 정확히 적어 놓고, 바로 아래 표 제목이 원인 후보였다 — 순위가 매겨진 표를 사람은 답으로 읽는다
  • 피파숫자로는 침묵하고 막대로는 말했다 — 표본이 모자라 비율을 로 숨기면서 막대는 실제 길이로 그렸다
  • 피파시즌 필터를 걸었는데 화면 문구는 “누적 전체 기준”으로 박혀 있었다
  • RL3D고장이 아닌데 고장으로 보였다 — 창이 화면 밖에 떠서, 지도 배경만 안 떠서, 필터에 다 걸려서. 검색어를 지도에 걸지 않은 이유가 특히 남는다 — 필터의 흔적이 화면에 안 남으면 나중에 “왜 안 보이지”의 원인이 된다

공개 저장소에 남의 것이 올라간다

둘 다 회귀 테스트 픽스처였다. 실제 응답을 그대로 두는 것이 골든 테스트의 가치인데, 그 실제 응답에 제3자 정보가 섞여 있다.

  • 피파픽스처가 실응답 그대로라 ouid(영구 식별자) · matchId(하나만 있어도 참가자를 조회할 수 있다) · 상대 구단주명 4명이 공개돼 있었다. 파일명도 matchId 라 내용만 고쳐서는 소용이 없었다
  • RL3D픽스처에 유튜브 설명문이 통째로 — 채널 운영자의 SNS·사업용 이메일까지. 파싱에 쓰이지도 않는 필드라 지워도 골든값이 안 바뀌었다
  • 둘의 답이 갈렸다RL3D 는 생성 스크립트를 고쳤고(안 고치면 다음 갱신에 그대로 돌아온다), 피파는 생성 스크립트가 없어 익명화 규칙을 docstring 에 적었다

안 여는 문서가 썩는다

틀린 조언은 없는 조언보다 나쁘다. 그리고 낡은 줄은 대개 초록 옆에 붙어 있어서 더 안 읽힌다.

노트를 쓰는 규약

제목은 무엇이 틀렸는지로 시작한다. 기능 자랑이 아니라 실패와 그 발견 과정이 본문이다.

근거를 건다. 각 노트 끝에 커밋 해시와 문서 위치를 표로 둔다. 링크를 걸기 전에 해시가 실재하는지 확인한다.

수치는 실측인용을 가른다. 커밋 메시지에 적힌 값은 그때의 주장이지 지금 잰 값이 아니다. 해시는 “누가 그렇게 적었다”의 근거지 “그게 맞다”의 근거가 아니다.

못 잰 것은 못 쟀다고 적는다. 무엇을 왜 안 쟀는지까지.

끝에 그 건에서 남긴 규칙을 한 줄로 뽑는다. 그 프로젝트 밖에서도 통해야 좋은 노트다.

파일 위치는 프로젝트마다 docs/notes/NN-slug.md 로 같다. 규약을 맞춰 두면 이 페이지가 긁을 때 규칙이 하나로 끝난다 — 실제로 두 번째 프로젝트를 쓰고 나서야 무엇이 공통인지 관찰로 알 수 있었다.