20 KiB
title, source_type, status, branch, related_projects, tags, created, target_merge, status_label, id, kind, project, work_item, inherits, refines, overrides, depends_on, contract_packet, parent_branch, contract_packet_sha256
| title | source_type | status | branch | related_projects | tags | created | target_merge | status_label | id | kind | project | work_item | inherits | refines | overrides | depends_on | contract_packet | parent_branch | contract_packet_sha256 | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| branch / feature-api-compatibility-deprecation-contract | branch-note | raw | feature-api-compatibility-deprecation-contract |
|
|
2026-05-22 | in-progress | BR-CA-SKELETON-OPERATIONAL-CONTRACT-026 | project-work-item | ca-skeleton-operational-contract | WI-CA-SKELETON-OPERATIONAL-CONTRACT-026 |
|
1 | b0999dd64e9b6815a5e42c1c75e34bdccd2eb746e6572aedb12a4ac6b1d21fd8 |
branch: feature-api-compatibility-deprecation-contract
Layer:
raw/branch-notes/— API compatibility와 deprecation 기준을 정의합니다.
부모 (필수)
- Parent project (canonical SSOT): raw/project-notes/ca-skeleton-operational-contract
ca-skeleton 은 별도 root branch 없이 project-note 가 SSOT 역할. 본 feature branch 는 project-note 의 운영 계약 중 해당 영역 (§<관련 섹션>) 의 결정/근거/금지 사항을 정제한다.
묶음
- raw/company-tech-blogs/api-versioning-github-rest-date-header
- raw/company-tech-blogs/api-versioning-stripe-date-based
- raw/official-docs/api-versioning-google-aip-180
- raw/official-docs/ci-openapi-snapshot-diff-tooling
- raw/official-docs/compat-rfc-8594-sunset-header
- raw/official-docs/google-aip-185-resource-versioning
- raw/official-docs/openapi-spec-3-1-0
- raw/official-docs/protobuf-reserved-vs-json-openapi-extension
- raw/official-docs/schema-protobuf-vs-json-evolution
- raw/official-docs/sunset-deprecation-headers-paired-usage
본 feature branch 는 leaf — 자식 자료 없음. Phase C2 실 코드 작성 단계에서 errors / interview prep / lectures 가 누적되면 본 섹션에서 그룹화.
오류 기록 (본 feature 작업 중 발생)
- (없음 — 현재 documented-only 단계)
면접 준비 (이 작업에서 나올 수 있는 면접 질문)
- (없음 — Phase C2 실 구현 단계에 누적)
브랜치 계약 패킷
- 생성 시 프로젝트 개정:
1 - 패킷 스키마:
contract_packet: 1 - 완료 조건: /v1 compatibility·deprecation contract test가 통과한다
상속한 프로젝트 결정
| Decision Ref | Project Summary | Branch Application | Source |
|---|---|---|---|
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-API-VERSIONING-001@1 |
URI prefix /v1이 default이며 X-Api-Version은 compatibility 실험용 보조 header다 | Work Item 완료 조건에 적용 | raw/project-notes/ca-skeleton-operational-contract |
브랜치 지역 결정
기존 branch-local 결정은 아래
## Decision Evidence Map / 결정-근거 매핑의 D-row가 소유하며 이 packet에서 복제하지 않는다.
| Decision ID | Decision | Relation | Supporting Claims | Status |
|---|
선언한 예외
| Override ID | Overrides | Reason | Approval | Status |
|---|
목표
API versioning만으로는 장기 유지보수가 부족합니다. breaking change, response field removal, deprecated field, migration window 기준을 skeleton에 포함해야 합니다.
- 이슈:
- PR:
범위
포함 범위
- breaking change 정의.
- response field removal 금지 기준.
- deprecated field 정책.
- migration window 기준.
- backward compatibility test 기준.
- OpenAPI diff 기준.
제외 범위
- public API product lifecycle.
- external developer portal.
- multi-version runtime router 구현.
TODO
TODO drained 2026-05-22 — 결정은 아래 "결정 사항" / "Breaking Change Catalog" / "Decisionized Work Items" 참조. breaking change 정의/response field removal/deprecation marker/migration window/backward compat/OpenAPI diff 모두 catalog 또는 표 row로 반영됨. 잔존 TODO 없음.
Work Item Contract
각 TODO는 아래 판정 단위로 재작성되어야 canonical 승급 가능합니다. TODO가 단순히 기준 작성으로 남아 있으면 이 branch는 완료로 보지 않습니다.
| field | required | rule |
|---|---|---|
| Decision | yes | 구현자가 선택해야 하는 기본값 |
| Allowed | yes | 허용되는 예외와 조건 |
| Forbidden | yes | 절대 금지되는 구현/문서 상태 |
| Required registry update | conditional | error/env/header/log/metric/capability 변경 시 필수 |
| Required contract test | yes | 계약 위반 시 실패해야 하는 테스트 |
| Failure condition | yes | review/build에서 실패로 판정할 상태 |
| Canonical extraction target | yes | wiki/projects 승급 위치 |
진행 중 메모
- 이 branch는 API contract baseline과 schema serialization contract를 보완합니다.
결정 사항 (decisions)
- 2026-05-22: compatibility/deprecation은 API versioning과 별도 기준으로 관리.
- 2026-05-22: breaking change catalog는 이 branch가 소유하고 OpenAPI diff 집행은
feature-contract-verification-test-suite가 수행. - 2026-05-22: migration window 기본값은 90일. internal-only API는 30일로 줄일 수 있으나 branch note에 근거와 소비자 목록이 필요.
- 2026-05-22: published response field removal은 deprecated marker + migration window + compatibility fixture 없이는 금지.
- 2026-05-22: API deprecation 응답은
Sunset: <date>+Deprecation: <date>헤더 함께 전송. 단독 Sunset 금지. 추가로Link: <url>; rel="sunset"권장.
근거 (필수, 최소 1개+)
본 branch의 결정 근거. 상세 비교는 §외부 근거 / 대안 조사 (있다면) 참조.
| Source | 정당화하는 결정 |
|---|---|
| raw/official-docs/compat-rfc-8594-sunset-header | IETF RFC 8594 Sunset header 표준 (ca-tmpl 채택 근거 |
| raw/company-tech-blogs/api-versioning-stripe-date-based | account pin + freeze; 외부 컨슈머 규모 큰 경우 우위 |
| raw/company-tech-blogs/api-versioning-github-rest-date-header | long EOL window + explicit 410 응답 |
| raw/official-docs/api-versioning-google-aip-180 | enum value 제거도 금지; ca-tmpl narrow enum = breaking 결정과 부분 정합 |
| raw/official-docs/sunset-deprecation-headers-paired-usage | 참조 |
| raw/official-docs/openapi-spec-3-1-0 | OpenAPI Specification v3.1 (JSON Schema 2020-12 alignment) — OAS 의 normative scope/structure 근거. ⚠️ Operation Object 의 deprecated: boolean 필드 자체는 OPENAPI31-C1~C7 발췌에 포함되지 않음 (raw 자체 Usage Boundary 명시) — D8 deprecation marker 의 OpenAPI spec normative 인용은 별도 raw 발췌 필요 |
외부 근거 / 대안 조사 (2026-05-22 — Group G-F: API Compatibility / Deprecation)
본 branch의 90d public + 30d internal migration window + breaking change catalog 7행 + Sunset header + OpenAPI deprecated:true 결정에 대한 외부 source.
- 채택 결정 (window + Sunset header + OpenAPI deprecation):
- raw/official-docs/compat-rfc-8594-sunset-header — IETF RFC 8594 Sunset header 표준 (ca-tmpl 채택 근거)
- 검토한 대안:
- 대안 1: Stripe date-based versioning (no removal, freeze forever) — raw/company-tech-blogs/api-versioning-stripe-date-based (account pin + freeze; 외부 컨슈머 규모 큰 경우 우위)
- 대안 2: GitHub X-GitHub-Api-Version header + 24mo EOL + 410 Gone — raw/company-tech-blogs/api-versioning-github-rest-date-header (long EOL window + explicit 410 응답)
- 대안 3: Google AIP-180 backward compat 분류 — raw/official-docs/api-versioning-google-aip-180 (enum value 제거도 금지; ca-tmpl
narrow enum = breaking결정과 부분 정합)
- 비교 핵심: Stripe(freeze forever) vs ca-tmpl(90d/30d window) vs GitHub(24mo EOL + 410): 외부 컨슈머 규모와 운영 비용 trade-off. ca-tmpl internal-first면 90d 합리. 보강 후보 2가지: (a) EOL 응답 코드(410 Gone)가 ca-tmpl catalog에 누락 — GitHub 사례 차용 검토, (b) Sunset(RFC 8594) + Deprecation 헤더는 함께 보내야 정합 — ca-tmpl 결정은 marker만 명시.
후속 보강 (2026-05-22): Sunset 헤더는 Deprecation 헤더와 paired로 보내야 함. raw/official-docs/sunset-deprecation-headers-paired-usage 참조.
Breaking Change Catalog
| change | classification | default action |
|---|---|---|
| remove response field | breaking | deprecate first, remove after migration window |
| rename response field | breaking | add new field, keep old deprecated field through window |
| change field type/format | breaking | new version or additive field |
| narrow enum values | breaking | new version |
| add required request field | breaking | new version or default server-side |
| add optional response field | additive | allowed with schema update |
| change error code/category | breaking for clients | foundation registry change + migration note |
Decisionized Work Items
| item | Decision | Allowed | Forbidden | Required test | Failure condition |
|---|---|---|---|---|---|
| migration window | 90 days public/default, 30 days internal-only | shorter only with owner approval | immediate field removal | compatibility fixture | deprecated field removed early |
| deprecation marker | OpenAPI deprecated: true + branch note |
response header optional | undocumented deprecation | OpenAPI diff | deprecated field lacks marker |
| breaking diff | verification suite release-blocking | warning-only only for additive diff | breaking diff warning-only | openapi-diff gate | breaking diff passes CI |
테스트 계약
- published response field가 사전 deprecation 없이 제거되면 실패.
- OpenAPI diff에서 breaking change가 감지되면 실패.
- deprecated field가 migration window 없이 제거되면 실패.
- backward compatibility fixture가 깨지면 실패.
결정-근거 매핑
각 결정이 어떤 raw source claim 으로 뒷받침되는지 명시. company-tech-blog 출처는
company-case-study로 라벨링하며 공식 best practice 로 격상하지 않는다.
| Decision ID | Decision | Supporting Claims | Evidence Strength | Open Risk |
|---|---|---|---|---|
| D1 | compatibility / deprecation 은 API versioning 과 별도 기준으로 관리 (2026-05-22) | UNSUPPORTED_DECISION (project-internal scoping decision; 외부 표준이 두 영역의 분리를 normative 로 강제하지 않음) | N/A | scoping 결정의 정합성은 sibling branch (feature-api-contract-baseline) 와 cross-review 필요 |
| D2 | breaking change catalog 7행 분류 — remove response field, rename, change type/format, narrow enum values, add required request field, add optional response field, change error code |
raw/official-docs/api-versioning-google-aip-180.md#AIP180-C1 (component 제거 금지), #AIP180-C2 (rename = remove+add), #AIP180-C3 (default behavior preservation 으로 additive 분류), #AIP180-C4 (required field 추가 금지), #AIP180-C5 (minor/patch client breaking 금지), raw/company-tech-blogs/api-versioning-github-rest-date-header.md#GH-APIV-C4 (GitHub 의 동일 7행 breaking 분류 사례) |
official-vendor-doc + company-case-study |
AIP-180 은 Google internal API design guideline — IETF/W3C 표준 아님 (외부 인용 시 "Google AIP" 명시 필수). GitHub 사례는 company-case-study — 7행 분류가 모든 API 의 표준이라는 일반화 금지 |
| D3 | OpenAPI diff release-blocking 집행은 feature-contract-verification-test-suite 가 수행 (이 branch 는 catalog 소유, 집행 위임) |
UNSUPPORTED_DECISION (project-internal SSOT 분할 결정; 외부 표준 근거 없음) | N/A | catalog owner 와 enforcement owner 분리 시 drift 위험 — verification suite 의 입력 catalog 정합성 추적 필요 |
| D4 | migration window 기본값 90일 (public) / 30일 (internal-only) | UNSUPPORTED_DECISION (cited AIP180-C1 은 same major version 안에서 "must not be removed" — ca-tmpl 의 window 후 제거 정책과 다름. cited GH-APIV-C7 의 24개월 EOL 도 90/30일과 직접 일치하지 않음. cited STRIPE-APIV-C4 는 "as long as possible" 철학으로 window 자체를 권고하지 않음) |
N/A | window 길이의 정당성은 internal-first skeleton 의 운영 부담 trade-off — 외부 표준 인용 불가. canonical 승급 시 design rationale 별도 문서화 필요 |
| D5 | published response field removal 은 deprecated marker + migration window + compatibility fixture 없이 금지 | raw/official-docs/api-versioning-google-aip-180.md#AIP180-C1 (component 제거 금지), #AIP180-C2 (rename = remove+add), #AIP180-C5 (minor/patch client breaking 금지), raw/company-tech-blogs/api-versioning-github-rest-date-header.md#GH-APIV-C4 (response field 제거가 breaking) |
official-vendor-doc + company-case-study |
AIP-180 은 same major version 안에서 사실상 영구 금지 — ca-tmpl 의 "migration window 후 제거 허용" 정책은 AIP 보다 약함 (외부 인용 시 정합성 caveat 필요) |
| D6 | API deprecation 응답은 Sunset: <date> + Deprecation: <date> 헤더 함께 전송; 단독 Sunset 금지; 추가로 Link: <url>; rel="sunset" 권장 |
raw/official-docs/sunset-deprecation-headers-paired-usage.md#SD-PAIR-C1 (Sunset = decommissioning 시점), #SD-PAIR-C3 (Deprecation = 상태 신호), #SD-PAIR-C5 (Sunset MUST NOT be earlier than Deprecation), #SD-PAIR-C6 (sunset / deprecation link relation 용도), raw/official-docs/compat-rfc-8594-sunset-header.md#RFC8594-C1 (Sunset 정의), #RFC8594-C4 (sunset link relation IANA 등록) |
official-standard |
IETF httpapi WG 의 권고 — client tooling 의 실제 paired 감지 여부는 vendor 별 (예: Spring HATEOAS, Apigee). 단독 송신을 안 하면 client 가 deprecation 감지 못 한다는 절대 사실은 spec 에 없음 (해석) |
| D7 | breaking diff CI gate 가 release-blocking; additive diff 만 warning-only 허용 | raw/official-docs/api-versioning-google-aip-180.md#AIP180-C3 (additive 의 default behavior 보존 시 호환), #AIP180-C5 (minor/patch breaking 금지), raw/company-tech-blogs/api-versioning-github-rest-date-header.md#GH-APIV-C5 (breaking 은 새 버전 release + 사전 공지) |
official-vendor-doc + company-case-study |
"release-blocking" 자동 enforcement 메커니즘 자체는 AIP-180 / GitHub 모두 정책만 명시 — CI gate 강제는 ca-tmpl 의 운영적 보강 |
| D8 | deprecation marker 는 OpenAPI deprecated: true + branch note; response header optional |
raw/official-docs/sunset-deprecation-headers-paired-usage.md#SD-PAIR-C3 (Deprecation 헤더 정의), #SD-PAIR-C6 (link relation), raw/official-docs/compat-rfc-8594-sunset-header.md#RFC8594-C1 (Sunset 정의), raw/official-docs/openapi-spec-3-1-0.md#OPENAPI31-C1 (OAS normative keyword 해석 BCP 14), #OPENAPI31-C2 (OAS = HTTP API contract scope), #OPENAPI31-C7 (Schema Object = JSON Schema 2020-12 superset — deprecated 가 OAS-specific extension 으로 언급되나 본 raw 발췌에 직접 인용 없음) — marker (OpenAPI) 와 응답 헤더의 paired 송신은 D6 에서 강제 |
official-standard + official-vendor-doc (partial — OpenAPI scope/normative-keyword 까지만) |
⚠️ OpenAPI Operation Object 의 deprecated: boolean 필드 자체의 normative 정의는 openapi-spec-3-1-0 raw 의 OPENAPI31-C1~C7 발췌에 포함되지 않음 (raw 자체 §"Usage Boundaries 이 자료가 증명하지 않는 것" 명시: "deprecated: true 의 정확한 의미론 — 본 발췌에 직접 인용 없음"). §4.8.10 Operation Object 의 deprecated 필드 별도 발췌 또는 §4.8.24 Schema Object 의 deprecated keyword 별도 발췌가 필요한 follow-up. 현재 OPENAPI31-* 는 OAS 의 scope/normative-keyword/JSON-Schema-alignment 만 corroborate — deprecation marker 의미론은 여전히 직접 표준 인용 부재 |
검증해야 할 주장
공식 문서/사례는 근거지만 내 프로젝트에서의 동작을 자동 보장하지 않는다. 구현 전/중/후 실제로 검증해야 하는 주장.
| Claim | Why uncertain | How to verify | Status |
|---|---|---|---|
Sunset + Deprecation 헤더가 paired 로 송신되며 Sunset >= Deprecation invariant 가 강제되는지 (SD-PAIR-C5 준수) |
header middleware 구현 위치 (Spring filter / interceptor / @ControllerAdvice) 에 따라 invariant 누락 가능 |
header invariant CI gate 추가 + integration test (deprecated endpoint 응답에 두 헤더 존재 + Sunset >= Deprecation 검증) | planned |
OpenAPI deprecated: true 마커와 응답 헤더의 동기화가 보장되는지 |
marker 추가만 하고 헤더 누락 또는 그 반대 가능성 | OpenAPI snapshot grep + 실제 응답 contract test cross-check | planned |
| 90d (public) / 30d (internal) migration window 가 release process 에 실제로 강제되는지 | window 정책이 process 문서에만 있고 CI / release gate 에 강제 메커니즘 없을 위험 | release calendar / CI gate 가 deprecation marker 추가 시각 + sunset date 차이를 검증하는지 dry-run | needs-confirmation |
breaking diff CI gate 가 release-blocking 으로 실제 동작하는지 (AIP180-C5 invariant 강제) |
gate 가 warning-only 로 misconfigured 가능 | breaking diff 의도적 도입 후 CI build fail 검증 | planned |
| 7행 catalog 의 모든 row 가 OpenAPI diff tool 의 분류와 1:1 mapping 되는지 | tool (openapi-diff / oasdiff) 의 자체 분류와 catalog 의 분류가 다를 위험 | tool dry-run 결과 + catalog mapping 표 작성 | planned |
EOL 응답 코드 (410 Gone, GH-APIV-C6) 가 ca-tmpl catalog 에 누락된 점 — sunset 이후 응답 정책 결정 필요 |
GitHub 사례 차용 검토 필요 항목으로 본문 명시 — 결정 미정 | catalog 보강 결정 + sunset 시점 이후 응답 contract test 작성 | needs-confirmation |
OpenAPI deprecated: true (Operation Object / Schema Object) 의 normative 정의를 표준 인용으로 확보 |
openapi-spec-3-1-0 raw 의 OPENAPI31-C1~C7 발췌에 deprecated boolean 필드 인용 누락 — D8 의 marker 정책이 외부 표준 직접 인용 없이 운영. raw 자체 Usage Boundary 가 "본 발췌에 직접 인용 없음" 명시 |
OpenAPI 3.1 §4.8.10 Operation Object + §4.8.24 Schema Object 의 deprecated 필드 발췌를 별도 raw 또는 기존 raw 보강으로 확보 → DEM D8 의 Evidence Strength 를 partial → official-standard 로 승급 |
needs-confirmation |
마주친 문제
- 아직 없음.
구현 가이드
- version·deprecation·sunset 값은 API registry가 소유하고 controller는 registry를 참조한다.
- additive fixture와 breaking fixture를 분리하며, 제거는 deprecation window와 소비자 확인 뒤에만 허용한다.
- OpenAPI diff가 breaking change를 검출하면 CI가 실패하고 승인 기록 없이는 우회하지 않는다.
엣지·실패·의존
- 필드 삭제·타입 변경·enum 축소는 기존 소비자를 깨뜨리므로 명시적 migration 경로가 필요하다.
- 본 계약은 API versioning·OpenAPI registry·contract verification Work Item에 의존한다.
관련 일일 노트
- 별도 일일 노트 없음.
완료 후 정리
- PR 링크:
- 리뷰 메모:
- 머지 결과 / 배포 환경:
- wiki 추출 대상 (verified만,
wiki/projects/로만 추출):actually-implemented항목:locally-verified항목:prod-verified항목:
- 추출하지 않을 항목 (planned / documented-only / abandoned):