앱 충돌로(out) 제발!
게시 됨: 2020-02-12프로그래머는 코드에서 충돌을 피하려고 합니다. 누군가가 자신의 응용 프로그램을 사용하는 경우 예기치 않게 중단되거나 종료되지 않아야 합니다. 이것은 가장 간단한 품질 측정 중 하나입니다. 앱이 자주 충돌하는 경우 제대로 수행되지 않은 것입니다.
앱 충돌은 프로그램이 값을 0으로 나누거나 컴퓨터의 제한된 리소스에 액세스하는 것과 같이 정의되지 않았거나 잘못된 작업을 수행하려고 할 때 발생 합니다. 응용 프로그램을 작성한 프로그래머가 명시적으로 수행할 수도 있습니다. "그런 일은 절대 일어나지 않을 것이므로 건너뛸 것입니다." - 이것은 매우 일반적이며 완전히 비합리적인 생각은 아닙니다. 발생할 수 없는 경우도 있습니다. 발생하지 않을 때까지... 발생합니다.
깨진 약속
어떤 일이 일어날 수 없다는 것을 알고 있는 가장 일반적인 경우 중 하나는 API입니다. 백엔드와 프론트엔드 간에 합의했습니다. 이것이 이 요청에 대해 얻을 수 있는 유일한 서버 응답입니다. 이 라이브러리의 관리자는 이 기능 동작을 문서화했습니다. 함수는 다른 작업을 수행할 수 없습니다. 두 가지 사고 방식이 모두 옳지만 둘 다 문제를 일으킬 수 있습니다.
라이브러리를 사용할 때 가능한 모든 경우를 처리하는 데 도움이 되는 언어 도구에 의존할 수 있습니다. 사용하는 언어에 유형 검사 또는 정적 분석이 없는 경우 직접 처리해야 합니다. 그래도 프로덕션 환경으로 배송되기 전에 확인할 수 있으므로 큰 문제는 아닙니다. 힘들 수 있지만 종속성을 업데이트하기 전에 변경 로그를 읽고 단위 테스트를 작성해야 합니다. 맞죠? 라이브러리를 사용하거나 라이브러리를 더 엄격한 유형으로 만들면 코드와 다른 프로그래머에게 더 나은 것을 제공할 수 있습니다.
백엔드-프론트엔드 통신은 조금 더 어렵습니다. 느슨하게 결합되는 경우가 많기 때문에 한쪽의 변경이 다른쪽에 어떤 영향을 미칠지 모른 채 쉽게 수행할 수 있습니다. 백엔드의 변경은 종종 프론트엔드의 가정을 깨뜨릴 수 있으며 둘 다 종종 별도로 배포됩니다. 그것은 나쁘게 끝나야 합니다. 우리는 인간일 뿐이고 때로는 상대방을 이해하지 못하거나 그 작은 변화에 대해 말하는 것을 잊는 경우가 있습니다. 다시 말하지만, 적절한 네트워크 처리에는 큰 문제가 없습니다. 디코딩 응답은 실패하고 우리는 이를 처리하는 방법을 알고 있습니다. 최고의 디코딩 코드라도 잘못된 디자인의 영향을 받을 수 있습니다.
부분 기능. 나쁜 디자인.
"여기에는 'isActive'와 'canTransfer'라는 두 가지 부울 변수가 있습니다. 물론 활성화되지 않은 상태에서는 전송할 수 없지만 이는 세부 사항일 뿐입니다." 여기에서 시작됩니다. 우리의 나쁜 디자인은 강타할 수 있습니다. 이제 누군가가 이 두 인수로 함수를 만들고 이를 기반으로 일부 데이터를 처리합니다. 가장 간단한 해결책은... 잘못된 상태에서 충돌이 발생하면 절대 발생해서는 안 되므로 신경 쓰지 않아도 됩니다. 우리는 가끔 신경쓰고 나중에 수정하거나 어떻게 해야 하는지에 대해 의견을 남깁니다. 하지만 결국 해당 작업을 완료하지 않고 배송될 수 있습니다.
// 의사 코드 함수 doTransfer(Bool isActive, Bool canTransfer) { If ( isActive 및 canTransfer ) { // 전송 가능한 작업 수행 } else if( isActive도 아니고 canTransfer도 아님) { // 전송할 수 없는 작업을 수행합니다. } else if ( isActive 및 not canTransfer ) { // 전송할 수 없는 작업을 수행합니다. } else { // 일명 ( isActive 및 canTransfer 아님) // 네 가지 가능한 상태가 있습니다. // 이런 일이 발생해서는 안 됩니다. 활성화되지 않은 경우 전송을 사용할 수 없어야 합니다. 크래시() } }
이 예는 어리석게 보일 수 있지만 때로는 이것보다 발견하고 해결하기가 조금 더 어려운 그런 종류의 함정에 빠질 수 있습니다. 부분 함수라고 하는 것으로 끝납니다. 이것은 다른 입력을 무시하거나 충돌하는 일부 가능한 입력에 대해서만 정의된 기능입니다. 부분 함수는 항상 피해야 합니다(동적 형식 언어에서 대부분의 함수는 부분 함수로 처리될 수 있습니다). 언어가 유형 검사 및 정적 분석으로 적절한 동작을 보장할 수 없는 경우 예기치 않은 방식으로 잠시 후 충돌이 발생할 수 있습니다. 코드는 계속해서 진화하고 있으며 어제의 가정이 오늘날에는 유효하지 않을 수 있습니다.
빨리 실패하십시오. 자주 실패합니다.
어떻게 자신을 보호할 수 있습니까? 최고의 방어는 공격이다! “빨리 실패하십시오. 자주 실패하십시오.” 그러나 우리는 앱 충돌, 부분적인 기능 및 잘못된 디자인을 피해야 한다는 데 동의하지 않았습니까? Erlang OTP는 예상치 못한 상태 후에 스스로 치유되고 실행 중에 업데이트된다는 신화적인 이점을 프로그래머에게 제공합니다. 그들은 그것을 감당할 수 있지만 모든 사람이 이런 종류의 사치를 갖는 것은 아닙니다. 그렇다면 왜 우리는 빠르고 자주 실패해야 합니까?
우선, 예상치 못한 상태와 행동을 찾기 위해 . 앱 상태가 올바른지 확인하지 않으면 충돌보다 더 나쁜 결과를 초래할 수 있습니다!
둘째, 다른 프로그래머가 동일한 코드 기반에서 공동 작업할 수 있도록 지원합니다 . 지금 프로젝트에 혼자라면 다른 사람이 있을 수 있습니다. 몇 가지 가정과 요구 사항을 잊어버릴 수 있습니다. 모든 것이 작동하거나 내부 메소드와 유형을 전혀 문서화하지 않을 때까지 제공된 문서를 읽지 않는 것이 오히려 일반적입니다. 그 상태에서 누군가가 예상치 못한 유효한 값으로 사용 가능한 함수 중 하나를 호출합니다. 예를 들어 정수 값을 사용하고 해당 시간(초) 동안 대기하는 '대기' 함수가 있다고 가정해 보겠습니다. 누군가 '-17'을 전달하면 어떻게 될까요? 그렇게 한 후 즉시 충돌하지 않으면 심각한 오류와 잘못된 상태가 발생할 수 있습니다. 영원히 기다리거나 전혀 기다리지 않습니까?

의도적 충돌의 가장 중요한 부분은 우아하게 수행하는 것 입니다. 응용 프로그램이 충돌하는 경우 진단을 허용하기 위해 몇 가지 정보를 제공해야 합니다. 디버거를 사용할 때는 매우 쉽지만 디버거 없이 앱 충돌을 보고할 수 있는 방법이 있어야 합니다. 로깅 시스템을 사용하여 애플리케이션 실행 간에 해당 정보를 유지하거나 외부에서 볼 수 있습니다.
의도적 충돌의 두 번째로 중요한 부분은 프로덕션 환경에서 이를 방지하는 것입니다...
실패하지 마십시오. 항상.
결국 코드를 배송하게 됩니다. 당신은 그것을 완벽하게 만들 수 없습니다. 정확성을 보장하는 것에 대해 생각조차 하기에는 너무 많은 비용이 듭니다. 그러나 오작동하거나 충돌하지 않도록 해야 합니다. 우리는 이미 빠르고 자주 충돌하기로 결정했는데 어떻게 이를 달성할 수 있습니까?
의도적 충돌의 중요한 부분은 비프로덕션 환경에서만 수행하는 것입니다 . 애플리케이션의 프로덕션 빌드에서 제거된 어설션을 사용해야 합니다. 이렇게 하면 개발 중에 도움이 되고 최종 사용자에게 영향을 주지 않으면서 문제를 발견할 수 있습니다. 그러나 잘못된 응용 프로그램 상태를 피하기 위해 때때로 충돌하는 것이 좋습니다. 이미 부분 함수를 만든 경우 어떻게 이를 달성할 수 있습니까?
정의되지 않고 유효하지 않은 상태를 표현할 수 없도록 만들고 그렇지 않으면 유효한 상태로 대체합니다. 쉽게 들릴지 모르지만 많은 생각과 노력이 필요합니다. 아무리 많아도 버그를 검색하고 임시 수정을 하고… 자동으로 일부 기능이 발생할 가능성이 줄어듭니다.
// 의사 코드 함수 doTransfer(상태 상태) { 스위치 ( 상태 ) { 케이스 State.canTransfer { // 전송 가능한 작업 수행 } 케이스 State.cannotTransfer { // 전송할 수 없는 작업을 수행합니다. } 케이스 State.notActive { // 전송할 수 없는 작업을 수행합니다. } // 활성화되지 않은 상태에서 전송이 가능함을 나타내는 것은 불가능합니다. // 세 가지 가능한 상태만 있음 } }
어떻게 무효 상태를 불가능하게 만들 수 있습니까? 앞의 두 가지 예를 선택해 보겠습니다. 두 개의 부울 변수 'isActive' 및 'canTransfer'의 경우 이 두 변수를 단일 열거형으로 변경할 수 있습니다. 가능한 모든 유효한 상태를 철저하게 나타냅니다. 그때에도 누군가가 정의되지 않은 변수를 보낼 수 있지만 처리하기가 훨씬 쉽습니다. 잘못된 상태가 내부로 전달되어 모든 것을 어렵게 만드는 대신 프로그램으로 가져오지 않는 잘못된 값이 됩니다.
우리의 대기 기능은 강력한 유형의 언어에서도 훌륭하게 개선될 수 있습니다. 입력에 부호 없는 정수만 사용하도록 할 수 있습니다. 컴파일러가 유효하지 않은 인수를 제거하므로 이것만으로도 모든 문제를 해결할 수 있습니다. 그러나 언어에 유형이 없으면 어떻게 될까요? 몇 가지 가능한 솔루션이 있습니다. 첫 번째 – 그냥 충돌합니다. 이 함수는 음수에 대해 정의되지 않았으며 유효하지 않거나 정의되지 않은 작업을 수행하지 않습니다. 테스트 중에 잘못된 사용을 찾아야 합니다. 단위 테스트(어쨌든 수행해야 함)는 여기에서 정말 중요합니다. 두 번째 – 이것은 위험할 수 있지만 상황에 따라 유용할 수 있습니다. 가능한 경우 잘못된 상태를 수정하기 위해 비프로덕션 빌드에서 어설션을 유지하는 유효한 값으로 폴백할 수 있습니다. 이와 같은 기능에는 좋은 솔루션이 아닐 수 있지만 대신 정수의 절대값을 만들면 앱 충돌을 피할 수 있습니다. 구체적인 언어에 따라 대신 일부 오류/예외를 발생/발생시키는 것도 좋은 생각일 수 있습니다. 사용자에게 오류가 표시되더라도 충돌하는 것보다 훨씬 더 나은 경험이므로 가능하면 대체할 가치가 있습니다.
여기서 한 가지 예를 더 들어보겠습니다. 프론트엔드 애플리케이션의 사용자 데이터 상태가 어떤 경우에 유효하지 않게 되는 경우 강제로 로그아웃하고 충돌하는 대신 서버에서 유효한 데이터를 다시 가져오는 것이 더 나을 수 있습니다. 사용자는 어쨌든 그렇게 해야 하거나 끝없는 충돌 루프에 빠질 수 있습니다. 다시 한 번 – 비프로덕션 환경에서 이러한 상황을 주장하고 충돌해야 하지만 사용자가 외부 테스터가 되도록 해서는 안 됩니다.
요약
충돌하고 불안정한 응용 프로그램을 좋아하는 사람은 없습니다. 우리는 그것을 만들거나 사용하는 것을 좋아하지 않습니다. 개발 및 테스트 중에 유용한 진단을 제공하는 어설션으로 빠르게 실패하면 많은 문제를 조기에 포착할 수 있습니다. 프로덕션에서 유효한 상태에 대한 폴백은 앱을 훨씬 더 안정적으로 만듭니다. 유효하지 않은 상태를 표현할 수 없게 만드는 것은 모든 종류의 문제를 제거할 것입니다. 개발 전에 유효하지 않은 상태를 제거하고 대체하는 방법에 대해 생각할 시간을 좀 더 주고 글을 작성하는 동안 몇 가지 주장을 포함하도록 시간을 더 주세요. 오늘부터 애플리케이션 개선을 시작할 수 있습니다!
더 읽어보기:
- 계약에 의한 디자인
- 대수 데이터 유형