본문 바로가기

공부/컴퓨터

Writing Solid Code 요약

반응형
버그는 개발자의 책임이며, 개발자가 찾아 없애야 한다
일반적으로 테스팅 그룹은 소프트웨어 상품의 전체 버그중 60 퍼센트만을 발견해 낸다. 결국, 개발자들은 테스팅 그룹이 소프트웨어의 구석구석에 숨어 있는 모든 버그를 발견할 수 없다는 것을 인정하게 되었다. 버그를 발견하고 수정하는 것은 개발자의 책임이라는 점을 분명히 하자. 매일 거의 출하 가능한 상품을 만든다는 목표를 설정하고, 버그가 발견되면 다른 새로운 작업으로 넘어가기 전에 그 버그를 수정해야만 한다.

중요한 2가지 질문
* 어떻게 하면 이 버그를 자동으로 발견할 수 있었을까
* 어떻게 하면 이 버그를 막을 수 있었을까

모든 컴파일러 경고 옵션을 활성화 시킨다
(사소한 경고라도 신경을 써라. 언젠가 골치거리로 변할지 모른다.)

단위검사를 갖고 있다면 그것을 사용하라
(JUnit, CppUnit 같은 것들과 TDD 같은 방법론 등을 적극적으로 활용하자.)

프로그램을 디버그용과 판매용의 두가지 버젼으로 관리한다
(디버그 버젼에는 중요한 체크포인트를 점검하는 코드를 과감히 추가하자.)

함수의 인수를 확인하기 위해 assertion을 사용한다
모든 진입점에서 데이터를 점검하면 버그를 발견하는데 오래걸리지 않는다.
가장 좋은 점검위치는 버그가 발생했을때 자동으로 발견되는 곳이다.

내 코드를 사용하는 다른 사람들의 시간을 아껴줄 것. 모호한 assertion에 대해 설명을 해놔라
(충분한 주석) 다른 사람이 소스코드를 보고 왜 assertion을 하는지를 이해하지 못하면 서로 시간낭비를 하게 된다.

임시적인 가정을 제거하거나 그것들이 적절한지 assert 한다
long이 4바이트인지 점검할 필요가 몇년이내에 생길 수 있다면 지금 점검코드를 추가하라.

불가능한 조건을 점검하기 위해 assertion을 사용하라
절대 발생할리 없는, 발생해서는 안되는 조건에 대해서 assertion을 사용하라

방어적 프로그램을 할 때 버그를 숨기지 않도록 주의한다
내가 작성한 방어적 기법 (프로그램이 죽지 않도록 처리해서) 때문에 버그가 숨겨질 가능성이 있다면 이를 경고할 수 있는 assertion을 추가하라.

결과를 확인하기 위해서는 이차적인 알고리즘을 사용한다
디버그 버젼에서는 최적의 실행알고리즘과 꼼꼼하게 체크하는 알고리즘 2개를 실행해서 결과를 비교하도록 한다.
엑셀의 예 : 엑셀은 속도가 관건이므로 재계산이 불필요한 셀을 판별해내는 복잡한 최적화 계산엔진이 있는데, 이를 점검하기 위해 무식하게 꼼꼼히 계산하는 디버그 엔진의 결과와 항상 비교하도록 되어 있다.

버그가 나타날때까지 기다리지 말고, 시동검사를 사용하라
여러분은 자신의 코드에서 위험스런 무언가를 발견할때마다, 자신에게 어떻게 하면 가능한 한 빨리 이 버그를 자동으로 발견할 수 있을까 라고 물어야 한다. 습관적으로 이런 질문을 함으로써 자신의 프로그램이 보다 강력해질 수 있는 여러 종류의 방법을 찾게 된다. 동료들과도 끊임없이 의논하며 의견을 나눠라.

불규칙한 동작을 제거하라. 발견된 버그를 재현할 수 있도록 조치하라
불규칙한 동작을 최소화 할 수 있도록 코드를 추가하라.

메모리 내의 쓸모없는 데이터는 오용되지 않도록 파괴하라
release 된 메모리는 불필요한 메모리의 티가 나도록 조치해두자

아주 드물게 일어나는 동작은 자주 일어나게 만들어라
디버그 버젼에는 점검을 위해 일부러 이런 현상을 일어나게 만드는 것도 방법이다.

서브시스템 점검용 루틴을 작성하고, 가능한 한 자주 사용하라
자료구조를 사용하기 전에 점검하는 코드를 디버그 버젼에 추가하자.
프로그램이 노는 시간을 사용해 최대한 자주 점검하는 것도 방법이다.

테스트용 루틴을 세심하게 설계하라. 아무렇게나 선택한 것이 있어서는 안된다
모든 결정은 내가 어떻게 한 것이 버그를 유발할까. 아니면 버그를 찾기 쉽게 도와줄까를 30초만 더 생각해보자. 메모리가 0으로 채워진 경우보다 0xCC로 채워진게 버그를 더 찾기 쉽다.

매우 명료하면서도 통일된 테스트 루틴을 작성하라
새로 들어온 프로그래머라도 숙련된 프로그래머의 시간을 빼앗지 않고도 스스로 익힐 수 있을 정도로 명료할 수도록 좋다.

디버그 버젼을 시판용 버젼처럼 생각하지 말라. 크기와 속도를 희생하고 버그를 잡는 것이 정상적인 디버그 버젼이다
하지만, 디버그 버젼을 개발팀 외부에 배포하는 것은 가급적이면 삼가해라.
또한 테스트도 하면서 작고 빠른 코드를 만들어 낼 수는 없을지에 대해 항상 고민하라.

버그를 지금 잡을 것인가 미루어 둘 것인가?
(미뤄둬 봐야 자신의, 팀의 손해다. 아무리 바빠도 발견되면 즉시 잡도록 하자.)

코드를 추적하면서 확인하여, 적극적으로 버그를 잡자
새로운 코드를 추가하면 반드시 브레이크 포인트를 설정해 한 줄 한줄 점검을 해라.
물론, 시간이 더 걸리지만 나중에 발견된 버그를 잡는 것보다는 훨씬 시간이 적게 걸린다.

코드의 구석구석을 모두 확인하자
모든 코드를 한번 이상 구석구석 확인해봐야 자신도 자신이 생기며 버그가 없다는 확신을 할 수 있을 것이다.

코드를 추적할때는 데이터의 흐름에 초점을 맞추라
데이터의 흐름 = 코드의 혈액, 0을 이용해 테스트 말고 눈에 띄는 다른 숫자를 사용하자. 한줄 한줄 꼼꼼히 추적하자. 해보라, 해보면 좋아하게 될 것이다. 결국 이것이 시간을 절약해준다.

에러가 일어나는 조건을 명백히 표시하여, 알아보기 쉽게하라. 에러코드를 리턴값에 묻히지 않도록 해라
다른 프로그래머가 한번에 실수 없이 코드를 작성하도록 하라. 한 함수가 여러 용도로 사용되어 이해하기 힘들게 만들지 마라.

여러분이 작성한 인터페이스에 함정이 없도록 조심하라
한번만 더 생각해보라. realloc 같은 함수처럼 다용도로 사용되어 이로 인한 함정에 빠져 전세계의 프로그래머들이 낭비하는 시간을 생각해보자. 우리는 그러한 함수를 만들어내지 말아야 한다.

다기능 함수를 만들지 말아라. 변수의 검증을 보다 용이하게 할 수 있도록 함수를 세분화하여 작성하라
realloc이야말로 지나치게 세밀하게 구현된 함수의 대표적인 예이다. 함수 하나로 되어 있는 완벽한 모미리 메니저다. 무지하게 나쁜 인터페이스의 전형적인 예다.

애매모호한 태도는 금물이다. 함수의 변수들을 명료하게 정의하라

합법적인 데이터를 입력하면 절대로 실패하지 않는 함수를 작성하라

호출하는 시점에서 이해하기 쉬운 함수를 작성하라. 불린 변수를 사용하지 말라
함수 호출문을 읽기만 해도 무슨 내용인지 알 수 있도록 하는게 가장 좋다.

사용상 주의점은 함수 안에 주석문으로 기록해두자

악마는 사소한 곳에 숨어있다
튼튼한 인터페이스를 설계하는 것은 그리 어렵지 않지만, 몸에 배어있는 코딩 습관을 버리겠다는 의지와 심사숙고하는 자세하 필요하다. 인터페이스만 약간 바꾸는 것만으로도, 다른 프로그래머들을 버그 없는 코드로 작성하는 길로 인도할 수 있다.

이 변수 혹은 표현식이 오버플로우 또는 언더플로우를 일으킬 수 있을까? 라는 질문을 항상 잊지 말자

함수의 실제 내용을 가능한 한 완벽하게 구현하라. 비슷하다는 버그가 있다는 뜻이다

여러 부분의 코드로 함수를 구현하지 말라
자기 일만 하는 함수로 만들어라. 더 작게 나눌 수 있으면 나눠라.

불필요한 if 문을 제거하라
특별한 경우는 단 한번으로 끝내라. 코드에서 군살을 빼자.

프로그래밍 언어의 위험한 구문은 사용하지 말라
서로 다른 종류의 연산자를 함께 사용하지 말라. 어쩔 수 없는 경우라면 괄호를 이용하여 각각의 연산을 명확하게 구분하라.
C 코드가 짧다고 해서 생성되는 기계어 코드가 짧은 것은 아니다.
필요없는 괄호라 생각되어도 없애지 말아라.

에러를 리턴하는 함수를 사용하지 말자
함수가 에러를 리턴한다면 다른 프로그래머들이 그 에러상황을 잘 못 처리하거나 무시하기 쉽다.

당신의 몫이 아닌 메모리는 절대 사용하지 말라
소유권을 해제한 메모리는 다시 사용하지 말라.

출력용 메모리를 작업용 버퍼로 사용해서는 안된다
필요한 버퍼는 필요할때 만들어 사용하며, 다른 용도로 같이 사용하지 말아라.
데이터를 정적 메모리 또는 전역 메모리로 보내지 마라.
자기것은 자기가, 전역 버퍼는 사용하지 말아라.

기생 함수를 만들지 말라
다른 함수의 기능을 가져다 만들때는 신중하게 하자. 다른 함수가 고쳐지면 이 함수는 바로 이상동작을 일으킬 확율이 많다.

프로그래밍 언어를 오용 / 남용하지 말라

코드를 작성할때는 평범한 프로그래머들을 생각하라
코드를 작성할때는 나중에 수정할 프로그래머들을 잊지 말자.

버그없는 프로그램을 만들기 위해 필요한 것은 올바른 자세와 습관이다
프로그래밍 자세와 습관이 '버그 유발형' 이라면 아직 갈 길이 먼 사람이다.

버그는 그냥 없어지지 않는다
버그는 절대로 스스로 자살하지 않는다.
1. 버그리포트가 잘 못 되었거나
2. 다른 프로그래머가 고쳤거나
3. 여전히 존재하지만 나타나지 않거나
어떤 경우인지 알아낼 책임은 프로그래머에게 있다.

버그 잡기를 뒤로 미루지 말고, 지금 당장 잡아라
* 지금 잡는 것이 편하고 적은 시간이 걸린다.
* 실수는 일찍 깨닫을 수록 같은 실수를 반복하지 않는다.
* 코드를 빠르게 작성하지만 꼼꼼하지 못한 프로그래머들이 제품 전체에 엉성한 흔적을 많이 남기는 상황을 막을 수 있다. 절대로 버그를 잡기 전에는 다른 일을 시키지 말아라.
* 버그를 나중에 몰아서 잡는 것은 프로그래머들의 사기에도 영향을 미친다. 계속 버그만 잡고 있으면 사기는 떨어진다.

증상만 치료하지 말고 원인을 제거하라
원인을 제거하지 못하면, 유사한 버그들이 계속해서 나타난다.

제품의 성패를 좌우할 정도가 아니라면, 절대로 코드를 청소하지 마라
수정한 코드는 항상 새로 작성한 코드로 간주하고 철저히 테스트 해야 한다. 그럴 자신이 없으면 절대로 섣불리 남의 코드를 청소하지 말아라.
나와 함께 일하는 프로그래머들은 절대 바보가 아니다.

불필요한 기능을 추가하려 하지 말라
별로 가치가 없는 기능은 과감히 제거하라.

기능을 추가하는 것은 절대 공짜가 아니다
꼭 필요한 기능이 아니라면 제품의 성패에는 도움을 주지 않으면서, 버그의 확율은 높이기만 하는 기능이 되기 쉽다.

불필요하게 융통성이 큰 프로그램을 작성하지 말라
사용하게 쉽게 만들기는 해야 하지만, 지나치게 융통성을 주게 만들지는 말아라.
이 두가지는 전혀 다른 이야기다.

문제가 해결될때까지 여러 방법을 해보는 것을 피하라. 여유를 갖고 정확한 해결책을 찾는 것이 낫다
이렇게 해보자 라는 의견들을 모아 고려는 해보되, 제대로된 방법을 찾기 위해 노력하라. 정확히 아는 사람은 이렇게 해보라는 충고가 아닌 이렇게 하면 됩니다 라고 의견을 줄 것이다.

작업단계를 작게 나누어 코딩과 테스트를 자주 반복하라. 계획이 지체되더라도 반드시 테스트를 끝낸 뒤에 다음 단계로 넘어가라
테스트는 나중에 몰아서 할 수 없다. 중간 중간 틈틈히 해나갈 수 있는 방법을 찾아라.

테스트 그룹이 코드에서 버그를 모두 발견해 줄 것이라고는 기대하지 말라
테스트 그룹의 사람들이 프로그래머 만큼 소스코드를 이해하고 테스트 하는 것은 불가능하다.

테스트 그룹은 버그를 찾아낼 의무가 없다
버그 발견에 대한 프로그래머의 반응은 놀라움 이어야 하며, 이들에게 감사해야 한다. 그리고 모든 버그는 중요하다. 중요하지 않은 버그는 없다.

자신만의 우선순위를 정하고 그것을 꼭 지켜라
정확성, 전체적인 효율, 테스트 용이성, 코드의 크기, 일관성 등 자신만의 규율을 정해라. 그러면 아주 오래된 코드를 보더라도 항상 그때 내가 왜 그렇게 코딩을 했는지 답할 수 있다. 예전 소스코드를 열어 보았을때 '내가 왜 그렇게 했는지 모르겠다.' 라고 답하게 된다면 자신만의 우선순위가 없다고 봐야 한다.

똑같은 버그에 다시 당하지 말라
프로그램에 버그가 없도록 하기 위해서는 많은 노력과 수고가 필요하다.
앞으로 버그를 발견할때마다 전에는 어떻게 버그의 원인을 찾았고 어떻게 그것을 해결했는가를 스스로에게 물으면서 자기만의 버그 퇴치법을 완성시키고 이것을 여러 사람들과 공유하라.
막연히 열심히 하는 것으로는 안된다. 전략적인 사고와 목표를 가지고 노력해야 한다.

부록 A의 코딩점검목록
실제 코딩과정에서 체크해볼만한 리스트를 제공하고 있다. 항상 점검해보자.



출처 : ezdoum.com
반응형