새소식

카테고리 없음

Trunk Based Development 에서 Feature Flag 경험하기

  • -
"기능을 개발했는데, 이 기능은 언제 배포되나요?"
xxx: "그냥 merge 하시면 바로 배포됩니다."
"배포가 바로 된다구요?"
xxx: "네"
"음? 스테이징도 없고 QA도 없어요?"
xxx: "네"

 

 

이전에 경험한 배포 프로세스

이전에 온프레미스 패키징 기반 배포를 경험하다가, 새롭게 넘어온 조직에서는 배포 프로세스가 완전히 달랐습니다.

예전 배포 프로세스

이전 회사에서의 배포 및 개발 프로세스는 위와 같았습니다.

하나의 기능이 나가기 위해서 수많은 confirm 이 필요했고 QA 조직이 꽤나 크게 존재했습니다.

 

직접 경험한 문제들

  • 문제 1. 배포 주기가 너무 길어서, 피드백이 느리다
  • 문제 2. 피드백에 대한 대응이 느리다
  • 문제 3. 우선적으로 구현해야하는 기능이 있다면, 병목이 너무 길어진다
  • 문제 4. 기획자와 QA가 생각하는 기능이 다르다면 개발자의 등이 터져버린다 !
  • 문제 5. 모든게 길어지다 보니 작업 단위가 커지게 되고, 기능에 대한 리뷰나 코드에 대한 리뷰가 점점 어려워진다

 

물론 이런 장점은 있습니다.

  • 장점 1. 안정성이 보장된다
  • 장점 2. 최종적인 배포는 어느정도 이상적인 방향으로 합의된다. (아마도)
  • 장점 3. 여러명이 작업해도 코드 베이스에 큰 문제가 발생하지 않는다.

 

Trunk Based Development 경험하기

"진우님, 저희는 Trunk Based Development 를 사용해요"

 

새로운 팀으로 오니, 개발 조직의 인원은 비슷한데 QA가 따로 없었습니다.

 

새로운 조직의 배포 프로세스

 

기획자가 기획을 주면 개발자는 빠르게 개발하고, 기획자에게 바로 피드백을 받을 수 있었습니다.

 

조직에 합류하고 단지 한 번의 기능만 개발해봤을 뿐인데, 굉장히 감동적이었습니다.

 

아하? 이게 진짜 에자일이군요.

에자일

 

장점 1: 요구사항에 대해서 대응이 빠르다

장점 2: 개발 완료 즉시 피드백이 가능하다

장점 3: 프로덕트에 대해 이해가 높은 기획자가 있다면 요구사항의 목적성이 분명하기 때문에 기획과 리뷰가 명확하다

 

그리고 TDD ~ 리팩토링을 하기에도 아주 용이했습니다.

  1. 명시된 요구사항을 그대로 테스트로 옮긴다
  2. 코드 품질이 엉망이더라도 테스트가 통과할 수 있는 절차지향적인 코드로 해결한다 (문제 이해 과정)
  3. 과감하게 Rollback or Merge
  4. 요구사항 맞는지 Feedback 후에 Refactorying

 

Feature Flag를 활용하기

그렇다면 개발에 있어서 안정성은 어떻게 할까요? QA가 따로 없다 보니 꼼꼼한 테스트는 힘들지 않을까요?

 

특히 어떤 기능이더라도 레거시가 존재하고, 해당 기능의 히스토리를 모르는 경우들이 존재합니다.

 

이런 기존 기능들을 안전하게 변경하고 배포하거나 방법으로 Feature Flag를 사용하고 있습니다.

 

 

이 뿐만 아니라, 특정 고객 / 조건별로 조건을 부여해서 기능을 제공할 수도 있습니다.

 

마틴 파울러의 글에 feature flag 의 다양한 활용에 대해서  다음과 같은 내용이 있습니다.

지금까지 기능 플래그를 부분적으로 구축된 기능을 숨기는 데 사용하는 것으로 설명했습니다. 일종의 기능 플래그를 
릴리스 토글이라고 부릅니다 . Hodgson은 또한 A/B 테스트를 위한 
실험 토글 , 운영 직원에게 제어 기능을 제공하는 작업 토글 , 다양한 사용자 하위 집합에 대한 기능 액세스를 제어하는 
​​권한 토글을 식별합니다.

 

 

다음과 같은 테스트도 없는 레거시 코드가 있다고 가정해봅시다.

public class Feature {
    
    private final ExternalService externalService;

    public void feature(DomainObject domainObject, int delta) {
        domainObject.addValue(delta);
        domainObject.addValue(delta);
        domainObject.addValue(delta);
        domainObject.addValue(delta);
        externalService.doSomthing(); // Unit 테스트 불가능
    }

}

 

이러한 레거시 코드를 변경하고 테스트 하는 것은 까다로운 일입니다.

 

테스트를 만들기 어렵고, 테스트를 작성하더라도 운영 중에 어떤 사이드 이팩트가 있을지에 대해서 확신하지 못합니다.

이럴때 Feature Flag를 쓰면 안심하고 배포할 수 있습니다 

 

그리고 상황에 따라서 테스트를 추가할 수 있게끔 만들어 볼 수 있겠습니다.

public class Feature {

    private final ExternalService externalService;

    public void feature(DomainObject domainObject, int delta) {
        if (domainObject.isFeatureFlagActive()) {
            // 새로운 로직
            domainObject.newOperation(delta);
        } else {
    		// 기존 로직
            domainObject.addValue(delta);
            domainObject.addValue(delta);
            domainObject.addValue(delta);
            domainObject.addValue(delta);
        }
        externalService.doSomthing();
    }
}

class TestClass {
    @Test //유닛테스트로 새로운 로직만 검증
    public void testFeature() {
        // Given
        final DomainObject domainObject = new DomainObject();
        final int delta = 1;
        // When
        domainObject.newOperation(delta);
        // Then
        final int expected;
        assertThat(domainObject.getDelta()).isEqualTo(expected);
    }
}

 

  • Feature Flag를 database에서 조회하고, 해당 flag가 활성화 되어 있다면 새로운 기능을 반영한다.
  • 만약 문제가 생기면 database에서 직접 update 해서 런타임에 기능을 비활성화 한다.
  • 문제가 없다면 다음 배포에서 해당 코드를 제거 및 리팩토링 한다.

 

 

https://www.atlassian.com/solutions/devops/integrations/feature-flags

https://martinfowler.com/bliki/FeatureFlag.html

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.