본문 바로가기
C.W.K.
Stream
Lesson 02 of 04 · published

이름 공간 둘, 반대 방향

~14 min · vocabulary, boundaries, data-integrity, design

Level 0흩어진 부품
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

같은 쌍, 두 번, 거꾸로

트랙 1 은 번역 함수로 끝났어. 설정 어휘가 들어가고 라우팅 어휘가 나오고, 건너는 자리에서만 적용되고 절대 되돌려 저장 안 하는 것. 그게 모든 앱을 깨뜨린 버그를 닫았지.

나중에 작업장들이 어느 두뇌가 일을 했는지 기록해야 했어. 출처 기록을 조회할 수 있게 철자가 하나여야 했고, 필요한 철자는 반대쪽이었어. 고르는 화면의 어휘, 사람이 알아보는 쪽. 그래서 두 번째 정규화가 생겼고, 라우팅 이름을 고르는 화면 이름으로 되돌려.

함수 둘. 같은 이름 쌍. 반대 방향. 그리고 이 가족의 규칙은 무뚝뚝해. 절대 합치지 마.

왜 이게 딱 정리할 거리처럼 보이나

둘을 처음 발견했다고 상상해봐. 같은 문자열 둘 사이를 매핑하는 함수 둘이, 같은 가족 안에, 둘 다 정규화-뭐시기라는 이름으로. 모든 본능이 이건 오타가 든 중복이고 처방은 방향을 하나 고르는 거라고 말해.

중복이 아냐. 서로 다른 질문에 답해.

  • 이 요청이 어느 엔드포인트로 가야 해? 답은 라우팅 어휘에 있어. 라우트 이름이 그거니까.
  • 이 기록이 누가 일했다고 적어야 해? 답은 고르는 화면 어휘에 있어. 기록을 읽는 사람이 알아보는 게 그거고, 설정 주인이 내놓는 것도 그거니까.

합치면 함수가 하나 사라지는 게 아니라, 둘 중 하나의 뜻이 바뀌어. 그리고 두 방향 다 이미 저장된 기록에 식별자를 써넣었어. 하나는 요청 로그에, 하나는 출처 행에. 그러니까 합병은 역사를 조용히 다시 해석해. 정리라는 깃발을 달고 도착하는 데이터 오염이야.

같은 값 집합 사이의 매핑 둘이 꼭 중복인 건 아냐. 합치기 전에 각각의 출력이 뭘 위한 건지 확인해. 시그니처가 똑같고, 구현이 거의 똑같고, 목적이 반대야. 어느 관례로든 이미 저장된 게 있으면 합병은 제일 나쁜 방식으로 되돌릴 수 없어. 크래시가 아니라, 이제 다른 뜻이 돼버린 기록 무더기.

이 구분이 살아남게 하는 법

방어는 주석이 아니라 이름이랑 위치야. 함수마다 정규화라는 일반 단어 말고 자기 목적지 어휘를 따서 이름 붙여. 라우팅 쪽이랑 고르는 화면 쪽이 호출 지점마다 둘 다 안 열어봐도 구별돼. 둘은 각 어휘를 소유한 관심사에 맞춰 다른 모듈에 살고. 그리고 각각이 자기 독스트링에, 일부러 반대로 돌고 절대 합치면 안 된다는 문장을 싣고 있어.

마지막 게 하중을 받는 부분이야. 결국 둘 다 발견할 사람은 아키텍처 문서를 읽고 있지 않을 거거든. 버그를 찾았다고 생각하는 그 순간에 둘 중 하나를 읽고 있을 거야.

Code

두 방향, 목적지를 따서 이름 붙인 것·python
# Two vocabularies, both legitimate, both owned by different concerns:
#
#   PICKER  : what a person selects and what a record should say
#             claude | chatgpt | gemini | grok | ollama
#   ROUTING : what the endpoints are actually named
#             claude | codex   | gemini | grok | ollama


# --- direction 1: routing. Lives with the chat client. -------------
_TO_ROUTING = {"chatgpt": "codex", "gpt": "codex"}


def routing_brain(brain: str) -> str:
    """PICKER -> ROUTING. Applied at the moment a settings value
    crosses into a chat route, and NEVER written back to settings:
    the picker namespace belongs to the settings owner.

    Runs in the OPPOSITE direction to `picker_brain` ON PURPOSE.
    Never merge the two - see that function's note.
    """
    return _TO_ROUTING.get((brain or "").strip().lower(),
                           (brain or "").strip().lower())


# --- direction 2: provenance. Lives with the queue kernel. ---------
_TO_PICKER = {"codex": "chatgpt", "gpt": "chatgpt"}


def picker_brain(brain: str) -> str:
    """ROUTING -> PICKER. Used when RECORDING which brain did work,
    so provenance rows are queryable under the name a person knows.

    Runs in the OPPOSITE direction to `routing_brain` ON PURPOSE.
    MERGING THEM IS A DATA MIGRATION, NOT A REFACTOR: rows already
    written under each convention would silently change meaning.
    """
    return _TO_PICKER.get((brain or "").strip().lower(),
                          (brain or "").strip().lower())


# The property that documents the relationship, and the one to keep
# in a test so nobody "simplifies" one of them into the other:
def test_the_two_directions_are_inverses_not_duplicates():
    assert routing_brain("chatgpt") == "codex"
    assert picker_brain("codex") == "chatgpt"
    assert routing_brain(picker_brain("codex")) == "codex"
    # and neither is the identity, which is what a merge would make
    # one of them become:
    assert routing_brain("chatgpt") != "chatgpt"
    assert picker_brain("codex") != "codex"

External links

Exercise

네 코드베이스에서 같은 값 집합 쌍 사이를 매핑하는 함수 둘을 찾아. 각각에 대해 그 출력이 뭘 위한 건지 한 문장으로 써. 문장이 다르면 둘 다 목적지를 따서 이름 바꾸고 합치지 말라는 메모를 각각에 붙여. 문장이 같으면 진짜 중복을 찾은 거야. 하나 지워.
Hint
저장이 승부를 갈라. 둘 중 어느 쪽 출력이든 저장소에 쓰인 적이 있으면, 코드가 그 뒤에 얼마나 깔끔해 보이든 합병은 이주야. 그리고 값은 코드에 있는 게 아니라 이미 쓰인 모든 행에 있어.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.