ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 레거시를 파악하고 변경해나가기: 우선순위와 고려사항들
    SE Concepts 2021. 3. 18. 22:20
    반응형

    최근 외주 인력이 운영하던 서비스를 내재화하는 팀으로 이동하게 되었습니다. 2000년대 중반에 시작되어 10년이 넘는 기간 동안 여러 회사와 다양한 외주 인력으로 관리 되던 서비스는 Spring, DWR, Struts, JSP, Apache Mina 등 다양하고 오래된 프레임워크를 사용하였습니다.  

     

    문서화도 잘 되어 있지 않고 단기간에 이제 계약이 끝나가 자세한 인수인계를 받기 어려운 서비스를 파악해가면서, 어떻게 30개로 흩어져 있는 코드를 BitBucket, 소스코드를 효율적으로 파악할 수 있을까?라는 생각을 많이 하였습니다.

     

    [1]의 Working Effectively with Legacy Code는 한 가지의 레거시 코드를 어떻게 안정적으로 변경해나갈 수 있을까?라는 부분에 Testing과 Scoping의 관점을 중점적으로 답해주고 있었습니다. 그러나 기존 구조상 Testing도 어렵고, 코드를 대부분 읽었으나 전반적인 Context가 이해되지 않은 시점에는 주된 해결책이 되지 못한다고 느꼈습니다.

     

    그러던 중 [2]의 Software Design X-Rays라는 책을 발견하였습니다. 마침 개인적으로 코드를 파악하기 위한 방법과 매우 유사한 형태를 제시하고 개념적으로 좀 더 정리되어 있었습니다. 이 글에서는 아래와 같은 사항들을 중점으로 레거시를 파악하고 변경해 나가는 부분을 좀 더 체계적으로 정리하고자 합니다:

     

    • 레거시, 기술부채, reckless debt 그리고 부채의 이자율
    • git history와 소스코드의 메타데이터 파악하기
    • 변경을 scoping하고 단계로 나누어 실행나가기

     


     

    레거시, 기술부채, reckless debt 그리고 부채의 이자율

    마이클 페더는 [1]에서 레거시 코드는 "테스트가 없는 코드"라고 표현했습니다. 레거시 코드는 어떤 시간이 지나고 나서의 바람직하지 못한 "이후의 상태"를 나타냅니다. 

     

    반면, 기술부채는 서비스의 비지니스를 담당하는 직군에게 기술적인 트레이드 오프에 대해 이야기를 나누거나, 리팩토링에 대한 필요성을 설명하기 위해 사용되는 은유적인 표현입니다 [3]. 파이낸스에서의 부채처럼 기술부채는 이자에 대한 지급을 동반하고, 소프트웨어 시스템을 진화시켜 나가는데 영향을 미칩니다

     

    기술부채는, 비록 실제로 구분이 어렵긴 하나, 트레이드 오프 간에 전략적 선택이라는 점에서 reckless debt(부주의에 의한 부채)과 엄연히 구분됩니다. 마틴파울러는 mess는 reckless debt이라고 표현하였는데요 [4]. 기술부채와 reckless debt을 구분하는 것이 매우 중요하다고도 언급하였습니다. 

     

    그렇기에 레거시 코드는, 부채의 이자율이 현재에 너무 높거나 줄일 수 있는 (과거의) 선택에 의한 부채, reckless debt(또는 mess)가 해당된다고 할 수 있습니다. 그렇기에 그러한 부채로 인한 "이자"를 지불해야 되지 않는 경우 debt에 해당되지 않습니다. 그러한 이자(또는 이자율)는 "시간"이라는 또 다른 측면을 고려하게 합니다.

     

    위의 내용을 인지하고 나면, 변경의 대상에 대한 우선순위는 "어떤 것에 팀이 가장 큰 이자를 지불하고 있나?"라는 질문을 통해 정리할 수 있습니다. 그러한 질문에 답하기 위해서는, 담당하는 서비스의 범위를 산정하고(scoping) 포함되는 다양한 코드가 어떻게 유기적으로 동작하는지 먼저 이해하여야 합니다.

     

    git history와 소스코드의 메타데이터 파악하기

    소스코드는 다양한 history로 인하여 만들어진 이후의 snapshot인 반면, 아래와 같은 version-control 데이터는 소스코드가 가지고 있지 못한 풍부한 데이터를 "시간"이라는 측면도 포함하여 제공해줍니다:

     

    git log - Image from Author

    위와 같은 Git의 정보에는 1) 특정 변경이 언제 발생하였는지, 2) 누가 그러한 변경을 발생시켰는지가 담겨있습니다. 이것은 "시간"이라는 측면과 유관자에 대한 정보를 들고있다는 것을 의미합니다. 또한 (git과 코드의 범위를 넘지만), 그러한 정보가 외부 컨택스트 정보(Jira 티켓, 이슈 처리 내역 등)와 결합되었을 때, 변경의 시작과 결과까지 파악할 수 있는 중요한 정보가 됩니다(이러한 작업은 '나무'를 보기 시작할 때 진행하는 것이 좋은 것 같긴 합니다).

     

    유관자에 대한 정보를 들고있다는 점은 (보통 규모가 어느 정도 있는) 어느 특정 개인도 복잡한 전체 서비스에 대한 완벽하고 상세한 이해는 불가능하기에 중요합니다. 그렇기에 소스코드는 git의 정보를 통해 아래와 같은 knowledge map으로 "어느 부분에 대한 특정 시점의 변경에 대해 누구에게 물어보아야 할까?"에 대한 답변을 가능하게 합니다:

     

    Knowledge map - Image from [2]

     

    실제로 위와 같은 이미지는 특정 도구([6]과 같은)를 사용하지 않는 이상 사용하기 어려운데요. 목적을 이루기 위해서는 git에 담긴 아래와 같은 정보를 추출하여 살펴보는 것만으로도 충분합니다:

     

    •  repository 이름
    • 브랜치명
    • 총 커밋수
    • 최근(대략 1년) 커밋수
    • 총 Author 수
    • 마지막 커밋 일시

    각 Repository, 브랜치별로 위와 같은 값들을 추출하여, 빠르게 자주 수정되거나, 활성화 되어 수정되는 부분을 탐색할 수 있습니다. 

     

    Image from [2]

    위의 이미지에서 보듯이, 대부분 소스코드의 변경 - 시간 차트가 위와 같은 그래프 형태를 가진다는 점을 고려하면 개발의 초기 시점에 생성된 파일들이 오래 남아 (비교적 안정적이겠으나)"높은 이자율"을 발생시킨다는 것을 알 수 있습니다. 그렇기에 git log의 시간적인 측면을 통해서 초기 시점에 개발되거나, 초기 시점에 자주 변경된 파일들을 특정하고 파악할 수 있게 됩니다.

     

    Version control 도구에서 얻은 정보는 유용하지만, Author 또는 사용 언어 등의 특성에 따라 많은 차이가 나기도 합니다. 그렇기에 아래의 '소스코드의 메타데이터'라는 다른 측면도 고려할 필요가 있습니다.

    소스코드의 메타데이터

    소스 코드의 메타데이터 역시, 직접 소스코드를 읽기 전에 간략한 정보를 통해 빠르게 코드를 "큰 그림에서" 파악할 수 있습니다. 그 중 소스 코드의 "총 라인 수"는 복잡도를 유추할 수 있는 간단하고도 중요한 지표입니다. 

     

    간단한 CLI 도구를 통해 코드의 총 라인 수를 구할 수 있습니다만, 소스코드에 대한 정적분석(일부 언어는 제한적이나) 도구를 사용하는 것은 그러한 총 라인 수 정보 외에도 소스코드에 대한 다양한 정보도 풍부하게 제공해줍니다.

     

    Image from [7]

    위와 같은 정적분석기 소나큐브(SonarQube)를 실행하여 라인 수, (예상) 버그 수, 코드스멜, 중복 등을 빠르게 체크하고 살펴볼 수 있습니다. 

     

    변경을 scoping하고 단계로 나누어 실행나가기

    담당하게 될 코드(또는 Repository들)의 경계가 정해졌다면, 다양한 스펙트럼의 코드의 지형을 변경을 위해 여러 측면(dimension)으로 파악하며 scoping해나가야 합니다. 

     

     

    코드의 나이

    이러한 측면의 한 종류인 "코드의 나이(Age)"를 파악하는 데에, 가장 큰 도움이 될 수 있는 정보는 위에서 git log로 살펴본 코드의 "시간적"인 측면입니다. 보통 "같이 변경되어야 하는 파일"들은 동시기의 변경 로그를 가지고 있기에, scoping에 유용한 지표가 됩니다.

     

    Image from [8]

    위의 이미지는, 30년 가까이 되어가는 파이썬의 개발 역사에서의 커밋과 author의 수를 보여주고 있습니다. 코드의 나이라는 측면으로 소스코드를 살펴보게 되면, 아래와 같이 구조적으로 같이 존재하는 파일끼리도 매우 다른 "나이"를 가지고 있음을 알 수 있습니다.

     

    Image from [9]

    그렇게 최근 변경된 코드 가까이에 10여년 넘게(매우 안정적이라고 추정되는) 변경되지 않은 소스코드가 존재하게 됩니다:

    Image from [9]

     

    인터페이스

    다른 한 가지 측면은, 넓은 범위에서부터 좁혀가며 인터페이스인 '점'들을 우선적으로 파악하고, 그러한 점들을 연결하여  scoping해나가는 것입니다. 여기서 인터페이스의 구체적인 것들은 외부와 주고 받은 규격서 또는 API 명세, Integration 테스트 및 유닛 테스트의 포인트들입니다.

     

    유닛테스트와 관련해서는, [1]에서도 리팩토링 이전에의 유닛테스트를 강조하고, 마틴파울러 등 다수가 그 중요성을 많이 강조해온 부분인 것 같습니다. 

     

    위와 같은 관점에서, 테스트가 존재하지 않던 레거시에 테스트를 더해나가는 작업은, '변경 이전의 최소한의 안정장치를 마련해나감'과 동시에 '구분되지 않던 복잡한 덩어리를 뜯어서 표시하기 위한 울타리를 만들어나간다'라는 느낌도 들게하는 것 같습니다. 

     

     

    Reference

    [1] Working Effectively with Legacy Code

    [2] Software Design X-Rays

    [3] wiki.c2.com/?WardExplainsDebtMetaphor

    [4] martinfowler.com/bliki/TechnicalDebtQuadrant.html

    [5] understandlegacycode.com/blog/key-points-of-software-design-x-rays/

    [6] github.com/smontanari/code-forensics

    [7] en.wikipedia.org/wiki/SonarQube

    [8] codescene.io/projects/1693/jobs/52747/results/scope/system-trends/by-date

    [9] codescene.io/projects/1693/jobs/52747/resultscodescene.io/projects/1693/jobs/52747/results/code/hotspots/system-map

    [10] https://octo.github.com/projects/repo-visualization

    반응형
Kaden Sungbin Cho