pile·
백엔드·flexflex·

되도록 최신 버전을 사용하는게 왜 이렇게 어려울까?

flex가 사내 라이브러리 버전을 "되도록 최신"으로 유지하기 위해 만든 version family 규칙과, 그 단순한 규칙이 대부분의 조직에서는 왜 서지 못하는지를 다룬다. 개별 레포가 버전을 고르는 대신 group name에 소속되면 그 family의 검증된 최신 버전이 기본값으로 따라오고, 같은 family는 함께 움직이며 아티팩트는 위에서 아래로만 흐른다. 글의 무게는 구현이 아니라 이 규칙이 서기 위해 필요한 땅, 그리고 그 땅을 세울 때 부딪히는 세 가지 문제에 있다.

핵심 포인트
  • 진짜 고통은 "어느 버전 써요?"가 반복되는 것이 아니라, 지금 써도 되는 버전인지 답해줄 권위 있는 출처가 어디에도 없다는 것이다.
  • 봇은 PR을 만들어 주지만 그 조직의 검증 상태까지는 알지 못한다. 판단 근거가 레포마다, 태그마다, 슬랙 스레드마다 흩어져 있다.
  • 문제 1은 닭과 달걀이다. 버전 규칙을 강제하려면 모든 레포가 같은 빌드 전략을 공유해야 하는데, 그 동형성 자체가 강제 장치 없이는 유지되지 않는다.
  • 문제 2는 비가역성이다. 예측 가능성과 비가역성은 같은 동전의 양면이라, 한 점에 응집시켰다는 것은 그 한 점의 실수가 곧 전 조직의 동시 장애라는 뜻이다.
  • 문제 3은 인식이다. 성공의 증거가 고통의 부재라는 형태로만 남고, 없어진 고통은 아무도 기억하지 않는다.
상세 정리
  • 규칙의 모양: 개별 레포가 어느 버전을 박아둘지 고르는 대신 group name에 소속되면 그 family의 검증된 최신 버전이 기본값으로 따라온다. 고를 선택지 자체가 없어지면서 버전 질문이 대부분 사라진다.
  • 구현은 사내 Gradle 플러그인이지만, 저자는 이 규칙이 자기 조직에서 어렵지 않은 이유가 플러그인의 영리함이 아니라 그 아래 필요한 조건이 이미 갖춰져 있어서라고 못박는다.
  • 문제 1의 구조: group name 하나로 버전·릴리스·태깅·게시 규칙을 전부 유도하려면 규칙을 적용받는 모든 레포가 같은 빌드 시스템 위에 있어야 한다. 어떤 레포는 Gradle, 어떤 레포는 Maven, 어떤 레포는 자체 스크립트면 정책을 해석하고 강제할 단일 지점이 없다.
  • 이질성의 출처: 대개 잘못이 아니라 성장의 흔적이다. 빠르게 크던 시기에 각 팀에 편한 방식으로 빠르게 가라며 부여한 자율성이 팀마다 다른 빌드 방식과 버전 관례, 게시 규칙으로 굳었다. 속도를 위해 옳았던 결정이 규모가 커진 뒤 부채로 청구된다.
  • 대부분의 시도가 죽는 자리도 여기다. 빌드가 제각각인 곳에 family를 얹으면 강제할 지점이 없어 일부 레포에만 걸치고 반쪽짜리 규칙은 흐지부지된다. 순환의 어느 고리를 먼저 끊을 것인가가 플러그인을 짜는 일보다 훨씬 어려운 진짜 문제라는 지적이다.
  • 문제 2의 전제: 내부 아티팩트의 흐름이 단방향으로 정렬돼 있어야 한다. 공통 라이브러리가 게시되면 서비스가 소비하고 그 반대는 일어나지 않으며 전체 의존이 순환 없는 하나의 DAG를 이룬다. 이건 플러그인이 만들어 주는 성질이 아니라 의존성을 설계할 때부터 지켜 온 제약이다.
  • 그래서 "버전 전파를 자동화할 수 있는가"는 사실 "의존이 단방향인가"에 달려 있다. 흐름이 한 방향이면 배포 순서를 기계가 계산할 수 있지만, A를 올리려면 B가 먼저이고 B를 올리려면 A가 먼저인 교착이 섞이면 계산 자체가 성립하지 않는다.
  • 응집의 대가: 각 레포가 자기 버전 규칙을 갖는 자유를 내려놓은 대신 조직 전체가 하나의 규칙 위에서 움직이게 됐고, 이 대가는 늦게 청구된다. 수백 개 아티팩트가 이미 그 규칙 위에서 돌기 시작하면 group name policy의 층위를 다시 긋거나 family 경계를 재설계하거나 게시 규칙을 손보는 일이 전 레포에 동시에 영향을 준다.
  • 이 문제는 도입할 때가 아니라 성공하고 한참 뒤 무언가를 바꿔야 할 때 모습을 드러낸다고 짚는다.
  • 문제 3의 구조: 새 기능은 데모할 수 있고 성능 개선은 그래프로 보여줄 수 있지만 "원래 있었어야 할 마찰이 처음부터 없는 상태"는 보여줄 대상이 없다. 설득하는 사람은 아직 아무도 절실하게 느끼지 않는 미래의 고통을 근거로 현재의 투자를 요구해야 하고, 일이 잘 끝나면 무엇을 막았는지조차 증명하기 어렵다.
  • 저자는 조직들이 이 문제를 미뤄둔 것이 판단력 부족이 아니라 난이도를 정확히 알기 때문에 내린 합리적 유보였을 것이라고 보되, 그 유보의 비용은 지금도 조용히 청구되고 있다고 덧붙인다.
  • family가 필요해진 구조: 한 레포 안에 여러 모듈을 두고 각각을 별도 아티팩트로 배포한다. 헥사고날을 따르니 도메인 하나에서 배포 대상이 여럿 나오고, family가 없으면 같은 레포 안 모듈끼리도 버전이 어긋나 소비자가 "이 모듈 3.1과 저 모듈 2.7이 같이 써도 되는 조합인가"를 매번 확인해야 한다.
  • 더 정확한 구조는 레포 : 도메인 : 모듈이 1 : N : M이다. 한 백엔드 레포가 열 개가 넘는 독립 도메인을 담고 각 도메인이 다시 모델·유스케이스·어댑터·저장소 같은 헥사고날 계층을 별도 모듈로 갖는다.
  • 헥사고날 경계를 패키지 네이밍이나 컨벤션이 아니라 각각의 Gradle 모듈로 쪼개 물리 경계로 강제했다. 의존 방향을 사람의 규율이 아니라 빌드가 막게 한 선택이다.
  • 폴리냐 모노냐의 구분보다 중요한 것: 레포 하나에 서비스 하나인 폴리레포도 아니고 전 조직이 한 레포에 든 모노레포도 아니다. 핵심은 버전 관리의 단위를 물리적 레포 경계에서 떼어냈다는 점이다.
  • family를 묶는 기준이 레포가 아니라 group name이라, 한 레포 안에 서로 다른 family가 공존할 수도 있고 여러 레포에 걸친 아티팩트가 한 family로 묶일 수도 있다. "레포=버전 단위"라는 암묵적 등식을 끊어둔 덕에 레포가 어떻게 쪼개지든 버전 체계가 흔들리지 않는다.
  • 폴리냐 모노냐의 논의가 레포 배치에서 멈추면 정작 버전 단위가 무엇이어야 하는지는 설계 밖에 남기 쉽다는 지적으로 마무리한다.
왜 읽나여러 레포에 흩어진 사내 라이브러리 버전을 통제하려다 매번 절반에서 멈춘 플랫폼 팀이, 도구를 만들기 전에 무엇이 갖춰져 있어야 하는지와 응집의 대가를 미리 가늠할 수 있다.
flex
flex 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. 백엔드·twilio-engTwilio Engineering·

    Programmable Messaging에서 Verify API로 마이그레이션하기

    Twilio의 Programmable Messaging API로 자체 OTP 솔루션을 운영하던 서비스가 Verify API로 전환하는 방법을 코드 예시와 함께 설명한다. Verify는 OTP 전송·검증을 위한 전용 API로, 전화번호 구매, 토큰 생성, DB 저장·만료 관리를 내부에서 처리해 개발자가 직접 구현할 코드를 크게 줄인다.

    요약 이어보기
    #authentication#twilio#sms+2
  2. 백엔드·포스타입포스타입·

    포스타입이 개인화 추천을 하는 방법 2부

    포스타입 백엔드 엔지니어가 벡터 기반 개인화 추천 시스템을 실제 운영하며 맞닥뜨린 성능 장애와 용량 문제를 해결한 과정을 담은 2부다. 수백만 개의 벡터 KNN 검색이 피크 시간대에 전체 Elasticsearch 검색 성능을 흔드는 문제부터 클러스터 OOM 사태까지, 쿼리 최적화와 인프라 분리 두 가지 경로로 근본 해결에 이른다.

    요약 이어보기
    #elasticsearch#vector-search#recommendation-system+2
  3. 백엔드·포스타입포스타입·

    포스타입이 개인화 추천을 하는 방법 1부

    포스타입이 태그 기반 추천의 한계를 극복하고 벡터 임베딩 기반 개인화 추천 시스템을 구축한 과정을 담은 1부다. 유사한 콘텐츠가 다른 용어를 쓰거나 동일한 태그가 전혀 다른 톤의 콘텐츠를 가리키는 문제를 임베딩 벡터로 해결하고, OpenSearch의 HNSW ANN 검색으로 수백만 벡터를 실시간 검색하는 시스템을 구축해 구매율 15% 향상을 달성했다.

    요약 이어보기
    #opensearch#vector-search#recommendation-system+2