Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[NDC12] 변화량 분석을 중심으로 한 저비용 고효율의 지속가능한 코드퀄리티 관리법 - 송창규

ChangKyu Song
April 28, 2013
260

[NDC12] 변화량 분석을 중심으로 한 저비용 고효율의 지속가능한 코드퀄리티 관리법 - 송창규

ChangKyu Song

April 28, 2013
Tweet

Transcript

  1. 발표자 소개 송창규 (남, 31세, 애인없음) 잡캐 프로그래머 [email protected] twitter

    @innovari Personal History 한스타 개발 (1999) 넥슨 크레이지 아케이드 BnB (2002) 디지팡 (2002~2003) 빅샷 (2004~2006) 버블파이터 (2006~2010) 마비노기2 (2010~2011) 네오플 던전앤파이터 (2011~)
  2. 발표 아젠다 게임 업계는 이미 레드오션 신규개발 vs. 라이브의 비중

    변화 패러다임의 변화 일단 만들 수 있나 어떻게 성공할 수 있나 성공한 프로젝트를 어떻게 개선/유지 하나
  3. 발표 아젠다 점점 중요해지고 있는 그동안 소홀했던 점들에 대해 함께

    고민 시도사례 / 아이디어 공유 돈을 벌고 있는 (그러나 노쇠해져가는) 프로젝트를 어떻게 잘 개선/유지 할 것인가 그것을 어떻게 지속가능 하게 만들 것인가
  4. 코드 퀄리티 관리 일반적으로 이야기되는 코드 퀄리티 관리 깨진 창문을

    방치하지 말자: 관리되지 않는다는 느낌 으로 인식시키면 안됨
  5. 코드 퀄리티 관리 관리되지 않는다는 느낌을 벗어나기가 힘들다 규모에 압도당하기

    쉽다 현명한 포기가 필요 Kowloon Walled City in Hong Kong – 대표적인 슬럼 도시
  6. 청소 메타포: 코드 퀄리티 관리는 청소와 흡사 살다보면 어지럽혀진다 어지럽힌걸

    꼭 청소할 필요는 없다 청소하지 않을수록 점점 살기 불편해진다 AND.. 청소되지 않은 집에서는 더 쉽게 어지럽히게 된다 ( 여럿이 사는 집이라면 더욱 ) 조금만 어지럽혀진 집은 그때그때 쉽게 정리하면 되지만, 대단히 어지럽혀진 집은 맘잡고 대청소를 해야한다 ( 여럿이 사는 집이라면 동의도 구해야한다 )
  7. 청소 메타포: 청소와 다른점? Time Size Code Size Team Size

    라이브 돌입 해외 진출 늘어가는 괴리 프로그래머 증가엔 제한이 있지만 코드는 시간에 따라 무한히 증가 코드퀄리티 관리 건물이 커질수록 사람 수도 증가 건물 청소
  8. 난관: 규모가 커지면… 어디서 창문이 깨지는지조차 알 수 없는다 한가지를

    정리하는 것도 비용이 너무 크다 코드 퀄리티 저하/향상이 체감되지 않는다 개선점이 유지되지 않고 곧 다시 망가진다 다수 인원의 alignment 가 쉽지 않음 <학습된 무기력>에 의해 개선의지 자체가 퇴색
  9. 피할수없는 코드 오염 인정하자: 라이브 서비스 코드 는 어차피 망가진다

    특히 라이브로 갈수록 “그거 할 시간에 컨텐츠를 더..” “괜히 건드렸다가 터지면..” 코드가 방대해질수록 학습된 무기력 프로그래머가 많아질수록 책임분산 TD 역할이 흐려질수록 일관성 있는 방향 alignment 약화 (퀄리티 ≒ 일관성)
  10. 하지만 정도의 차이가 있다 여러 프로젝트 경험이 있다면 경험해봤을 것

    더 관리되는 코드와 덜 관리되는 코드의 차이 더 잘 망가지는 코드와 덜 망가지는 코드의 차이 망가지는 속도의 차이 개발 편의성, 안정성과의 연관성 분명 중요한데 어떻게 퀄리티를 끌어올릴지는 막막..
  11. 시간을 고려하여 관점 확장 자동화 기법의 기존 관점: (Static Analysis

    등..) “소스코드를 데이터 로 보기” 시간 개념을 도입, 확장한 관점: “소스코드를 변화하는 데이터 로 보기” 현재 데이터 과거 데이터 미래 데이터 변화 변화 데이터
  12. 변동성 소프트웨어 퀄리티에서의 화두: “어떻게 나눌 것인가” 논리적인 분류와 함께

    의존성이 주로 이야기됨 변동성 의존성과 함께 가장 중요한 고려사항 잘 언급되지 않는 경향 김학규 님의 글 중, “ 소프트웨어를 잘 만드는 방법 - 변동성” 아키텍팅에 관심을 있다면: 강력 추천
  13. 변동성 변동성이 낮은 것이 변동성이 높은 것에 의존하면, 변동성이 낮은

    것의 변동성이 그만큼 높아진다 게임 엔진이 게임 로직을 참조하면 엔진의 변동성이 높아짐 UI System 이 UI Logic 을 참조하면 UI System 의 변동성이 높아짐 …자동차 범퍼의 예를 생각해보면 쉽게 이해가 갈 것이다. 차체와 범퍼가 구분되어 있지 않으면, 범퍼의 교체 비용이 차체까지 확산되는 것이다. - from <소프트웨어를 잘 만드는 방법 – 변동성> - 김학규
  14. 변동성을 기준으로 나누기 “프로젝트 전체의 유지 보수 비용을 낮추기 위해서는

    변동성이 높은 부분과 낮은 부분을 올바르게 나누어야 한다” “…보통 자동차는 10장 내외의 철판, 범퍼등의 파트로 나뉘어져 있다. 만약 100 장 이상의 철판으로 잘게 나뉘어진 자동차를 상상해보라. 아마 자동차가 아니라 누더기가 되지 않을까?” 간단한 접촉사고가 났다고 생각하면.. - from <소프트웨어를 잘 만드는 방법 – 변동성> - 김학규
  15. “변동성의 분리 사례” Implementation Interface Data Structure Algorithms Data Code

    Script Source C++ Code Logic Code Framework Code .cpp .h - from <소프트웨어를 잘 만드는 방법 – 변동성> - 김학규 pimpl, … Template Data-driven Scripting Framework / Library C++ compiler 헤더 파일 (.h) vs. 소스 파일 (.cpp) 인터페이스 vs. 구현 알고리즘 vs. 데이터 타입 코드 vs. 데이터 C++ 소스 vs. 스크립트 소스 프레임워크 코드 vs. 로직 코드
  16. 현재 데이터 과거 데이터 미래 데이터 변화 변동성 측정 “변동성

    을 정량적으로 측정 가능하지 않을까?” SVN 등 VCS 를 쓴다는 것은 과거의 코드와 코드 변화를 가지고 있다는 것 (정성적) 소프트웨어 변동성을 (정량적) Repository 변화량으로 측정해보자! 변화 과거 변화량 분석
  17. 기대효과: “설계”와 “구현”을 위한 프로파일링 공간적 내용만 볼 수 있는

    소스코드에 시간적 변화량를 가시화하여 설계와 구현에 참고하고 활용할 수 있지 않을까? ex) 인터페이스가 적절히 잘 나누어졌는가? 모듈 재구성 / 리팩토링 현 구현에서 패턴화시켜 분리가 가능한가? 정적인 부분은 프레임웍화 동적인 부분은 스크립트화/데이터화
  18. 기대효과: “자주 변하는 곳이 중요한 곳이다” 최근 변한곳은 근미래에 변할

    가능성이 높으므로 중요도가 높다 한동안 변하지 않았다면 크게 신경쓸 필요가 없음 함께 늘어놓지 않고 숨겨두면 가독성 향상 최근 참조된 영역은 근미래에 다시 참조될 가능성이 높다
  19. 활용 #1 변동성이 높은 파일 변동성이 높은 파일은 말그대로 “HOT”

    한 파일 개선시켰을때 효용이 높다 신규 프로그래머의 진입장벽/정보과잉 해소
  20. 활용 #2 변동성이 낮은 파일 변동성이 낮은 파일은 별도 프로젝트나

    DLL 로 분리를 고려 개발환경의 복잡도를 낮출 수 있다 빌드 시간을 줄일 수 있다 WTL80 DNFSpline tinyxml EquipmentImagePack … 뭔가 라이브러리화 해야할것 같은 놈들
  21. 시간에 따른 파급력 조사 활용 #4 변동성이 높으면서 파급력도 높은

    파일 irdmonster.h 의 변경으로 인해 재컴파일 된 파일 수: 1주일 평균 약 3178 개 !
  22. commit 내용 중 바뀐 부분의 함수명을 추적해서 함수별 변화량 분석

    Profiler::Dump Profiler::ResetNode 활용 #5 함수별 변화량 분석
  23. Packet Handler 와 함께 setState 가 빈번하게 변화 State Machine

    관련해서 데이터드리븐 고려 가능 활용 #5 함수별 변화량 분석 (던파)
  24. 활용 #6 로그 메시지를 고려한 변화량 추적 (던파) bugfix 로그

    고려 변화량 추적: 나름 그럴싸 CNATFighter::setSTATE****** onChangeSkillEffect onBeforeAttack / onAfterAttack State Machine 관련해서 시스템 마련/교육/체크리스트 고려가능
  25. 활용 #6 로그 메시지를 고려한 변화량 추적 (던파) revert 로그

    고려 변화량 추적: 의의가 크지 않음 1. 대상 자체가 적다 2. 같은 기간에 작업된것이 함께 revert 되는 경우들
  26. 주로 보상치 계산에서 높은 빈번한 등장 (밸런싱 문제?) 서버에서 스크립트를

    통한 계산등을 고려할 수 있지 않을까? 활용 #6 로그 메시지를 고려한 변화량 추적 (CA BnB)
  27. 아예 소스 라인별로 변화량을 추적하면 더 구체적인 것들을 알 수

    있지 않을까? 소스 내 상수 또는 파라메터라던가.. 자꾸 변하는 if 내 조건문 이라던가.. 함수내 변동성이 두드러지는 부분의 분리를 위해서라던가.. BUT.. 파일과 함수는 indexing 할 key 가 있지만 라인별 바뀐 부분은 어떻게 추적 하지? 활용 #6 소스 라인별 변화량 추적
  28. 소스를 라인단위로 hashing 한 후 LCS 응용 1 공통부분 sequence

    의 변화량는 보존 이동 2 다른부분 sequence 의 변화량은 정규화 후 증가 활용 #6 소스 라인별 변화량 추적
  29. 소스를 라인단위로 hashing 한 후 LCS 응용 1 공통부분 sequence

    의 변화량는 보존 이동 2 다른부분 sequence 의 변화량은 정규화 후 증가 활용 #6 소스 라인별 변화량 추적
  30. 변동성이 큰 파일은 묶지 않으므로 기존 UnityBuild 의 단점 해소

    가능 #include 누락 오류가 숨겨지는 문제 파일 제거시 UnityBuild 재구축 필요 작은 cpp 만 바꾸어도 큰 파일로 컴파일됨 활용 #5 UnityBuild
  31. 활용 #5 UnityBuild 적용 53% 감소 (x2.1 향상) Before After

    반절만 적용하고도 2배 이상 속도향상
  32. 정리 1부 변화량 추적 변동성이 높은 낮은 파일은 별도로 분리

    개발환경의 복잡도 저하, 빌드 시간 단축 변동성을 기준으로 UnityBuild 적용 빌드 시간이 단축되면서 기존 UnityBuild 기법의 단점 보완 변동성이 낮으면서 파급력이 높은 파일 설계나 의존방식을 재점검 함수별 변동성 (+로그 고려) 그럴싸해보이는 결과 변동성이 높은 부분의 분리나 공정 효율 리서치 버그수정이 잦은 부분에 대해 시스템이나 프로세스 점검
  33. 왜 commitor 를 분석하지 않는가? “숫자의 함정” 에 빠질 수

    있다 “프로그래머별 커밋 당 버그 비율” 따위를 측정하게됨 이런 수치가 존재한다는것만으로 디펜시브해지고, 커밋을 오래 끌게된다 릴리즈 전 빠른 버그 발생과 수정은 오히려 권장사항 대표적인 숫자의 함정 “버그 발생 수” 프로그래머 KPI TDD 도입에 “테스트 실패율” 표시 “단기 매출” 중심의 기획 평가 …
  34. 영역으로 구분지어 점진적으로 정리하라 한번에 모든 것을 정리하는 것은 때론

    비용이 크고 효율적이지 않다 중요한 곳과 중요하지 않은 곳을 구분하여 정리한다 현명한 포기가 필요 정리된 곳과 정리되지 않은 곳을 구분하여 점진적으로 정리한다
  35. 어느경우 쓰레기를 쉽게 버릴것 같은가? “더럽네” vs. “깨끗하네” 로 인지됨

    청소한 후에는 사람들이 쓰레기를 쉽게 버리지 않음 청소 전 청소 후 다시 어지럽히지 않기
  36. 어느경우 쓰레기를 쉽게 버릴것 같은가? 양쪽다 “더럽네” 라고 인지 여전히

    사람들은 쉽게 쓰레기를 버린다 청소 전 청소 후 청소해도 보람이 없다! 다시 어지럽히지 않기
  37. 다시 어지럽히지 않기 지켜지지 않음 All or None: 전부 치우기

    전에는 효과를 보기 힘든것이 많다 보람이 없다 - “뭐 나아지는거 있어?” 관심없으면 잊혀진다 인간은 망각의 동물 알아도 습관은 고치기 힘들다 프로그래머가 많을수록 난이도 급상승 청소의 메타포만으로는 한계가 있다 보이는대로 일일히 체크하고, 잔소리하고, 고치는것은 구시대적
  38. 공정을 개선하라 시스템화, 프로세스 구축 필요 잔소리를 할 필요도 들을

    필요도 없다 힘들게 습관을 고칠 필요가 없다 실수를 걱정하지 않아도 된다 지속 가능하다
  39. 기존의 도구 / 시스템들 Assertion ASSERT(..) Lock Ordering Needs_Lock Check

    Caller Thread System Scoped Lock Smart Pointer format string Nullable Type / Non-nullable Type 분리 Validator Static Analysis Syntax Checking Unit Tests Compiler Warning 코드퀄리티 저하를 예방하는
  40. 코드퀄리티 저하를 예방하는 Assertion 특히 멀티쓰레딩 관련 Assertion 들은 매우

    유용: 같은 로직흐름에서 재현이 되지 않는 부분을 smoke test 만으로 감지할 수 있게 해줌 없을때와 천지차이 Lock Ordering 데드락 방지 Needs_Lock 락 없는 접근 방지 Check Caller Thread 부르지 말아야 할 쓰레드에서 부르는 것을 방지
  41. 리빙포인트: ASSERT 를 구분하면 좋다 Domain System / Performance-critical vs.

    Not critical Compile Compile / Do not compile Action Ignore / MsgBox / Log / SendPacket MsgBox Ignore this / Ignore this type / Ignore all
  42. 코드퀄리티 저하를 예방하는 System Scoped Lock Smart Pointer 여러분들 다

    아시는 그것 format string boost::format C#-style format Nullable Type / Non-nullable Type 분리 NULL check 는 가장 흔하면서 치명적인 실수 모조리 NULL check 를 넣는것이 좋지만은 않다
  43. 코드퀄리티 저하를 예방하는 Validator Static Analysis ex) LINT Syntax Checking

    ex) php -l /path/to/your/file.php Unit Test Compiler Warning 역시나 다 아시는 그것
  44. Validator: 문제 지속가능하지 않다 (일반적으로) 계속 운용되지 않는다 강제성이 없다

    / 행동을 유발하지 않는다 자주 볼수 있는 현상: 1. 한번 돌려보고 정리한 후  망각 2. 혹은 돌려보고 “이런게 있네”  망각 3. 정기적으로 돌려보려 하지만  잘될리 없다
  45. 현재 데이터 과거 데이터 미래 데이터 변화 지속 가능한 Code

    Validation 빌드시스템에서 validation 수행 지속적으로 수행 가능하다 관리된 상태를 위해 과거를 모두 정리할 필요가 없다 과거를 신경쓰지 않을 수 있다 커밋될 때 validation 을 수행 + 강제성을 부여할 수 있다 default 로 validation check 하되, 과거 데이터는 <ignore> tagging _CRT_SECURE_NO_DEPRECATE, #pragma warning(disable:####) Validator 에 파일마다 태깅하고 선택적으로 무시할 수 있는 기능을 넣어라 주의: 미래에 추가되지 않도록 유의할것. 가능하다면 추가되는것도 시스템으로 막는다 변화 미래 변화 관리 과거~현재는 영역으로 분리하고, 미래는 시간으로 분리하라
  46. Code Validation 활용 활용 제안: 단순 Coding Style 관리 금지된

    API 관리 주석 관리 중복 관리 스크립트 소스 오타 관리 Format String 관리 초기화 관리 삭제 관리
  47. 단순 Coding Style 관리 단순 coding style 관리 금지된 API

    관리 주석 관리 중복 관리 스크립트 소스 오타 관리 format string 관리 초기화 관리 삭제 관리 naming convention, white space, 금지된 API 호출, … ex) 별도의 Timer System 이 있기 때문에 GetTickCount, timeGetTime 이용 금지 파일 입출력시 전용 프레임웍 이용: CreateFile, fopen 이용 금지 쓰레드 생성시 CreateThread 이용 금지, _beginthread / _beginthreadex 만 사용할것 로직에서는 scoped lock 만 사용할것. EnterCriticalSection 직접 사용 금지
  48. 주석 관리 사용하지 않는 주석처리된 코드 미리 약속한 주석 컨벤션

    (doxygen/함수 파라메터 주석 등) 코드양에 비해 부족한 주석 단순 coding style 관리 금지된 API 관리 주석 관리 중복 관리 스크립트 소스 오타 관리 format string 관리 초기화 관리 삭제 관리
  49. 중복 관리 코드 퀄리티 관리에서 가장 치명적인것 중복을 감시해보자 추가된

    코드의 diff 부분을 column 으로 전체 소스를 row 로 잡아 일정 이상의 연속끊김 등을 처리한 LCS 응용으로 중복정도를 검사가능 whitespace는 무시하고 라인단위보다 구문분석단위면 좋겠지 시간복잡도 O(NM), 공간복잡도 O(2N) 만으로 OK backtrack 하려면 좀더 들지만.. 중복 단순 coding style 관리 금지된 API 관리 주석 관리 중복 관리 스크립트 소스 오타 관리 format string 관리 초기화 관리 삭제 관리
  50. 스크립트 오타 관리 스크립트는 편리하지만 오타에 의한 실수가 많음 실행이

    되어야만 확인가능 Symbol 만 뽑아낸 후 통계적으로 오타를 예방한다면? 다른 Symbol 과 문자열 유사도 높으면서 단 한두건밖에 존재하지 않는다면 경고 처리 단순 coding style 관리 금지된 API 관리 주석 관리 중복 관리 스크립트 소스 오타 관리 format string 관리 초기화 관리 삭제 관리
  51. Format String 사소하면서 치명적인.. NULL pointer 와 함께 사소하면서 치명적

    문제를 발생시킨다 boost::format 를 쓸 경우.. C++ 식 해결법이지만, 많은 사람에게 익숙하지 않음 (오히려 망가지기 쉽다) 별도 format string 을 사용하는 프로젝트엔 꼭 구코드가 함께한다 일관성이 흐트러지기 쉬움 단순 coding style 관리 금지된 API 관리 주석 관리 중복 관리 스크립트 소스 오타 관리 format string 관리 초기화 관리 삭제 관리
  52. Format String “커밋시 자동보정 하도록 하는건 어떨까?” Type Checking 은

    컴파일러에게 맡긴다 별도의 Static Analysis 를 위해선 C++ 컴파일러를 만들어야함 Validator 는 Type Checker 가 없을 경우 삽입하고 인자 개수와 Type Checker 만을 확인 아주 간단한 텍스트 프로세싱으로 가능 단순 coding style 관리 금지된 API 관리 주석 관리 중복 관리 스크립트 소스 오타 관리 format string 관리 초기화 관리 삭제 관리
  53. 자동보정 Pre-commit Hook 에서 커밋 내용을 바꿀 수 있나? 바꿀

    수 있다 BUT 바꾸면 안됨 SVN 클라이언트가 캐시로 인해 오동작 서버에서 별도로 변경 및 커밋을 Trigger 귀찮긴 하지만 생각해볼 수 있는 방법 단순 coding style 관리 금지된 API 관리 주석 관리 중복 관리 스크립트 소스 오타 관리 format string 관리 초기화 관리 삭제 관리
  54. 초기화 관리 또다른 사소하면서 치명적인.. NULL pointer 관리와 함께 사소하면서

    치명적 문제를 발생시킨다 당연하지만 누락이 빈번 int, float, pointer type 등의 primitive type 은 반드시 생성자에서 초기화하도록 강제 성능상 문제있는곳은 따로 ignore flag tagging
  55. 삭제 관리 사람들은 삭제를 하지 않는다 프로그래머만 국한된 얘기가 아님

    만드는건 당장 필요하지만 제거하는건 당장 필요하지 않다 보이지 않지만 중요한.. 문제가 되었을때는 이미 늦어있다 – Resource Leak 나중에 가면 대처하는것도 일이다 생몰 쌍 맞추기 생성자 / 파괴자 안의 생성/파괴 쌍을 규격화시키고 맞춰주기 delete 누락 방지 기본적으로 new 만 있고 delete 가 없을 경우 경고 소유권 이전시 별도 표시하도록 (ignore flag)
  56. 리빙포인트: Validator의 Action Level 을 구분하면 좋다 커밋 거부 자동

    보정 본인 통지 프로그래밍 팀 통지 프로그래밍 팀장 통지 Lazy Action 코드 주석, 중복 등은 빡빡하게 관리하면 더 불편해질 수도 있다 ex) 코드 주석은 1주일 후 자동 제거 3회 이상 중복 코드는 1주일마다 지속적 통지 시간에 따라 통지 수준을 높이기
  57. 기존 도구 / 시스템들 Assertion System Validator 지속 가능한 Code

    Validation Code Validation 활용 단순 Coding Style 관리 금지된 API 관리 주석 관리 중복 관리 스크립트 소스 오타 관리 Format String 관리 Initialization 관리 정리 3부 공정 개선을 위한 팁
  58. 추후 시도/개선사항 인터페이스의 변화를 추적 서비스 적용 여부와 함께 추적

    서비스 적용 전 버그 수정 커밋은 미덕 서비스 적용 후 버그 수정 커밋은 악덕 좀더 밀도있는 변화 추적 커밋 할때마다  컴파일 할때마다 Symbol 별 변화 추적 실제 변동성 진단에 기반한 효율적 개선 사례 다양한 지속가능한 Validator 시도
  59. 저비용 고효율 자동화 현명한 포기 점진적 정리 분리 사람이 일일히

    하기에 큰 규모 1. 분석 자동화 2. 운용 자동화 변동성이 많은 것이 중요한 것이다 중요한 것 위주로 신경쓴다  중요하지 않는것은 신경을 끈다 (현명한 포기)  중요하지 않는것은 신경이 쓰이지 않도록 한다 “청소하는 요령” 한번에 모두 정리하려면 힘빠진다. 점진적으로 정리해나간다 청소한곳과 안한곳을 구분짓는다 (공간적 분리) 청소한곳은 다시 어지럽히지 않는다 (시간적 분리)