3 분 소요


펌프 앤 덤프 소프트웨어의 시대: 기술 부채를 떠넘기는 개발 문화

우리 업계에 새로운 유행이 있다. 빠르게 만들고, 화려하게 포장하고, 문제가 터지기 전에 빠져나가는 것. 주식 시장의 “펌프 앤 덤프(pump and dump)” 사기와 똑같은 패턴이 소프트웨어 개발에서도 벌어지고 있다. 그리고 이 쓰레기를 치우는 건 항상 다음 팀, 다음 개발자의 몫이다.

펌프 앤 덤프 소프트웨어란 무엇인가

펌프 앤 덤프 소프트웨어는 단기적인 성과와 데모를 위해 설계된 코드를 말한다. 겉으로는 작동하는 것처럼 보이지만, 실제로는 유지보수가 불가능하고 확장성이 없으며, 기술 부채 덩어리다.

이런 코드의 특징은 명확하다:

# 전형적인 펌프 앤 덤프 코드
def process_user_data(data):
    # TODO: 나중에 리팩토링 (2019년에 작성됨)
    # FIXME: 이거 왜 작동하는지 모르겠음
    try:
        result = data['user']['profile']['settings']['preferences']['theme']
    except:
        result = "default"  # 모든 예외를 삼켜버리기
    
    # 하드코딩된 값들
    if result == "dark":
        return {"color": "#000000", "bg": "#1a1a1a"}
    else:
        return {"color": "#ffffff", "bg": "#fafafa"}

이 코드는 작동한다. 데모에서도 잘 돌아간다. 하지만 3개월 후 다른 테마를 추가해야 할 때? 6개월 후 data 구조가 바뀔 때? 그때는 원래 개발자는 이미 다른 프로젝트로, 아니면 다른 회사로 떠났다.

왜 이런 현상이 만연해졌나

인센티브 구조의 문제

대부분의 회사에서 개발자는 “배포한 기능 수”로 평가받는다. 코드 품질? 측정하기 어렵다. 기술 부채 감소? 경영진 눈에 보이지 않는다.

// 평가 시즌에 작성되는 코드
const quickWin = async (userId) => {
  // 직접 DB 쿼리 (ORM? 그게 뭔데?)
  const result = await db.raw(`
    SELECT * FROM users 
    WHERE id = ${userId}  // SQL 인젝션? 뭐 어때
  `);
  
  // 캐싱? 다음 분기에 하죠
  // 에러 핸들링? 프로덕션에서 테스트하면 되죠
  return result[0];
};

짧아지는 재직 기간

평균 재직 기간이 2년도 안 되는 시대다. 내가 작성한 코드의 결과를 내가 책임질 일이 없다. 이직하면 끝이다.

스타트업 문화의 그림자

“일단 출시하고 나중에 고치자”는 말은 PMF(Product-Market Fit)를 찾는 초기 스타트업에서는 합리적이다. 문제는 시리즈 C 받은 500명 규모 회사에서도 같은 논리가 통용된다는 것이다.

실제 피해 사례: 코드로 보는 참상

유지보수 불가능한 코드가 어떻게 생겼는지 보자.

// "빠르게" 만들어진 API 엔드포인트
export const handleRequest = async (req: any, res: any) => {
  // any 타입의 향연
  const data: any = req.body;
  
  // 비즈니스 로직 500줄이 한 함수에
  if (data.type === "create") {
    // 100줄의 로직...
  } else if (data.type === "update") {
    // 또 다른 100줄...
  } else if (data.type === "delete") {
    // 그리고 또...
  } else if (data.type === "createOrUpdate") {
    // 왜 이게 있지?
  } else if (data.type === "softDelete") {
    // 아...
  } else if (data.type === "hardDelete") {
    // 제발...
  }
  
  // 테스트? 작성자가 퇴사해서 불가능
  res.json({ success: true });  // 항상 성공
};

이런 코드를 물려받으면 선택지는 두 가지다. 전체를 다시 짜거나(시간과 예산 없음), 그 위에 또 다른 펌프 앤 덤프 코드를 얹거나.

펌프 앤 덤프를 방지하는 방법

1. 코드 리뷰의 진짜 의미를 되찾자

LGTM 도장 찍기 식의 코드 리뷰는 무의미하다. 진짜 질문을 해야 한다.

❌ "LGTM"
❌ "몇 가지 nit이 있지만 approve"

✅ "이 함수가 400줄인데, 왜 분리가 안 될까요?"
✅ "여기 try-catch가 모든 예외를 삼키는데, 로깅이라도 해야 하지 않을까요?"
✅ "이 로직이 다른 곳에서 재사용될 것 같은데, 테스트가 없네요"

2. 기술 부채를 가시화하라

# tech-debt.yaml - 기술 부채 트래킹
- id: TD-001
  created: 2024-01-15
  author: "kim@company.com"
  severity: high
  description: "User 서비스에 any 타입 남용"
  estimated_fix_hours: 16
  blocking_features: ["user-analytics", "audit-log"]
  
- id: TD-002
  created: 2024-01-20
  author: "lee@company.com"  
  severity: critical
  description: "결제 모듈 테스트 커버리지 0%"
  estimated_fix_hours: 40
  blocking_features: ["international-payment"]

이런 파일이 있으면 최소한 “몰랐다”는 변명은 못 한다.

3. 퇴사 전 코드 정리를 문화로

# 퇴사 체크리스트 (제안)
□ 내가 작성한 코드 중 TODO/FIXME 정리
□ 문서화되지 않은 암묵적 지식 정리
□ 후임자와 페어 프로그래밍 세션 2회 이상
□ 내가 만든 기술 부채 목록 작성

솔직한 의견: 이건 시스템의 문제다

개인을 탓하기 쉽다. 하지만 인센티브가 잘못되면 행동도 잘못된다. 빠른 배포를 보상하고 품질은 무시하는 시스템에서, 품질 좋은 코드를 기대하는 건 순진한 생각이다.

해결책? 측정할 수 없으면 관리할 수 없다.

# 빌드 파이프라인에 품질 게이트 추가
quality_metrics = {
    "test_coverage": 80,  # 최소 80%
    "cyclomatic_complexity": 10,  # 함수당 최대 10
    "any_type_count": 0,  # TypeScript any 금지
    "todo_count_delta": 0,  # TODO 증가 금지
}

if not meets_quality_gate(quality_metrics):
    sys.exit(1)  # 머지 불가

강제하지 않으면 안 한다. 슬프지만 현실이다.

펌프 앤 덤프 소프트웨어는 기술 부채 그 이상이다. 그건 다음 개발자에게 보내는 저주 편지다. 우리 모두 언젠가 그 다음 개발자가 된다는 걸 기억하자.

댓글남기기