충분하고 정밀한 명세와 코드가 같은 것이라는 주장에 반박하며, 명세를 가능한 구현들의 집합을 나타내는 추상화로 설명합니다.
지난주 글을 빼먹어서 죄송합니다! 아팠다가 그다음엔 바빴습니다.
이번 주에는 제가 특히 거슬려 하는 것을 다루고 싶은데, 이 만화가 그걸 가장 잘 보여줍니다:
"포괄적이고 정확한 명세"가 반드시 코드인 것은 아닙니다. 명세는 가능한 구현들의 집합 에 대응하고, 코드는 그 집합 안의 단일한 구현입니다. 그 집합에 원소가 둘 이상 있는 한, 명세와 코드 사이에는 분리가 있습니다.
다음과 같이 요청하는 비즈니스 담당자(bp)를 생각해 봅시다:
마일을 킬로미터로 변환하는 도구가 필요합니다.
이게 포괄적인 명세일까요? 어쩌면 그럴 수도 있습니다. Claude Code에 넘겨서 모든 설계 결정을 맡기면, 마일을 km로 변환하는 프로그램을 내놓을 것입니다. 동시에 여기에는 빠진 세부사항이 엄청나게 많습니다. 어떤 언어로 만들까요? UX는 어떨까요? 커맨드라인 스크립트여야 할까요, 모바일 앱이어야 할까요, 아니면 엔터프라이즈 SaaS여야 할까요? 이런 이유로 Claude의 결과물을 그 비즈니스 담당자에게 주면 아마 만족하지 못할 것입니다. 가능한 구현들의 집합에는 그들이 원하는 프로그램도 들어 있지만, 원하지 않는 프로그램도 아주 많이 들어 있습니다.
그래서 이제 그들은 이렇게 말합니다:
웹사이트의 텍스트 상자여야 합니다.
좋습니다. 그러면 많은 가능성이 더 배제되지만, 여전히 결정해야 할 것이 많습니다. React인가요, vanillajs인가요, 아니면 htmx인가요? 출력은 별도의 텍스트 상자여야 할까요, 아니면 팝업이어야 할까요? 변환값으로 1.6, 1.61, 1.609 중 무엇을 써야 할까요? 그러니 이것도 여전히 "포괄적이고 정확한 명세"가 아니라고 주장할 수 있습니다. 하지만 비즈니스 담당자가 Claude가 무엇을 만들든 만족한다면 어떨까요? 그렇다면 그들의 명세는 충분히 포괄적이고 정확했던 셈입니다. 자신들의 문제를 해결하는 프로그램을 얻었으니까요!
이제 위 만화는 더 구체적인 주장을 합니다. 프로그램을 생성할 수 있을 만큼 "충분히 포괄적이고 정확한" 명세는 코드라는 것입니다. 그런데 이것은 LLM 이전에도 사실이 아니었습니다. 프로그램 합성, 즉 명세로부터 이에 부합하는 프로그램을 자동 생성하는 것은 활발한 연구 분야입니다! 제가 마지막으로 확인했던 2019년에는 타입 명세로부터 지역 함수를 생성하는 정도였는데, LLM과 함께 이것이 어떻게 바뀌었는지는 모르겠습니다. 그래도 이것은 코드와 포괄적인 명세가 서로 다른 것임을 보여줍니다.
제가 여기서 말하고 싶은 것은, 명세는 코드의 추상화라는 점입니다.1 모든 명세에 대해, 그 명세를 만족하는 가능한 프로그램들의 집합이 존재합니다. 명세가 더 포괄적이고 더 정확할수록, 이 집합에 들어 있는 프로그램 수는 더 적어집니다. spec1이 spec2의 상위집합에 대응한다면, 우리는 spec2가 spec1을 정제한다고 말합니다. 어떤 명세가 충분하다는 것은 더 이상 정제될 필요가 없다는 뜻입니다. 어떤 구현이 주어지더라도 (합리적인 범위 안에서2) 명세 작성자는 만족할 것이기 때문입니다. 명세는 충분하기 위해 완전히 포괄적일 필요는 없습니다.
이 만화는 한 걸음 더 나아가 주장합니다. "충분히 상세한 명세는 코드다"라는 사실이야말로, 설령 명세로부터 코드를 자동 생성할 수 있게 되더라도 프로그래머가 일자리를 잃지 않을 이유라는 것입니다. 그리고 이것 역시 여전히 사실입니다.
우리는 흔히 그 추상적 명세를 형식 언어를 통해 표현합니다. 보통 이 말을 하면 저는 TLA+나 UML, 심지어 Planguage까지 떠올리지만, 가장 흔한 예시는 테스트 스위트일 것입니다. 테스트도 명세입니다! 그리고 대체로 비프로그래머가 형식 언어로 무언가를 성공적으로 인코딩하도록 만드는 것은 불가능해 보입니다. Cucumber는 비즈니스 담당자가 형식 명세를 쓰게 하려던 실패한 시도였습니다.
하지만 그렇다고 포괄적인 명세가 "코드"가 될까요? 저는 아니라고 봅니다. 명세를 프로그래밍 언어로 인코딩하는 것은 가능합니다(다시 말해 테스트 스위트처럼요). 하지만 그것은 어디까지나 인코딩일 뿐입니다. 명세는 여전히 가능한 구현 프로그램들의 집합에 대응하고, 우리가 그것을 인코딩하지 않더라도 여전히 유용합니다. "코드"와 "명세"를 구별되는 개념으로 유지하는 것은 유익합니다.