Computer Science/Software Design

A Philosophy of Software Design | John Ousterhout | Talks at Google

c4fiber 2023. 10. 23. 21:21

pintOS 강의를 들으면서 권영진 카이스트 교수님이 추천해주신 영상입니다.

정말 좋은 내용이 많으니 꼭 직접 시청하시길 권장합니다.

 

@제가 작성한 내용은 골뱅이(at sign)으로 시작합니다. 참고가 될 내용이나 제 해석을 작성할때 사용합니다.

 

A Philosophy of Software Design | John Ousterhout | Talks at Google

https://youtu.be/bmSAYlu0NcY?si=G9KqjYZbOkMGMiHK


문제 쪼개기

Problem Decomposition

Computer Science에서 가장 중요한 콘셉트를 하나 고른다면 무엇을 고를 것인가?
- Abstraction
- Testing
- Complexity
- Layers of Abstraction (도널드 커누스)

제 생각은 Problem Decomposition 입니다.

복잡한 문제를 어떻게 받아들이고 이를 상대적으로, 독립적으로 구축할 것인가? (relatively, independently)


가르쳐서 좋은 프로그래머로 만들 수 있는가?

Can great programmer be taught?

우리는 10x 프로그래머 라고해서 평범한 개발자들 보다 10배 이상의 능률을 보이는 개발자가 있다고 말합니다.
왜 이러한 대단한 능력을 가르치려고 하지 않는걸까요? 심지어 가능하기나 할까요?

흔히 말해 생산성이 높은 대단한 프로그래머들은 재능이 있어서 스스로 깨우치고 성장한다고 생각합니다.
하지만 현실에서의 top 프로그래머와 average 프로그래머의 차이점은 얼마나 연습했는가 (practiced)?

이것이 유일한 consistent correlating factor (일관된 상관관계를 나타내는 요소) 입니다.
저는 이를 가르칠 수 있다고 생각합니다.


얕은 클래스(Shallow Class)

비용: 모든 유저들이 알아야 하는 내용들 (Interface)
효과: 이 class를 사용함으로서 제공되는 기능의 유용함

모든 클래스는 Deep Class가 되어야 한다.

 

몇몇 개발자들은 "클래스나 메소드를 가능한 잘게 쪼개야 한다"고 이야기한다. 표현을 빌리자면 "한가지 목적만 가져야한다" 라고 표현을 하는데 심지어 몇라인 이상인 코드는 반드시 쪼개라는 경우도 있다. 혹시 구체적인 숫자를 들어본 적이 있는가? 20라인 정도가 보편적인 것 같은데 나는 10라인도 들어봤다.

 

Shallow Class는 너무 쪼갠 나머지 내부에 기능하는 코드보다 함수를 호출하는데 더 많은 키스트로크(키보드 타자 횟수)를 요구하기도 한다. 이런 얕은 클래스들은 정보 은닉(Information Hiding)이 없다. (@ 괜찮은 링크가 있어서 추천합니다. 읽어보시면 도움이 될겁니다.)

Q. 미래를 위해 인터페이스를 여유롭게 생성하고 인자를 더 받을 수 있도록 생성하는건 어떻게 생각하는가?

A. 소프트웨어는 미래를 시각화하는게 불가능하다. 인터페이스는 특정 기능에 굉장히 특화되어있기 때문에 여유공간이 거의 없을 것이다. 만약 인터페이스에 문제가 있고 이를 해결하기 위해서는 보통 인터페이스를 바꾸게 될 것이다. 1step 정도 미래를 예상해서 설계하는 것은 괜찮다고 생각한다. 하지만 더 멀리 내다보는 것은 크게 의미없는 일이다. 그렇게 하고 싶다면? 물론 할 수는 있다. 


존재하지 않는 에러를 정의하라.

사람들은 더 많은 예외상황을 처리하려고 노력한다. 내가 잘 막아내고 있는것처럼 생각한다.
하지만 예외상황이 많을 수록 더 많은 버그를 만들어내고, 연계되는 다른 예외상황을 만들어낼 수도 있다.ㅇ

ex) Tcl unset: 변수를 삭제하는 명령어였다. 많은 사람들이 존재하지도 않는 변수를 삭제하려고 해서 exception으로 처리했다. 하지만 사람들은 지속적으로 이런행위를 반복했다.
내가 했어야 하는 일은 "redefine semantics"였다. unset 명령어를 변수를 "삭제"하는게 아니라 "사라지게" 만들었다. 기존에 없던 변수면 이미 없고, 존재하는 변수는 사라지게 만들면 된다.

ex) File Deletion: 이미 열려있는 파일을 삭제하려면 그 파일을 사용하고 있는 프로그램을 모두 종료시켜야 한다. 하지만 그 프로그램을 못찾는다면? 재부팅을 해도 시스템 데몬이 사용하고 있다면?
UNIX는 아름다운 방법을 사용했다. 파일을 삭제하면 namespace, directory에서만 삭제해서 파일시스템에서는 찾을 수 없게 했다. 마지막 프로그램이 파일을 읽기를 끝내면 그때 정리한다.

내가 생각하기로는 소프트웨어 디자인에서 중요한 부분은 무엇이 중요한지, 중요하지 않은지를 찾는것이다.
이상적으로는 가능한 작은 부분이 중요하게 여겨져야한다. 정말 중요하고 문제가 되는 부분을 찾아서 당신의 시스템에 반영해야 한다.

 

Q. Exceptions vs Return Value 어느걸 사용하는게 적절할까요?
A. stack에서 멀리 보내려면 exception, 한단계씩 보내려면 return value


Tactical vs Strategic Programming

내가 생각하기에는 좋은 디자인의 가장 큰 장애물은 마인드셋이다.
마인드셋 없이는 절대 좋은 디자인을 내놓을 수 없다.

대부분은 전술적 접근(Tactical Approach)을 한다.
- 목표: 다음 기능, 버그 고치기
문제는 최대한 깔끔하게 만들겠지만 몇가지 숏컷을 만들고.. kluges(임시 방편, 지름길) 만들고... 임시방편을 만들고...
팀의 모든 사람들이 이러한 형태를 갖추게 된다면 이를 고치는데 수맣은 시간과 노력이 들어갈 것이고 매우 빠르게 스파게티 코드로 변화할 것이다.

문제는 complexity는 한 하나의 실수때문에 발생하는게 아니라 수많은 실수로 인해서 발생한다.
물론 시간을 들여서 처리할 수 있겠지만 결국은 압도당해서 절대 하지 않게 될 것이다.

Tactical Tornado: 혼자서 수많은(약 80%만 작동하는) 저품질의 코드를 생산하는 사람을 말한다. 그리고는 wake of destruction을 그 뒤에 남긴다.
대부분의 조직에서는 이러한 사람들이 영웅으로 받아들여진다. 겨우 내일까지 작동하는? 심지어 이런사람들이 10x 프로그래머라고 생각하는 사람도 있다!

이처럼 tactical한 접근은 매우 빠져들기 쉬우며 그러지 않기 쉽지않다.
좋은 디자인을 하기 위해서는 작동하는 코드로는 충분하지 않다는걸 깨닭아야 한다.
작동하는 코드는 단 하나의 목표가 될 수 없다. not single goal, table stakes


계속해서..

you have to invest. 내 의견으로는 결국 다 돌아오게 된다.

초반엔 얼마나 천천히 가야하는가? 언제서야 tactical한 접근을 따라잡을 수 있나?
내 의견으로는 왜 이러한 접근을 해야하는지 깨닭고 난 후 6 ~ 12개월정도 걸린다고 생각한다.


얼마나 투자할 것인가?

망가진, crappy한 코드로도 성공은 할 수 있다.
하지만 그러지 않고도 성공할 수 있다. Google, VMware는 그러지 않고도 성공했다.
이런 좋은 문화를 가지고 있다면 최고의 프로그래머들을 끌어모으는데 꽤 좋은 위치에 서있게 될 것이다.
우리는 10x 현상을 알고있다. 더 좋은 제품을, 더 빠르게 만들고 제공하기 위해서는 최고의 프로그래머들을 영입하는 것이다. 그래서 좋은 디자인 문화를 강하게 지지한다면 최고의 인재르 고용할 수 있게 해준다고 생각한다.
(I think the strongest argumentin favor of a good design culture is that it allows you to hire top people)

얼마나 투자할 것인가? 내가 다시 물어보자면 얼마나 투자할 수 있는가? 너 자신에게 물어라 "나는 내 인생에서 이 단계에 얼마나 투자할 수 있는가?" 나는 대략 10% ~ 20% 라고 생각한다.

적어도 10%는 투자할 수 있을 것이다. 매몰비용이 아니라 반드시 돌아온다.
영웅적인 큰 투자가 아니라 조금씩의 꾸준한 투자. 모든 시스템을 완전히 디자인하기위해 6개월을 투자할 수는 없다. 조금씩 점진적으로 투자해야한다.

그래서 모듈을 새로 만든다면 인터페이스를 설계할 때 조금의 시간을 투자하고 deep classes와 함께하도록 시도해라. 어떻게 진행되는지 문서로 작성하고 , 단위테스트도 물론이다.

처음엔 잘 모를것이다. 제대로 안되는게 당연하다. 그게 소프트웨어의 룰이다. 항상 무언가를 발전시키고, 증진시킨다고 생각하라. 항상 더 좋게 만들 수 있도록 살펴봐라.

이러한 이유중에 첫번째는 당신은 아마도 무언가를 만들때 망치게 될 것이다. 그래서 이를 적어도 반반으로 만들고 싶으면 무언가를 발전시켜야 한다. (@ 이부분이 와닿으면서도 재밌습니다.)
(One reason for this is you're brobably making something worse when you go in also. So even if you just want to break even, you've got to find something to improve)

그래서 나는 그저 반반으로 만들려고 노력하는 것이라고 생각하는 편이다.
일반적으로는 사람들이 존재하는 코드에서 개선사항을 찾을때, 가장 적은줄의 코드로 변경할 수 있는 가능성을 찾는다. 내가 생각하기엔 그들은 그저 겁먹은 거라고 생각한다. (난 이해하지 못하고, 뭔가 부숴버릴것 같고, 그래서 global variables 들을 사용해서 점프하고..)

그러지 마라. 깔끔한 방법(clean way)을 시도하고, 찾아봐라. 스크래치나 당신이 알고있는 것을 통해 모든 시스템을 구성하고 작동시킨다면 굉장히 이상적이다. 하지만 항상 그렇진 못하고, 당신이 감당할 수 있는 것보다 더 큰 스케일을 경험할 것이다.

당신에게 한마디 한다면 내가 할 수 있는 최고의 부분(point)은 무엇인가? 내가 할 수 있는 최선을 다하고 있는가?를 물어라. 그냥 boss를 위해 내일까지 완성시킨다고 just hack 하지 말고 내가 가능한 최선을 다하고 있는지 물어라.


질문: layers of abstraction 과 performance가 충돌할 때 어떤 기준으로 어느쪽을 선택하는가?

우리는 복잡도(Complexity)를 관리하기 위해서 레이어가 필요하다. 이럴땐 꽤 좋다.
속도(성능)에 문제가 있다면 내가 생각하기엔 사람들은 너무 많은 레이어를 만든다. 내 생각엔 몇몇개의 두터운 레이어 보다 수많은 얇은 레이어들을 만들어서 전형적인 실수라고 생각한다.
두번째는 성능적으로 문제가 있는 몇몇의 경우에는 핵심적인 성능 매트릭스(metrics)가 문제인데, 내 생각에는 적절한 레이어와 함깨 충분히 해결할 수 있는 문제라고 생각한다.
디자인을 하면서 생각해보고 최고의 성능을 레이어링과 함께 성취할 수 있도록 시스템 디자인을 하면서 고민해봐야 한다고 생각한다.


이 코스가 효과가 있나?

꽤 의미있는 시간이라고 생각한다. 세번째 구간에 들어가면 학생들은 서로를 더 비판적으로 코드리뷰를 진행하게된다.
결국 프로그래밍에 대한 시각을 코스를 수강하기 전과 비교해 굉장히 많이 바꾸게 된다.
학생들이 별로 배우지 못해도 내가 배우게된다 (@ 개인적으로 재미있던 포인트입니다 ㅋㅋㅋㅋㅋ)
많은 학생들이 대부분 비슷한 실수를 하게 되는데 이를 통해서 각 학생이 어떤 접근으로 이러한 실수를 만들었는지도 알게된다. 심지어 나의 디자인 문제를 바라보는 시각이 몇가지를 바꾸기도 했다.

그중에 하나는 단 한곳에서만 쓰이는 클래스라도 살짝은 범용적으로 설계해야 한다는 것이다. 실제로 클래스를 간단하고, 싶게 (simpler, deeper)하게 만들게 된다. 그걸 보기전에는 알아채지 못했는데 몇몇 학생들의 프로젝트를 보면서 알게 되었다.