로직을 한 줄 쓰기도 전에, 내 데이터는 거짓말을 하고 있었다

오랫동안 나는 내 문제가 코드 안에 있다고 믿었다. 결과가 이상하게 나오면 나는 내 로직, 내 모델, 내가 자랑스러워하던 그 영리한 부분들을 뒤지고 다녔다. 그러나 진짜 범인은 거의 언제나 그 모든 것보다 한 층 아래, 조용히 점검되지 않은 채 앉아 있었다. 바로 데이터 그 자체였다. 깔끔해 보였다. 하지만 아니었다. 그리고 내가 그 위에 쌓아 올린 모든 것이 그 거짓말을 고스란히 물려받았다.

이것은 세상에서 가장 지루한 종류의 실패이고, 바로 그래서 가장 위험하다. 아무도 숫자가 늘어선 행들을 감사하는 데 시간을 쓰고 싶어 하지 않는다. 그래서 아무도 하지 않고, 그 썩은 부분은 모든 것을 떠받친 채 맨 밑바닥에 그대로 남아 있다.

깨끗한 데이터라는 신화

원시 시장 데이터는 안심이 될 만큼 단정해 보인다. 가지런한 행들, 질서정연한 타임스탬프, 순서대로 행진하는 그럴듯한 숫자들. 그 단정함은 일종의 분장이다. 가끔 고장 나는 실제 시스템에서 수집된 현실의 데이터는, 스스로를 드러내지 않는 작은 손상들로 가득 차 있다. 어느 순간이 그냥 사라진 공백, 중복된 항목, 어긋나거나 잘못 정렬된 타임스탬프, 장애가 난 동안 멈춰서 반복되는 값, 이따금 등장하는 불가능한 숫자, 통째로 미묘하게 제자리를 벗어난 구간들.

이 중 어느 것도 경고 라벨을 달고 도착하지 않는다. 그것들은 좋은 데이터와 똑같이 생겼다. 같은 가지런한 행 안에 앉아 있기 때문이다. 내용물이 정직하지 않을 때조차 포맷은 정직하고, 한눈에 보이는 것은 오직 포맷뿐이다.

쓰레기가 들어가면, 자신만만한 쓰레기가 나온다

반드시 체화해야 할 사실은, 시스템은 데이터가 틀렸다는 걸 모른다는 것이다. 알 수가 없다. 시스템은 당신이 건네주는 것을 그대로 받아, 진실에 적용하는 것과 정확히 똑같은 성실함으로 손상을 처리하고, 그런 다음 그 썩은 토대 위에 깨끗하고 자신만만하며 전문가다워 보이는 결과를 만들어 낸다.

바로 거기에 함정의 전부가 있다. 출력은 멀쩡해 보인다. 손상은 문제를 가리키며 비명을 지르는 명백한 오류로 전파되지 않는다. 손상은 그럴듯한 오답으로 전파된다. 충분히 믿을 만하고, 당신의 직관적 점검을 통과하며, 그 위에 기꺼이 다음 것을 쌓아 올리게 되는 결과 말이다. 그 틀림이 수면 위로 떠오를 즈음이면, 그게 떠오르기라도 한다면, 그것은 발밑의 바닥이 단단하다고 전제한 채 쌓인 여러 겹의 작업 아래에 파묻혀 있다.

그럴듯함 속에 숨는 오류들

나는 천천히, 위험한 데이터 오류는 명백한 오류가 아니라는 것을 배웠다. 0이라는 가격, 통째로 빠진 하루, 누가 봐도 말이 안 되는 값. 이런 것들은 잡힌다. 표면적으로 터무니없기 때문이다. 첫 번째 상식 점검에서 걸린다.

정작 당신을 다치게 하는 오류는 그럴듯한 것들이다. 틀렸지만 여전히 믿을 만한 범위 안에 잘 들어와 있는 값. 단 한 단위 어긋난 타임스탬프. 상류의 무언가가 조용히 추측으로 메워 넣은 공백. 이런 것들은 당신이 가진 모든 상식 점검을 곧장 통과해 버린다. 정상적인 데이터와 똑같이 생겼기 때문이다. 그것들은 다른 무언가와 일부러 대조해 검증하지 않는 한 진실과 구별되지 않는다. 가장 값비싼 손상은 올바르게 보일 만큼 예의 바른 종류의 손상이다.

나는 건물의 엉뚱한 층을 디버깅하고 있었다

부끄러울 만큼 오랫동안, 뭔가 이상하게 나오면 내 본능은 내 로직에서 버그를 찾으러 가는 것이었다. 흥미롭고 정교한 부분, 내가 고민할 가치가 있다고 여겼던 그 부분에서 말이다. 버그는 거의 한 번도 거기 있지 않았다. 그것은 아래쪽, 데이터 안에, 내가 그냥 한 번도 검증해 보지 않은 입력에 관한 어떤 가정 속에 있었다.

말하자면 나는, 기초를 가로질러 번지고 있는 균열의 근원을 찾겠다고 건물의 꼭대기 층들을 뒤지고 있던 셈이다. 나는 그 우아한 코드를 보고 또 보며 아무 잘못도 찾지 못했고, 점점 더 어리둥절해졌다. 정말로 아무 잘못이 없었기 때문이다. 그 코드는 설계된 그대로, 나쁜 입력을 나쁜 출력으로 정확히 처리하고 있었다. 로직은 무죄였다. 죄가 있는 것은 그것이 딛고 선 땅이었다.

데이터 품질은 끝낼 수 있는 작업이 아니다

좀 더 겸허해지게 만든 깨달음 중 하나는, 데이터를 한 번 청소하고 끝내는 게 아니라는 것이었다. 그건 환상이다. 새 데이터는 계속 도착하고, 새로운 손상을 달고 도착한다. 어떤 소스가 조용히 포맷을 바꾼다. 상류 제공자에게 장애가 생기고, 그다음 미심쩍은 무언가로 공백을 백필한다. 몇 달 동안 믿음직하던 무언가가 평범한 어느 화요일에 새로운 방식으로 망가진다.

데이터 품질은, 내가 마침내 이해하게 된 바로는, 목록에서 체크 표시 하나 찍고 끝내는 일회성 청소가 아니다. 그것은 지속적인 규율이다. 모든 입력을 향한 의심의 자세를 영원히 유지하는 것이다. 당신이 지켜보기를 멈추는 그 순간이야말로, 새롭고 창의적인 손상이 아무 저항 없이 걸어 들어오는 바로 그 순간이기 때문이다.

가차 없이 검증하고, 아무것도 믿지 마라

실질적인 해법은 모든 입력을 깨끗함이 입증되기 전까지 유죄로 취급하는 것이었다. 공백을 점검하라. 중복을 점검하라. 불가능한 값을, 꾸준히 앞으로 나아가지 못하는 타임스탬프를, 실제 데이터라면 결코 그러지 않을 방식으로 숫자가 변하기를 멈추는 수상한 구간을 점검하라. 그리고 결정적으로, 이 점검들을 파이프라인에 직접 심어 넣어, 손상이 몇 달 뒤에 더는 추적할 수 없는 결과 속 설명 불가의 이상함으로 발견되는 대신, 도착하는 바로 그 순간 정문에서 요란하게 잡히도록 하라.

그것은 지루하고, 방어적이며, 지독히도 빛이 안 나는 일이다. 동시에 그것은 하류의 모든 것이 바위 위에 세워질지 모래 위에 세워질지를 결정하는 일이다. 문 앞에서의 요란한 실패는 선물이다. 당신이 물려받아 그 위에 쌓아 올리게 되는 조용한 실패는 느린 재앙이다.

더 깊은 교훈: 토대가 천장을 정한다

이 모든 것의 밑바탕에 깔린 원리는 단순하고 가차 없다. 위에 얹힌 모델이 아무리 뛰어나도 아래에 깔린 데이터의 썩음을 보상하지 못한다. 당신이 만드는 모든 것의 품질은, 당신이 그것에 먹이는 것의 품질에 의해 절대적으로 상한이 정해진다. 나쁜 입력에서 추론으로 빠져나올 수는 없다. 그 위에 더 자신만만하고 더 정교한 틀림을 만들어 낼 수 있을 뿐이다.

그러니 깨끗한 입력을 보장하는 그 따분하고 고마움도 못 받는 노동은, 내가 예전에 그렇게 취급했듯 진짜 작업보다 격이 낮은 게 아니다. 그것이 바로 진짜 작업의 토대이고, 그 위의 구조물 전체는 언제까지나 딱 그 토대만큼만 정직하다. 나는 그걸 건너뛰고 흥미로운 부분으로 곧장 가고 싶어 했다. 이제 나는 안다. 그 아래의 지루한 부분이 거짓말을 하고 있다면, 흥미로운 부분은 무가치하다는 것을.

— 시그널도, 수익도 없습니다. 투자 자문이 아닙니다.