NLP Blog

1. 실용주의 철학

|

1장. 실용주의 철학

Topic 1. 당신의 인생이다

나는 당신의 기대대로 살기위해 이 세상에 있는게 아니고, 당신도 내 기대대로 살기 위해 이 세상에 있는게 아니다
- 브루스 리 (Bruce Lee)

  • 개발자들은 여러 불만이 있다.
    • 기술의 변화를 쫓아가지 못한다
    • 자신의 성과를 몰라준다
    • 월급이 너무 적다
    • 팀 분위기가 별로다
    • 미국이나 유럽으로 가고싶다
    • 재택근무가 하고싶다
  • Answer: 왜 직접 바꾸지 않는가?
  • 소프트웨어 개발은 가장 주도권이 있는 직업 중 하나이다
  • 하지만 개발자들은 변화를 피하는 것 같음, 따라서 다음 팁이 가장 중요하다

Tip3 : 당신에게는 Agency* 가 있다
*Agency : the ability to take action or to choose what action to take

Martin Fowler : 당신은 당신의 조직을 바꾸거나, 당신의 조직을 바꿀 수 있다.

  • 이 업계는 여러분에게 놀랄 만큼 다양한 기회를 준다. 주도적으로 행동해서 그 기회를 잡아라

관련 항목

  • 항목 4. 돌멩이 수프와 삶은 개구리
  • 항목 6. 지식 포트폴리오

Topic 2. 고양이가 내 소스 코드를 삼켰어요

약점이 보이는 것에대한 두려움이 가장 큰 약점이다.
- J.B.보쉬에

  • 실용주의 철학의 초석 중 하나는 자신과 자신의 행동에 대해 책임을 지는 것
    • 자신의 경령 개발
    • 자신의 학습 및 교육
    • 자신의 프로젝트
    • 자신의 일상 업무
  • 책임을 지는 일이 분명 프로그래밍에서 가장 즐거운 부분은 아니지만, 반드시 일어난다

팀 내 신뢰

  • 여러분의 팀이 여러분을 믿고 의지할 수 있어야 한다. 여러분도 또한 다른 팀원 누구에게나 편하게 의지할 수 있어야 한다.
  • 연구에 따르면 창의성과 공동 작업에는 팀 내의 신뢰가 절대적으로 필요로 함
  • 신뢰에 바탕을 둔 건강한 환경에서는 안전하게 여러분의 생각을 말하거나 아이디어를 제안 할 수도 있음

책임지기

  • 책임은 여러분이 적극적으로 동의하는 것
  • 뭔가 제대로 처리하겠다고 약속을 하더라도 모든 면을 반드시 직접 통제하지는 못함
    • 개인적으로 최선을 다하는 것이 전부가 아님
    • 여러분이 통제할 수 없는 위험 요소가 있지 않은지 분석해야 함
    • 여러분에겐 불가능하거나 위험 요소가 너무 큰 상황, 또는 결과가 윤리적으로 심각하게 우려되는 상황에서는 책임을 맡지 않을 권리가 있다
  • 결과에 대한 책임을 지기로 했다면 나중에 그 결과를 감당해야 함
  • 실수를 저지르거나 잘못된 판단을 내렸다면, 정직하게 인정하고 다른 방안을 제안하도록 노력해야 함
  • 다른 사람 혹은 다른 무언가를 비난하거나 변명을 만들어 내지 마라
  • 해결책을 찾아내야 하는 사람은 여러분이다.

Tip4 : 어설픈 변명할고 대안을 제시하라

  • 변명 말고 대안을 제시하라. 안된다고 하지 말고 상황을 개선하기 위해 무엇을 할 수 있는지 설명하라.
    • 작업을 완료하려면 자원이 더 필요할 수도 있다
    • 혹은 사용자와 더 시간을 보내야 할 수도 있을 것이다
    • 여러분 자신에게 도움이 필요할 수도 있다
    • 부탁을 어려워하지 말고 도움이 필요하다는 사실을 인정하라.
  • 어설픈 변명을 늘어놓기 전에 그 변명거리를 없애도록 노력해보라. 그래도 꼭 해야겠다면 여러분의 고양이에게 먼저 해 보라.

관련 항목

  • 항목 49. 실용주의 팀

도전해 볼 것

  • 다른 직업군이 변명하는 걸 상상해보자, 그 사람이나 회사에 무슨 생각이 드는가? (변명하지 말자)
  • “잘 모르겠어요” 다음에는 “하지만 알아볼게요”가 붙어야 한다. 모른다는 것은 인정하더라도 전문가답게 책임을 지는 좋은 방법이다.

Topic 3. 소프트웨어 엔트로피

  • 엔트로피의 증가는 소프트웨어 개발에 많은 영향을 끼친다
    • 엔트로피 : 시스템 내의 무질서한 정도
  • 열역학 법칙에 따르면 우주의 엔트로피는 점점 증가한다.
  • 소프트웨어의 무질서도가 증가할 때 우리는 이를 소프트웨어의 부패라고 한다 또는 기술 부채 (Technical Debt)라고 한다.
  • 무엇이 소프트웨어를 부패하게 만들까?
    • 도심에서 어떤 건물은 아름답고 깨끗한 반면, 어떤 건물은 형편없다, 무엇이 그렇게 만들까? -> 깨진 창문

Tip5 : 깨진 창문을 내버려 두지 말라

  • “깨진 창문”을 고치지 않은 채로 내버려 두지 말라.
    • 나쁜 설계
    • 잘못된 결정
    • 형편없는 코드
  • 발견하자마자 바로 고쳐라
  • 적절히 고칠 시간이 없다면 일단 판자로 덮는 것만이라도 하라 -> 주석 처리 및 TODO 처리
  • 깨끗하고 잘 기능하던 시스템이 일단 창문이 깨지기 시작하면 급속도로 악화되는 경우를 많이 보았음
  • 방치는 다른 어떤 요인보다도 부패를 더 가속화 시킴
  • 엔트로피가 우리를 지배하도록 내버려 두지 말라

우선 망가트리지 말라

  • 깨진 창문 하나는 내리막길로 가는 첫걸음이다.
  • 깨진 창문이 꽤 있는 프로젝트에서 일할 때는 ‘나머지 코드가 전부 쓰레기니까 나도 그렇게 하지 뭐’ 라는 사고에 빠지기 쉽다
  • 여러분이 속한 프로젝트의 코드가 깨끗하고 아름답다면, 즉 깔끔하고 잘 설계되었으며 우아하다면, 특별히 주의를 더 많이 기울여서 엉망으로 만들지 않으려 할 것임
  • 명심하라 “깨진 창문은 없어야 한다”

관련 항목

  • 항목 10. 직교성
  • 항목 40. 리팩터링
  • 항목 44. 이름 짓기

Topic 4. 돌멩이 수프와 삶은 개구리

  • “시작 피로 Start-up fatigue”를 이겨내야 할 필요가 있다.
    • 큰 무리 없이 요구할 수 있을 만한 것을 찾아라
    • 그걸 잘 개발하라
    • 일단 무언가 생기면 사람들에게 보여 주고 그들이 경탄하게 하라
    • 그러고는 “물론 ……를 추가하기만 하면 더 나아질 수도 있겠죠” 라고 말하면서 그다지 중요하지 않은 척 가장하라
    • 물러나 앉아 여러분이 애초에 원했던 그 기능을 추가해 달라고 사람들이 부탁하기 시작할 때까지 기다려라
    • 계속되는 성공에 합류하기란 쉽다
    • 미래를 살짝이라도 보여 주면 사람들은 도와주기 위해 모여들 것이다. (허락은 얻는 것보다 용서를 구하는 것이 더 쉽다)

Tip6 : 변화의 촉매가 되라.

마을 사람의 경우

  • 프로젝트는 서서히, 하지만 가차없이 구제불능인 상태가 되어 버린다.
  • 소프트웨어 참사는 대부분 너무 작아 알아채기 힘들 정도의 문제에서 시작됨
  • 프로젝트는 대부분 어느날 갑자기 폭주한다.
  • 시스템은 애초의 명세와는 조금씩 조금씩 기능이 달라짐

Tip 7 : 큰 그림을 기억하라

  • 냄비안의 개구리처럼 서서히 온도가 바뀐다면 알아차리기 힘들것이다
  • 개구리처럼 되자 말라. 큰 그림에 늘 주의를 기울여라. 당장 하고 있는 일에만 정신을 쏟지 말고, 주변에서 무슨일이 벌어지는지 늘 살펴보라.

관련 항목

  • 항목 1. 당신의 인생이다
  • 항목 38. 우연에 맡기는 프로그래밍

도전해 볼 것

  • 변화를 촉진하려고 할 때 여러분이 좋은 영향을 끼칠 것인지 아닌지는 어떻게 알 수 있을까?
  • 주변을 살피고 의식하는 습관을 들이고 여러분의 프로젝트에 적용하라

Topic 5. 적당히 괜찮은 소프트웨어

우리는 종종 뭔가 나아지게 하려다가 괜찮은 것마저 망친다.
- 셰익스페어, «리어왕» 1막 4장

  • 실세계엥서는 진정 완벽한 것을 만들어 내기란 불가능 함
  • 특히 버그 없는 소프트웨어는 더더욱. 시간, 기술, 기질 같은 것이 모두 힘을 합쳐 우리를 방해한다.
  • 적당히 괜찮은 소프트웨어를 만들도록 자신을 단련할 수 있다.
    • 사용자나 미래의 유지 보수 담당 아니면 자기 자신이 마음의 평화를 유지하기에 적당할 정도로 괜찮으면 됨
  • “적당히 괜찮은” != 너절하거나 형편없는 코드
  • 중요한 것은, 생산해 낸 것이 적당히 괜찮게 사용자의 요구를 충족하는지 결정하는 과정에 사용자가 참여할 기회를 가져야 한다는 것

타협 과정에 사용자를 참여시켜라

  • 소프트웨어가 얼마나 종아야 하는지 사용자에게 물어본 적이 한 번이라도 있는가?
  • 새로운 상품을 만들고 있다면 제약 조건이 좀 다를 것
  • 여러분이 만드는 시스템의 범위와 품질은 해당 시스템의 요구 사항 중 하나로 논의되어야 함

Tip8 : 품질을 요구 사항으로 만들어라

  • 놀랍게도 많은 사용자가 멋지고 휘황찬란한 버전을 위해 일 년을 기다리느니 차라리 오늘 당잠 좀 불편한 소프트웨어를 사용하고 싶어 함
  • 오늘의 훌륭한 소프트웨어는 많은 경우 환상에 불과한 내일의 완벽한 소프트웨어보다 낫다.
  • 사용자에게 뭔가 직접 만져볼 수 있는 것을 일찍 준다면, 피드백을 통해 종국에는 더 나은 해결책에 도달할 수 있을 것 (Lean Start-up, Agile?)

멈춰야 할 때를 알라

  • 완벽하게 훌륭한 프로그램을 과도하게 장식하거나 지나칠 정도로 다듬느라 망치지 말라
  • 그냥 넘어가고 코드를 현재 상태로 한동안 그대로 놓아두라. 완벽하지 않을 수도 있다.
  • 그래도 괜찮다. 완벽해지기란 불가능하다.

관련 항목

  • 항목 45. 요구 사항의 구렁텅이
  • 항목 46. 불가능한 퍼즐 풀기

도전해 볼 것

  • 여러분이 일상적으로 사용하는 소프트웨어 도구나 운영체제를 보라. 이를 개발하는 조직이나 개발자들이 완벽하지 않다고 여겨지는 소프트 웨어를 거리낌 없이 출시한다는 증거를 찾을 수 있는가?
    • 사용자로써 (1) 그들이 모든 버그를 제거할 때까지 기다리겠는가?
    • (2) 복잡한 소프트웨어를 사용하면서 어느 정도의 버그는 감내하겠는가?
    • (3) 결함이 더 적은 간단한 소프트웨어를 선택하겠는가?
  • 모듈화가 소프트웨어 출시에 미치는 영향을 생각해보라
    • 모놀리식 소프트웨어 블록을 필요한 품질 수준으로 만드느 것
    • 마이크로서비스들로 설계된 시스템을 만드는 것 중 어느게 오래 걸릴까? 장단점은?
  • 기능 블로트 (feature bloat) 현상에 빠진 유명한 소프트웨어를 떠올릴 수 있는가?

Topic 6. 지식 포트폴리오

지식에 대한 투자가 언제나 최고의 이윤을 낸다.
- 벤저민 프랭클린

  • 여러분의 지식과 경험이야말로 가장 중요하고 날마다 쓰이는 전문가 자산이다.
  • 하지만 불행히도 이 자산은 기한이 있는 자산이다.
  • 새로운 기술, 언어, 환경이 개발됨에 따라 지식은 옛것이 된다.
  • 여러분의 지식 가치가 점점 떨어짐에 따라 회사나 클라이언트가 보는 여러분의 가치 역시 떨어짐
  • 새로운 것을 배우는 능력은 여러분의 가장 중요한 전략 자산임

지식 포트폴리오

  • 지식 포트폴리오 관리는 투자 포트폴리오 관리와 매우 유사하다.
    • 진지한 투자자는 주기적으로 투자하는 습관이 있다.
    • 장기적인 성공의 열쇠는 다각화다
    • 똑똑한 투자자는 보수적인 투자와 위험이 크지만 보상이 높은 투자 사이에서 포트폴리오의 균형을 잘 맞춘다.
    • 투자자는 최대 수익을 위해 사게 사서 비싸게 팔려고 한다.
    • 포트폴리오는 주기적으로 재검토하고 재조정해야 한다.
  • 성공적인 경혁을 위해서는 이와 동일한 지침을 사용해서 지식 포트폴리오에 투자해야 한다.

포트폴리오 만들기

주기적인 투자

  • 여러분의 지식 포트폴리오에 소량으로라도 주기적으로 투자해야 함
  • 습관 자체가 금액의 합계만큼이나 중요하다

다각화

  • 더 여러 가지를 알수록 자신의 가치는 더욱 높아진다.
  • 기본적으로 현재 작업에 사용하는 기술에 관해서는 속속들이 알아야 한다.
  • 하지만 거기서 멈추지 말라. 컴퓨터 분야의 지형은 빠르게 변한다.
  • 기술외의 분야도 포함하여 여러분에게 필요한 다른 역량도 잊지 말라

리스크 관리

  • 기술은 위험하지만 잠재적으로 보상이 높은 것에서 리스크가 낮고 보상도 낮은 것에 이르기까지 다양한 스펙트럼 위에 존재함
  • 여러분의 기술 달걀을 모두 한 바구니에 담지 말라

싸게 사서 비싸게 팔기

  • 새롭게 떠오르는 기술이 인기를 끌기 전에 미리 알고 학습하는 게 저평가 된 주식을 찾아내는 것만큼이나 어려울 수 있지만, 이익 또한 그만큼 클 수 있다.
  • 예시 : JAVA

검토 및 재조정

  • 이 산업은 매우 동적이다.
  • 한동안 사용하지 않던 데이터베이스 기술을 복습해야 할 필요
  • 다른 언어를 시도해 본 덕에 새로운 일자리를 구할 때 더 유리할 수 있음

  • 이 모든 지침 중에 제일 중요한 것은 가장 간단하게 실행할 수 있는 것이다.

Tip9 : 지식 포트폴리오에 주기적으로 투자하라

목표

매년 새로운 언어를 최소 하나는 배워라

  • 다른 언어는 동일한 문제를 다르게 푼다.
  • 몇 가지 서로 다른 접근법을 알면 사고를 확장하고 판에 박힌 사고에 갇히는 걸 예방하는 데에 도움이 된다.

기술 서적을 한 달에 한 권씩 읽어라

  • 깊이 있는 지식을 원하다면 긴 글 형식의 책을 읽어야 한다
  • 현재 프로젝트와 관련 있는 흥미로운 주제의 기술 서적을 서점에서 찾아보라
  • 습관이 들면 한달에 한 권씩 일어라
  • 현재 사용하는 기술을 일단 완전히 익혔다면, 가지를 쳐서 지금 하는 프로젝트와 관련 없는 분야까지 공부 범위를 넓혀라

기술 서적이 아닌 책도 읽어라

수업을 들어라

지역 사용자 단체나 모임에 참여하라

다른환경에서 실험해 보라

요즘 흐름을 놓치지 말라


  • 투자를 지속하는 것이 중요하다. 한 기술의 새로운 용어나 기능에 익숙해지면 다음으로 넘어가 새로운 것을 배워라
  • 이런 기술들을 프로젝트에서 영영 사용하지 않거나 심지어 자신의 이력서에 올려좋지 않아도 상관없다.
  • 학습 과정에서 사고가 확장될 것이다. 이로 인해 새로운 가능성이 열리고 문제를 해결하는 새로운 방법이 떠오를 것이다.
  • 사고 간의 교접(cross-pollination)이 중요하다.
  • 새로이 배운 교훈을 현재 프로젝트에 적용하도록 노력하라.

학습의 기회

  • 누군가 질문을 했을 때 그걸 모르는 경우
    • 답을 찾기 위한 개인적인 도전으로 생각하자
    • 주위에 물어보자
    • 웹을 검색해보자
    • 사용자용 문서뿐 아니라 학술 자료도 찾아보자
  • 스스로 답을 찾지 못하면 답을 찾아줄 수 있는 사람을 찾자
  • 다른 사람과 이야기함으로써 개인 네트워크를 구축하는 데 도움이 되기도한다
  • 언제나 무엇을 읽을 수 있는 준비를 하자

비판적 사고

  • 마지막으로 중요한 점은 여러분이 읽거나 듣는 것에 비판적으로 생각하는 것

Tip10 : 읽고 듣는 것을 비판적으로 분석하라.

  • 비판적 사고는 그 자체만으로 하나의 학문을 이룬다.

왜냐고 다섯번 묻기 Five Whys

누구에게 이익이 되나?

어떤 맥락인가?

  • 모든 일에는 각각의 맥락이 있기에, ‘만병통치약’인 해결책은 대게 통하지 않는다.

언제 혹은 어디서 효과가 있을까?

왜 이것이 문제인가?

관련 항목

  • 항목 1. 당신의 인생이다
  • 항목 22. 엔지니어링 일지

도전해 볼 것

  • 이번 주부터 새오운 언어를 배우기 시작하라
  • 새 책을 하나 읽기 시작하라
    • 매우 상세한 구현과 코딩을 하고 있다면 설계와 아키텍처에 대한 책을 한 권 읽어라
    • 고차원의 설계를 하고 있다면 코딩 테크닉을 다루는 책을 한 권 읽어라
  • 밖으로 나가서 지금 수행하고 있는 프로젝트에 관여하지 않는 사람 혹은 같은 회사에 근무하지 않는 사람과 기술에 관한 대화를 하라

Topic 7. 소통하라!

나는 무시당하느니 차라리 샅샅이 훑어보는 시선이 낫다고 봐요
- 메이 웨스트

  • 최고의 아이디어, 최상의 코드 혹은 아주 실용적인 발상이 있다고 해도 다른 사람들과 소통할 수 없다면 궁극적으로 아무 효용이 없다.
  • 효과적인 소통 없이는 아무리 훌륭한 아이디어라고 고립되고 만다.

Tip11. 한국어든 영어든 하나의 프로그래밍 언어일 뿐이다.

청중을 알라

  • 전달하려는 내용을 제대로 전달하고 있는 경우에만 소통하고 있다고 할 수 있다.
  • 그렇게 하기 위해서는 청중의 요구와 관심, 능력을 이해할 필요가 있다
  • 다른 모든 형태의 의사소통과 마찬가지로 여기서도 비결은 피드백을 모으는 것
  • 소통하면서 청중에 대한 지식을 쌓아 나가라

말하고 싶은게 무언지 알라

  • 무엇을 말할지 미리 계획하라. 개요를 작성하라. 그리고 자문하라
    • 이렇게 하면 내가 표현하고 싶은 것을 듣는 사람에게 통하는 방법으로 잘 전달할 수 있나? 그렇게 될 때까지 다듬어라
  • 문서를 작성할 때만 그런게 아니다. 중요한 회의, 주요 클라이언트와의 대화 때도 제대로 전달하는데 필요한 전략을 몇 개 세워라

때를 골라라

  • 청중이 무엇을 듣기 원하는지 이해하려면 그들의 우선순위를 알아야 한다.
  • 소스 코드 일부가 사라진 일로 상사에게 막 꾸지람을 들은 관리자를 붙잡고 소스 코드 저장소에 대한 아이디어를 이야기해 주면 경청할 것이다.

스타일을 골라라

  • 전달하는 스타일을 청중에 어울리도록 조정하라
  • 어떤 스타일을 원하는지 청자에게 물어보라
  • 하지만 누군가가 뭔가를 짤막하게 설명해 달라고 부탁하는데, 그 설명을 종이 대여섯 장 이하로 줄일 방법이 없다는 생각이 들면, 사실이 그렇다고 말하라.

멋져 보이게 하라

  • 멋진게 아닌 것보다 훨씬 낫다
  • 최신 기술을 활용하라

청중을 참여시켜라

  • 청중을 통해 피드백을 받고 그들의 머릿속을 도용하라

경청하라

  • 여러분이 다른 사람들의 말을 귀 기울여 듣지 않는다면 그들 역시 여러분의 말을 듣지 않을 것이다
  • 질문을 해서 사람들이 이야기를 하도록 북돋우거나, 토론의 내용을 그들 자신의 표현으로 다시 말해 달라고 요청하라

응답하라

  • 언제나 이메일과 음성 메시지에 답을하라.
  • 심지어 단순히 “다음에 답해 드리겠습니다” 이더라도
  • 늘 사람들에게 응답해 주면 때때로 저지르는 실수에 대해 훨씬 더 관대해질 것이고, 여러분이 그 사항을 아직 잊지 않았다고 느끼게 할 것이다.

Tip12 : 무엇을 말하는가와 어떻게 말하는가 모두 중요하다

문서화

  • 실용주의 프로그래머는 문서화를 전체 개발 프로세스의 필요 불가결한 부분으로 받아들인다

Tip13 : 문서를 애초부터 포함하고, 나중에 집어넣으려고 하지 말라.

  • 소스코드의 주석으로 보기 좋은 문서를 쉽게 생성할 수 있다
  • 모듈과 외부로 노출하는 함수에는 주석을 다는 것을 추천한다

요약

  • 청중을 알라.
  • 말하고 싶은게 무언지 알라.
  • 때를 골라라.
  • 스타일을 골라라.
  • 멋져 보이게 하라.
  • 청중을 참여시켜라.
  • 경청하라.
  • 응답하라.
  • 코드와 문서를 함께 둬라.

관련 항목

  • 항목 15. 추정
  • 항목 18. 파워 에디팅
  • 항목 45. 요구 사항의 구렁텅이
  • 항목 49. 실용주의 팀

도전해 볼 것

  • 맨먼스 미신과 피플웨어를 비롯하여 팀 내의 의사소통에 관한 내용이 실려 이씨는 훌륭한 책이 몇 권 읽다. 앞으로 18개월 이내에 이 책들을 반드시 읽을 수 있도록 노력하라
  • 다음번 pt때 이 토픽의 내용을 적용해보라 그리고 피드백을 들어라

온라인 의사소통

  • ‘보내기’ 버튼을 누르기 전에 검토를 하라
  • 맞춤법에 맞는지, 자동 교정이 엉뚱하게 바꾸지는 않았는지 확인
  • 형식을 간단하고 명확하게
  • 답장할 때 원본 메시지의 인용은 최소한으로
  • 다른 사람의 이메일을 인용한다면 누구의 것인지 밝히고 본문 속에서 인용
  • 화내고 욕하거나 논란을 부추기는 글을 보내지 말라
  • 보내기 전에 받는 사람 목록을 확인하라

Chapter1-map

Comment  Read more

22. 대규모 변경

|

22. 대규모 변경

  • 구글에서는 코드베이스 전반을 전면적으로 건드리는 변경을 원자적으로 수행한다는 아이디어를 포기함
  • 코드베이스와 엔지니어의 수가 늘어날수록 원자적으로 수행하 ㄹ수 있는 변경의 크기는 줄어듬
    • 영향받는 모든 프리서브밋 검사와 테스트를 수행하기가 어렵고, 서브밋 전에 변경에 포함된 파일 전부가 최신 버전인지도 확신할 수 없게 됩니다.
  • 기반 인프라를 계속 개선시켜야 하는데, 전면적인 변경은 점점 어려워짐
  • 이를 해결할 수 있는 대규모 변경 방법을 현실에서 수행할 방법을 찾아내야 함

22.1 대규모 변경(Large Scale Change)이란?

  • LSC는 연관되어 있으나 현실적인 한계 때문에 원자적으로 서브밋할 수 없는 변경들의 집합
  • 이유는
    • 너무 많은 파일을 건드려서 기반 도구가 단번에 커밋하지 못함
    • 너무 커다란 변경이라 병합 과정에서 항상 출동이 나서
  • LSC는 리포지터리 토폴러지에 영향을 많이 받음
  • 분산 리포지터리를 이용하거나 여러 리포지터리를 연합해 이용하고 있다면 원자적으로 변경하기가 기술적으로도 애초에 불가능 할 것임
  • 구글의 경우 LSC는 거의 항상 자동화 도구를 이용해 생성함
  • LSC로 인해 생성되는 변경들은 대체로 다음과 같이 분류할 수 있음
    • 코드 베이스 전반을 훑는 분석 도구로 찾은 공통 안티패턴 청소
    • 폐기 대상 API 호출 대체
    • (컴파일러 업그레이드 등) 저수준 인프라 개선사항 활성화
    • 사용자들을 옛 시스템에서 새로운 시스템으로 마이그레이션
  • 구글에서 수행하는 LSC의 대다수는 기능은 거의 변경하지 않음
  • 주로 명확성, 최적화 미래 호환성 개선등이 목표
  • 구글 스케일에서는 몇백만개의 참조를 수정할 때도 있다

22.2 누가 대규모 변경을 처리하나?

  • 구글에서는 LSC의 상당 비중을 인프라팀들이 수행함
  • 인프라팀은 이런일에 대한 전문가이다. 전문가가 일을 처리하는 것이 좋다
  • 조직차원에서 처리해야 한다
  • 대규모로 변경해야 할 시스템을 소유한 팀이 주도해야 변경을 완료하는 데 유리함

22.3 원자적 변경을 가로막는 요인

  • 구글의 LSC 프로레스를 이야기하려면 먼저 왜 수많은 변경들이 원자적으로 커밋될 수 없는지를 이해해야 함
  • 이상적인 세계에서는 모든 논리적 변경은 하나의 원자적 단위로 묶어서 다른 변경들과 독립적으로 한 번에 테스트하고 리뷰하고 커밋할 수 있어야 함

22.3.1 기술적 한계

  • VCS에서는 기능을 수행하는 비용이 변경의 크기에 비례해 커짐
  • 수십 개 규모의 작은 커밋을 무리없이 처리할 수 있지만, 파일 수천 개를 원자적으로 커밋하기에느 ㄴ힘들어 할 수 있음
  • 중앙집중형 VCS에서는 커밋 중에는 다른 사용자가 쓰기 작업을 못함
  • 거대한 커밋은 다른 사용자들의 일을 멈춰세운다.
  • 인프라에 따라 아예 불가능한 일일 수 있다

23.3.2 병합 충돌

  • 변경의 규모가 커질수록 자연스럽게 병합 시 충돌이 생길 가능성이 커짐
  • 회사가 작다면 아무도 출근안했을때 얼른 처리해볼 수도 있지만, 구글은 불가능…
  • 변경하는 파일이 적다면 병합 출동 확률이 작으므로 문제없이 커밋될 가능성이 높아짐

22.3.3 유령의 묘지

  • 유령의 묘지(haunted graveyard)란 너무 오래되고 둔하고 복잡해서 아무도 손대려 하지 않는 시스템을 뜻함
  • 사업상 아주 중요한 시스템 중에 유명의 묘지가 많음
  • 코드베이스에서 이런 부분은 LSC 프로세스를 방해하는 걸림돌
  • 더 나은 라이브러리와 컴파일러를 도입하지 못하게 하고, 낡은 시스템을 폐기시키지 못하게 함
  • 테스트를 충실히 하면 이 유령의 묘지를 없앨 수 있음

22.3.4 이질성

  • LSC가 가능하려면 작업을 사람이 아닌 컴퓨터가 대부분 처리해주어야 함
  • 프리서브밋 검사에서 이러한 검사를 실패로 처리하는 경우가 있음
  • LSC때는 눈감아 줘야 함

22.3.5 테스트

  • 모든 변경은 테스트 되어야 한다
  • 변경의 덩치가 커지면 제대로 테스트하기가 훨씬 어려움
  • 작고 독립적인 변경은 검증하기 쉬움

22.3.6 코드리뷰

  • 모든 변경은 서브밋하기 전에 리뷰를 거쳐야 함
  • LSC도 예외는 아님
  • LSC를 별도의 샤드로 나누면 훨씬 쉬워짐

22.4 대규모 변경 인프라

  • 구글은 LSC를 가능하게 하기 위해 인프라에 막대한 투자를 함
    • 변경 생성
    • 변경 관리
    • 변경 리뷰
    • 테스트 도구
    • 문화적 규범의 진화

22.4.1 정책과 문화

  • 구글은 LSC를 만들려는 팀과 ㅣ개인을 위한 가벼운 승인 프로세스를 고안
    • 다양한 언어의 미묘한 특성에 익숙한 숙련된 엔지니어 그룹이 감독
    • 새로 만들려는 LSC 관련 도메인 전문가 초대
    • 변경 작성자가 구글의 기술과 인적 지원을 최대한 활용하여 가장 이상적인 변경을 생성하도록 돕는 것

22.4.2 코드베이스 인사이트

  • LSC를 진행하려면 코드베이스 전반을 분석할 수 있어야 함
  • 의미론적 인덱싱 도구, 컴파일러 기반 인덱스 등을 이용하여 코드베이스에 대한 분석을 해야 함
    • ClangMR
    • JavaFlume
    • Refaster

24.4.3 변경 관리

  • 마스터 변경을 여러 개의 샤드로 나눈 후 테스트, 메일링, 리뷰, 커밋 단계를 독립적으로 관리해주는 도구
  • 구글은 Rosie를 사용 함

24.4.4 테스트

  • 테스트도 중요함
  • 표준적인 프로젝트별 프리서브밋 테스트를 이용하지 않음

24.4.5 언어 지원

  • 구글은 LSC를 주로 언어별로 진행하며, 언어에 따라 LSC 난이도가 크게 다름
    • 타입 별칭과 전달 함수를 지원하는 프로그래밍 언어라면 새로운 시스템으로 마이그레이션하는 중에도 기존 코드가 문제없이 동작하게 만들기가 훨씬 쉬움 (C++, JAVA…)
    • 아닌 애들은 어려움 (Python, JS…)
  • 자동 포맷터도 중요함

22.5 대규모 변경 프로세스

  • 관한 부여
  • 변경 생성
  • 샤드 관리
  • 마무리 청소

22.5.1 권한 부여

  • LSC 작성자에게 다음의 내용을 포함한 간단한 제안 문서를 작성해달라고 요청
    • 변경을 제안하는 이유
    • 코드베이스 전반에 주는 예상 영향 (ex: 제안한 LSC로 인해 생성되는 작은 샤드 수)
    • 리뷰어들이 던질만한 질문과 그에 대한 답변
  • 이후 프로세스 감독자 10여 명으로 구성된 위원회에 제안을 송부
  • 피드백
  • 글로벌 승인자에게 할당
  • 위원회는 승인에 관대하다

22.5.2 변경 생성

  • 승인을 얻은 LSC작성자는 실제로 코드를 수정하기 시작함
  • 거대한 전역 변경 하나를 생성한 후 다수의 독립적인 샤드로 쪼갬
  • 변경 생성 프로세스는 가능한 한 자동화해야 함

22.5.3 샤드로 나누기와 서브밋

  • 글로벌 변경을 생성했다면, Rosie를 실행함
    • 거대한 변경을 하나 입력받아서 서브밋할 수 있는 샤드들로 쪼개 줌
    • 프로젝트 경계와 소유권 규칙을 참고
    • 개별 샤드를 독립된 테스트-메일-서브밋 파이프라인에 태움

테스트하기

  • 독립된 샤드 각각은 구글의 CI 프레임워크인 TAP에 의해 테스트됨
  • 변경이 크므로 이 테스트는 부하가 크다
  • 실제 대다수의 샤드는 천 개 이하의 테스트만 수행 (구글의 테스트는 수백만 개에 이름)
    • 더 많은 테스트를 수행해야 하는 샤드들은 일단 하나의 그룹으로 묶음
    • 그룹 내 샤드들에 영향받는 테스트 모두를 1차로 수행
    • 2차로는 샤드별로 테스트하는데, 해당 샤드에 영향받는 테스트 중 1차 때 실패한 테스트들만 수행
    • 2차부터는 비용이 거의 공짜
  • 구글의 자동화 도구에서는 서브밋 시 최근에 튄 불규칙한 테스트르리 무시하도록 함

리뷰어에게 메일 보내기

  • OWNERS 파일 기반으로 리뷰 요청

리뷰하기

  • LSC는 엄격하게 리뷰하지는 않음
  • 인프라팀을 믿고 피상적인 수준으로만 살펴보고 통과함

서브밋하기

  • 개별 변경을 커밋

22.5.4 마무리 청소

  • 옛 시스템 완벽히 제거
  • 중요한 참조들을 마이스레이션한 후 옛 참조들은 자연스럽게 사라지도록 내버려두기
  • 대규모 변경이 애써 제거한 심볼이나 시스템이 다시 사용되는 일을 막아주는 방책이 꼭 필요함
    • Tricorder

22.7 핵심 정리

  • LSC 프로세스는 되돌릴 수 없다고 여기던 기술적 결정들을 다시 생각해볼 수 있게 해줌
  • 전통적인 리팩터링 모델은 코드 규모가 커지면 한계를 드러냄
  • LSC에 성공하려면 LSC를 습관처럼 진행해야 함 (코드베이스가 오래될 수록 고치기가 점점 어려워 짐)

Comment  Read more

23. 지속적 통합

|

23. 지속적 통합

  • 정의 : 팀원들이 작업 결과를 자주 통합하는 소프트웨어 개발 방식이다. 통합할 때마다 자동 빌드 (테스트 포함)하여 통합 오류를 빠르게 찾아낸다
  • 현대적인 분산 애플리케이션에서 자주 통합한다라는 말은 무슨 의미일까요?
    • 요즘 시스템들을 리포지터리 안의 코드 말고도 동적으로 변하는 요소가 많음
    • 마이크로서비스 시스템에서는 문제의 원인이 코드베이스가 아니라 느슨하게 결합된 다른 마이크로서비스와의 네트워크 실패 때문인 경우가 더 많음
    • 전통적인 지속적 빌드에서 확장하면 업스트림 마이크로서비스의 변경들도 테스트 할 수 있음
  • 현대적 어플리케이션에서는 굉장히 많은 주변 요소를 모두 의존석으로 간주하고 이 변경까지 지속적으로 통합 해야함
    • 데이터 -> 머신러닝 모델 갱신
    • 운영체제, 런타임, 클라우드 호스팅 서비스, 디바이스
  • 재정의
    • 빠르게 진화하는 복잡한 생태계 전체를 지속적으로 조립하고 테스트하는 개발 방식
  • CI와 테스트는 강하게 엮여 있음
    • 코드(와 다른요소) 가 변경되어 지속적으로 통합되는 개발/릴리스 워크플로에서 무슨 테스트를 언제 실행하야 하는가?
    • 워크플로의 각 테스트 지점에서 (적절한 충실성을 갖춘) 테스트 대상 시스템(SUT)을 (합리적인 비용으로) 어떻게 구성해야 하는가?
  • 프리서브밋과 포스트 서브밋, 스테이징 배포 전에는 어떤 테스트를 각각 수행해야 할까?

23.1 지속적 통합이란?

23.1.1. 빠른 피드백 루프

  • 코드 변경 과정에서 문제를 발경해낼 수 있는 위치들
    • 편집/컴파일/디버스 -> 프리서브밋 -> 포스트서브밋 -> 릴리스 후보(RC) -> RC 승격 (스테이징 등 임시 환경) -> 최종 RC승격 (프로덕션)
  • 일반적으로 문제가 오른쪽 끝 가까이까지 살아남을수록 비용이 커지는 이유는
    • 문제의 코드에 익숙하지 않은 엔지니어가 분류해야 함
    • 변경 작성자가 무엇을 왜 변경했는지 기억해내고 조사하는 노력이 더 많이 듬
    • 바로 옆 동료부터 최종 사용자까지, 다른 이들에게 부정적인 영향을 줌
  • CI는 우리가 빠른 피드백 루프를 이용하도록 유도하여 버그 비용을 최소로 줄여줌
  • 피드백이 빠른 순서로 보면
    • 로컬 개발시의 편집-컴파일-디버그 루프
    • 프리서브밋 시 변경 작성자에게 주어지는 자동 테스트 결과
    • 두 프로젝트를 변경한 후 통합 시 오류. 양쪽 변경들을 서브밋한 다음 함께 테스트할 때 발견 (예: 포스트서브밋)
    • 내 프로젝트와 섭스트림 마이크로서비스 의존성 사이의 호환성 충돌. 업스트림 서비스의 최신 변경이 스테이징 환경에 배포될 때 QA 엔지니어가 발견
    • 기능을 먼저 이용해본 사내 이용자의 버그 리포트
    • 외부 고객 혹은 언론 매체의 버그 리포트
  • 카나리 배포를 활용하면 프로덕션에서 일어나는 문제가 확실하게 줄어듬
    • 버전 왜곡이 일어날 수 있음
  • 실험과 기능 플래그도 매우 강력한 피드백 루프
    • 변경을 컴포넌트 단위로 격리한 후 프로덕션 환경에서 동적으로 켜고 끌 수 있게 하여 배포 위험을 줄여주는 기법

볼 수 있고 조치할 수 있는 피드백

  • CI가 제공하는 피드백을 많은 사람이 볼 수 있어야 함
  • 테스트 리포트 시스템은 빌드와 테스트의 실패 이력도 자세히 알려줌
  • CI 테스트가 제공하는 피드백은 모두 조치가 가능해야 함

23.1.2 자동화

  • 빌드와 릴리스 프로세스를 자동화, 각각을 지속적 빌드와 지속적 배포하고 한다.
  • 지속적 테스트는 두 단계 모두에 자동으로 붙는다.

지속적 빌드

  • 가장 최근의 코드 변경을 헤드(트렁크)에 통합한 다음 자동으로 빌드와 테스트를 수행함
  • 테스트로 빌드의 한 과정으로 보기 때문에 컴파일을 통과하더라도 테스트에 실패하면 ‘빌드 실패’로 간주함
  • true head는 최신 병경이 커밋된 버전
  • green head는 CB가 검증한 최신 변경

지속적 배포

  • 릴리스 자동화 - 헤드로부터 지속해서 최신 코드와 설정 정보를 가져와서 릴리스 후보를 만들어내는 작업
  • 릴리스 후보 (RC) : 자동화된 프로세스가 만든, 서로 밀접하게 관련된 요소들로 구성된 배포 가능한 단위. 시족적 빌드를 통과한 코드, 설정 정보, 기타 의존성들을 조합해 만든다.
  • 정적인 설정 정보들은 모두 릴리스 후보에 묶어 승격시켜서 해당 코드와 함께 테스트 되도록 해야 함
  • 지속적 배포 : 지속해서 릴리스 후보를 조립한 다음 다양한 환경에 차례로 승격시켜 테스트하는 활동. 프로덕션까지 승격시키는 경우도 있고 그렇지 않은 경우도 있다.
  • RC가 운영환경들에서 단계별로 승격될 때 이상적으로는 아티팩트들 (ex: 바이너리, 컨테이너)을 다시 컴파일하거나 빌드하지 않아야함
  • 로컬에서 개발할 때부터 도커 같은 컨테이너를 이용하면 좋음 (구글은 Borg를 이용)

22.1.3 지속적 테스트

  • 그림 23-2 참조
  • 무슨 테스트를 언제 실행하지를 정하는게 CI의 주된 목표
  • 위 그림에서 오른쪽으로 이동할수록 점진적으로 범위가 더 넓은 자동화 테스트를 수행해야 함

프리서브밋만으로 부족한 이유

  • 원점 회귀가 좋다면서 외 모든 테스트를 프리서브밋에 하지 않는가?
    • 비용 때문
    • 프리서브밋 테스트가 수행되는 동안에도 리포지터리는 계속 수정 될 수 있어서, 장시간 걸린 테스트가 쓸모없어질 수 있음

프리서브밋 vs 포스트서브밋

  • 프리서브밋 테스트
    • 빠르고 안정적인 테스트
    • 커버리지를 다소 포기, 포스트때 다 잡아내자
    • 해당 변경이 이루어지는 프로젝트로 한정
    • 밀폐 테스트 권장
    • 불안정안 테스트를 해보되, 실패하면 비활성화하는 것도 방법
  • 포스트서브밋 테스트
    • 더 오래걸리고 약간 불안정한 테스트도 포함 가능

릴리스 후보 테스트

  • RC 전체를 광범위하게 검증하는 더 큰 테스트들을 수행
  • RC는 일련의 테스트 환경에서 다음 단계 환경으로 승격될 때마다 매번 테스트 됨
  • 테스트 환경
    • 샌드박스
    • 임시 환경
    • 공유 테스트 환경…
  • 수동 QA 테스트까지 진행함
  • CB의 포스트서브밋 때 똑같은 테스트를 이미 수행했더라도 RC는 매 단계에서 철저하게 다시 테스트함 이유는
    • 온전성 검사 : 코드를 잘라와 RC용으로 다시 컴파일할 때 예상치 못한 일이 발생하지 않았는지를 재차 확인
    • 검사 용이성 : RC의 테스트 결과를 확인하려는 엔지니어가 지속적 빌드 로그를 파헤쳐보지 않고도 해당 RC와 관련된 결과를 바로 살펴볼 수 있음
    • 체리픽 허용 : RC에 체리픽 수정을 반영한다면 지속적 빌드가 테스트한 코드와 달라짐
    • 비상 배포 : 비상 상황 발생 시, 지속적 배포는 참 헤드의 코드를 잘라와서 배포를 해도 괜찮겠다는 마음의 안정을 가져다주는 데 꼭 필요한 최소한의 테스트만 실행할 수 있음

프로덕션 테스트

  • 동일한 테스트 스위트를 프로덕션 환경에서도 그대로 수행함 (prober)
  • 다음의 두 가지 검증을 위함
    • 프로덕션이 테스트 대로 올바르게 동작하는가?
    • 프로덕션을 검증하기에 적합한 테스트인가?
  • CI는 경보와 같다
    • 프로덕션 서비스 가동 시간 목표를 100%로 잡으면 말도안되는 비용이 들듯이, CI 테스트를 모두 녹색으로 유지하겠다도 말이 안됨

23.1.4 CI의 과제

  • 프리서브밋 최적화 : 앞서 언급한 잠재적인 문제를 고려하여 프리서브밋 시 어떤 테스트를 어떻게 수행해야 하나?
  • 범인 찾기 (culprit finding)와 실패 격리 (faliure isolation) : 문제를 일으킨 코드가 어느 것이고, 어느 시스템에서 문제가 발생했을까요?
  • 자원 제약 : 테스트를 실행하기 위해 필요한 자원을 최적화

23.1.5 밀폐 테스트

  • 서비스 중인 백엔드와의 통신은 안정적이지 않을 수 있어서 구글은 범위가 넓은 테스트에는 밀폐된 백엔드를 주로 이용함
  • 밀폐 테스트 (hernetic test) : 모든 것을 갖춘 테스트 환경에서 수행하는 테스트
  • 테스트에 영향을 주는 데이터가 적어도 외부 의존성 때문에 바뀔 일은 없음

23.2 지속적 통합 @ 구글

23.2.1 TAP: 구글의 글로벌 지속적 빌드 시스템

  • TAP : Test Automation Platform
  • 매일같이 5만 개 이상의 변경을 처리하고 40억 개 이상의 테스트 케이스를 싱행함

프리서브밋 최적화

  • 팀별로 빠르게 실행할 수 있는 테스트들을 따로 추려달라고 요청함 -> 보통 유닛테스트
  • 프리서브밋을 통과한 병경은 95% 이사의 매우 높은 확률로 나머지 테스트도 통과
  • 잠재적으로 영향받을 수 있는 모든 테스트를 비동기적으로 수행함ㅁ
  • 팀별로 빌드 경찰 (Build Cop)을 둬서 담당 프로젝트의 테스트를 모두 통과 시키는 일을 책임 짐

범인 찾기

  • 비결정적인 테스트가 만연하고, 테스트 인프라 자체도 간헐적으로 문제가 생겨서 진짜 실패인지를 확신하기가 쉽지않음
  • 실패지 TAP은 자동으로 변경 각각을 독립적으로 다시 테스트 함
  • 이진 검색으로 범인 찾기

실패 관리

  • 롤백
  • 롤백이 없다면 테스트 실패를 빠르게 조치할 수 없어서 시스템을 믿지 못하게 됨

자원 제약

  • 대부분의 테스트는 Forge라는 분산 빌드&테스트 시스템으로 실행 함
  • 어떤 테스트를 언제 실행해야하는지 방법을 고안해내야 함
    • 다운스트림 의존성 그래프 분석
  • 빨리 끝나는 테스트를 먼저 실행함
    • 사람들이 빠른 테스트를 할 수있는 코드 변경을 선호하게 바뀜

23.2.2 사례연구 : 구글 테이크 아웃

23.2.3 하지만 나는 CI를 감당할 여력이 없어

  • 멋지지만 이런 시스템을 구축할 시간도 자금도 없다면?
  • 각자의 조직에서 서비스를 운영하며 생기는 문제 처리에 지불하고 있는 비용을 생각해보자
    • 진짜 돈 뿐만이 아니라, 안좋은 인식 및 사기 문제
    • 비상 대응이 잦아지면 빡친다
  • 원점 회귀하자

23.4 핵심 정리

  • CI 시스템은 무슨 테스트를 언제 실행해야 할지를 결정함
  • 코드베이스가 오래되고 규모가 커질수록 CI 시스템이 더욱 필요해짐
  • 빠르고 더 안정적인 테스트는 프리서브밋 단계에서, 느리고 덜 결정적인 테스트는 포스트 서브밋 단계에서 실행하도록 최적화 해야 함
  • 볼 수 있고 조치할 수 있는 피드백이 CI 시스템을 더효율적으로 만들어줌

Comment  Read more

21. 의존성 관리

|

21. 의존성 관리

  • 의존성 관리란?
    • 우리가 통제하지 못하는 라이브러리, 패키지, 그 외 의존성들의 네트워크를 관리하는 일
    • SWE에서 제일 어렵다
  • 의존성 관리에서 답하려는 질문은
    • 외부 의존성의 버전을 업데이트하는 방법은 무엇일까요?
    • 버전을 기술하는 방법은 무엇일까요?
    • 의존성에서 어떤 유형의 변경이 허용되거나 예상되나요?
    • 다른 조직에서 만든 코드에 의존하는 게 직접 만드는 것보다 낫다고 판단하는 기준은 무엇일까요?
  • 소스코드와 비슷하지만 훨씬 어렵다
  • 의존성 관리는 시간과 확장 양 축 모두에서 복잡도를 키운담
  • 업스트림 의존성들은 내가 작성한 코드와 손발을 맞출 수 없으므로 내 빌드나 테스트를 실패하게 만들 가능성이 크다
  • 다른 조건이 모두 같다면 의존성 관리 문제보다는 소스 관리 문제를 선택하라 -> 직접 구현해라?
  • OSS가 커지면서 의존성 그래프도 점점 커짐
  • 더 이상 의존성 관리를 회피할 수는 없음

21.1 의존성 관리가 어려운 이유

  • 정의 부터가 쉽지않다
  • 수많은 의존성들로 구성된 네트워크와 그 네트워크에 미래에 일어날 변화까지 고려해 관리하는 방법을 강구해야 함
  • 시간이 흐르면서 의존성 네트워크의 여러 노드가 새 버전을 출시하고 그중 몇개는 중대한 업데이트일 것
  • 전이 의존성들에서 이루어지는 이러한 업그레이드 도미노를 어떻게 관리해야 할까요…?

21.1.1 요구사항 충돌과 다이아몬드 의존성

  • 의존성 네트워크상의 두 의존성 사이에서 요구사항이 충돌할 때 어떻게 해결하는가?
    • 다양한 이유로 문제가 발생함
    • 운영체제, 언어버전, 컴파일러
    • 업그레이한 라이브러리가 기존 버전과 호환되지 않아서
  • 대표 문제는 다이아몬드 의존성 문제 (diamond dependency problem)

[그림 21-1]

  • 위 그림에서 libbase는 liba과 libb 모두에서 쓰이며, liba와 libb는 상위 노드인 libuser가 이용함
  • 이 때 libbase가 하위호환 되지 않는 업데이트를 했는데, liba만 업데이트를 따라 갔다면?
  • 언어에 따라 문제가 생길수도, 안생길 수도있음
    • 자바, js등은 그냥 여러 버전을 설치해서 해결해버림 -> 구글에서 극혐하는 해결법
    • C++은 ‘하나의 정의 규칙 One Definition Rule (ODR)’을 거슬렀기 때문에 undefined symbol 에러가 발생함
    • Function 정도는 shading할 수 있지만 타입은 불가능
  • 위 문제를 쉽게 해결하는 유일한 방법은 모두와 호환되는 더 상위 혹은 하위 버전의 라이브러리를 찾는 것 뿐
  • 이게 불가능하다면 문제가 되는 의존성을 로컬에서 따로 패치해야 함 -> 매우매우 어려운 일

21.2 의존성 임포트하기

  • 프로그래밍 측면에서 보면, 내가 직접 구현하는 것 보다 만들어진거 쓰는게 낫다

21.2.1 호환성 약속

  • 시간을 고려하기 시작하면 몇가지 복잡한 트레이트 오프가 생겨남
    • “개발” 비용을 줄이는 반면에 지속적인 유지보수 비용을 감안해야 함
    • 업그레이드 할 계획이 없던 의존성이라 해도 보안 취약점이 발견될 수 있음
  • 따라서 다음을 고려해야 함
    • 호환성이 얼마나 잘 지켜지나요?
    • 진화가 얼마나 빠르게(크게) 이루어지나요?
    • 변경 처리 방법은 무엇일까요?
    • 각 버전의 지원 기간은 어떻게 되나요?

C++

  • C++ 표준 라이브러리는 거의 무한대의 하위 호환성을 제공 -> ABI ㅎ호환성

Go

  • Go 언어는 대부분의 릴리스에서 소스코드가 호환되지만, 바이너리 호환은 되지 않음

Abseil

  • 완전한 하위 호환성을 보장하지 않음
  • 대신 자동 리팩터링 도구를 함께 제공 함

Boost

  • 하위 호환성을 보장해주지 않음 (실험적인 프로젝트이기 때문)

21.2.2 임포트 시 고려사항

  • 구글에서는 의존성을 임포트하려는 엔지니어들에게 몇 가지 질문들을 던져보라고 권한다
    • 여러분이 실행해볼 수 있는 테스트가 딸려 있는 프로젝트인가요?
    • 테스트는 모두 통과하나요?
    • 의존성 제공자는 누구인가요? 똑같이 호환성을 보장하지 않는 오픈 소스 프로젝트더라도 경험과 기술력 차이는 어마어마할 수 있음
      • C++ 표준 라이브러리, 자바의 Guava 라이브러리 vs 깃허브나 npm의 임의의 프로젝트
    • 지향하는 호환성 정책은?
    • 앞으로 어떤 분야나 용도를 지원해나갈 것인지 자세히 설명하고 있는지?
    • 얼마나 인기 있는 프로젝트인지?
    • 언제까지 이용할 것인지?
    • 파괴적인 변경이 얼마나 빈번하게 행해지고 있는지?
  • 조직 관점에서의 질문
    • 구글이 같은 기능을 새로 구현하려면 얼마나 복잡한지?
    • 그 의존성을 최신 상태로 유지하면 어떤 이점이 있는지?
    • 업그레이드는 누가 할 건지?
    • 업그레이드하는 난이도는 어느 정도일 거라 예상하는지?
  • 참고자료 : Russ Cox : Our Sofware Dependency Problem

21.2.3 의존성 임포트하기@구글

  • 구글은 거의 다 직접 개발해버려서 의존성 관리가 아닌 소스코드 관리를 해버림
  • 그럼에도 외부 프로젝트를 쓰는 경우, ‘third_party’라는 디렉터리에 추가한다
    • 그런데 여기에도 하이럼의 법칙이 적용되서 망할 수 있음

21.3 (이론상의) 의존성 관리

  • 변경 불가
  • 유의적 버전
  • 하나로 묶어 배포하기
  • 헤드에서 지내기

21.3.1 변경 불가(정적 의존성 모델)

  • 애초부터 변경 자체를 허용하지 안흠
  • API 병경, 기능 (동작) 변경, 아무것도 안됨
  • 사용자 코드 동작에 영향을 주지 않는 선에서의 버그 수정만 유일하게 허용
  • 변경 불가는 사실 대부분의 신생 조직에게는 적합한 모델 일 수 있다
  • 하지만 프로젝트가 오래될 수록 의존성을 업그레이드해야함 하는 보안 버그나 기타 사항이 생길 수 있음
  • 이런 사항이 생기면 대처하기가 어렵다

21.3.2 유의적 버전(SemVer)

  • SemVer는 오늘날 의존성 네트워크를 관리하는 가장 대표적인 방법 (ex: 4.28.1)
    • 메이저 : API가 변경되어 의존성을 이용하던 기존 코드를 깨뜨릴 수 있음
    • 마이너 : 순수하게 기능 추가만 있음 (기존 코드를 깨뜨리지 않음)
    • 패치 : API에 여향을 주지 않는 내부 구현 개선과 버그 수정
  • 버전 표기에 이렇게 의미를 담으면 API가 호환되면서 더 최신 버전 같은 표현이 가능
  • 메이저 버전이 달라지면 일반적으로 호환성이 크게 떨어짐
  • 이렇게 요구사항 표현법을 정형화하면 의존성 네트워크를 소프트웨어 컴포넌트 (노드)와 그 사이의 요구사항(에지)의 집합으로 표현 가능
  • 충족 가능성 솔버 (SAT solver) 사용하여 요구사항을 모두 충족하는 버전의 집할을 찾아볼 수 있음
  • 하지만 이것이 모든것을 해결해주진 않음, SAT solver로도 답을 못찾을 수 있음 -> 의존성 지옥 dependency hell

21.3.3 하나로 묶어 배포하기

  • 애플리케이션 구동에 필요한 의존성들을 모두 찾아서 애플리케이션과 함께 배포한다 -> 리눅스 배포판
  • 전문 배포자가 필요함

21.3.4 헤드에서 지내기

  • 구글 C++ 커뮤니티 일원들이 일고있는 모델
  • 트런크 기반 개발을 의존성 관리 영역까지 확장한 것
  • SemVer를 버리고 의존성 제공자가 변경사항을 커밋하기 전에 생태계 전체를 대상으로 검증하게 함
  • 모든 컴포넌트가 항상 최신 버전에 의존
  • 유닛 테스트 및 CI가 필수적으로 필요함
  • API 제공자 다운 스트림 의존성들이 깨지는 지 확인 가능 해야함
  • API 소비자가 테스트들이 계속 통과되고 지원 가능한 방식으로 의존성을 이용 중

21.4 유의적 버전의 한계

  • 사실 사람들이 SemVer의 메이저, 마이너, 패치를 잘 지키는가? 안지킬 수도 있다
  • SAT solver의 믿음도 떨어짐

21.4.1 지나치게 구속할 수 있다.

  • SemVer의 major 버전이 바뀌었다고 무조건 못쓰게 되느냐? 아님!
  • 내가 import한 library의 전부를 쓰는 것이 아니기 때문에, 내가 사용하는 부분과 필요없는 부분만 변경되었는데 major 버전이 바뀌었을 수 있다.
  • 이로 인해서 의존성 지옥에 빠질 수 있음

21.4.2 확실하지 않을 약속일 수 있다.

  • SemVer는 강제적 혹은 완벽하게 API를 제어하는 것은 아니기 때문에, 완벽히 믿을 수 없음
  • patch 버전이 바뀌었다는 것은 내부 사용 API를 바꾸었다는 뜻인데, 하이럼의 법칙에 따라 외부 사용자가 내부 API를 사용해서 patch버전만 바뀌었는데 소스가 깨질 수 있음…
  • SAT solver는 괜찮다고 하는데 소스가 깨질 수 있다

21.4.3 버전업 동기

  • 메이저 버전업을 위축시키는 동기가 될 수 있음
  • Go나 Closure 메이저 버전업을 완전히 새로운 패키지로 취급함

21.4.4 최소 버전 선택

  • 기본적으로는 허용가능한 최신 버전을 선택하여 설치함
  • 최소 버전은 명시되어있는 최소 버전을 선택하여 설치 -> 안정성 증가
  • 그나마 개발자가 설치해서 동작했던 버전을 설치할 수 있다

21.4.5 그래서 유의적 버전을 이용해도 괜찮은가?

  • 유의할 점
    • (SemVer 버전업 시 사람에 의한 오류가 나지 않도록) 의존성 제공자들이 버전을 정확하고 책임감 있게 관리합니다.
    • (지나친 구속과 지나친 약속을 피하기 위해) 의존성들이 충분히 세분화되어 있습니다.
    • (직간접적으로 사용하는 의존성에서 호환된다고 가정하고 진행한 변경임에도 여러분의 코드가 예기치 못한 방식으로 오작동하는 일을 피하기 위해) 모든 API를 제공자의 의도대로 사용합니다.
  • 하지만 이같은 노력에도 규모가 커질수록 SemVer의 단점이 드러남

21.5 자원이 무한할 때의 의존성 관리

  • 현재 업계가 SemVer에 의지하는 이유
    • 내부 정보만 있으면 됨 (API 제공자가 다운스트림 사용자가 어떻게 이용하는지까지 알 필요 없음)
    • 충분한 테스트를 수행할 컴퓨팅 자원, 테스트 결과를 모니터링해줄 CI 시스템이 존재하지 않아도 됨
    • 관행임
  • 만약 더 많은 컴퓨팅 자원을 쓸 수 있고 다운스트림 의존성 정보를 더 쉽게 얻을 수 있다면 이를 활용할 수 있다.
    • 실제로 의존성 테스트를 돌려 볼 수 있다면? 통계적으로 안전하다 라고 이야기 할 수 있음
    • 의존성 업데이트를 CI 기반 모델 적용 가능
    • 이를 적용하려면? 갈길이 굉장히 멀다
  • 위 모델을 위한 필요조건
    • 모든 의존성이 단위 테스트를 제공해야 함
    • OSS 생태계 대부분의 의존성 네트워크를 이해해야 함
    • CI를 수행하기 위한 컴퓨팅 자원이 충분해야 함
    • 의존성을 고정해 놓은 경우가 많음
    • CI 이력과 평판을 명시하는 것도 좋은 방법일 수 있음
  • 결국 헤드에서 지내기 모델로 수렴함

21.5.1 의존성 내보내기

  • 구글의 경우 gflags와 AppEngine의 사례를 통해 의존성을 밖으로 내보냈다가 큰 손해를 본 적이 있음
  • 하지만 구글은 평판으로 인해 deprecated도 쉽게 시킬 수 없었음…

21.7 핵심 정리

  • 의존성 관리보다는 되도록 버전관리가 되도록 합시다. 더많은 코드를 조직내로 가져와 투명성과 통제력을 높인다면 문제가 훨씬 단순해짐
  • 소프트웨어 엔지니어링 프로젝트에서 의존성 추가는 공짜가 아님, 의존성 네트워크를 관리하는 것은 매우 비싸다
  • 의존성 역시 하나의 계약임, 주고 받는 것이 있다. 지금 당장뿐 아니라 미래에 대한 약속도 명확하게 포함되어야 함
  • SemVer는 변경이 얼마나 위험할지를 ‘사람’이 ‘추정’하는 간단하지만 정보가 일부 손실되는 표현법임. 하지만 SAT solver 방식의 패키지 관리자는 이 추정이 틀릴 리 없다고 동작함 -> 의존성 지옥 or 소스깨짐
  • 이에 비해 테스트와 CI는 새로운 버전들이 잘 어울려 돌아가는지를 ‘실제로’보여줌
  • SemVer 기반 패키지 관리에 최소 버전 선택 전략을 가미하면 충실성이 올라간다. API제공자와 사용자의 환경이 비슷할 확률이 올라감
  • 단위 테스트, CI, 저렴한 컴퓨팅 자원이 의존성 관리를 이해하고 처리하는 방식에 변화를 가져올 수 있음
  • 의존성을 제공하는 일 또한 매우 비싸다.

Comment  Read more

16. 버전 관리와 브랜치 관리

|

16. 버전 관리와 브랜치 관리

  • VCS는 꼭 써라!
  • 트렁크 기반 개발이 구글이 쓰니까 짱짱이다!

16.1 버전 관리란?

  • skip

16.1.1 버전 관리가 중요한 이유

  • skip 너무 당연함

16.1.2 중앙집중형 VCS vs 분산형 VCS

  • SVN, GIT?
  • skip

16.1.3 진실 공급원

  • 중앙집중형 VCS는 시스템 설계에서부터 진실 공급원 (source-of-truth)이라는 개념을 사용
    • Trunk에 가장 최근 커밋된 것이 현재 버전임
    • 개발자가 프로젝트를 체크아웃하면 기본적으로 이 트렁크 버전이 제공
    • 수정한 내역을 이 버전위에 다시 커밋하면 해당 변경이 완료됨
  • 분산형 VCS에는 단일 진실 공급원 (Single Source of Truth : SSOT)라는 개념이 존재하지 않음
    • 분산형 VCS를 운형하려면 정책과 규범을 더 명확하게 정해 지켜야 함
    • 잘 관리되는 프로젝트들은 특정 리포지터리의 특정 브랜치 하나를 진실 공급원으로 정하여 혼란의 여지을 없앰 (GitHub or GitLab…)
    • 변경 사항이 주 리포지터리의 트런크 브랜치에 반영되어야 비로소 작업이 완료됨

시나리오: 명확한 진실 공급원이 없다면?

  • 팀원의 리포지터리에서 가져온 이후 무엇이 수정되었는지가 명확하지 않을 수 있음
  • 중앙의 진실 공급원이 없다면 누군가는 다른 릴리스에 포함시킬 기능 목록을 따로 관리해야 할 것이고 이는 결국 중앙화된 진실 공급원 모델을 모방한 것임
  • 새로운 팀원이 합류한다면 정상 작동하는 최신 코드를 어디에서 복사해 올 것인가…?

16.1.4 버전관리 vs 의존성 관리

  • 버전 관리 정책은 개념적으로 의존성 관리 (21장 참고)와 매우 비슷함
  • 차이점은 크게 두가지
    • VCS 정책은 주로 코드를 어떻게 관리할지를 다루고, 대체로 훨씬 세세하게 관리
    • 의존성 관리는 훨씬 어려움, 다른 조직에서 통제하는 프로젝트들을 관리해야하기 때문

16.2 브랜치 관리

16.2.1 ‘진행 중인 작업’은 브랜치와 비슷하다

  • WIP는 모두 하나의 브랜치와 같다
  • 개발자가 상위 진실 공급원으로 푸시하기 전까지 수많은 변경하상을 로컬 리포지터리에 커밋해놓는 분산형 VCS 모델을 생각하면 더 명확함
  • 중앙집중형 VCS에서도 아직 커밋하지 않고 계류 중인 변경들은 브랜치에 커심한 변경과 개념적으로 다르지 않음
    • Perforce는 파일을 변경 중인 사람이 누구인지 찾고, 그 사람이 아직 커밋하지 않은 병경의 내용을 검토해볼 수 있음
  • 예제
    • Widget의 이름을 OldWidget으로 바꾸자!
      • 진실 공급원 리포지터리를 트렁크 브랜치에 있는 Widget의 이름을 바꿉니다.
      • 진실 공급원 리포지터리의 모든 브랜치에서 Widget의 이름을 바꿉니다.
      • 진실 공급원 리포지터리의 모든 브랜치에서 Widget의 일므을 바굽니다. 그리고 Widget을 참조하는 계류 중인 변경들까지 모두 찾아서 바꿉니다.

16.2.2 개발 브랜치

  • 일관된 단위 테스트 (11장 참고)가 보편화되기 전 시절에는 작은 병경 하나라도 시스템을 망가뜨릴 위험이 컸기 때문에 ‘트렁크’를 특별 취급하는 것이 당연
    • 테크리드 왈 “우리는 새로운 변경이 모든 테스트를 통과할 때까지 트렁크에 커밋하지 않습닏. 그리고 우리 팀은 기능별로 개발 브랜치를 따로 만들어 사용하죠”
  • 개발 브랜치 (dev branch)는 ‘구현은 다 했지만 커밋하진 않았어요’와 ‘이제부터 이 코드를 기준으로 개발하세요’의 중간 단계
  • 제품 안정성 유지 차원에서 개발 브랜치를 과하게 사용하는 버전 관리 정책은 근본적으로 잘못됨, 결국은 똑같은 커밋들이 트렁크에까지 병합될 것므로, 큰 단위로 한꺼번에 병합하기보다는 작게 작게 자주 병합하는게 쉬움
    • 각각의 변경을 작성한 당사자가 병합하는 편이 서로 관련 없는 변경들을 나중에 누군가 몰아서 병합하는 것보다 쉬움
    • 병함 시 프리서브밋 테스트가 문제를 발견했을 때도 똑같음
  • 거대 개발 브랜치의 병합은 고통임!

개발 브랜치에 중독되어 가는 과정

  • ‘오래된 개발 브랜치를 병합하니 안정성이 떨어진다’ -> ‘병합은 위험하다’ : 개발 브랜치에 중독
  • ‘더 나은 테스트’와 ‘브랜치 기반 전략 탈피’로 정면 돌파 해야 함
  • 조직이 커지면 개발 브랜치의 수도 늘어나며 병합 난이도가 자연스래 치솟음
  • 병합 후 다시 테스트하는 데 드는 노력은 사실 모두 무가치한 오버헤드
  • 트렁크 기반 개발이 필요!
    • 테스트와 CI를 적극 활용하여 모든 빌드와 테스트가 항상 성공하도록 관리
    • 완벽하지 않거나 테스트되지 않은 기능은 비활성화
    • 엔지니어 개개인이 트렁크와 동기화하고 트렁크에 커밋!

16.2.3 릴리스 브랜치

  • 제품의 릴리스간격이 몇 시간 이상이면 릴리스 브랜치를 따로 생성하는게 좋음
  • 개발 브랜치와 달리 릴리스 브랜치는 대체로 무해함
  • 개발 브랜치와 릴리스 브랜치의 가장 큰 차이는 생을 마감하는 모습
    • 개발 브랜치는 트렁크에 다시 병합될 텐데, 중간에 다른 팀이 브랜치를 추가로 따서 가지를 더 뻗을 수도 있음
    • 반면 릴리스 브랜치는 홀로 존재하다가 결국 사라짐
  • 구글의 DORA (DevOps Research and Assessment) 조직의 연구 결과에 따르면 최고 수준의 기술 조직에는 릴리스 브랜치조차 없음
    • 트런크로부터 하루에도 몇 번씩 릴리스할 수 있는 지속적 배포 (CD)가 잘 자리 잡은 조직에서는 대체로 릴리스 브랜치를 건너뜀
    • 수정사항을 적용해 다시 배포하는게 훨씬 쉽기 때문
  • 같은 DORA 연구에서 ‘트렁크 기반 개발’ 조직과 ‘장시간 유지되는 개발 브랜치가 적은’ 조직일 수록 기술적 성취가 뛰어나다고 이야기 함

16.3 버전 관리 @ 구글

  • 구글은 소스코드 대부분은 하나의 리포지터리, 즉 모노리포에서 관리되며 약 5만여 엔지니어에게 공유됨
  • Chromium, Android 같은 오픈 소스 프로젝트를 제외하고는 거의 모든 프로젝트가 모노리포에서 진행
  • Piper라는 중앙집중형 VCS 사용 (80TB가 넘음!)
    • CaaS, 프로덕션 환경에서 분산 마이크로서비스 형태로 구동
    • 동시 편집 지원
    • 하루 6 ~ 7 만 건의 커밋 처리
  • 모든 디렉터리 계층에 OWNERS 파일을 두어 커밋을 승인할 수 있는 엔지니어의 사용자 이름을 기록

16.3.1 원-버전

  • 구글 버전 관리 정책의 중심에는 One-Version이 있음
  • 단일 진실 공급원 개념을 확장한 개념
  • 모든 의존성이 우리 리포지터리에 담겨 있고 각 의존성은 단 하나의 안정된 버전만 존재해야 함
  • 서드 파티 패키지들도 Piper에는 단 하나의 버전만 저장해둬야 함 (완벽히 지킬 수는 없음)
  • 내부 패키지의 경우 다시 패키징하거나 이름을 바꾸지 않고는 포크할 수 없음 (원본과 포크 버전을 별다른 추가 노력 없이 같은 프로젝트에 섞어 둘 수 있는 기술적으로 안전한 방법)

16.3.2 시나리오: 여러 버전을 허용한다면?

  • 어떤 팀이 공통 인프라 코드에서 버그를 발견
  • 이 팀은 원본 인프라의 코드를 바로 수정하지 않고 포크하여 버그를 자체 해결하는 전략을 택함, 라이브러리 이름이나 심볼을 바꾸지 않은 채로! (위 원-버전 정책을 어김)
  • 주변 팀한테 여기 Abseil의 개선 버전을 체크인해뒀으니 필요하면 쓰세요~ -> 망하기 시작
  • 21장에서 살표보겠지만 위험한 상황으로 접어듬 (그림 16-2, 16-3 참조)
  • 운 좋으면 빌드실패, 최악은 어려운 런타임 버그
  • 포크가 코드베이스에 갈림길을 하나 추가해버림
  • 같은 대상을 가리키는 전이 의존성 (transitive dependency)들은 반드시 단 하나를 가리켜야 함.
  • 즉, ‘새로운 의존성을 하나 추가한다’라는 간단한 일이 자칫하면 코드베이스 전체의 테스트를 모두 수행하여 존재하는 모든 갈림길들의 조합을 다시 점검해야 하는 큰 작업으로 번질 수 있음
  • 이것을 가능하게 하는 여러 트릭 (셰이딩 등)이 존재하지만 모두 무가치한 쓸데없는 짓

16.3.3 원-버전 규칙

개발자가 ‘이 구성요소는 어떤 버전을 사용해야 하죠?’ 라고 문든 상황을 만들지 않아야 합니다.

  • 의존성을 새로 추가할 때 선택할 수 있는 버전을 제한 한다.

16.3.4 장수 브랜치는 (웬만하면) 금지

  • 개발 브랜치를 되도록 만들지 말고, 만들더라도 매우 짧게 쓰고 없애야 함
  • 대규모 변경 (22장 참고)을 위한 위리 정책과 도구들은 트렁크 기반 개발이 중요하다는 데 더욱 힘을 실어 줌
    • 코드베이스 전반에 적용되는 광범위하고 얕은 변경은 그 자체로 이미 엄청난 규모의 작업
    • 트런크에 체크인된 모든 것을 수정해야 함
    • 그런데 무수한 개발 브랜치들까지 찾아서 고드를 적절하게 수정해야 한다면 부담이 더욱 더 커짐
    • 분산형 VCS는 영향 받는 브랜치를 모두 찾아내는게 불가능 할 수도 있음
  • 구글은 빌드 호라이즌 (Build Horizon)이라는 정책을 써버 잠재적인 버전 왜곡이 지속되는 기간의 상한선을 정해 둠
    • 프로덕션 환경에서 구동 중인 모든 제품은 최대 6개원 안에 다시 빌드하여 재배포 해야함

16.3.5 릴리즈 브랜치는 어떤가?

  • 릴리스 브랜치가 광범위한 비용을 발생시키지는 않음

16.4 모노리포 (단일 리포지터리)

  • 모노리포 방식은 그 자체로 몇가지 이점을 제공
    • 그 중 최고 : 원-버전을 고수하기 쉬움

중요한 것은 모노리포냐 아니냐가 아니라 ‘원-버전’ 원칙을 최대한 준수하는 것

  • VCS와 빌드 시스템 같은 소프트웨어 엔지니어링 도구들은 세분화한 리포지터리들과 모노리포를 영리하게 혼합하여 점점 더 모노리포와 비슷한 경험을 제공하도록 지화 함
    • git submodule
    • Bazel의 외부 의존성
    • CMake의 서브 프로젝트
    • 가상 모노리포 (VMR)

16.5 버전 관리의 미래

  • VCS는 확장성을 개선해 더 큰 리포지터리를 지원할 것
  • 리포지터리들이 프로젝트와 조직 경계를 넘어 더 유연하게 연동되도록 해주는 반대 방향의 기술도 발전할 것임
  • 아마도 현존하는 패키지 관리 단체나 리눅스 배포자 중 하나가 업계 표준의 가상 모노리포 구축을 촉진할 것
  • 그 모노리포가 제공하는 유틸리티를 이용하여 상호 호환되는 의존성들을 하나의 단위처럼 활용하기가 쉬워질 것
  • 버전 번호는 차츰 타임스탬프 정도의 의미로 퇴색될 것

16.6 마치며

  • 원-버전 규칙 적극 권장
  • 개발자들이 어디로 커시해야 할지, 혹은 어느 버전을 이용해야 할지를 선택할 수 없어야 함

16.7 핵심 정리

  • VCS 꼭 쓰셈
  • 어느 버전을 사용할지 선택할 수 있다면 잠재적으로 확장성이 떨어진다는 뜻
  • 원-버전 규칙은 조직의 효율에 지대한 영향을 줌. 어디에 커밋할지 혹은 어느 걸 사용할지 성택할 일을 없애면 일이 기가 막히게 단순해짐
  • 어떤 언어에서는 셰이딩, 분리 컴파일, 링커 숨기기 같은 기술로 문제를 회피할 수 있지만, 쓸모없는 짓, 기술 부채만 쌓임
  • 트런크 기반으로 개발하는 조직일수록 성과가 좋을 가능성이 높음

Comment  Read more