Skip to content

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로 변경됩니다).

        from typing_extensions import override
        class Child(Base):
            @override
            def method(self):
                pass
      
      • 소스가 복잡해질수록 어떤 메서드가 오버라이드되었는지 파악하기 어려워집니다. override 데코레이터를 명시적으로 붙여 오버라이드 되고 있는 메서드를 쉽게 파악할 수 있습니다.

모듈 초기화

  • Guidance
    • __init__.py 파일에는 꼭 필요한 경우에만 하위 기능의 임포트를 정의해야 합니다.
      • 불필요한 임포트는 코드베이스의 명확성을 떨어뜨리며, 추가적인 메모리 및 시간이 소비되게 합니다.
      • 임포트가 필요한 경우에는 반드시 api 코드 등 외부에서 자주 사용되는 코드의 실행 시간에 영향이 없도록 주의해야 합니다.
        • 만약 module_a/__init__.py에서 module_b를 임포트한다면, module_a를 임포트해야 하는 코드, 예를 들어 module_a/module_c/api.py 를 불러오는 경우에 module_b를 전혀 사용하지 않더라도 module_b가 임포트됩니다. module_btorch와 같은 큰 패키지가 포함되어 있다면 치명적입니다.
    • __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에 정의합니다.
      • 이 때, headrepo를 반드시 기입합니다.
    • 상세 error class는 각 기능 repo 의 {module_name}/error/error.py에 정의합니다.
      • 이 때, error_codemessage(default)를 반드시 기입합니다.
    • 위에서 정의한 error class를 원하는 위치에서 raise 구문을 통해 발생시킵니다.
  • Guidance
    • message handling
      • 이 때, init argument 로 추가적인 message를 기입할 수 있습니다.
      • 또, raise ... from e 구문을 이용해 error 의 구체적인 원인(cause)을 명시할 수 있습니다.
      • 예시
        try:
            ...
        except Exception as e:
            raise SaigeCustomError("additional info") from e
        
      • 최종적으로 출력되는 message string은 다음과 같은 규칙으로 생성됩니다.
        • "{default_message} ({additional_message}) ({cause_class}: {cause_message})"
      • 더이상 get_error_message() 함수를 사용하지 않습니다.

API 기능

  • Definition
    • API 기능은 코드베이스 외부에서 호출되는 기능을 의미합니다.
  • Rule
    • API로 제공되는 코드에는 항상 error.handler.error_handler 데코레이터가 적용되어야 합니다.
    • API로 제공되는 코드는 {module_directory}/api.py 경로에 위치해야 합니다.
      • 이는 코드베이스에서 API로 제공되고 있는 코드를 찾기 쉽게 합니다.
  • Guidance
    • API 코드의 임포트 경로에는 불필요한 타 패키지의 임포트를 최대한 제거해야 합니다.
      • 특히 torch 와 같은 큰 패키지가 불필요하게 임포트 되는 경우 불필요한 시간과 메모리를 소비하게 됩니다.

ResearchToolkit

  • Rule
    • ResearchToolkit에는 연구 수행을 위한 코드가 위치합니다 (Notion 툴, 벤치마크 툴 등).
    • ResearchToolkit만을 위한 디펜던시는 requirements/dev.txt로 분리해 관리합니다.