당신이 신뢰하는 라이브러리에 숨어 있는 몇 가지 공통적인 버그들
저는 Langchain, Vite, Bat와 같은 대규모 저장소에 작은 수정 사항들을 보내는 데 시간을 보냅니다.
이 프로젝트들은 서로 다른 언어와 도메인을 사용하며, 메인테이너들은 전문가들입니다.
놀라운 점은 버그의 개수가 아닙니다. 바로 패턴입니다. 대부분의 버그는 옷만 다르게 입었을 뿐, 몇 가지 정해진 형태를 띠고 있습니다.
이러한 형태를 파악하고 나면, 버그가 프로덕션에 도달하기 전에 잡아낼 수 있습니다. 제가 발견한 다섯 가지 공통 패턴은 다음과 같습니다.
잘못된 입력 키 (Wrong Input Keys) Langchain에서 한 이름 변경(rename) 함수는
old_path라는 키를 찾았습니다. 하지만 시스템은 실제로path라는 키를 보냈고, 코드는 충돌(crash)했습니다. 리뷰를 통과하는 이유: 개발자가 시스템이 실제로 보내는 입력값이 아니라, 함수가 원하는 입력값을 수동으로 만들어 테스트했기 때문에 유닛 테스트를 통과했습니다. 확인 방법: 함수가 특정 키를 읽는다면, 해당 객체가 생성되는 위치를 찾으세요. 아무도 그 키를 설정하지 않는다면 버그를 발견한 것입니다. 실제 호출자(caller)를 대상으로 테스트하세요.Truthiness 함정 (Truthiness Traps) 흔한 실수는 "이 값이 설정되었는가"를 확인하려 할 때 truthiness 체크를 사용하는 것입니다. 예시:
const clause = defaultValue ? \DEFAULT ${defaultValue}` : '';만약 값이 0이라면, 코드는 해당 분기를 건너뜁니다. 0은 유효한 값이지만 "falsy"로 취급되기 때문입니다. 확인 방법: 항상 0, 빈 문자열, 그리고false`를 테스트하세요. 코드가 "값이 없음"과 "값이 있지만 0임"을 구분하지 못한다면, 그 코드는 잘못된 것입니다.부호 없는 정수 언더플로우 (Unsigned Integer Underflow) Bat 프로젝트에서는 터미널 너비를 계산하기 위해 산술 연산을 사용했습니다. 너비가 너무 작으면 뺄셈 결과로 언더플로우가 발생합니다. 부호 없는 타입(unsigned types)에서 이는 매우 큰 숫자로 되돌아가거나(wrap) 충돌을 일으킵니다. 확인 방법: 사용자 입력을 사용하는 부호 없는 타입의 모든 뺄셈에는 포화 연산(saturating subtraction)이 필요합니다. 0과 1로 테스트하세요.
인코딩 및 엣지 케이스 (Encoding and Edge Cases) 텍스트 규칙은 비 ASCII 문자를 보기 전까지는 단순해 보입니다. Mistune는 생성기(generator)가 생성할 수 있는 중첩된 구분자(stacked delimiters) 문제로 어려움을 겪었습니다. Wenmode는 유니코드 결합 문자(Unicode combining marks)를 처리할 때 실패했습니다. 리뷰를 통과하는 이유: ASCII는 모든 테스트를 통과합니다. 버그는 직접 타이핑하지 않는 입력값이 들어올 때만 나타납니다. 확인 방법: 차분 테스트(differential test)를 사용하세요. 여러분의 출력값을 검증된 다른 구현체와 비교해 보세요.
안전하지 않은 파싱 (Unsafe Parsing) Vite에는 가드(guard) 없이 URL을 디코딩하는 미들웨어가 있었습니다. 잘못된 형식의 URL은
URIError를 발생시켜 미들웨어를 중단시켰습니다. 확인 방법: 직접 만들지 않은 데이터 중 디코딩하거나 파싱해야 하는 모든 것에는try/catch블록이 필요합니다. 깨진 문자열을 코드에 던져보고, 코드가 유연하게 대처하는지 아니면 망가지는지 확인하세요.
저의 습관은 간단합니다. 버그를 수정할 때, 바로 옆에 있는 코드도 살펴봅니다. 한 핸들러에 버그가 있다면, 형제 핸들러도 같은 형태의 버그를 가지고 있을 가능성이 높기 때문입니다.
출처: https://dev.to/greymothjp/the-same-few-bugs-keep-hiding-in-libraries-you-already-trust-1pgp
