스터디/Five Lines of Code

Five Lines of Code 9장. 코드 삭제의 미학

마디니 2023. 11. 5. 16:57
반응형

"적은 것이 더 낫다"

코드를 유지 관리하기 위해서는 지속적인 노력이 필요하기 때문에 코드를 보유 하고 있다면 이는 곧 '비용'이다.

오랜 시간 공들여 만들었다고 그 코드가 더 가치가 높은 것은 아니다.

가치는 투자(코딩 시간, 코드 양)에서 나오는게 아니라 투자의 결과(유지보수에 대한 비용)에 대해 나오기 때문에

이를 이해하고 코드 작업을 하는 것이 중요하다. 

 

시스템에는 오래 지속되는 코드가 있을 수 밖에 없고, 도메인의 복잡성에 따라 필요한 코드 양도 다르지만

'적은 것이 더 낫다'

 

코드 복잡성과 코드 삭제의 관계

프로그래밍 연대기

  • 1994: 컴퓨터를 사용해서 추상화 없는 계산 수행
  • 1952: 링커(linker)가 개발되어 컴퓨터가 순수한 계산대신 기호로 작업할 수 있게 되었다.
  • 1957: 컴파일러 발명, 루프와 같은 높은 수준의 제어 연산자 사용 가능해짐
  • 1972: 포인터와 참조를 통해 간접적으로 데이터 사용 -> 데이터 추상화
  • 1994: 소프트웨어 설계에 필요한 디자인 패턴 등장
  • 1999: 마틴파울러의 표준 리팩터링 패턴 등장
  • 2011: 마이크로서비스 아키텍처

오늘날 우리는 코드를 작성하고 시스템을 구축하는 데 매우 능숙하다. 

우리가 구축하는 시스템은 크고 복잡하기 때문에 완전히 이해하기가 힘들다. 

제거할 수 있는 코드를 파악하기 위해 많은 시간을 투자해야 하기 때문에 다음 과제는 '코드 삭제'와 관련된 발전이 되지 않을까?

 

코드 복잡성

시간이 지날 수록 시스템은 성장하고 그만큼 복잡해진다. 

  • 도메인 복잡성: 도메인이 기본적으로 가지고 있는 것. 우리가 해결하려고 하는 문제(=도메인)는 본질적으로 복잡하다.
  • 부수적 복잡성: 도메인에서 요구하지 않았지만 우연히 추가된 모든 복잡성

 

부수적 복잡성의 종류

  • 경험 부족으로 인한 기술적 무지
    • 불필요한 결합을 추가하지 않고도 문제를 해결할 수 있는 기술 또는 지식이 부족해서 발생 (정말로 몰라서-)
    • 책, 블로그, 컨퍼런스, 튜토리얼, 공동프로그래밍을 통해 지식을 공유하고 '의도적인 연습'을 통해 기술 향상을 위해 지속적으로 노력해야 한다.
공동 프로그래밍이란?
페어 프로그래밍 또는 몹 프로그래밍이 있으며, 키보드 앞에 앉은 사람은 다른 사람이 지시하는 것 외에는 아무것도 해서는 안된다.
지식을 직접 공유하면서 낭비를 줄이고 전달받은 지식을 코드화하는데만 집중할 수 있게 인지부하를 줄인다.
코드는 실시간으로 검토되기 때문에 더 나은 품질로 이어진다.

 

  • 시간 압박으로 인한 기술적 낭비
    • 문제나 모델을 충분히 이해하지 못하기 때문에 테스트나 리팩터링을 건너뛰기도 하고, 기한을 맞추기 위해 프로세스를 우회한다.
    • 보통 시간압박으로 인해 발생된다.
    • 모범사례를 건너뛸 기회가 없도록 해야한다 - 프로젝트 관리자, 고객 및 기타 이해관계자에게 올바른 소프트웨어 구축에 대해 가르친다.
  • 환경에 따른 기술적 부채
    • 핫픽스(hotfix)의 경우 중요한 문제를 우선적으로 처리해야하기 때문에 일단 적용(push) 후 나중에 적절하게 수정한다.
    • 전략적으로 발생시키는 기술적 부채로 '일시적'이며 이후 수정이 보장된다면 본질적으로 잘못된 부채는 아니다.
  • 성장에 따른 기술적 장애물
    • 개발을 더디게 만드는 모든 것
    • 문서, 테스트 및 실존하는 (기존의)모든 코드는 물론 다른 모든 범주가 포함된다.
    • 문서, 코드 등 그 자체로는 나쁘지 않지만 드물게 사용되는 문서나 기능, 또는 코드를 유지보수 하는 상황에서는 나쁘다. 
    • 조금이라도 사용되지 않는 것은 제거하는 것이 좋다. 

코드 삭제

친밀도에 따른 코드 분류

  • 우리는 최근에 개발한 코드에 익숙하다. 
  • 삭제에 들어가는 시간 비용도 친숙한 코드를 삭제하는 것이 더 적게 든다.
  • 특정 시기가 지나면 코드가 알 수 없는 범주로 빠르게 이동하면서 해동 코드와의 친밀도가 떨어지기 때문에  컷오프(유지 시간의 한계)를 두고 코드를 관리하는 것이 좋다.

레거시 시스템에서의 코드 삭제

'레거시 코드'는 수정하기가 겁나는 코드 를 말한다. 서커스 팩터를 잃는 순간 우리는 건드리기가 꺼려지는 미지의 코드(레거시 코드를)가지게 된다. 

서커스 팩터란?

프로젝트를 진행하는 팀원 중 얼마나 많은 사람이 갑작스럽게 빠져야 프로젝트가 중단되거나 심각한 상황에 놓이는지를 가리키는 지표

 

스트랭글러 무화과나무 패턴

  • 레거시 코드 제거하는 방법
    • 거의 사용되지 않는다면 제거
    • 작은 부분만 많이 사용되고 있으면 해당 부분만 수정하고 나머지를 제거
    • 모든 것이 많이 사용된다면 코드에 익숙해져서 안정적으로 만들기
    • 각 부분이 얼마나 호출되는지, 그중 얼마나 많은 호출이 성공했는지 알아야 한다.
    • 우선적으로 레거시 코드가 나머지 소프트웨어와 얼마나 밀접하게 연결되어 있는지 알아야 한다. 레거시 코드가 얼마나 사용되고 있는지 확인하기

 

코드 개선을 위한 스트랭글러 무화과나무 패턴 사용

스트랭글러 무화가 나무 패턴은 기존 나무 줄기에 씨를 뿌리고 자라면서 숙주를 감싸 궁극적으로 숙주 나무를 교살해 죽이는 스트랭글러 무화가나무의 이름을 따서 명명(숙주는 레거시를 의미)되었다.

 

레거시 코드 (변경 전)
변경 후

 

이 시점에서 레거시 코드가 모두 Gate 클래스의 함수이기 때문에 얼마나 많은 접점이 있는지 알 수 있다.

 

아래와 같이 게이트 클래스에 모니터링을 추가해서 모니터링을 쉽게 할 수도 있다.

 

모든 호출/실패 로그 기록

 

위 코드를 운영에 반영해 팀내에서 월/분기/반기 등 얼만큼의 주기동안 얼마나 사용되는지 파악한다.

 

사용되지 않는 중요하지 않는 코드가 있다면 게이트에서 메서드를 삭제한다. ('삭제 후 컴파일 하기 패턴')

 

코드가 사용되고 있고 중요한 코드라면 아래 두가지중 하나를 취한다.

1) 해당 부분을 리팩터링해서 결합과 취약성을 제거하고 코드를 최신으로 분류한다.

2) 해당 부분을 다시 만들고 만들어진 코드가 준비되면 게이트를 변경해서 새 버전으로 전환한다.

 

레거시 코드를 처리하는 방법

 

동결된 프로젝트에서 코드 삭제

동결된 프로젝트는 코드에 국한되어 있지 않다. DB테이블, 통합, 서비스등이 포함되며 원래 작성자가 프로젝트에 대해 잊은 경우 존재조차 알아차리기 힘들 수 있다.

변경 할 때마다 고려 대상이 되기 때문에 정신적인 오버헤드를 가중시진다.

 

바람직한 결과를 기본값으로 설정

  • 프로젝트가 코드베이스 외부에 영향을 미치지 않으면 별도 브랜치에 보관 후 태그를 지정하고 6주 후 태그 삭제한다고 메모를 작성한다. 
  • 프로젝트에 코드 외적인 변경사항이 포함되어 있으면 브랜치에 넣지 못한다. 이 때는 제거할 모든 구성 요소를 프로젝트 관리 도구에 기록해두고 티켓을 6주 후로 예약한다.

 

스파이크와 스태빌라이즈(안정화)로 낭비 줄이기

  • 프로젝트가 코드베이스 외부에 영향을 미치지 않으면 별도 브랜치에 보관 후 태그를 지정하고 6주 후 태그 삭제한다고 메모를 작성한다. 
  • 프로젝트에 코드 외적인 변경사항이 포함되어 있으면 브랜치에 넣지 못한다. 이 때는 제거할 모든 구성 요소를 프로젝트 관리 도구에 기록해두고 티켓을 6주 후로 예약한다.

 

동결된 프로젝트 처리

 

 

버전관리에서 브랜치 삭제

브랜치를 만드는 이유

  • 긴급 패치(hotfix)수정
  • 커밋에 태그를 지정하면 release 처럼 해당 위치로 되돌아갈 수 있다.
  • 다른 사람의 업무를 방해하지 않고 일하기 위해

사용한 브랜치는 삭제해야 한다. (release 브랜치에 머지하면서 삭제도 함께 하는것이 좋다)

병합하는데 충돌발생할 가능성이 높아지기 때문에 브랜치 수명을 짧게 가져가는 것이 좋다.

아래와 같은 경우에도 마찬가지다.

  •  동결된 프로젝트, 스파이크 프로토타입
  • 통합 과정을 통화하지 못해서 차단된 브랜치

브랜치 제한으로 낭비 최소화

  • 협업 방법 칸반
    • 칸반 보드를 사용하여 진행중인 작업(WIP)을 제한한다. 
    • 팀이 진행할 수 있는 티켓 수의 상한선을 정한다.
    • 사람들이 새로운 작업을 시작하지 못하기 때문에 개발에서의 병목현상을 노출하고 해결하는 데 도움이 된다. 
  • 칸반과 같이 브랜치 수를 제한하기

 

브랜치 처리 방법

 

코드 문서 삭제

 

문서는 정확하게 세가지 조건을 충족해야 한다.

  • 관련성: 올바른 질문에 답해야 한다.
  • 정확성: 답은 정확해야 한다.
  • 발견 가능성: 답을 찾을 수 있어야 한다.

문서가 부정확한 경우 혼란과 의심을 유발하고 최악의 경우 오류를 유도할 수 있기 때문에 정확성은 매우 중요한다.

 

지식을 문서화하는 방법을 결정하는 알고리즘

  • 문서화는 관련성이나 정확성을 잃을 수 잇으며 모든 것을 문서화 해야하는 것은 아니다.
  • 문서화를 하는 것이 의미가 있는지 여부를 결정할 때 다음 절차를 수행한다.
    1. 문서화 대상이 자주 바뀌면 문서화를 해서 얻을 수 있는 것이 없다.
    2. 드물게 사용하는 경우 문서화한다.
    3. 그렇지 않고, 자동화할 수 있으면 자동화 한다.
    4. 그렇지 않으면 외운다.
  • 최신화를 자주 하는 것이 좋다 -> 새로운 팀원들이 문서를 살펴보고 부정확한 사항을 수정하도록 하기
  • 의심스러운 부분에는 플래그 지정하기

 

문서를 처리하기

 

테스트 코드 삭제

  • 자동화된 테스트는 다양한 형태로 제공되며 문서보다 더 많은 송석을 가지고 있다.
  • 테스트의 다양한 유형에 따라 서로다른 가중치가 부여된다.

개발을 방해하는 테스트들은 아래와 같다.

 

낙관적 테스트 삭제

  • 테스트는 신뢰 할 수 있어야 한다. green 테스트를 통해 코드의 동작을 신뢰할 수 있어야 한다.
  • 실패 할 수 없는 테스트는 가치가 없다
  • "실패한 적이 없는 테스트를 절대 신뢰하지 말라"

 

비관적 테스트 삭제

  • 위와 비슷하게 red 테스트는 무엇인가에 문제가 존재하고 수정 필요가 있다는 것을 의미해야 한다.
  • 항상 레드인 테스트를 한다면 실제 오류가 발견되어도 놓칠 수 있다.

 

불안정 테스트 수정 또는 삭제

  • 불안정 테스트는 항상 같은 결과를 내지 않는 예측할 수 없는 레드 또는 그린테스트 이다.
  • 어쩔 때는 통과하고 어쩔 때는 통과하지 않는 테스트..
  • 우리는 테스트가 레드인 경우에만 행동하므로 우리 코드에서 레드 테스트를 통과하지 않은 코드는 존재할 수 없다.

 

복잡한 테스트를 제거하기 위한 코드 리팩터링

  • 섬세한 설정이 필요한 경우 테스트를 리팩토링 하거나 복잡한 테스트 셋을 만들어야 하는 경우가 있다.
  • 리팩토링을 하는 것이라 가치 있는 일을 하고 있다고 느끼는데 이는 위험하다
  • 테스트를 리팩터링 해야한다는 것은 테스트중인 코드에 적절한 아키텍처가 없다는 신호이다.
  • 모든 리팩터링은 테스트가 아니라 코드에 집중돼야 한다.

 

속도를 높이는 테스트 문화

  • 어떤 테스트의 경우(종단간 테스트 등) 느리며 다른 테스트를 실행하는 빈도에 영향을 줄 수 있다. 
  • 이경우 느린 테스트와 빠른 테스트를 분리하고 가능한 자주 빠른테스트를 실행하거나 느린 테스트의 실패원인을 관찰하여 필요 없다면 제거한다.

 

자동화된 테스트를 처리하는 방법

 

설정 코드 삭제

설정 코드의 용이함

  • 코드를 늘리지 않고 설정을 통해 소프트웨어의 유용성을 높일 수 있다. 
  • 설정을 기능 플래그 형태로 제공하면 배포를 분리하여 배포 빈도를 높이고 릴리스할 때 기술적 결정 대신 사업적인 결정을 내릴 수 있다.

설정 코드 trade off

  • 설정을 추가 할 때마다 코드의 복잡성이 증가한다.
  • 모든 설정 (플래그)에 대해 테스트해야하기 때문에 테스트 양이 두배로 증가한다.

 

설정의 예상 수명으로 범위 지정

  • 가능한 많은 설정을 임시적인 것으로 생각한다.
  • 예상 수명을 기준으로 실험적인 것, 과도기적인 것, 영구적인 것으로 구분한다.
  •  

실험을 위한 설정

  • 기능이 출시 된 후 제거될 것들 (6주 내 완료 되어야 한다)
  • 베타테스트, A/B 테스트
  • 실험을 위한 설정이 영구적인 설정이 되어 사용 복잡성을 증가시키지 않도록 주의

 

과도기적인 설정

  • 비즈니스 또는 코드베이스가 중요 변경사항을 겪고 있는 동안 유용한다. (예) 레거시 시스템에서 새로운 시스템으로의 전환)
  • 6주 내에 끝나지 않는 장기적 전환이 될 수 도있다.
  • ????많은 유형이 전환이 사용자에게는 잘 보이지 않는다. 릴리스와 배포를 연결하는 것으로 만족 할 수 있다
  • ???? 나머지와 분뤼된 중앙의 어떤 지점에서 전환과 관련된 모든 설정을 모을 수 있다.
  • 시스템 전환이 완료되고 오래된 부분을 제거할 수 있는 위치로 설정을 사용하는 경우 레거시 구성요소에 대한 모든 접근을 차단해야 한다.

 

영구적인 설정

  • 사용량을 증가시키거나 유지보수가 간편해야 한다. -> 반대인 경우 제거 필요
  • 코드 내 설정 플래그 뒤에 차이점을 두어 고객에게 동일한 소프트웨어을 재사용 하는 것
  • 다양한 사용자 계층을 지원해서 다양한 비즈니스 규모에 맞게 설정할 수 있따.

 

 

라이브러리 제거를 위한 코드 삭제

  • 서드 파티 라이브러리를 통해 많은 기능을 손쉽게 구현 할 수 있다.
  • 라이브러리는 편하지만 업데이트를 해야 하기 때문에 때때로 코드를 수정해야 한다. -> 최소한의 지식 및 인지부하
  • 업데이트나 코드베이스 조정에 대해 얼마나 시간이 걸릴지 예측할 수 없기 때문에 라이브러리를 사용할 때는 예측 가능성을 읽어버린다.
  • 버그가 발생하는 경우 라이브러리 직접 수정 필요
  • 의존성의 의존성을 신경써야 한다.
  • 라이브러리가 자체가 또 다른 공격 경로가 될 수 있다.

 

외부 라이브러리에 대한 의존도 제한

  • 고품질을 공급하는 업체의 라이브러리를 선택하여 해당 업체의 내부 품질 및 보안 요건을 믿는다.
  • 자주 업데이트 한다.
  • 종속성을 가시적으로 만든 다음 각 라이브러리가 개선 용도인지 중요 용도인지 분류한다.
  • 개선용 라이브러리가 손상되면 제거하고 대체 라이브러리를 찾는다.
  • 개선용 라이브러리를 중요 라이브러리로 승격 시킬때는 사용하지 않는 라이브러리가 코드 베이스에 있다면 제거 한다.

 

서드파티 라이브러리를 다루는 방법

 

작동 중인 기능에서 코드 삭제

  • 코드 비용과 기능 이점의 균형을 조정할 때 다음과 같은 다양한 요소를 가지고 판단한다.
    • 얼마나 오랫동안 복잡성의 증가를 받아들일지
    • 어떻게 예측 가능성을 평가할 지
    • 테스트를 어떻게 할지
    • 사람들을 얼마나 잘 참여시키는지
    • 비용/편익 등등
  • 기능상 이점이 너무 복잡하면 리팩터링 하거나 코드를 제거해서 비용을 줄이는 방법을 찾는 것이 좋은 경우도 있다.
  • 사용되지 않는 것은 잠재력과 상관 없이 '비용'일 뿐이다.

 

작동중인 기능을 다루는 방법

 

 

 

반응형