여기는 반려동물 가게의 결제 페이지입니다. 일부러 작게 만들었지만, 실제 코드베이스가 가진 종류의 결합도(coupling)를 가지고 있습니다: 두 기능이 조용히 세 번째 기능에 의존합니다.
TAX_RATE = 0.10 # 10% 세금
LOYALTY_PER = 10.0 # 주문 총액 $10당 로열티 포인트 1점
FREE_SHIP_AT = 30.0 # 이 금액 이상에서 무료 배송(및 실버 등급)
...
고객이 사료 세 봉지와 목줄을 장바구니에 담았습니다: 세금 부과 전 $27.30, 세금 부과 후 $30.03입니다. 30달러 선을 간신히 넘겼기 때문에 무료 배송과 실버 등급을 받습니다.
수정 사항 (The fix)
이제 AI 에이전트에게 세금 계산을 '더 정확하게' 만들라고 요청해 보세요. 이 에이전트는 한 줄만 변경합니다:
-TAX_RATE = 0.10
+TAX_RATE = 0.095
테스트를 실행하면 다음과 같습니다:
$ pytest -q
... [100%]
3 passed in 0.01s
녹색입니다. 에이전트는 작업이 완료되었다고 보고합니다.
하지만 총액은 이제 $29.89가 되었습니다. 고객은 30달러 미만으로 떨어져 무료 배송과 실버 등급을 잃었습니다. 아무것도 실패하지 않았습니다. 왜냐하면 어떤 테스트도 "이 장바구니는 무료로 배송된다"라고 말하지 않았기 때문입니다. 아무도 그 테스트를 작성하지 않습니다. 당신은 변경하는 대상만 테스트할 뿐, 그 출력값을 읽게 되는 두 기능을 테스트하지 않습니다.
이것이 AI가 만든 변경 사항의 패턴입니다: 에이전트는 A를 수정하고, B, C, D는 그것을 따라 움직입니다. 아무것도 B, C 또는 D에 고정시키지 않았기 때문에 스위트(suite)는 계속 녹색으로 유지됩니다.
게이트(Gate)가 있는 경우의 모습
우리는 이와 정확히 같은 상황을 위해 구축한 pytest 플러그인인 nightward를 통해 동일한 변경 사항을 실행해 보았습니다. 아래 출력은 실제 실행에서 가져온 것이며, 길이만 잘라낸 것입니다:
$ nightward run .
3 passed in 0.02s
...
단 한 줄이 바뀌었지만, 제품의 세 가지 다른 부분에서 세 가지 동작(behavior)이 움직였습니다. nightward review는 정확히 무엇이 움직였는지 보여줍니다:
[CHANGED] checkout_total
- "tax": 2.73,
- "total": 30.03
...
그리고 누군가 결정할 때까지 게이트는 닫힌 상태를 유지합니다:
$ nightward gate
boundary breached (3 unapproved)
$ echo $?
...
[15-second demo: an AI fixes one line, every test passes, nightward shows three customer-facing behaviors moved]
예상 값 없음(No expected values)
여기에 전체 테스트 파일이 있습니다. 빠진 부분을 주목하세요:
from shop import checkout_total, loyalty_points, shipping_fee
CART = [
...
assert 라인이 없습니다. behavior fixture는 오늘 코드가 반환하는 값을 기록합니다. 처음 실행할 때 사람이 그 값들을 읽고(nightward review) 한 번 승인합니다(nightward approve). 이 값들은 코드 옆에 일반 JSON 형태로 저장되고 커밋됩니다:
{
"env": {
"os": "Windows",
...
그 이후부터는 모든 실행이 그 승인된 기준선(baseline)과 비교되며, 기능별로 그룹화되어 변경된 모든 것이 나열됩니다.
만약 예상 값을 작성하는 것이 실제 작업처럼 느껴져 테스트를 미뤄왔다면, 바로 이 부분이 변화할 지점입니다. 무엇이 정답인지 미리 결정하지 않습니다. 코드의 현재 동작을 보고 "네, 이것"이라고 말하면 됩니다.
하지 않는 것들(What it does not do)
이것은 중요하기 때문에 별도의 섹션으로 다룹니다.
- 무엇이 올바른지 알지 못합니다. nightward가 브론즈 티어(bronze tier)가 버그라고 알려줄 수는 없습니다. 어쩌면 새로운 세금률이 법규일 수 있고 고객은 실제로 배송비를 지불해야 할 수도 있습니다. 이 시스템은 단지 변경 사항이 조용히 통과되는 것을 막을 뿐입니다. 인간이 의도적으로 승인하거나(intended) 코드를 수정하여(regression) 고쳐야 합니다.
- 캡처된 것만 보호합니다. 테스트가 배송비를 캡처하지 않으면, 배송비 변경은 눈에 띄지 않고 지나갑니다. 사용자가 알아차릴 만한 출력값들을 캡처하세요.
- 테스트 생성기가 아닙니다. 사용자 대신 테스트를 작성해주지 않습니다. 기존의 테스트들을 일종의 게이트(gate)로 변환할 뿐입니다.
타임스탬프, UUID와 같은 휘발성 값들은 비교 전에 정규화되므로, 매번 실행될 때마다 작동하는 게이트는 버그로 간주되지 않습니다. 노이즈가 많은 게이트는 무용지물(dead gate)입니다.
AI 에이전트 루프를 위해 구축됨
분리는 의도적입니다. 에이전트(agent)는 게이트를 실행하고 판결을 읽을 수 있습니다 (nightward run, nightward gate 또는 nightward_run / nightward_status MCP 도구). 하지만 승인(approving)은 인간만 할 수 있는 CLI 단계이며, 그 결과는 코드 리뷰를 거치는 파일 차이점(file diff)입니다. 에이전트는 자신이 확인할 수는 있지만 변경할 수는 없는 "완료됨(done)"의 정의를 받습니다.
5분 만에 사용해 보세요
사용자들이 볼 수 있는 무언가에 대해 behavior(name, value, group=...)를 호출하는 테스트를 추가한 다음:
nightward init
nightward run .
nightward review
...
그 후에는 CI에서 nightward gate을 실행하면 승인되지 않은 변경 사항이 나타날 때마다 0이 아닌 값으로 종료됩니다.
현재 AI 에이전트가 요청한 변경 사항 외에 무엇을 변경했는지 어떻게 확인하시나요? 여러분에게 효과적인 방법과 nightward가 부족한 부분이 있다면 듣고 싶습니다.
AI의 도움을 받아 작성되었으며; 위에 제시된 모든 출력은 실제 실행 결과입니다.