Safety stack 없는 AI speed는 결국 fragility가 된다
AI-generated code의 가장 위험한 점은 항상 틀리다는 데 있지 않습니다.
가장 위험한 점은 너무 자주, merge해도 될 만큼 그럴듯해 보인다는 데 있습니다.
바로 그 점이 리스크입니다. 명백히 깨진 code는 잡힙니다. 하지만 그럴듯해 보이고, 몇 개의 happy-path tests를 통과하고, 동시에 중요한 boundary를 조용히 약하게 만드는 code가 production까지 들어갑니다.
만약 workflow가 prompt, paste, review, merge에 머무른다면, generation speed가 올라갈수록 시스템을 바꾸는 속도와 그 변경을 신뢰할 수 있는 정도 사이의 간격은 더 커집니다.
해법은 code review에서 더 많은 영웅담을 기대하는 것이 아닙니다. 해법은 서로 다른 failure modes를 서로 다른 지점에서 잡는 layered safety stack입니다.
Layer 1: types, schemas, contracts를 통한 prevention
가장 싼 defect는 프로그램이 아예 표현할 수 없는 defect입니다.
그래서 첫 번째 layer는 prevention입니다.
이 layer에서는 runtime behavior가 문제가 되기 전에 surface area를 좁힙니다.
- Branded types와 phantom types 는 구조는 비슷하지만 의미는 다른 값을 섞는 실수를 막아 줍니다.
- Runtime schemas 예를 들어 Zod는 untyped data가 들어오는 boundaries를 지켜 줍니다.
- Contracts 는 중요한 code paths에 preconditions, postconditions, invariants를 부여합니다.
AI-generated code에서는 이 layer가 더 중요합니다. 모델은 겉보기엔 그럴듯하지만 실제로는 category mistakes를 포함하는 구현을 자주 만듭니다. types와 schemas가 약하면 모델이 “거의 맞는” 상태로 통과할 공간이 너무 큽니다.
Layer 2: property-based tests를 통한 verification
Example tests는 유용하지만, 사람에게도 모델에게도 overfit되기 쉽습니다.
모델은 function 하나와 그에 딱 맞는 happy-path test를 함께 만들 수 있습니다. pull request에서는 생산적으로 보이지만, 실제 semantics는 거의 고정되지 않은 경우가 많습니다.
Property-based testing은 질문 자체를 바꿉니다. 세 개의 example에서 동작하는지를 묻는 대신, input의 전체 클래스에 대해 무엇이 항상 참이어야 하는지를 묻습니다.
가장 ROI가 높은 시작점은 보통 다음과 같습니다.
- round trips
- idempotence
- ordering invariants
- monotonicity
- invalid input에 대한 올바른 error behavior
여기서는 AI가 꽤 도움이 됩니다. signature나 doc comment만으로도 쓸 만한 first draft properties를 제안하는 편입니다. 인간의 review는 여전히 필요하지만, blank-page problem은 훨씬 줄어듭니다.
Layer 3: mutation testing을 통한 assessment
Coverage는 quality metric이 아닙니다. Execution metric입니다.
Mutation testing은 진짜 중요한 질문을 던집니다. code가 그럴듯하지만 잘못된 방향으로 바뀌었다면, 당신의 tests는 그것을 알아차릴 수 있는가.
그래서 mutation testing은 contracts와 property tests 위에 놓입니다. 둘을 대체하려는 것이 아니라, 둘이 실제로 일을 하고 있는지 측정하려는 것입니다.
이 점은 AI-generated test suites에서 특히 중요합니다. 모델은 보기에는 훌륭하고 많은 lines를 실행하지만, 실제로는 거의 아무것도 검증하지 않는 tests를 쉽게 만듭니다. Mutation testing은 그 false confidence를 드러냅니다.
실용적인 접근은 항상 전체 codebase에 full mutation analysis를 돌리는 것이 아닙니다. 실용적인 접근은 다음과 같습니다.
- critical modules부터 시작하고
- changed code에 incremental mutation testing을 적용하고
- survivors를 엄격하게 triage하고
- suite가 성숙할수록 thresholds를 올리는 것
AI 시대의 mutation testing은 false confidence에 대한 해독제입니다.
Layer 4: runtime containment와 recovery
강한 verification stack도 모든 것을 잡아내지는 못합니다.
그래서 바깥 layer는 runtime containment입니다.
여기서 crash-only design, deadlines, circuit breakers, leases, capability-based boundaries가 중요해집니다. 무언가가 빠져나가더라도, 시스템이 하나의 bad path를 cascading incident로 키우지 않도록 하기 위해서입니다.
많은 팀에게 이 layer는 작게 시작합니다.
- external calls에 대한 explicit timeouts
- state-changing endpoints에 대한 idempotency keys
- 불안정한 dependencies 앞에 두는 circuit breakers
- 민감한 operations를 위한 narrow capability surfaces
목표는 완벽함이 아닙니다. 목표는 bounded blast radius입니다.
왜 layers는 함께 있을 때 더 강한가
각 layer는 서로 다른 failure class를 잡습니다.
Types와 contracts는 obvious invalid states를 막습니다. Property-based tests는 semantics를 검증합니다. Mutation testing은 tests에 실제 teeth가 있는지 확인합니다. Runtime containment는 끝내 빠져나간 것들을 다룹니다.
핵심은 여기에 있습니다. 완벽한 한 가지 technique가 필요한 것이 아닙니다. 서로 독립적으로 실패하는 여러 개의 불완전한 techniques가 필요합니다.
현실적인 rollout은 어떤 모습인가
대부분의 팀은 모든 것을 한 번에 켜지 않는 편이 좋습니다.
가장 현실적인 순서는 대체로 이렇습니다.
- TypeScript 또는 Rust boundaries를 강화한다
- external inputs에 runtime schemas를 추가한다
- critical functions에 contracts를 도입한다
- serializers, reducers, validators에 property tests를 쓴다
- high-risk modules에 incremental mutation testing을 넣는다
- 자주 깨지는 dependencies 주변에 runtime containment를 추가한다
이 순서가 먹히는 이유는 각 layer가 다음 layer를 강화하기 때문입니다. 더 좋은 schemas는 더 좋은 properties를 만들고, 더 좋은 properties는 mutation scores를 끌어올리며, mutation feedback은 contracts나 test depth가 아직 약한 지점을 보여 줍니다.
AI 시대 engineering의 진짜 변화
이기는 팀은 가장 많은 code를 생성하는 팀이 아닙니다. 더 많은 generated code를 trust를 잃지 않고 흡수할 수 있는 팀입니다.
그 문제를 푸는 것이 safety stack입니다.
Safety stack은 AI를 speed amplifier에서 reliability amplifier로 바꿉니다. 모델은 implementations, tests, contracts, rules 생성을 도와줍니다. Stack은 그 artifacts가 스타일상 그럴듯하다는 이유가 아니라 deterministic systems를 통해 계속 검증되도록 만듭니다.
production engineering에서 AI를 진지하게 쓰려면, 목표 수준은 여기여야 합니다. review checklist 하나 더 추가하는 것도 아니고, “be careful”라는 prompt 하나 더 넣는 것도 아닙니다.
모든 generated change가 prevention, verification, assessment, containment를 통과해야 하는 layered system입니다.
그렇게 해야 빠른 code가 신뢰할 수 있는 code가 됩니다.