아이디어와 인사이트

AI 퍼스트 개발, 코딩 가드레일, 그리고 폐기 가능한 아키텍처를 탐구합니다.

Fuck-u-code: AI 개발 과정이 잊어버린 결정론적 품질 관문

타입 검사, 린트, 아키텍처 규칙은 갖췄다. 하지만 결정론적 체계는 복잡도, 중복, 명명 재앙을 전혀 보지 못한다. 비용 0원인 해결책은 이것이다.

솔직히 말해 보자. 지금 대부분의 AI 코드 생성 과정은 어떻게 생겼는가. Cursor나 Claude Code로 코드를 생성한다. TypeScript 엄격 모드가 타입 불일치를 잡아주니 을 돌린다. 세미콜론 논쟁을 병합 과정에서 하고 싶지 않으니 ESLint를 돌린다. 순환 임포트가…

Rust의 Property-Based Test가 당신의 Unit Test가 놓치는 버그를 찾아낸다

Example-based testing은 당신이 생각해낸 입력만 커버한다. Property-based testing은 무작위 데이터를 생성하고, invariant를 검증하며, 실패를 최소한의 counterexample로 축소한다.

당신은 함수를 작성했다. 과 로 테스트했다. 통과했다. 배포했다. 한 사용자가 한 개의 원소를 가진 slice를 넘겼다. 당신의 함수는 그것을 무시하고 지나쳤다. 이슈가 열렸다. 당신은 테스트 파일을 응시하며 이렇게 뻔한 것을 어떻게 놓쳤는지 의아해한다. 놓친 이유는…

버전 1은 결코 문제가 아니다: AI coding과 장기 유지보수

AI coding 도구는 버전 1을 생성하는 데 탁월합니다. 진짜 엔지니어링 과제는 버전 4에서 시작됩니다. 팀이 나머지를 깨뜨리지 않고 무언가를 바꿔야 할 때입니다.

모든 AI coding 데모는 같은 흐름을 따릅니다. 누군가 모델에 프롬프트합니다. 동작하는 앱이 나타납니다. 청중은 감탄합니다. 감탄할 만합니다. 속도는 실제입니다. 능력도 실제입니다. 버전 1은 AI가 루프에 있을 때 진짜로 더 빠르게 배포됩니다. 문제는 버전 1이 결코 어려운…

AI 생성 코드와 대체 가능성 원칙

AI 생성 코드의 진짜 품질 기준은 첫날에 동작하느냐가 아닙니다. 30일째에 나머지를 전부 rewrite하지 않고도 그 코드를 교체할 수 있느냐입니다.

AI 생성 코드 품질에 대한 대부분의 논의는 생성 시점의 정확성에 초점을 맞춥니다. 출력이 컴파일되는가? 테스트를 통과하는가? 명세에 맞는가? 이것들은 기본 조건일 뿐입니다. 진짜 비용에 대해서는 아무것도 알려주지 않습니다. 진짜 지표는 대체 가능성입니다. 요구사항이 변할 때 이…

AI codebase를 위한 결정론적 가드레일

사람의 리뷰는 일관적이지 않습니다. AI 리뷰는 더 나쁩니다. AI 생성 codebase에 대해 확장 가능한 유일한 방어는 결정론적 enforcement입니다. 무시되는 제안이 아니라 빌드를 실패시키는 규칙입니다.

AI 생성 코드에 대한 표준 조언은 "주의 깊게 리뷰하라"입니다. 이 조언은 정확하지만 규모에서는 쓸모없습니다. AI 출력을 리뷰하는 개발자는 주의력이 높고, 해당 도메인에 익숙하고, 시간 압박이 없을 때 문제를 잡아냅니다. 그 외의 모든 조건에서 — 그리고 대부분의 조건이 그렇습니다…

단위 테스트는 통과했지만, 프로덕션 코드는 여전히 망가져 있다.

코드 커버리지 지표는 허위 안전감을 조성한다. 단위 테스트가 실제로 잠을 설치게 하는 버그를 놓치는 이유, 그리고 대신 무엇을 테스트해야 하는지 알아보자.

코드 커버리지가 90%인데도 새벽 2시에 호출을 받았다. 단위 테스트는 통과했다. CI는 초록불이었다. 그런데 버그는 어김없이 프로덕션에 올라갔다. 커버리지가 거짓말을 한 건 아니지만, 진실을 말해준 것도 아니다. 커버리지는 어떤 라인이 실행됐는지 쟀을 뿐, 어떤 동작이 실제로…

Rust 런타임 contracts는 릴리스 빌드에서 오버헤드 없이 사용할 수 있지만, 컴파일러가 대신 해주지는 않는다

Rust는 디버그 assertions를 자동으로 제거하지만, 진정한 design-by-contract는 debug_assert! 이상의 것이 필요하다. 릴리스 바이너리에서 완전히 사라지는 zero-cost runtime contracts를 만드는 방법을 소개한다.

Rust는 개발 환경에서 runtime contracts를 강제할 수 있고, 릴리스 빌드에서는 완전히 지워버릴 수 있다. 전제 조건은 이 언어가 contract를 first-class 개념으로 다루지 않는다는 것이다. 필요한 구성 요소는 주어지지만, 직접 연결해야 한다. 는 가장 먼저…

0개, 1개, 아니면 12개: 프로덕션 함수에 실제로 필요한 assertion의 개수

개발자들은 assertion을 색종이처럼 뿌리거나 아예 사용하지 않는다. 유용한 invariant와 프로덕션 crash를 유발하는 요인을 구분하는 결정 프레임워크를 소개한다.

대부분의 프로덕션 codebase는 두 진영 중 하나에 속한다. A 진영은 를 장식용 조미료처럼 다루어 함수가 편집증적인 변호사가 쓴 법률 계약서처럼 읽힐 때까지 한 줄 걸러 뿌린다. B 진영은 assertion을 개발 중에만 쓰는 보조 바퀴로 여겨 빌드 시점에 모두 제거하고,…

검증 계층이 비즈니스 로직보다 더 커지는 경우

수동 검증은 codebase를 부풀리고 여전히 엣지 케이스를 놓친다. 선언적 스키마로 runtime contracts를 간섭 없이 강제하는 방법을 알아본다.

API가 요청을 받을 때마다 검증한다. 함수가 외부 시스템으로부터 인자를 받을 때마다 확인한다. 이를 수동으로 하면, 단일 엔드포인트에 비즈니스 로직보다 더 많은 검증 코드가 쌓일 수 있다. 이것이 runtime contracts의 숨겨진 비용이다. 타입 시스템이 거짓말을 하기 때문에…

TypeScript strictNullChecks는 컴파일 타임 가드일 뿐, 런타임 방패가 아니다

strict mode는 내가 작성한 null만 잡아낼 뿐, API나 DOM 쿼리, JSON.parse를 통해 런타임에 유입되는 null은 잡지 못한다. 타입 시스템의 한계가 끝나는 지점과 진짜 방어가 시작되는 지점을 살펴 본다.

에서 를 켰다. 모든 빨간 물결표시를 고쳤다. 과 는 이제 해결된 문제라는 자신감을 안고 프로덕션에 배포했다. 그런데 백엔드 응답 구조가 바뀌고, DOM 쿼리가 아무것도 반환하지 않았고, TypeScript가 안전하다고 말한 바로 그 코드에서 이 을 던지며 죽었다. 무슨 일이 일어난…