개발을 일시 중지할 수는 없습니다. 그것이 가장 먼저 받아들여야 할 사실입니다. 티켓은 계속 쌓이고, 고객은 배송을 기다리며, 문서를 작성하기로 결정했다고 해서 기존 코드가 실행을 멈추지는 않습니다. 어떤 엔지니어링 매니저도, 첫날부터 존재했어야 할 명세서를 작성하기 위해 팀의 한 달간의 개발 중단을 승인하지 않을 것입니다. OpenSpec은 그린필드(greenfield) 같은 환상이 아니라 현실을 위해 만들어졌습니다. OpenSpec은 고객을 포함하여 이미 보유하고 있는 시스템에 그대로 덧붙여 사용할 때 가장 효과적입니다.
여기서의 목표는 재작성이 아닙니다. 정직한 고고학 작업입니다. 운영 환경에서 실제로 실행 중인 것을 파헤쳐 정확하게 기술하고, 코드가 발전함에 따라 그 기술 내용도 함께 진화하도록 만드는 것입니다. 명세서가 시스템과 일치할 때, 다음 분기에 합류할 엔지니어들과 현재 IDE에 자리 잡은 AI 도구들의 작업이 훨씬 수월해집니다. 단 한 번의 릴리스도 놓치지 않고 이를 수행하는 방법은 다음과 같습니다.
실제로 수행하고 있는 일부터 시작하십시오
저장소를 열어보면 controllers, models, services, utils와 같은 폴더들이 보일 것입니다. 이것들은 기술 계층이며, 여러분에게 거짓말을 하고 있습니다. 이들은 시스템이 비즈니스를 위해 무엇을 하는지 설명하지 않습니다. JavaScript 파일로 가득 찬 폴더는 주문이 어떻게 배송으로 이어지는지 설명해주지 않습니다. OpenSpec을 소급 적용하려면 '기능(capabilities)' 단위로 생각해야 합니다.
전체 스택을 다른 언어로 재작성하더라도 살아남을 안정적인 비즈니스 운영 단위를 찾으십시오. 대부분의 제품 기반 기업에서는 다음과 같은 항목들이 반복해서 나타납니다: 주문(Orders), 결제(Billing), 재고(Inventory), 고객(Customers), 알림(Notifications). 이러한 핵심 기능 중 5~8개를 선정하십시오.
각 기능에 대해 다음 다섯 가지 질문에 반드시 답하십시오. 이 기능은 현실 세계의 어떤 문제를 해결하는가? 코드는 실제로 어디에 위치하는가(하나의 서비스인가, 세 개의 마이크로서비스인가, 아니면 아무도 건드리고 싶어 하지 않는 레거시 모듈인가)? 무엇이 이를 트리거하는가(사용자 클릭, 예약된 cron 작업, 인바운드 웹훅)? 어떤 데이터가 입력되고 어떤 데이터가 출력되는가? 마지막으로, 어떤 다른 시스템이 이 기능에 의존하고 있는가(즉, 이 부분이 작동을 멈추면 무엇이 망가지는가)?
아주 냉정하게 작성하십시오. 만약 'Customers' 기능이 Rails 모놀리스, Node API, 외부 CRM에 흩어져 있다면, 있는 그대로 기록하십시오. 여러분의 지도는 설계자의 꿈이 아니라 실제 지형과 일치해야 합니다.
희망 사항이 아닌 진실을 기록하십시오
문서화 작업에서 가장 위험한 문장은 "이걸 적는 김에 고치는 게 좋겠다"입니다. 멈추십시오. 여러분은 결제 흐름을 재설계하는 것이 아닙니다. 지금 이 순간 실제로 신용카드로 결제가 이루어지고 있는 그 결제 흐름을 기술하는 것입니다.
만약 주문을 넣는 것이 즉각적인 결제 승인을 트리거하고 백그라운드 워커를 통해 이메일을 발송한다면, 정확히 그 순서를 기록하십시오. 다음 분기에 추가할 계획인 이벤트 큐를 끼워 넣지 마십시오. 검증이 실제로는 서비스 클래스 깊숙한 곳에서 이루어지고 있음에도 불구하고 API 에지(edge)에서 일어나는 것처럼 꾸미지 마십시오. 포부보다 정확성이 훨씬 중요합니다.
잘못된 문서는 문서가 없는 것보다 더 나쁩니다. 잘못된 문서는 신입 사원들에게 존재하지 않는 동작을 기대하도록 학습시킵니다. 또한 AI 코딩 어시스턴트를 근거 없는 희망 사항에 기반한 가상의 경로로 인도합니다. 명세서가 운영 환경과 일치할 때, 여러분은 신뢰할 수 있는 기준점을 만들게 됩니다. "의도된" 흐름을 추측할 필요가 없으므로 디버깅이 빨라집니다. 시작점이 실제임을 알기 때문에 리팩터링이 더 안전해집니다.
API에서 계약(Contract)을 추출하십시오
API 엔드포인트는 이미 규칙을 강제하고 있습니다. 다만 그 규칙이 암시적일 뿐입니다. OpenSpec을 소급 적용한다는 것은 이러한 규칙들을 명시적으로 끌어내는 것을 의미합니다.
입력값과 검증부터 시작하십시오. 엔드포인트가 실제로 무엇을 허용합니까? 타입, 필수 필드, 최대 길이, 그리고 필드 간의 의존성을 기록하십시오. 그런 다음 비즈니스 동작을 기술하십시오. 이 호출이 레코드를 생성합니까, 부수 효과(side effect)를 트리거합니까, 아니면 단순히 다른 서비스에 대해 상태를 검증합니까? 구체적으로 작성하십시오.
마지막으로 응답을 목록화하십시오. 성공 시 무엇을 반환합니까? 정확한 에러 코드는 무엇이며 어떤 조건에서 나타납니까? 단순히 "에러를 반환함"이라고 쓰지 마십시오. "결제 주소가 누락되면 422를 반환하고, 다른 프로세스에 의해 이미 재고가 예약된 경우 409를 반환함"이라고 작성하십시오. 이러한 정밀함이 모호한 경로를 프론트엔드 팀, QA 엔지니어, 자동화 도구가 신뢰할 수 있는 '계약'으로 바꿔줍니다.
숨겨진 규칙을 찾아내십시오
시스템 내에서 가장 가치 있는 지식 중 일부는 틈새에 숨겨져 있습니다. 서비스 클래스 내부의 조건문 블록에 묻혀 있거나, 데이터베이스 트리거에 끼워져 있거나, 혹은 2년 동안 아무도 건드리지 않은 저장 프로시저에 작성되어 있습니다. 이것들이 바로 여러분의 비즈니스 규칙이며, 대개 시스템 장애가 발생하거나 처음부터 자리를 지켜온 유일한 엔지니어를 붙잡고 물어봐야만 겨우 다시 발견되곤 합니다.
이 규칙들을 수면 위로 끌어올리세요. 이미 알고 있는 것부터 시작하십시오. '일정 금액 이상의 주문은 진행 전 관리자의 승인이 필요하다', '비활성 사용자 계정은 새 주문을 생성할 수 없다', '환불은 정산이 완료되기 전까지만 가능하다'와 같은 것들 말입니다. 각 규칙을 해당 기능 바로 옆에 작성하되, 프로덕트 매니저가 번역기 없이도 읽을 수 있을 만큼 명확한 언어로 작성하십시오.
이러한 규칙들을 중앙 집중화하면 단순히 문서화하는 것 이상의 효과를 얻을 수 있습니다. 중복된 부분을 찾아낼 수 있고, 충돌을 드러낼 수 있습니다. 또한, 누군가 존재조차 잊고 있던 제약 조건을 실수로 위반하는 단 한 줄의 코드를 커밋하기 전에, 팀 전체가 정책을 논의할 수 있는 단일 창구를 제공하게 됩니다.
배관 구조를 매핑하라
현대의 시스템은 이벤트 기반으로 작동합니다. 한 서비스에서의 동작은 사용자에게 눈에 보이는 결과가 전달되기 전, 대여섯 개의 다른 서비스로 파급됩니다. 여러분은 이러한 파급 효과를 기록해야 합니다. 핵심 워크플로우에 대해 하나의 이벤트에서 다음 이벤트로 이어지는 흐름을 매핑하십시오. '주문 생성'이 '재고 예약'으로 이어지고, 이것이 다시 '결제 확인'을 기다리는 식입니다. 일부 연결 고리가 취약하거나 서로 다른 프로토콜을 사용하더라도 전체 체인을 그려내십시오.
내부 트래픽에만 머물지 마십시오. 외부 서비스는 여러분이 그렇게 간주하든 아니든 시스템의 일부입니다. 각 연동에 대해 목적, 애플리케이션의 인증 방식, 그리고 장애 발생 시의 동작을 기록하십시오. 결제 게이트웨이가 30초 후에 타임아웃이 발생하며 일반적인 500 에러를 반환합니까? 배송 API가 주말에 잘못된 형식의 JSON을 반환합니까? 인증 제공자가 자체 문서에 명시된 것보다 더 빨리 리프레시 토큰을 만료시킵니까? 이러한 세부 사항들은 사소해 보일 수 있습니다.
