회사 내부 백엔드 팀원들에게 진행했던 소프트한 컨퍼런스 입니다.

주제는 우리가 지금 가진 기술 부채는 무엇이며,
그 부채를 해결하기 위해 우리는 무엇을 해야할 것인지 논의하는 자리였습니다.

기술부채는 무엇입니까?

기술적 부채(技術的 負債, technical debt)란, 
**사회와 각 개인들로 이루어진 소프트웨어 등의 결함에 따라서 새로운 기능을 개발하는 데 장애요인이 발생하는 것을 말함.**

부채가 이자를 내고 돈을 쓰는 시점을 당기는 것처럼 기술 부채는 기술적으로 해결되어야할 문제를 뒤로 미루고, 비즈니스 문제를 해결하는 시점을 당기는 것입니다.

기술 부채 항목들

  • 설계된 것을 문서화 하지 않는다.
  • 유닛 테스트를 작성하지 않는다.
  • 더 이상 사용되지 않는 DB의 항목을 지우지 않는다.
  • 반복되는 일을 자동화 하지 않는다.
  • 긴급하게 스펙을 변경한다.

망해가는 회사 프로세스

악취 나는 코드/작성되지 않는 테스트 => 개발속도 저하/ 품질 저하 => 비지니스 속도 저하/고객만족감소 => 수익감소 => 기술적 채무 상환 보류 => 악취나는 코드/작성되지 않는 테스트(반복)

성공하는 회사의 프로세스

향상된 코드/자동화 테스트 => 개발속도 증가/품질 향상 => 비지니스 속도 증가 => 고객 만족도증가/수익증가 => 기술적 자산에 대한 투자 => 향상된 코드(반복)

기술 부채를 어떻게 하면 없앨 수 있을까요?

  • 이 질문에 대한 답은 ‘No’ 입니다.
  • 많은 회사들이 DDD(Deadline Driven Development)에 의한 기술 부채가 발생하고 이는. 피할 수 없는 자연스러운 현상입니다.

다만 한가지 기억해야하는 것이 어떻게 빚을 갚을 것이냐는 겁니다.
빚을 지고 있으면, 마음이 불편합니다. 특이 빚을 처음 질 때 가장 불편합니다.
하지만 그 상황이 계속 되면 그 빚을 지고 있는 상황이 익숙해집니다.

빌리는 것은 쉽지만 갚은 것은 어렵습니다.
게다가 한 번에 갚는 것은 매우 어렵습니다.

지금 당장 문서를 쓰지 않고, 테스트를 작성하지 않는 것은 쉽지만 나중에 그것을 회복하기는 어렵습니다.

완성도(Completeness) vs 적시성(Timeliness)

완성도(Completeness)는 소프트웨어가 기능적 요구사항을 완벽하게 충족하는지 여부를 나타내는 것입니다.

높은 완성도는 품질 높은 소프트웨어를 만드는데 중요합니다.

완성도가 높은 소프트웨어는 적은 결함과 높은 성능을 가지며, 사용자들의 만족도가 높아지게 됩니다. 하지만 완성도를 높이기 위해서는 시간과 비용이 많이 들어가게 됩니다.
적시성(Timeliness)은 소프트웨어를 빠르게 개발하여 빠른 시장 진입이나 기업 성장에 도움을 주는 것을 의미합니다.
적시성이 높은 소프트웨어는 빠르게 수정되어 사용자들의 요구사항에 더욱 빠르게 대응할 수 있으며, 경쟁 우위를 유지하는데 중요합니다. 하지만 적시성을 높이기 위해서는 완성도를 희생할 수 있으며, 이로 인해 기술부채가 발생할 수 있습니다.
따라서 개발자들은 완성도와 적시성을 모두 고려하여 적절한 기술부채를 유지해야 합니다.

완성도를 높이기 위해 기술부채를 적극적으로 늘리는 것은 바람직하지 않지만, 완성도와 적시성 사이에서 타협점을 찾아야 합니다.

이를 통해 소프트웨어 개발의 질을 유지하면서도 빠른 시장 진입과 경쟁 우위를 유지할 수 있습니다.

documentation, test coverage등의 문제를 해결한 완성도 높은 서비스 vs 부족하더라도 시장성이 있는 서비스

→ 결국 현실적인 상황때문에, 후자를 선택할 수 밖에 없습니다.

우리의 대응

“방망이 깎던 노인” 스토리. 수필 속의 주인공이 방망이를 주문하는데, 노인이 해가 질 때 까지 방망이를 깎고 또 깎고, 재촉하는 주인공에게 노인이 이렇게 이야기합니다.

“글쎄, 재촉을 하면 점점 거칠고 늦어진다니까. 물건이란 제대로 만들어야지, 깎다가 놓치면 되나.”

결국 밤늦게 집에 와서 방망이를 내놨더니 아내는 예쁘게 깎았다고 야단이다.
라는 글에서 보통 개발자들은 보통 이 수필속 노인이 됩니다.
견고한 아키텍처를 만들고 싶지만 현실은 최소기능제품(Minimum Viable Product, MVP) 형태로 서비스를 만들어야 할 때가 많고,
스트레스도 받고, 그렇게 개발하고 싶어하지 않습니다.

돈도 시간도 인적자원도 다 부족한 탓일 겁니다.

이제는 이 빚을 어떻게 안만들까를 고민하기보다, 어떻게 빚을 갚을지를 고민해야할 시간이라고 생각합니다.
돈을 빌리면 갚아야 하고, 원금을 갚을 수 없다면 이자라도 갚는 것이 현실이라 생각합니다.

주기적인 이자 상한

기술 부채를 상환해본다고 했을 때, 당장은 급해 죽겠으니까 이자부터라도 갚는 것을 생각해볼 수 있습니다.

Refactoring: 소프트웨어 코드의 구조나 설계를 개선하여 유지보수 및 확장성을 향상시키는 작업입니다.
주기적인 Refactoring은 기술부채를 줄이고 코드 품질을 높이는데 도움이 됩니다.
또한 코드의 복잡성을 낮춰 개발자들이 코드를 이해하기 쉽게 만들어주기도 합니다.
주기적으로 Refactoring을 수행하면 코드의 유지보수와 확장성을 개선할 수 있습니다.

Pair-programming: 두 명의 개발자가 하나의 컴퓨터로 협력하여 코드를 작성하는 방법입니다.
주기적인 Pair-programming은 코드 품질을 향상시키고 기술 부채를 줄이는 데 도움이 됩니다.
또한 개발자들이 서로 경험을 공유하고 서로의 능력을 향상시키는 데도 도움이 됩니다.
주기적으로 Pair-programming을 수행하면 개발자들 간의 커뮤니케이션도 개선됩니다.

  • 보통 코드를 작성한 인원이 해당 코드의 유지보수까지 맡게 됩니다.
  • 그 인원이 휴가, 퇴사, 입원과 같은 이유로 자리를 비운다면 이 코드는 바로 방치가 될 것이고, dead code가 될 가능성이 높습니다.
  • 한명은 프로덕트 코드를, 한명은 테스트 코드를 작성하는 등의 페어프로그래밍이 이를 방지할 수 있다.

Risk management: 프로젝트에서 발생할 수 있는 위험을 파악하고 그에 따른 대응책을 수립하는 작업입니다.
주기적인 Risk management는 프로젝트의 위험을 예측하고 방지할 수 있도록 도와줍니다.
또한 위험 대응책을 만들어두면 프로젝트가 진행되는 동안 예기치 않은 문제가 발생했을 때 대처할 수 있습니다.
주기적으로 Risk management를 수행하면 프로젝트의 안정성을 높일 수 있습니다.

Testcase: 소프트웨어의 기능을 검증하기 위한 테스트 케이스를 작성하는 작업입니다.
주기적인 Testcase 작성은 코드의 품질을 높이고 기술 부채를 줄이는 데 도움이 됩니다.
또한 테스트 케이스를 작성함으로써 코드의 동작을 확인하고 오류를 발견할 수 있습니다.
주기적으로 Testcase를 작성하면 소프트웨어의 품질을 향상시킬 수 있습니다.

  • 로그는 100% 재현이 불가능합니다.
  • 따라서 사후 처리를 위해서는 디버그 범위를 줄이는 것이 도움이 된다. 테스트 케이스 작성하기가 효과적인 방법이다.

여유(돈)가 있다면 원금 상한

Restructuring: 기존의 코드를 개선하여 유지보수와 확장성을 향상시키는 작업입니다.
코드의 복잡성을 줄이고 개발자가 이해하기 쉬운 구조로 변경함으로써 기술 부채를 해결할 수 있습니다.
이를 위해 코드 리팩토링 작업을 수행하거나, 디자인 패턴을 도입하는 등의 방법을 사용할 수 있습니다.
(외부의 전문가들을 데려와서 노하우를 배우는 등의 방법도 필요에 따라 좋습니다.)

Dead code detection: 프로젝트에서 사용되지 않는 코드를 제거하는 작업입니다.
사용되지 않는 코드는 유지보수 비용만 불필요하게 증가시키고, 코드를 이해하기 어렵게 만들어 기술 부채를 증가시킵니다.
Dead code detection을 수행하여 불필요한 코드를 제거함으로써 기술 부채를 해결할 수 있습니다.

  • static analyzing tool
  • Naver Deview 2일차의 런타임 데드 코드 분석 도구 Scavenger - 당신의 코드는 생각보다 많이 죽어있다. 이 내용도 도움이 될 거 같습니다.

Continuous migration: 기술 부채를 해결하기 위해서는 최신 기술과 도구를 적극적으로 도입해야 합니다.
Continuous migration은 이전 시스템을 새로운 시스템으로 지속적으로 이전시켜가는 작업입니다.
이를 통해 기존 시스템에서 발생하는 기술 부채를 줄이고, 새로운 기술과 도구를 적극적으로 도입하여 기술 부채를 해결할 수 있습니다.

  • 오래된 시스템(legacy)을 마이그레이션 해봅시다.

Risk management: 프로젝트에서 발생할 수 있는 위험을 파악하고 그에 따른 대응책을 수립하는 작업입니다.
예측치 못한 위험이 발생했을 때 대처하기 쉬워지므로 비용과 시간을 절약할 수 있습니다.

Risk management은 프로젝트에서 발생할 수 있는 위험 요소를 파악하고 그 위험을 최소화하기 위한 일련의 절차를 수행하는 활동입니다.
이는 프로젝트에서 발생할 수 있는 모든 위험을 파악하고, 위험의 가능성과 영향을 평가하며, 위험을 감소시키기 위한 계획을 수립하고 실행하는 과정을 포함합니다.

Risk management는 다음과 같은 단계를 거쳐 수행됩니다.

  1. Risk identification: 프로젝트에서 발생할 수 있는 모든 위험을 파악하고 문서화합니다. 이를 위해, 이전 프로젝트나 유사한 프로젝트의 위험을 분석하거나, 프로젝트 팀의 경험을 활용하는 등의 방법을 사용합니다.
  2. Risk analysis: 파악한 위험에 대해 가능성과 영향을 평가합니다. 이를 위해, 각각의 위험에 대해 확률과 영향의 크기를 평가하여 위험의 우선순위를 결정합니다.
  3. Risk response planning: 각각의 위험에 대해 대응책을 수립합니다. 이를 위해, 위험을 회피, 전이, 완화, 수용하는 등의 방법으로 대응책을 마련합니다.
  4. Risk monitoring and control: 대응책을 수행하며 위험을 감시하고, 위험 상황이 발생했을 때 즉각적으로 대처합니다. 이를 위해, 위험 상황을 모니터링하고, 위험 상황 발생 시 팀원들에게 통보하며, 위험 관리 계획을 업데이트합니다.

Risk management는 프로젝트의 성공을 위해 중요한 활동입니다. 위험을 사전에 파악하고 대응책을 마련하여 위험을 감소시키면 프로젝트의 성공 가능성이 높아지며, 기술 부채를 해결하는데에도 큰 도움이 됩니다.

후불코딩

부채를 해결하는 방법은 기술부채를 후불코딩으로 생각하는 마인드 변화가 필요합니다.

  1. Static Analysis (정적 분석)
    정적 분석은 코드를 실행하지 않고 코드의 문법, 구조, 규칙, 패턴 등을 검사하는 방법입니다. 이를 통해 버그나 코드의 품질을 향상시키는 데 도움이 됩니다. 부채 정리를 스마트하게 하기 위해서는 정적 분석을 통해 코드의 품질을 높이고 잠재적인 버그를 사전에 발견할 수 있도록 하는 것이 중요합니다.
  2. Test code (테스트 코드)
    테스트 코드는 코드를 실행해보면서 코드의 동작을 검증하는 것입니다. 테스트 코드를 작성하면 코드를 수정할 때마다 자동으로 검증할 수 있어서 코드의 안정성과 신뢰성을 높일 수 있습니다. 부채 정리를 스마트하게 하기 위해서는 테스트 코드를 작성하여 코드를 검증하고 변경 사항이 있을 때마다 자동으로 실행하도록 구성하는 것이 중요합니다.
  3. CI/CD (지속적 통합/배포)
    CI/CD는 코드를 빌드하고 테스트하고 배포하는 프로세스를 자동화하는 방법입니다. CI/CD를 구성하면 코드 변경 사항을 즉시 테스트하고 배포할 수 있어서 빠른 개발과 안정적인 배포를 동시에 할 수 있습니다. 부채 정리를 스마트하게 하기 위해서는 CI/CD를 구성하여 코드 변경 사항이 발생하면 자동으로 테스트하고 배포하도록 하는 것이 중요합니다.
  4. Communication (의사 소통)
    부채 정리는 여러 사람이 함께 작업해야 합니다. 이때 각각의 역할과 책임을 명확하게 하고 의사 소통을 잘하는 것이 중요합니다. 부채 정리를 스마트하게 하기 위해서는 팀원들과 의견을 공유하고 문제점을 해결하는 데에 적극적으로 참여하는 것이 중요합니다.