일시적인 버그 방지
이 페이지는 개발자가 일시적인 버그를 방지하기 위해 따를 수 있는 아키텍처 패턴과 팁을 다룹니다.
일반적인 근본 원인
일시적인 버그를 해결할 때 자주 나타나는 몇 가지 근본 원인을 확인했습니다.
- 백엔드 또는 프론트엔드에서 더 나은 상태 관리가 필요합니다.
- 프론트엔드 코드에 개선이 필요합니다.
- 테스트 커버리지가 부족합니다.
- 레이스 조건이 발생합니다.
프론트엔드
응답 순서에 의존하지 않기
여러 요청을 다룰 때 응답 순서가 트리거된 순서와 일치한다고 가정하기 쉽습니다.
항상 그런 것은 아니며 순서가 바뀌었을 때만 발생하는 버그를 초래할 수 있습니다.
예시:
-
diffs_metadata.json
(가벼움) -
diffs_batch.json
(무거움)
기능이 둘 모두의 데이터를 필요로 하는 경우 작업을 시작하기 전에 두 데이터가 로드 완료되었는지 확인하십시오.
수동으로 테스트할 때 느린 연결 시뮬레이션하기
브라우저 개발 도구에 네트워크 조건 템플릿을 추가하여 느린 연결과 빠른 연결 사이를 전환할 수 있습니다.
예시:
- 거북이:
- 다운로드: 50kb/s
- 업로드: 20kb/s
- 지연: 10000ms
축소된 요소
이벤트 리스너를 설정할 때, 이벤트 위임이 불가능한 경우 모든 관련 이벤트 리스너가 확장된 콘텐츠에 대해 설정되어 있는지 확인하십시오.
확장된 콘텐츠가 다음일 때 포함됩니다:
-
보이지 않는 (
display: none;
). 일부 JavaScript는 요소가 제대로 작동하기 위해 보이는 상태여야 합니다. 예를 들어, 측정을 수행할 때 그렇습니다. - 동적 콘텐츠 (AJAX/DOM 조작).
충족되지 않은 조건으로 인해 발생한 일시적인 버그를 감지하기 위한 assertion 사용
일시적인 버그는 애플리케이션의 상태가 하나 이상의 조건을 충족한다고 가정하에 실행되는 코드의 맥락에서 발생합니다. 우리는 서버 측 API 응답이 항상 속성 그룹을 포함한다고 가정하는 기능을 작성하거나, 애플리케이션이 새로운 상태로 성공적으로 전환되었을 때만 작업이 실행된다고 가정할 수 있습니다.
일시적인 버그는 불만족한 조건에 대해 사용자나 개발자에게 알리는 메커니즘이 없기 때문에 디버깅하기 어렵습니다. 이러한 조건은 일반적으로 코드에 명시적으로 표현되지 않습니다. 이러한 상황에서 유용한 디버깅 기법은 주장을 배치하여 모든 가정을 명시적으로 만드는 것입니다. 이들은 어떤 충족되지 않은 조건이 버그를 유발하는지 감지하는 데 도움이 될 수 있습니다.
상태 변이를 위한 선행 조건 주(asserting pre-conditions on state mutations)
일시적인 버그로 이어지는 일반적인 시나리오는 사용자 작업이 완료된 경우에만 상태를 변형해야 하는 폴링 서비스입니다. 우리는 주장을 사용하여 이 선행 조건을 명시적으로 만들 수 있습니다:
// 이 작업은 폴링 서비스에 의해 호출됩니다. 이 작업이 분배될 때 모든 선행 조건이
// 충족되어 있다고 가정합니다.
export const updateMergeableStatus = ({ commit }, payload) => {
commit(types.SET_MERGEABLE_STATUS, payload);
};
// 주장을 추가하여 모든 선행 조건을 명시적으로 만들 수 있습니다
export const updateMergeableStatus = ({ state, commit }, payload) => {
console.assert(
state.isResolvingDiscussion === true,
'병합 가능한 상태를 업데이트하기 전에 논의 해결 요청이 완료되어야 합니다.'
);
commit(types.SET_MERGEABLE_STATUS, payload);
};
API 계약 주(asserting API contracts)
주장을 사용하는 또 다른 유용한 방법은 서버 측 엔드포인트에서 반환된 응답 페이로드가 API 계약을 충족하는지 감지하는 것입니다.
관련 읽기
Debug it!는 비결정론적 버그를 진단하고 수정하는 기술을 탐구하고 디버깅이 더 쉬운 소프트웨어를 작성하는 방법을 다룹니다.
백엔드
Sidekiq 작업과 잠금
Sidekiq를 통해 비동기 작업을 처리할 때, 동일한 인수로 2개의 작업이 동시에 처리되는 경우가 있습니다.
이를 올바르게 처리하지 않으면 outdated하거나 부정확한 상태가 발생할 수 있습니다.
예를 들어, 객체의 상태를 업데이트하는 작업자가 있다고 가정합시다. 작업자가 객체의 상태를 업데이트하기 전에(예: #update_state
), 적절한 상태가 무엇인지 확인해야 합니다(예: #check_state
).
동시에 2개의 작업이 진행 중일 때, 작업 순서는 다음과 같이 진행될 수 있습니다:
- (작업자 A)
#check_state
호출 - (작업자 B)
#check_state
호출 - (작업자 B)
#update_state
호출 - (작업자 A)
#update_state
호출
이 예시에서 작업자 B
는 업데이트된 상태를 설정해야 합니다. 그러나 작업자 A
가 #update_state
를 호출하는 시점이 조금 늦습니다.
이 문제는 데이터베이스 잠금 또는 Gitlab::ExclusiveLease
를 사용하여 방지할 수 있습니다. 이렇게 하면 작업이 한 번에 하나씩 처리됩니다. 이는 또한 작업이 멱등성으로 표시될 수 있도록 합니다.
재시도 메커니즘 처리
객체/레코드가 재검토 가능한 실패 상태에 있는 경우가 있습니다.
객체가 재검토 가능한 상태에 있는 경우, 사용자가 무엇을 해야 할지 알 수 있도록 적절한 메시지가 표시되도록 해야 합니다. 또한, 재시도 기능이 트리거될 때 상태를 올바르게 재설정할 수 있도록 해야 합니다.
오류 로깅
오류 로깅은 반드시 일시적인 버그를 직접적으로 방지하지는 않지만 그것을 디버깅하는 데 도움이 될 수 있습니다.
코딩할 때 가끔 예외가 발생할 것으로 예상하고 이를 진압합니다.
오류를 구제할 때마다 로깅하면, 사용자가 볼 수 있는 일시적인 버그를 유발하는 경우에 도움이 됩니다.
버그 보고서를 조사할 때 엔지니어가 오류가 발생했을 때의 로그를 살펴보아야 할 수도 있습니다. 오류가 기록된 것을 보면 잘못된 일이 발생했음을 나타내는 신호가 될 수 있으며, 이는 다르게 처리될 수 있습니다.