의존성을 관용적으로 사용하기
모든 의존성은 특정한 형태의 문제를 위해 만들어졌습니다. 그 API는 모델을 인코딩합니다: 무엇을 소유하고, 무엇을 사용자에게 맡기며, 무엇을 보장하고, 어디에서 확장을 기대하는지. 관용적인 해결책, 즉 라이브러리의 문서와 표준 예제가 보여줄 해결책을 작성하십시오. 문제가 다른 형태(임피던스 불일치)를 가질 때는, 작업에 맞는 올바른 도구를 선택하십시오. 피해야 할 실패는 바퀴의 재발명, 프레임워크와의 싸움, 그리고 해당 사례를 위해 만들어진 기능을 조용히 과소 활용하는 것입니다.
마찰은 냄새입니다: 명백한 경로에 캐스트, 패치 또는 우회 작업이 있다면 모델을 잘못 읽었거나 도구가 잘못된 것입니다. 가장 비용이 많이 드는 위반은 마찰이 전혀 없습니다: 타입 검사를 통과하고 로컬하게 보이지만 라이브러리가 제공하기 위해 존재하는 보장을 무너뜨리므로, 의도적으로 찾아야 합니다.
의존성을 사용하는 코드 작성
기본 경로입니다. 검토, 감사, 추가 및 업그레이드는 이 경로에서 분기됩니다(아래).
-
문제를 재정의하십시오. XY 문제를 주의하십시오: 작업은 종종 사용자나 자신으로부터 해결책의 형태로 도착합니다. 일반적인 문제로 재정의하십시오:
- "업로드를 세 번 재시도"는 재시도 정책입니다.
- "각 계정의 최신 송장에 대해
row_number() over (partition by account_id)가 필요합니다"는 그룹당 상위 N개이며, ORM이 윈도우 함수 없이 모델링할 수 있습니다.
"라이브러리가 X를 할 수 없습니다"는 누가 주장했든 3단계에서 검증해야 할 주장입니다.
완료 조건: 라이브러리 문서가 사용할 용어로 문제를 명명할 수 있을 때.
-
이미 해결된 곳을 찾으십시오. 가장 가까운 순서로:
- 사용 중인 프레임워크: 워크플로우 엔진의 재시도 정책 및 타이머, GraphQL 클라이언트의 페이지네이션 및 스토어 업데이터, 테스트 러너의 가짜 타이머.
- 프로젝트의 선언된 의존성 (
package.json,pyproject.toml,go.mod,Cargo.toml) 및 코드베이스가 이를 가져오는 방식. 기존 사용법은 라이브러리의 설계와 다를 수 있는 팀의 관례를 보여줍니다. - 프로젝트의 공유 패키지. 종종 프로젝트가 원하는 방식으로 의존성을 래핑합니다.
- 사용 중인 런타임 버전(
engines,.nvmrc,requires-python)에 대한 표준 라이브러리. 광범위한 라이브러리는 사람들이 기억하는 것보다 더 많은 것을 다룹니다: Python의itertools,collections,functools,pathlib; 최신 Node의fetch,AbortSignal.timeout,util.parseArgs,node:test.
완료 조건: 모든 소스를 확인하고, 비어 있을 수 있는 후보 목록을 확보했을 때.
-
설치된 버전의 문서에서 모델을 학습하십시오. 잠금 파일에서 버전을 확인하십시오; 일치하는 소스만 유효합니다. 다음 순서로 읽으십시오:
- 이 의존성에 대한 프로젝트의 지침. GUIDANCE.md에 따라 찾고 유지합니다. 이는 의도적인 팀 결정에 대한 권위 있는 정보이며 라이브러리 모델의 캐시입니다: 기록된 버전이 잠금 파일과 다르거나, 주장이 접근 방식을 결정할 때 아래 소스에서 확인하십시오.
- 참조 체크아웃: 프로젝트에 클론되거나
opensrc와 같은 도구로 가져온 의존성의 저장소로, 일반적으로AGENTS.md또는CLAUDE.md에 포인터가 있습니다. 태그를 잠금 파일과 확인하십시오. 문서, 예제 및 테스트는 의도된 사용법에 대한 최고의 기록입니다. - 설치된 패키지: 내보낸 타입과 그 문서 주석. 종종 문서와 예제가 제거됩니다.
- 온라인: 위의 두 가지 중 문서가 없는 경우, 해당 메이저 버전의 공식 문서, 개념 및 가이드 페이지, API 참조; 그런 다음 일치하는 태그의 업스트림 저장소.
공개 API에만 기반하여 구축하십시오: 문서가 설명하는 것, 선언된 진입점(
exports맵,__all__, 밑줄로 시작하지 않는 이름)을 통해 도달하는 것. 동작을 이해하기 위해 소스를 읽으십시오; 거기서만 찾은 것은 비공개입니다. Semver는 공개 API와 그 외의 것을 보호합니다. 라이브러리에 대한 기억은 문서와 대조할 가설이며, 우연히 열게 된 런타임 파일은 문서를 대체할 수 없습니다.완료 조건: 코드를 작성하거나 판단하기 전에 문서에서 다음 질문에 답할 수 있을 때:
- 무엇을 위해 만들어졌으며, 의도적으로 무엇을 생략합니까?
- 무엇을 소유(저장, 캐시, 라이프사이클, 재시도, 트랜잭션)하고, 무엇을 생성하거나 선언하도록 요구(코드젠 출력, 스키마, 타입)하며, 각 부분이 코드의 어느 부분을 위한 것입니까?
- 어떤 보장(데이터 마스킹, 워크플로우 결정론, 트랜잭션 격리)을 시스템의 어떤 부분 사이에서 유지하며, 무엇이 조용히 이를 무너뜨릴까요? 종종 자체 모듈 사이에 위치합니다.
- 어떤 공개 API와 문서화된 확장 지점(옵션, 플러그인, 미들웨어, 훅, 이스케이프 해치)이 이를 다루며, 정식 예제는 어떤 모습이며, 어느 모듈이 무엇을 가져오는지까지?
- 어떤 검사(린트 플러그인, 컴파일러 또는 코드젠 검증, 엄격한 타입 모드)를 제공하며, 프로젝트가 이를 실행합니까?
-
적합성을 선택하십시오:
- 문제가 모델과 일치: 사용하십시오.
- 일부가 주요 API 밖에 있음: 해당 부분에 대해 문서화된 이스케이프 해치(ORM의 raw SQL, 헤드리스 컴포넌트, 커스텀 리졸버)를 사용하십시오. 이스케이프 해치는 설계의 일부입니다.
- 설치된 버전에 없음: 변경 로그에 최신 버전이 있음을 보여주면 UPGRADING.md에 따라 업그레이드를 제안하십시오.
- 임피던스 불일치: 모델이 맞는 의존성을 사용하거나, 이 의존성을 피하는 일반 코드를 우리가 소유한 경계 뒤에 사용하십시오. 설치되었다고, 또는 근처에서 사용된다고 해서 해당 라이브러리가 올바른 도구인 것은 아닙니다.
완료 조건: 하나를 선택하고 모델에서 이를 정당화할 수 있을 때.
-
라이브러리 방식으로, 공개 API에 코드를 작성하십시오. 처음 작동하는 버전은 종종 관용적인 버전이 아닙니다. 주변 코드와 정식 예제가 일치하지 않으면 정식 예제를 따르고 사용자에게 차이를 알리십시오. 진행하면서 라이브러리 자체 검사를 실행하십시오; 프로젝트가 이를 생략하면, 활성화하면 무엇을 잡을 수 있는지 말하십시오.
완료 조건: 라이브러리의 검사가 통과되고, 아래 냄새가 적용되지 않거나(또는 각각이 보고할 선택인 경우), 라이브러리 유지 관리자가 설계된 대로 사용된 라이브러리로 코드를 인식할 때.
-
마찰이 지속되면, 먼저 설계를 라이브러리의 모델에 맞게 조정하십시오; 그 제약은 종종 교훈(워크플로우 엔진의 결정론 규칙, GraphQL 클라이언트의 데이터 마스킹)을 인코딩합니다. 그런 다음 4단계를 다시 방문하십시오. 사용자의 동의 없이 라이브러리에 반대되는 작업을 하지 말고, 제안과 우회 코드의 주석에서 의존하는 문서화되지 않은 동작을 명명하십시오. 모델과 제품 요구 사항 간의 충돌은 사용자의 결정입니다.
냄새: 라이브러리와 싸우기
각각은 4단계로 돌려보냅니다. 라이브러리가 제공하기 위해 있는 것에 따라 순위를 매기십시오: 무너진 보장은 오늘 아무것도 깨지지 않았더라도 다른 모든 것보다 우선하며, 모델을 인코딩하는 팀 규칙은 모델의 심각도를 가집니다.
- 무너진 보장: 생성된 코드가 생성된 모듈 외부에서 사용됨(다른 컴포넌트의 프래그먼트 타입), 다른 모듈의 마스킹된 데이터 읽기, 관련 없는 호출자 간에 공유된 하나의 프래그먼트 또는 스키마, 결정론적 워크플로우 내부의 비결정론. 이들은 타입 검사를 깔끔하게 통과하므로 의도적으로 검색하십시오.
- 코드가 라이브러리의 타입을 만나는 곳의 캐스트 또는 타입 검사기 억제(
as,any,@ts-expect-error,# type: ignore). - 비공개 API: 내부 경로(
pkg/dist/…,pkg._internal), 밑줄 이름, 문서화되지 않은 내보내기, 라이브러리 객체의 비공개 필드로의 캐스트, 소스에서만 발견된 동작. 불안정하거나 실험적인 API는 문서화되어 있지만 semver 외부에 있습니다: 문서가 여기서 권장할 때만 사용하고 그렇게 말하십시오. - 라이브러리 동작을 변경하는 패치(패키지 매니저 패치, 몽키패칭, 벤더 포크). 문서가 이미 약속하는 대로 라이브러리가 작동하도록 하는 패치는 버그 수정이며 올바른 조치입니다: 수정 사항으로 유지하고 업스트림 이슈를 링크하십시오.
- 중복된 상태: 라이브러리가 소유하는 것에 대한 두 번째 진실의 원천 (예: GraphQL 캐시가 저장소 상태로 미러링되거나 프레임워크가 이미 추적하는 재시도 횟수).
- 라이브러리가 콜백, 이벤트 또는 훅을 제공하는 곳에서 라이브러리 "이후"에 코드를 실행하는 타이머, 플래그 또는 효과 체인.
- 모든 호출 사이트에서 재정의되는 기본값, 또는 모든 호출에서 동작을 취소하는 래퍼.
- 주로 래핑하는 API의 이름을 바꾸는 얕은 래퍼. 삭제 테스트를 적용하십시오: 삭제가 호출을 라이브러리로 다시 이동시킬 뿐이라면, 존재 가치가 없습니다.
- 필요한 것보다 전체가 더 많이 보여서 다시 빌드된 라이브러리 기능의 일부.
냄새: 라이브러리 과소 활용
코드는 공개 API에서 작동하지만, 이 사례를 위해 만들어진 기능이 코드를 더 짧거나 선언적으로 만들 수 있습니다. 찾으려면 전달하는 각 옵션 객체의 타입과 1단계의 문제에 대한 라이브러리 가이드를 읽으십시오.
- 하나의 작업을 만드는 여러 호출: upsert가 있는 find-then-create, 루프의 쓰기(배치 API가 있는 경우).
- 출력을 재구성하는 글루 코드: 쿼리나 훅이 select 또는 projection 옵션을 취하는 경우.
- 라이브러리가 추적하는 상태를 수동으로 다시 빌드: 로딩, 보류, 더티 또는 재시도 플래그.
- 모든 호출 사이트에서 동일한 옵션: 기본값, 공유 구성, 테마 또는 팩토리가 있는 경우.
- 라이브러리가 선언하도록 하는 것에 대한 명령형 코드: 스키마, 변형, 관계, 정책.
비례는 양방향으로 작동합니다: 몇 줄의 명확한 일반 코드가 작업을 수행할 때, 일반 코드가 관용적입니다.
검토, 감사, 추가 및 업그레이드
- 다른 사람의 변경 사항 검토: REVIEWING.md를 읽으십시오.
- 코드베이스의 의존성 사용 감사: AUDITING.md를 읽으십시오.
- 패키지 추가, 또는 의존성 또는 런타임 업그레이드: 먼저 UPGRADING.md를 읽으십시오.
- 의존성에 대한 프로젝트 지침 찾기, 작성 또는 업데이트: GUIDANCE.md를 읽으십시오.
회신: 작성하지 않은 동작을 수행하는 라이브러리 기능; 직접 작성한 범용 코드에 대해 확인한 사항과 아무것도 맞지 않은 이유; 건너뛴 각 의존성과 일치하지 않은 모델 부분; 유지한 각 냄새와 그 이유. 의존성에 대해 지속적인 것을 배웠다면, GUIDANCE.md에 따라 제안하는 지침을 추가하십시오.