펌프 앤 덤프 소프트웨어 시대: 빠르게 만들고 버리는 개발 문화의 실체 The Age of Pump and Dump Software: The Reality of Build-Fast-Abandon Culture
펌프 앤 덤프 소프트웨어의 시대: 기술 부채를 떠넘기는 개발 문화
우리 업계에 새로운 유행이 있다. 빠르게 만들고, 화려하게 포장하고, 문제가 터지기 전에 빠져나가는 것. 주식 시장의 “펌프 앤 덤프(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) # 머지 불가
강제하지 않으면 안 한다. 슬프지만 현실이다.
펌프 앤 덤프 소프트웨어는 기술 부채 그 이상이다. 그건 다음 개발자에게 보내는 저주 편지다. 우리 모두 언젠가 그 다음 개발자가 된다는 걸 기억하자.
The Age of Pump and Dump Software: The Culture of Passing Technical Debt
There’s a new trend in our industry. Build fast, package it pretty, and get out before things explode. The same pattern as stock market “pump and dump” fraud is happening in software development. And the ones left cleaning up the mess are always the next team, the next developer.
What is Pump and Dump Software
Pump and dump software refers to code designed for short-term wins and demos. It looks like it works on the surface, but in reality, it’s unmaintainable, unscalable, and a massive ball of technical debt.
The characteristics of such code are clear:
# Typical pump and dump code
def process_user_data(data):
# TODO: refactor later (written in 2019)
# FIXME: no idea why this works
try:
result = data['user']['profile']['settings']['preferences']['theme']
except:
result = "default" # swallow all exceptions
# hardcoded values
if result == "dark":
return {"color": "#000000", "bg": "#1a1a1a"}
else:
return {"color": "#ffffff", "bg": "#fafafa"}
This code works. It runs fine in demos too. But when you need to add another theme 3 months later? When the data structure changes 6 months later? By then, the original developer has already moved to another project, or another company.
Why Has This Become So Prevalent
The Incentive Structure Problem
In most companies, developers are evaluated by “number of features shipped.” Code quality? Hard to measure. Technical debt reduction? Invisible to management.
// Code written during performance review season
const quickWin = async (userId) => {
// Direct DB query (ORM? What's that?)
const result = await db.raw(`
SELECT * FROM users
WHERE id = ${userId} // SQL injection? Whatever
`);
// Caching? Next quarter
// Error handling? Test in production
return result[0];
};
Shrinking Tenure
We’re in an era where average tenure is under 2 years. I’ll never have to deal with the consequences of the code I write. Just change jobs and it’s over.
The Shadow of Startup Culture
“Ship now, fix later” makes sense for early-stage startups finding PMF (Product-Market Fit). The problem is the same logic applies in 500-person companies that just raised Series C.
Real Damage: The Horror in Code
Let’s see what unmaintainable code looks like.
// An API endpoint built "quickly"
export const handleRequest = async (req: any, res: any) => {
// A parade of any types
const data: any = req.body;
// 500 lines of business logic in one function
if (data.type === "create") {
// 100 lines of logic...
} else if (data.type === "update") {
// Another 100 lines...
} else if (data.type === "delete") {
// And more...
} else if (data.type === "createOrUpdate") {
// Why does this exist?
} else if (data.type === "softDelete") {
// Ah...
} else if (data.type === "hardDelete") {
// Please...
}
// Tests? Impossible, author quit
res.json({ success: true }); // Always succeeds
};
When you inherit code like this, you have two choices. Rewrite everything (no time or budget), or pile another layer of pump and dump code on top.
How to Prevent Pump and Dump
1. Reclaim the True Meaning of Code Review
Rubber-stamp LGTM code reviews are meaningless. Ask real questions.
❌ "LGTM"
❌ "A few nits but approved"
✅ "This function is 400 lines, why can't it be split?"
✅ "This try-catch swallows all exceptions, shouldn't we at least log?"
✅ "This logic looks reusable elsewhere, but there are no tests"
2. Make Technical Debt Visible
# tech-debt.yaml - Technical debt tracking
- id: TD-001
created: 2024-01-15
author: "kim@company.com"
severity: high
description: "Any type abuse in User service"
estimated_fix_hours: 16
blocking_features: ["user-analytics", "audit-log"]
- id: TD-002
created: 2024-01-20
author: "lee@company.com"
severity: critical
description: "Payment module test coverage 0%"
estimated_fix_hours: 40
blocking_features: ["international-payment"]
With a file like this, at least no one can claim “I didn’t know.”
3. Make Pre-departure Code Cleanup a Culture
# Offboarding checklist (proposed)
□ Clean up TODO/FIXME in code I wrote
□ Document undocumented tribal knowledge
□ At least 2 pair programming sessions with successor
□ Write list of technical debt I created
Honest Opinion: This is a System Problem
It’s easy to blame individuals. But when incentives are wrong, behavior follows. In a system that rewards fast shipping and ignores quality, expecting quality code is naive.
The solution? You can’t manage what you can’t measure.
# Add quality gates to build pipeline
quality_metrics = {
"test_coverage": 80, # minimum 80%
"cyclomatic_complexity": 10, # max 10 per function
"any_type_count": 0, # ban TypeScript any
"todo_count_delta": 0, # no TODO increase
}
if not meets_quality_gate(quality_metrics):
sys.exit(1) # block merge
If you don’t enforce it, it won’t happen. Sad, but reality.
Pump and dump software is more than technical debt. It’s a curse letter sent to the next developer. Remember, we all become that next developer someday.
댓글남기기