104
10만 라인 26280시간 PYCON APAC 2016 Minyoung Jeong The Beatpacking Company

10만 라인, 26280시간의 이야기

Embed Size (px)

Citation preview

Page 1: 10만 라인, 26280시간의 이야기

10만 라인 26280시간

PYCON APAC 2016 Minyoung Jeong

The Beatpacking Company

Page 2: 10만 라인, 26280시간의 이야기

Minyoung Jeong

CTO, The Beatpacking Company

2014/2015 PyCON KR

Founder, AWSKRUG

AWS Community Hero

Love Python!

Page 3: 10만 라인, 26280시간의 이야기
Page 4: 10만 라인, 26280시간의 이야기
Page 5: 10만 라인, 26280시간의 이야기

2013. 04. 04

Page 6: 10만 라인, 26280시간의 이야기

9 changed files with 787 additions and 0 deletions

Page 7: 10만 라인, 26280시간의 이야기

.gitignore README.rst

beat/__init__.py docs/Makefile docs/beat.rst docs/conf.py

docs/make.bat setup.py

Page 8: 10만 라인, 26280시간의 이야기

.gitignore README.rst

beat/__init__.py docs/Makefile docs/beat.rst docs/conf.py

docs/make.bat setup.py

Page 9: 10만 라인, 26280시간의 이야기

.gitignore README.rst

beat/__init__.py docs/Makefile docs/beat.rst docs/conf.py

docs/make.bat setup.py

Page 10: 10만 라인, 26280시간의 이야기

2016. 08. 10

Page 11: 10만 라인, 26280시간의 이야기
Page 12: 10만 라인, 26280시간의 이야기
Page 13: 10만 라인, 26280시간의 이야기
Page 14: 10만 라인, 26280시간의 이야기
Page 15: 10만 라인, 26280시간의 이야기

• REST API Server

• CMS

• Backoffice

• Automation

• … Client 개발을 제외한 거의 전부

Page 16: 10만 라인, 26280시간의 이야기

Single monolithic repository

Page 17: 10만 라인, 26280시간의 이야기
Page 18: 10만 라인, 26280시간의 이야기

스타트업에서 개발을 한다는건…

Page 19: 10만 라인, 26280시간의 이야기
Page 20: 10만 라인, 26280시간의 이야기

철로가 준비되는 시간을 기다려 주지 않는다

Page 21: 10만 라인, 26280시간의 이야기

일정을 개발이 주도하기 어렵고 변수가 많다

Page 22: 10만 라인, 26280시간의 이야기

목표 최소한의 품질은 항상 달성하자

Page 23: 10만 라인, 26280시간의 이야기

극한 직업 비트 개발팀이 일하는 법

Page 24: 10만 라인, 26280시간의 이야기

1. Fork

2. Push

3. Pull Request 4. Review & Approval

5. CI & Lint & Codecov 6. Result hook

7. Merge

Page 25: 10만 라인, 26280시간의 이야기
Page 26: 10만 라인, 26280시간의 이야기

“Github_장애.jpg”

Page 27: 10만 라인, 26280시간의 이야기

과업(Task) 단위의 PR

Page 28: 10만 라인, 26280시간의 이야기

“출시 3일전에 실행한 ‘git merge upstream master’ 에서 1395개의 충돌을 발견한 프로그래머”

Page 29: 10만 라인, 26280시간의 이야기

가능하면 작은 단위로

Page 30: 10만 라인, 26280시간의 이야기
Page 31: 10만 라인, 26280시간의 이야기

Review

Page 32: 10만 라인, 26280시간의 이야기
Page 33: 10만 라인, 26280시간의 이야기

Review의 이유

• 해당 구현에 대한 팀원간 이해를 높인다.

• 좀 더 나은 구현에 대한 토론을 가능하게 한다.

• 심지어는 해당 구현이 필요한지에 대한 얘기 조차도 할 수 있다.

• 인수 인계를 하게 된다(!)

Page 34: 10만 라인, 26280시간의 이야기
Page 35: 10만 라인, 26280시간의 이야기

Merge 정책

Page 36: 10만 라인, 26280시간의 이야기
Page 37: 10만 라인, 26280시간의 이야기

http://homu.io

Page 38: 10만 라인, 26280시간의 이야기
Page 39: 10만 라인, 26280시간의 이야기
Page 40: 10만 라인, 26280시간의 이야기

안전한 MASTER

Page 41: 10만 라인, 26280시간의 이야기
Page 42: 10만 라인, 26280시간의 이야기
Page 43: 10만 라인, 26280시간의 이야기
Page 44: 10만 라인, 26280시간의 이야기

테스트

Page 45: 10만 라인, 26280시간의 이야기
Page 46: 10만 라인, 26280시간의 이야기

무엇을 점검할까?

• 사람은 항상 실수한다. 실수를 blame하지 말고 실수를 하지 못하게 하자.

• 프로젝트가 시간이 지나면 지날수록 코드의 가독성은 바로 생산성이 된다.

• 테스트는 외부에 노출되는 Endpoint를 주된 목표로 하자.

Page 47: 10만 라인, 26280시간의 이야기
Page 48: 10만 라인, 26280시간의 이야기

lint - 개발 권고 사항 강제

Page 49: 10만 라인, 26280시간의 이야기

lint - import 실수 예방

Page 50: 10만 라인, 26280시간의 이야기
Page 51: 10만 라인, 26280시간의 이야기

flake8 / autopep8

Page 52: 10만 라인, 26280시간의 이야기

E501,F401,E402,F841

Page 53: 10만 라인, 26280시간의 이야기
Page 54: 10만 라인, 26280시간의 이야기

왜 Endpoint 위주로 테스트를 할까?

Page 55: 10만 라인, 26280시간의 이야기

미성숙한 요구사항 API 위주의 개발

Page 56: 10만 라인, 26280시간의 이야기

API 명세 산출

개념 구현

안정 구현

테스트

Page 57: 10만 라인, 26280시간의 이야기

• REST Endpoint를 호출하여 의도된 결과와 의도된 실패를 점검한다.

• 자명한 것 ( 1+1 = 2)을 테스트 한다. Framework나 의존성의 업그레이드시 발생하는 Breaking Changes에 대응할 수 있다.

• 외부 I/O에 대한 상태를 관리할 수 있다면 Mock보다는 실제 서버를 이용한다. (버그를 피하거나, 버그를 전제로 하거나.)

• 테스트에서 놓쳤던 버그는 테스트를 꼭 만들어 넣어야. 자존감을 유지할 수 있다.

Page 58: 10만 라인, 26280시간의 이야기

빠른 테스트는 매우 중요

Page 59: 10만 라인, 26280시간의 이야기

테스트가 느리면 CI를 장애물로 여기게 된다

Page 60: 10만 라인, 26280시간의 이야기

test coverage

Page 61: 10만 라인, 26280시간의 이야기
Page 62: 10만 라인, 26280시간의 이야기

만든 테스트의 유효성

Page 63: 10만 라인, 26280시간의 이야기

이 PR의 CI 통과를 얼마나 신뢰할 수 있는가?

Page 64: 10만 라인, 26280시간의 이야기
Page 65: 10만 라인, 26280시간의 이야기

문서

Page 66: 10만 라인, 26280시간의 이야기

–Hong Minhee , creator of Wand

“실수로 문서화를 영어로 했었음”

Page 67: 10만 라인, 26280시간의 이야기
Page 68: 10만 라인, 26280시간의 이야기

Sphinx + httpdomain

Page 69: 10만 라인, 26280시간의 이야기
Page 70: 10만 라인, 26280시간의 이야기

코드에서 문서를 찾는 개발자들.jpg

Page 71: 10만 라인, 26280시간의 이야기
Page 72: 10만 라인, 26280시간의 이야기

문서화에 실패한 이유

• 내부 고객(클라이언트 개발자)과의 커뮤니케이션에 있어서 주요한 수단이 되지 못했다.

• 문서를 보는 사람이 없으니 갱신이 되질 않아 문서 자체의 효용이 사라짐.

• 문서 자체의 효용이 사라져 다시 주된 수단이 되기 어려워 지는 악순환의 반복.

Page 73: 10만 라인, 26280시간의 이야기
Page 74: 10만 라인, 26280시간의 이야기

사업을 위해 부채를 피할 수 없는 것 처럼….

Page 75: 10만 라인, 26280시간의 이야기

적절한 기술 부채는 꼭 필요하다

Page 76: 10만 라인, 26280시간의 이야기
Page 77: 10만 라인, 26280시간의 이야기

적정 기술

Page 78: 10만 라인, 26280시간의 이야기

기술을 적용함에 있어 적정한 시점과 비용에 대한

고민

Page 79: 10만 라인, 26280시간의 이야기

오늘도 기술부채를 늘린 개발자가 용서를 구하고 있다.jpg

Page 80: 10만 라인, 26280시간의 이야기

사례1. 비용 평가의 실패

Page 81: 10만 라인, 26280시간의 이야기

2013년

• async 좋은건 알지만….

• 도입하면 복잡도가 증가하고 (아직) 필요가 크지 않다.

• 나중에 필요하면 도입할 수 있지 않겠어….?

Page 82: 10만 라인, 26280시간의 이야기

그리고 도래한 2016년

Page 83: 10만 라인, 26280시간의 이야기

Async가 필요하다!

• 인스턴스당 처리 효율이 너무 떨어져 비용이 낭비된다.

• 외부 시스템 연동이 이제 너무 많아서 대부분 시간이 I/O

• gevent monkeypatch가 모든걸 해결해 주시겠지…?

Page 84: 10만 라인, 26280시간의 이야기

그때와 지금은 모든게 다르다

Page 85: 10만 라인, 26280시간의 이야기
Page 86: 10만 라인, 26280시간의 이야기

시점에 대한 후회• 2013년에 단 3줄로 처리 되었을 문제가….

• 2016년엔 2000줄의 수정과 31개의 파일 변경, 개발+테스트+머지까지 1달 이상의 기간을 필요로 했다.

• 이미 복잡도가 상승한 시스템에서 ‘monkey patch’같은 magic은 불가능.

• 개발 초기 단기의 복잡도 상승을 과대 평가하여 이후 막대한 비용을 지불.

Page 87: 10만 라인, 26280시간의 이야기

사례2

Page 88: 10만 라인, 26280시간의 이야기

Read The F*** Manual

Page 89: 10만 라인, 26280시간의 이야기

SQLAlchemy Transaction Management

Page 90: 10만 라인, 26280시간의 이야기
Page 91: 10만 라인, 26280시간의 이야기

“autocommit” mode should not be considered for general

use.

Page 92: 10만 라인, 26280시간의 이야기

Implicit vs Explicit

Page 93: 10만 라인, 26280시간의 이야기

Implicit Transaction• SQLAlchemy가 암시적 Transaction 관리를 수행

한다.

• 기본적으로 session이 생성되는 시점에 무조건 Transaction을 수행하고, commit 혹은 rollback 되어 종료되는 즉시 새로운 Transaction 생성.

• INSERT/UPDATE/DELETE같은 변경뿐 아니라 SELECT만 수행하는 경우에도 Transaction 문맥하에서 수행된다.

Page 94: 10만 라인, 26280시간의 이야기
Page 95: 10만 라인, 26280시간의 이야기

The Last “SPOF”• 대부분의 DBMS에서 Transaction과 Connection

비용은 클 수 밖에 없다.

• W:R Ratio가 9:1에 가까움에도 Transaction이 과다해 DB에 부하가 너무 크게 집중되는 현상의 반복.

• Connection pooling 같은 방법이 실질적으로 효과를 발휘할 수 없다.

Page 96: 10만 라인, 26280시간의 이야기

PEP 20 -- The Zen of Python

“Explicit is better than implicit”

Page 97: 10만 라인, 26280시간의 이야기

sessionmaker( bind=engine,

autocommit=True)

Page 98: 10만 라인, 26280시간의 이야기
Page 99: 10만 라인, 26280시간의 이야기

Python의 배신• PEP 249 — Python Database API Specification v2.0

• There isn’t even a way to explicitly start a transaction in the PEP 249 API; no begin() or similar. Instead, you are always in a transaction;

• psycopg2는 PEP249의 작동을 준수하고, SQLAlchemy+psycopg2 에서 autocommit mode의 설정은 작동에 영향을 주지 못한다.

• 대신 isolation_level=‘AUTOCOMMIT’ 으로 조정해야 한다.

Page 100: 10만 라인, 26280시간의 이야기

문서에 없는데….?

Page 101: 10만 라인, 26280시간의 이야기

신속하게, 그러나 신중하게

Page 102: 10만 라인, 26280시간의 이야기

• Session 관리는 가장 초창기에 들어간 코드 중에 하나지만, 전반적인 Scale-up에 있어 끝까지 발목을 붙잡았다.

• 일반적인 서비스에서 DB가 병목이면서 동시에 SPOF가 되는걸 충분히 이해하고 있음에도 해당 비용을 평가하지 않았다.

• 이미 복잡도가 상승한 시점에서 Session의 설정을 바꾸는건 무척이나 비용이 크고 조심스러운 작업이 되어 쉽게 시도하지 못했다.

• 충분히 예측 가능한 기본적인 부분들에 대해서는 조금 더 신중했어야.

Page 103: 10만 라인, 26280시간의 이야기

느슨하지만 엄격하게• 처음부터 완벽한 선택을 할 수는 없기에 거기에 너

무 매몰되어 적정 시점을 놓치는건 너무 뼈아프다.

• 하지만 신중해야 되는 부분에서는 신중할 필요가 있다.

• 이런 알면서 (잘)못하는 것을 어떻게 다루는지가 결국 장기적인 프로젝트 관리

• 목표는 엄격하지만, 단계는 느슨하게.

Page 104: 10만 라인, 26280시간의 이야기