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

종료 코드는 네가 던진 것보다 좁은 질문에 답해

~12 min · verification, measurement, media-tooling, assertions

Level 0불 꺼진 작업장
0 XP0/35 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

성공을 보고하고, 쓰레기를 내놓다

인코딩이 돌았어. 에러도 안 찍고 0 으로 끝났고. 나온 조각은 원래여야 할 길이의 대략 25 배였어.

고장 난 건 하나도 없었어. 명령은 특정 매개변수로 필터를 걸라는 요청을 받았고, 정확히 그걸 했지. 그 매개변수가 아무도 원하지 않은 길이를 만들어낸 것뿐이야. 종료 코드는 네가 지정한 연산을 내가 끝냈나에 답했어. 출력이 맞나보다 훨씬 좁은 질문이지. 그 둘을 헷갈리는 게, 이 규율 전체가 막으려고 존재하는 실패 양상이고.

절차 말고 산출물을 재

따라 나오는 규칙은 말하기 쉽고 지키기 좀 성가셔. 파일을 만드는 연산마다, 끝나면 그 파일을 재. 길이가 얼마여야 했는지 계산하지 말고 재봐. 네가 건 게인을 믿지 말고 음량을 분석해. 인코더가 시킨 대로 냈으려니 하지 말고 스트림을 세어봐.

구분은 의도 확인이랑 결과 확인 사이에 있어. 인자를 제대로 넘겼는지 확인하는 건 의도 확인이고, 대부분의 에러 처리가 실제로 하는 게 그거야. 산출물을 열어서 이게 뭐냐고 묻는 게 결과 확인이고, 시킨 걸 정확히 했는데 결과가 틀린 도구를 잡아내는 건 이쪽뿐이야.

문서도 같은 방향으로 거짓말해

같은 영역의 친척 함정 하나. 미디어 도구는 지금 돌리는 빌드엔 없는 기능을 있다고 보고할 수 있어. 도움말 목록이 정적인 표에서 필터를 뽑아 보여줄 수 있거든. 그래서 필터가 지원되는 걸로 나오는데, 실제 바이너리엔 그걸 구현한 라이브러리가 없어.

작업장의 대응은 기능 질문을 출력 질문처럼 다루는 거야. 문서를 읽지 말고, 진짜로 한 프레임 렌더를 돌려서 되는지 봐. 작은 습관인데 일반적인 형태가 있어. 시스템이 자기에 대해 보고할 수 있으면, 그 보고는 주장이야. 그리고 시험 대상이 하는 주장은 구할 수 있는 증거 중 제일 약한 거고.

에러가 없다는 데 걸지 말고 산출물에 걸어. "예외가 안 났다"는 빈 파일이랑도, 잘린 파일이랑도, 포맷이 틀린 파일이랑도, 멀쩡한 파일이랑도 다 양립해. 그 넷은 에러 처리 입장에선 구별이 안 되고, 측정 입장에선 어처구니없이 쉽게 구별돼. 출력이 중요한 파이프라인 단계라면, 자기가 뭘 만들었는지 보는 걸로 끝내야 해.

Code

의도 확인이랑 결과 확인의 차이·python
# INTENT ONLY - this is what most pipelines do
def encode(src: Path, dst: Path) -> Path:
    subprocess.run([FFMPEG, "-i", str(src), *PROFILE, str(dst)], check=True)
    return dst        # exit 0. we know nothing about dst.


# RESULT - open the artifact and ask what it actually is
def encode_checked(src: Path, dst: Path, expect_s: float) -> Path:
    subprocess.run([FFMPEG, "-i", str(src), *PROFILE, str(dst)], check=True)

    got = probe_duration(dst)                       # measured, not computed
    if abs(got - expect_s) > 0.05:
        raise BadEncode(f"{dst.name}: {got:.2f}s, expected {expect_s:.2f}s")

    streams = probe_streams(dst)                    # enumerated, not assumed
    if [s["codec_type"] for s in streams] != ["video", "audio"]:
        raise BadEncode(f"{dst.name}: unexpected stream layout {streams}")

    return dst

# The 25x-too-long clip passed the first version and fails the second
# on its first assertion - with the actual number in the message.
도구한테 뭘 할 수 있냐고 묻지 말고 시켜봐·bash
# WEAK: a help listing may be a static table compiled in,
# not a reflection of what this build can actually run.
ffmpeg -h filter=somefilter        # "supported" proves little

# STRONG: render one frame through it and see.
ffmpeg -v error -f lavfi -i color=c=black:s=64x64:d=1 \
       -vf "somefilter=..." -frames:v 1 -f null - \
  && echo "actually works in THIS build"

# Same principle as measuring output: a system's report about
# itself is a claim. Exercise the capability instead.

External links

Exercise

네 파이프라인에서 파일이나 레코드나 메시지를 만들면서, 지금은 '예외가 안 났으면 성공'으로 처리되는 단계를 하나 골라봐. 산출물 자체에 대한 측정을 하나 붙여. 크기든, 개수든, 체크섬이든, 다시 읽어보기든. 그리고 실패 메시지에 관측값을 담아서 단언해. 그다음 일부러 입력을 망가뜨려서, 네가 보고 행동할 수 있는 메시지로 그 단언이 터지는지 확인해.
Hint
출력이 제일 멀리 하류에서 쓰이는 단계부터 시작해. 조용한 결함을 추적하는 값이 제일 비싼 자리가 거기거든. 앞에서 만들어진 틀린 산출물이 끝에서 발견되면, 어디서 틀어졌는지 파이프라인 전체를 뒤져야 하니까.

Progress

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

댓글 0

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

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