Development guide
이 문서는 SaigeToolkit의 개발 방식 및 아키텍쳐에 대한 가이드입니다. 일관적인 코드베이스 구조를 통해 프로젝트의 유지보수성을 높이고, 새로운 개발자가 프로젝트에 쉽게 참여할 수 있도록 도와줍니다.
각 가이드 항목은 Definition, Rule, Guidance로 구성되어 있습니다.
Definition은 관련된 개념을 설명하며, Rule은 코드베이스에 반드시 적용되어야 하는 규칙을 나타냅니다. Guidance는 권장사항을 나타내며, 웬만한 경우에는 따르는 것이 좋으나 필수적이지는 않습니다.
빌더
- Definition
- 빌더는 클래스의 인스턴스를 생성하는 로직을 담당합니다.
- Rule
- 빌더 클래스 혹은 메서드는 항상
{module_directory}/builder.py에 위치시켜야 합니다.
- 빌더 클래스 혹은 메서드는 항상
추상화 및 상속
- Rule
- 추상화된 베이스 클래스는 항상
{module_directory}/base_{component}.py에 위치시킵니다.
- 추상화된 베이스 클래스는 항상
- Guidance
- 한 파일에 여러 종류의 베이스 클래스를 정의하는 것은 지양해야 합니다.
- 자식 클래스는 부모 클래스에서 정의한 인터페이스를 변경하지 않아야 합니다.
- (=자식 클래스에서는 기능의 확장만을 수행해야 합니다).
-
메서드 오버라이드 시에는 빌트인 기능인
typing_extensions.override데코레이터를 활용해 주세요 (Python 3.12부터는typing.override로 변경됩니다).- 소스가 복잡해질수록 어떤 메서드가 오버라이드되었는지 파악하기 어려워집니다.
override데코레이터를 명시적으로 붙여 오버라이드 되고 있는 메서드를 쉽게 파악할 수 있습니다.
- 소스가 복잡해질수록 어떤 메서드가 오버라이드되었는지 파악하기 어려워집니다.
모듈 초기화
- Guidance
__init__.py파일에는 꼭 필요한 경우에만 하위 기능의 임포트를 정의해야 합니다.- 불필요한 임포트는 코드베이스의 명확성을 떨어뜨리며, 추가적인 메모리 및 시간이 소비되게 합니다.
- 임포트가 필요한 경우에는 반드시 api 코드 등 외부에서 자주 사용되는 코드의 실행 시간에 영향이 없도록 주의해야 합니다.
- 만약
module_a/__init__.py에서module_b를 임포트한다면,module_a를 임포트해야 하는 코드, 예를 들어module_a/module_c/api.py를 불러오는 경우에module_b를 전혀 사용하지 않더라도module_b가 임포트됩니다.module_b에torch와 같은 큰 패키지가 포함되어 있다면 치명적입니다.
- 만약
__init__.py파일에는 되도록 로직을 작성하지 않아야 합니다.- 로직이 필요하다면 다른 파일에 분리해 작성해야 합니다.
결합도 관리
- Definition
- 한 요소를 변경하거나 수정할 때 다른 요소에 영향을 미치는 정도를 결합도라고 합니다. 결합도는 일대다 및 연쇄적인 변경을 유발하며, 코드베이스의 유지보수성을 떨어뜨립니다.
- Guidance
- 코드 간의 결합도를 최소화하는 방향으로 작업해야 합니다.
- 만약 그럴 수 없는 경우, 서로 결합도가 존재하는 코드들은 (연쇄 변경이 필요한 코드들은) 최대한 가까운 위치에 배치합니다. 코드의 경우 파일 안에서 연달아 위치하도록, 파일의 경우 같은 디렉토리에 위치하도록 합니다.
레거시 관리
- Guidance
- 더 이상 사용되지 않는 코드라 판단되는 경우 즉시 삭제합니다.
- 레거시 코드는 코드베이스의 복잡성을 증가시키며, 유지보수성을 떨어뜨립니다.
- 만약 다시 사용될 가능성이 있는 코드라 해도, 버전 관리 도구를 통해 다시 복구할 수 있습니다.
- 일부 오래된 코드베이스에서만 사용되는 기능인 경우, 해당 코드가 계속 SaigeToolkit에서 관리되어야 하는지 해당 기능의 관리자와 함께 다시 검토합니다.
- 재사용성이 없다고 판단되면 해당 코드를 삭제하고 관련 리포지토리로 이전해 관리합니다.
- 같은 기능을 하는 새 구현이 이미 존재한다면, 레거시를 삭제하고 새 구현을 사용하도록 권장합니다.
- 더 이상 사용되지 않는 코드라 판단되는 경우 즉시 삭제합니다.
디펜던시 관리
- Rule
- 제품화에 필요한 디펜던시는
requirements/prod.txt에 명시해야 합니다. - 연구 및 개발에 필요한 디펜던시는
requirements/dev.txt에 명시해야 합니다. - 양 쪽 모두에 해당하는 디펜던시는
requirements/common.txt에 명시해야 합니다. - 불필요한 디펜던시는 발견하는 즉시 제거합니다.
- 제품화에 필요한 디펜던시는
SaigeError 정의
- Definition
- API 기능 호출 도중 문제가 생겼을 때, 외부로 노출할 error_code, error_message 를 정의한 클래스입니다.
- Rule
- 각 repo base class 는
SaigeToolkit/error/base.py에 정의합니다.- 이 때,
head와repo를 반드시 기입합니다.
- 이 때,
- 상세 error class는 각 기능 repo 의
{module_name}/error/error.py에 정의합니다.- 이 때,
error_code와message(default)를 반드시 기입합니다.
- 이 때,
- 위에서 정의한 error class를 원하는 위치에서
raise구문을 통해 발생시킵니다.
- 각 repo base class 는
- Guidance
messagehandling- 이 때, init argument 로 추가적인
message를 기입할 수 있습니다. - 또,
raise ... from e구문을 이용해 error 의 구체적인 원인(cause)을 명시할 수 있습니다. - 예시
- 최종적으로 출력되는 message string은 다음과 같은 규칙으로 생성됩니다.
- "
{default_message}({additional_message}) ({cause_class}:{cause_message})"
- "
- 더이상
get_error_message()함수를 사용하지 않습니다.
- 이 때, init argument 로 추가적인
API 기능
- Definition
- API 기능은 코드베이스 외부에서 호출되는 기능을 의미합니다.
- Rule
- API로 제공되는 코드에는 항상
error.handler.error_handler데코레이터가 적용되어야 합니다. - API로 제공되는 코드는
{module_directory}/api.py경로에 위치해야 합니다.- 이는 코드베이스에서 API로 제공되고 있는 코드를 찾기 쉽게 합니다.
- API로 제공되는 코드에는 항상
- Guidance
- API 코드의 임포트 경로에는 불필요한 타 패키지의 임포트를 최대한 제거해야 합니다.
- 특히
torch와 같은 큰 패키지가 불필요하게 임포트 되는 경우 불필요한 시간과 메모리를 소비하게 됩니다.
- 특히
- API 코드의 임포트 경로에는 불필요한 타 패키지의 임포트를 최대한 제거해야 합니다.
ResearchToolkit
- Rule
- ResearchToolkit에는 연구 수행을 위한 코드가 위치합니다 (Notion 툴, 벤치마크 툴 등).
- ResearchToolkit만을 위한 디펜던시는
requirements/dev.txt로 분리해 관리합니다.