일시적인 버그 방지

이 페이지는 개발자가 일시적인 버그를 방지하기 위한 아키텍처 패턴과 팁을 다룹니다.

일반적인 원인

일시적 버그를 해결할 때 자주 나오는 몇 가지 원인을 확인했습니다.

  • 백엔드나 프론트엔드에서 상태 관리가 더 필요함.
  • 프론트엔드 코드가 개선이 필요함.
  • 테스트 커버리지 부족.
  • 경합 조건.

프론트엔드

응답 순서에 의존하지 마세요

여러 요청을 처리할 때, 응답의 순서가 트리거된 순서와 일치한다고 가정하는 것은 쉽지만 항상 그렇지는 않으며, 순서가 바뀌면 발생하는 버그를 유발할 수 있습니다.

예시:

  • diffs_metadata.json (가벼움)
  • diffs_batch.json (무거움)

원하는 기능이 두 가지 모두에서 데이터를 필요로 하는 경우, 작업하기 전에 두 가지가 모두 로드되었는지 확인하세요.

수동 테스트 시 느린 연결 시뮬레이션

브라우저의 개발자 도구에 네트워크 조건 템플릿을 추가하여 느린 연결과 빠른 연결 간에 전환할 수 있도록 합니다.

예시:

  • 터틀:
    • 다운로드: 50kb/s
    • 업로드: 20kb/s
    • 지연 시간: 10000ms

축소된 요소

이벤트 리스너를 설정할 때 이벤트 위임을 사용할 수 없는 경우, 확장된 내용에 대해 모든 관련 이벤트 리스너가 설정되었는지 확인하세요.

특히, 해당 확장된 내용이 다음과 같은 경우:

  • 보이지 않음 (display: none;). 일부 JavaScript는 측정을 하는 경우와 같이 요소가 제대로 작동하려면 해당 요소가 보이어야 할 수도 있습니다.
  • 동적으로 변경되는 콘텐츠 (AJAX/DOM 조작).

충족되지 않은 조건으로 인한 일시적인 버그를 감지하기 위해 단언 사용

일시적인 버그는 애플리케이션 상태가 하나 이상의 조건을 만족하고 실행된 코드의 맥락에서 일어납니다. 우리는 서버 측 API 응답이 항상 특정 속성 그룹을 포함한다고 가정하는 기능을 작성할 수도 있고, 응용 프로그램이 새로운 상태로 성공적으로 전환된 경우에만 작업이 실행되는 것으로 가정할 수도 있습니다.

일시적인 버그는 사용자나 개발자에게 미달 조건에 대해 경고하는 메커니즘이 없기 때문에 디버깅하기 어렵습니다. 이러한 상황에 유용한 디버깅 기술은 모든 가정을 명시적으로 만들기 위해 단언을 배치하는 것입니다. 이를 통해 버그의 원인이 되는 미달 조건을 감지하는 데 도움이 될 수 있습니다.

상태 변이 전 조건 단언하기

미달 조건으로 이어지는 일시적인 버그로 이어지는 일반적인 시나리오는 사용자 작업이 완료된 경우에만 상태를 변이시키는 폴링 서비스가 있는 경우입니다. 우리는 이러한 선행 조건을 명시적으로 만들기 위해 단언을 사용할 수 있습니다.

// 이 액션은 폴링 서비스에서 호출됩니다. 액션이 디스패치될 때 모든 선행 조건이 충족되었다고 가정합니다.
export const updateMergeableStatus = ({ commit }, payload) => {
  commit(types.SET_MERGEABLE_STATUS, payload);
};

// 단언을 추가함으로써 모든 선행 조건을 명시적으로 만들 수 있습니다.
export const updateMergeableStatus = ({ state, commit }, payload) => {
  console.assert(
    state.isResolvingDiscussion === true,
    'Resolve discussion request must be completed before updating mergeable status'
  );
  commit(types.SET_MERGEABLE_STATUS, payload);
};

API 계약 단언하기

단언을 사용하는 또 다른 유용한 방법은 서버 측 엔드포인트에서 반환된 응답 페이로드가 API 계약을 충족하는지 여부를 감지하는 것입니다.

관련 독서 자료

디버그 하기!는 결정적이지 않은 버그를 진단하고 수정하고 디버그하기 쉬운 소프트웨어를 작성하기 위한 기술에 대해 다룹니다.

백엔드

락이 있는 Sidekiq 작업

Sidekiq을 통해 비동기 작업을 처리할 때, 동일한 인수를 가진 2개의 작업이 동시에 진행될 수 있습니다. 올바르게 처리되지 않으면 오래된 또는 부정확한 상태로 이어질 수 있습니다.

예를 들어, 객체의 상태를 업데이트하는 워커가 있는 경우, 워커가 개체의 상태를 업데이트하기 전에 적절한 상태를 확인해야 합니다.

동시에 2개의 작업이 진행될 때, 작업의 순서가 다음과 같이 될 수 있습니다:

  1. (워커 A) #check_state 호출
  2. (워커 B) #check_state 호출
  3. (워커 B) #update_state 호출
  4. (워커 A) #update_state 호출

이 예에서, 워커 B가 업데이트된 상태를 설정해야 합니다. 그러나 워커 A#update_state를 너무 늦게 호출합니다.

이러한 상황은 데이터베이스 락 또는 Gitlab::ExclusiveLease를 활용하여 회피할 수 있습니다. 이렇게 하면 작업이 한 번에 하나씩 진행되게 할 수 있습니다. 또한 이를 통해 작업을 멱등하게 표시할 수 있습니다.

재시도 메커니즘 처리

객체/레코드가 다시 확인될 수 있는 상태에 있을 수도 있는 시점이 있습니다.

객체가 재확인 가능한 상태에 있는 경우, 사용자가 무엇을 해야 하는지 알 수 있도록 적절한 메시지가 표시되고, 재시도 기능이 트리거될 때 상태가 올바르게 재설정되도록 해야 합니다.

오류 로깅

오류 로깅은 임시 버그를 직접적으로 막는 것은 아니지만 디버깅하는 데 도움이 될 수 있습니다.

코딩할 때 가끔은 특정 예외가 발생할 것으로 예상하고 해당 예외를 처리합니다.

오류가 발생했을 때 로깅하는 것은 사용자가 볼 수 있는 임시 버그의 원인이 될 수 있으므로 오류를 처리하는 데 도움이 됩니다. 버그 보고서를 조사하는 동안, 엔지니어는 발생한 시간에 대한 로그를 살펴봐야 할 수 있습니다. 오류가 로깅되는 것을 확인하면 잘못된 점을 다르게 처리할 수 있음을 의미할 수 있습니다.