오랜만에 복잡한 문제를 단순한 형태로 모델링해서 해결했다. 문제 상황은 대략 라이브러리의 메이저 버전을 위해 자동 마이그레이션을 지원하는 것이다. 각 메이저 버전에는 이전 버전의 코드를 자동으로 변환해주는 변환기가 함께 배포된다. 만약 1.x에서 3.x로 업데이트할 때는 2.x 변환기, 3.x 변환기를 순차적으로 실행해야 한다. 즉, 하위 버전에서 출발해 상위 버전에 도착하는 경로를 구해야 한다.

그런데 베타 버전(x.y-beta)과 알파 버전(x.y-alpha.z)을 고려해야 한다. 베타 버전에는 직전 버전에 대한 변환기가 있고, 알파 버전에는 변환기가 없다. 나이브하게 생각해보면 출발 버전 유형과 도착 버전 유형의 조합에 대해 모든 경우를 분기해서 경로를 구할 수 있을 것처럼 보인다.

세로로 정렬된 버전 업그레이드 흐름도. 가장 아래에 1.x가 있고, 위쪽으로 2.x, 3.x, 4.x가 순차적으로 배치되어 있다. 각 버전은 위를 향한 실선 화살표로 연결되어 있으며, 하위 메이저 버전에서 상위 메이저 버전으로의 직선적인 변환 경로를 나타낸다. 모든 노드는 연한 녹색 배경의 둥근 사각형으로 표시되어 있다.버전 계보를 나타내는 흐름도로, 변환히가 있는 안정 버전과 베타 버전, 알파 버전이 공간적으로 분리되어 배치되어 있다. 오른쪽에는 버전 흐름이 세로로 정렬되어 있으며, 1.x에서 2.x, 3.x, 4.x를 거쳐 5.x-beta로 업그레이드된다. 2.x에서 3.x-beta로, 3.x에서 3.x-alpha.0으로의 분기 경로가 명확히 표시된다. 왼쪽에는 알파 버전들이 독립적으로 배치되어 있으며, 2.x-alpha.0에서 2.x-alpha.1로 이어지는 체인과 2.x-alpha.k, 3.x-alpha.0이 포함된다. 실선과 곡선 화살표는 2.x 또는 3.x에서 각 알파 및 베타 버전으로의 파생 관계를 나타낸다. 녹색 노드는 변환기가 있는 버전, 흰색 노드는 변환기가 없는 버전을 의미한다.

그런데 실제로 이를 조건 분기로 구현해보면 상당히 복잡해진다. 더 일반화된 방법이 필요하다. 버전 관계를 다시 정의해보자. 변환기를 기준으로 생각해보면 모든 버전 사이에 순서가 있다고 할 수는 없다. 버전을 poset으로 모델링해보면 어떨까? a ≺ b 관계가 성립하는 경우에는 b의 변환기를 이용해 a를 마이그레이션할 수 있다. 이 관계가 성립하려면 (1) a가 안정 버전이고, (2) b가 알파 버전이 아니고, (3) major(a) < major(b)이어야 한다.

1

If you have a fediverse account, you can quote this note from your own instance. Search https://social.silicon.moe/users/parksb/statuses/115769680055349773 on your instance and quote it. (Note that quoting is not supported in Mastodon.)