1P by GN⁺ 15시간전 | ★ favorite | 댓글 1개
  • 과거 Ada 개발 환경에서는 코드 포매팅 문제를 이미 해결한 경험이 있음
  • 개발자들은 DIANA 중간 표현(IR) 형태로 코드를 다뤘고, 각자가 원하는 프리티프린팅 설정으로 뷰잉을 했음
  • 현대에도 linter나 포매팅 정책 등으로 반복되는 비효율과 논쟁이 존재함
  • 당시 Rational R1000 워크스테이션은 혁신적인 개발 환경과 기능을 제공함
  • Coding 포매팅 문제에서 한 세대 전 방식을 참고해 오늘의 개발 관행 변화를 위한 아이디어를 제안함

코드 포매팅 논쟁 – 1980년대의 해결책

  • 필자는 고등학교 시절 Ada 컴파일러 작업에 참여했던 컴퓨터 사이언스 교사, Mr. Paige와의 경험을 언급함
  • 2016년에 linter 툴 세팅에 불편을 호소하며 “이런 문제를 왜 여전히 겪어야 하냐”고 질문했을 때, 이미 40여 년 전에 이 문제가 해결된 경험을 소개받음

Ada와 DIANA의 등장

  • Ada 개발자들은 텍스트 소스코드를 저장하는 대신, DIANA(Descriptive Intermediate Attributed Notation for Ada) 라는 중간 표현을 이용함
  • 각 개발자는 자신만의 프리티프린팅 설정으로 소스를 원하는 방식으로 조회할 수 있었음
  • 포매팅 논쟁이나 linter 이슈가 존재하지 않았으며, 에디터에서는 프로그램 트리를 직접 수정할 수 있었음(현대의 projectional editing과 유사함)

Rational R1000 – 선구적 개발 환경

  • Rational R1000 워크스테이션은 증분 컴파일, 정적 분석, 버전 관리, 디버깅 등 다양한 고급 기능을 내장함
  • DoD 등 정부 프로젝트, 국제우주정거장(ISS), F-22 전투기 등 중요한 SW 개발에 활용되었으며 UML의 탄생에도 이바지함
  • Grady Booch에 따르면, R1000은 DIANA 기반의 머신으로 소스코드를 저장하지 않고 DIANA 트리의 프리티프린팅만을 사용함

DIANA 기반 개발의 이점

  • 포매팅 논쟁이나 linter 세팅, 에디터 환경 통일이 필요 없었음
  • 하드웨어 가속으로 증분 컴파일, 손쉬운 리팩토링(용어 미정), 빠른 통합 등 혁신적 개발 경험 제공
  • 개발 효율과 대형 시스템 작업에 중요한 영향을 끼쳤음

오늘날의 시사점

  • 하드웨어 가속 컴파일은 덜 중요하지만, 포매팅 문제 해결은 여전히 미흡한 상황임
  • 주류 방식이 projectional editing이나 라이브 환경은 아니지만, 과거의 접근법처럼 더 효율적이고 논쟁이 적은 개발 관행의 도입을 고민할 시점임

참고 자료

  • 필자는 이 주제를 조사하며 R1000 관련 다양한 문서와 기술 보고서를 인용함
Hacker News 의견
  • 나는 사람들이 왜 linter 세팅에 그렇게 신경 쓰는지 이해하지 못함, 이건 명백하게 쓸데없는 논쟁임 그냥 하나로 결정해서 자동으로 linter 돌리고 끝내면 됨 실제 소프트웨어 엔지니어링 할 시간이 더 중요하다고 느낌 어떤 포맷이든 팀에서 결정해서 쓰다 보면 일주일 내로 익숙해짐
    • 소스 코드 포매터와 lint 프로그램은 다르다는 점을 이야기함, 포매터는 코드 레이아웃만 맞추고, linter는 코드 내 가능성 있는 버그나 에러를 찾아줌 어떤 도구는 둘 다 지원하지만, 그건 구현 세부사항임 lint란 무엇인지에 대해서는 https://en.wikipedia.org/wiki/Lint_(software) 참고할 수 있음
    • 나는 대부분의 시간을 코드 "읽기"에 쓰고, 코드 레이아웃이 내가 코드를 읽는 속도와 관련 있음 다른 레이아웃에 익숙해질 순 있겠지만, 모든 레이아웃이 똑같이 읽기 쉬운 것은 아님 나는 코드를 시각적 패턴으로 기억하고, 레이아웃 기반으로 코드를 조각내서 읽음 역설적이게도 나는 마음 속 눈으로 시각화하지 못하는 aphantasia를 가지고 있음에도, 시각적 단서로 더 잘 기억함
    • 어떤 linter 세팅은 분명 장점이 있음 예를 들어, 테이블에 trailing comma를 쓰면 마지막 줄에 새로운 항목 추가할 때 한 줄만 편집하면 되고, diff도 더 간편해짐 그래서 잘못된 선택은 내 삶을 더 어렵게 만듦 정렬된 리스트나 include도 마찬가지로, 정렬 안 하면 항상 끝에 추가해서 머지 컨플릭트가 많이 발생함 자동 포매터의 장점처럼, 정렬로 시간도 절약해야 함 그리고 나는 일관성 없는 스타일에 민감함 한 가지 스타일로만 통일하는 게 제일 중요함
    • 세부 옵션에는 그렇게까지 신경 안 쓰지만, 공통 linter 설정으로 PR 접근 시 쓸데없는 노이즈를 줄이는 게 필수라고 생각함 git이나 각종 PL이 행 단위로만 처리하는 건 우아하지 않다고 느낌 Ada 언어처럼 더 세련된 방식을 쓴 예는 내 경력 20년 동안 한 번밖에 못 봤음 뭔가 진정 우아하고 효율적인 걸 만들기 어렵고, 이미 충분히 좋은 대안이 많으면 더더욱 널리 퍼지기 힘듦
    • 나도 어릴 때 이 논쟁에 빠져본 적 있음 왜 그랬냐면, 내가 팀에서 제일 똑똑하다고 착각했기 때문임 내 의견이 제일 중요하다고 생각했음 모두가 들어야 한다고 믿었음 근데 틀렸었음
  • 여기서 고려해야 할 트레이드오프는, 텍스트 외 다른 포맷을 쓰면 grep, diff, sed, 버전 관리 같은 범용 툴 사용성이 떨어짐 결국 특수한 툴이나 포맷, IDE 확장에 의존도가 올라감 유닉스 철학의 강점은 평문(Plain Text)을 통한 조합성이니까 이 논쟁을 쉽게 자르는 질문이 있음 : 에디터에서 처음 공백 너비만 자유롭게 세팅할 수 있으면, 탭을 선호하는 사람들의 논거가 더 있을지?
    • 텍스트 기반 접근이 장점이 있지만, 우리는 조금만 내려가다가 더 높은 산(프로젝셔널 에디팅)으로 갈 수 있었는데 못 가고 바닥에서 헤매고 있다고 느낌 예시들 모두 구조적 정보가 있는 코드에선 더 잘 동작함: 예를 들어, 심볼 검색(텍스트 grep보다 훨씬 자주 씀)은 ast-grep을 쓸 수 있고, diff는 semanticdiff.com처럼 구조적 이동이나 의미없는 변화 무시를 지원할 수 있음 sed의 대안으론 @codemod/cli 사용 가능 버전 관리도 Unison 같은 언어에서 비-의미 변화(순서, 공백 등)로 인한 컨플릭트 자동 회피로 실험이 많이 됨
    • 이 아이디어가 반복해서 등장하지만, 단순 포매터 한 번 돌리는 대신 엄청 복잡한 도구들이 필요해진다는 점에서 비용 대비 효과가 별로임 모든 개발자가 자신 입맛에 맞게 보는 게 실제론 큰 의미 없음 그동안 함께한 어떤 팀에서든 다 같이 규칙을 정하고 포매터로 강제 적용했는데, 처음엔 동의 안 해도 곧 익숙해짐 포매팅 논쟁은 대표적인 시간낭비임
    • grep, diff, sed, 줄 기반 머지 등은 사실 코드 조작에는 나쁜 툴임 이쪽 논쟁보다 더 나은 도구를 고민해야 한다고 생각함
    • 중간 표현(Intermediate Representation)을 텍스트로 해두면 grep/diff/sed 모두 작동 가능함 만약 AST 기반 포매터만 쓴다면, 코드는 특정 AST에 맞춰 정규화된 형태로 저장되고, 에디터는 AST 파싱 후 사용자가 원하는 포맷으로 보여준 뒤 저장 땐 정규화된 형태로 변환하면 됨
    • 운영체제 전체가 이런 소스 파일에 의존해서 찍어냈음 유닉스 철학도 모든 툴이 평문 텍스트만 고려하고 파싱할 줄 알아야만 진짜로 빛을 발함
  • 소스 코드 포매팅엔 타이포그래피적 요소도 분명히 있다고 생각함 무조건 개인 취향이라고 보는 주장엔 동의하지 않음 특정 포매팅은 의미와 구조를 효과적으로 전달하는 도구임 자동화 툴로 최소 토큰만 직렬화해서 다시 복원하면 이런 가치는 사라짐 https://naildrivin5.com/blog/2013/05/17/source-code-typograp...
    • 인쇄 전문가들은 표나 수식의 간격, 정렬을 위해 오랫동안 정성을 들였음 외부인은 잘 못 느끼지만, 이런 부분이 굉장히 중요했음 소스코드도 좀 더 정교하게 포매팅하는 발전이 가능할 거라고 생각함
    • 예를 들어, Python의 black formatter는 SQLAlchemy 쿼리를 지나치게 여러 줄로 쪼갠 결과 가독성이 오히려 떨어진다는 불만 있음
    • 코드의 타이포그래피에 대한 중요성을 왜 많은 사람들이 못 느끼는지 항상 의아했음
    • 타이포그래피에 신경 쓰면서, 한편으론 프로그래밍 언어 관습에 무비판적으로 따르는 건 가장 잘못된 방향 같다고 느낌, 예를 들어 register 같은 단어를 기꺼이 쓰는 문화가 있으면서, 포인터는 별표(*)로 표기하는 관습 등, 모든 표기 하나하나가 더 직관적이고 명확할 수 있는데 굳이 복잡하고 알아보기 힘든 방식을 고집함 기호나 예약어 역시 더 친숙하고 자연스러운 용어로 바꿔도 되는데, 전통적/기존 관례에 너무 집착해서 가독성을 희생하고 있다고 생각함 예시로 C 코드의 strcpy 함수도 더 명확하고 읽기 쉬운 용어와 문법으로 완전히 재구성 가능하다고 설명함
    • C에서의 parameter 선언이 modifiers, 데이터 타입, 이름으로 구성됨을 설명한 뒤, char argv[]와 같은 예시로 복잡한 선언의 가독성 문제를 꼬집음 그리고 C++ 스타일의 포매팅(예: char a, b)도 오해를 낳을 수 있다며, 그런 스타일은 피해야 한다고 생각함
  • 나는 이 논쟁의 전제에 동의하지 않음 코드 포매팅은 굉장히 중요한 커뮤니케이션 수단임 잘된 포매팅은 개발자가 (1) 포매팅의 중요성을 인지하고, (2) 규칙을 잘 지키고, (3) 좋은 취향과 (4) 예외 상황에도 잘 대응하는 판단력을 갖췄다는 시그널로 작용함 이 네 가지는 단순 포매팅 이상의, 개발 역량 전반에 영향을 미치는 중요한 능력임 그런데 오토포매터나 linter 룰이 이런 시그널을 약화시키고 Goodhart의 법칙처럼 원래 취지를 잃는 문제가 있음
    • 블로그 내용을 짧고 간단하니 꼭 직접 읽어보라고 권유함, 제목만 보고 즉각적인 반발을 거두지 말고, 'should'와 'unnecessary'란 단어의 맥락을 이해하면 좋겠음
    • 포매팅에 무관심한 사람은 (1) 여러 명이 한 파일을 편집한 경험이 없거나, (2) 브랜치 머지를 해 본 적이 없거나, (3) 대규모 코드베이스 유지보수 경험이 없거나, (4) 대규모 리팩토링 경험이 없거나, (5) 코드 히스토리를 발견하거나 diff/비교 툴을 활용해본 적이 없거나, (6) 코드베이스 자동화 도구 개발 경험이 없거나, (7) 협업 의식 없이 자기만 신경 쓰는 스타일이라고 생각함
    • 그 항목들 모두 '아니오'라면, 그냥 패스/커밋 시 포매팅만 자동화하고, 자동화 안 거치면 CI에서 에러나면 충분하다고 생각함 세부에 집착하는 것보다, 그냥 기본값에 맞추고, 개인 스타일을 포기하는 것도 오히려 장점이라고 생각함 기본값 그대로 두면 코드를 보는 누구에게나 익숙하게 느껴짐 게다가 포매팅과 린팅은 서로 다르지만, 둘 다 자동화 도구로 한 번에 해결 가능함
    • 동일한 평가를 펜글씨로도 배울 수 있다며, 농담삼아 앞으로는 PR을 필기체로 제출하게 해야겠다는 유머를 덧붙임
    • "좋은 포매팅 규칙을 선택할 취향"에 의존하는 건 오히려 논쟁만 불러오고 시간을 낭비하게 만들기 쉽다고 권유함 Go와 Rust처럼 내장 포매터에 맡기는 게 더 나음
  • 문법 트리 기반으로 코드를 저장하고, 사람이 보는 코드는 그걸 렌더링하는 형태로 다루는 시도가 20~25년 전부터 있었음 최초로 refactoring이 유행한 90년대부터 비롯된 흐름임 Visual Age 등의 IDE는 파일 시스템 대신 데이터베이스에 코드를 저장하는 모델을 도입한 바 있음 intentional programming이나 모델 기반 개발(모델드리븐 개발)도 이 사이클의 일부 refactoring의 본질은 AST(문법 트리) 변환임, 심볼명 변경이 아주 쉽고, 직접 소스 코드에서 찾아 바꿀 필요 없음 그런데 사람들은 여전히 파일 편집에 익숙하고, 구조 자체를 저장하는 접근이 대중화되는 데는 저항과 마찰이 있음 시간이 흐르고도 포매팅 논쟁을 계속하는 이유가 이 대안의 필요성을 보여주는 셈임 언어나 에디터 수준에서 robust한 symbol rename조차 제대로 제공하지 못하는 사례가 흔함
    • 추상화 레이어 혼합이라는 의견임 AST도 결국 파일에 저장돼야 하기에, 사람이 읽을 수 있는 파일에 대해 AST-aware한 툴을 돌리는 것과 크게 다를 바 없음 저장 포맷이 그렇게 중요하지 않고, 핵심은 더 똑똑한 툴의 문제임 Microsoft의 Roslyn 같은 사례나, 현대 컴파일러들이 API로 코드베이스와 상호작용하려는 방향성을 예시로 듬
  • 어떤 포매팅은 AST만으로 도출할 수 없다는 점을 직접 예를 듦 예를 들어, 여러 assignment 코드가 있을 때, 세 줄이 일렬로 정렬됐는지, '=' 기준으로 정렬됐는지, 또는 들여쓰기를 해서 구조의 깊이가 더 드러나도록 탭을 넣었는지 등이 모두 다름 값 자체를 강조하려면 숫자를 오른쪽 정렬, 구조 강조 땐 멤버 변수를 맞춰서 보기 쉽게 하는 등, 저자가 강조하고 싶은 코드의 측면이 다를 수 있음 이런 정보는 추가적인 메타데이터가 없으면 AST로 추출 불가라고 주장함
    • 무슨 말인지 알겠지만 실제로 첫 두 가지 방식 밖에 안 씀 실제 목적은 강조라기 보단 가독성이었음 AST로 변환할 때 손실되는 게 있지만 이 정도라면 얻는 게 훨씬 많다고 봄 게다가 AST에 남기는 식으로 강조하는 변형도 충분히 가능함 예를 들어 setValue([bar, glob], 1) 또는 스타일 오버라이드를 위한 주석 문법 등 다양하게 대응 가능함
    • '바람직한 코드 포매팅'은 결국 주관임, 예시처럼 어떤 사람은 2/4/8칸, 어떤 사람은 열 맞춤을 선호할 수 있음 AST는 소스코드의 포매팅 정보를 갖지 않으니 자동 도출이 불가능함
    • 두 번째, 세 번째 포매팅 예시는 실제로는 구조적 설계 문제(Law of Demeter 위반)라 포매팅의 범주가 아님
  • Projectional Editing은 텍스트 소스에도 적용 가능함 JetBrains MPS에서 코드에서 표를 렌더링하는 예시 영상도 존재 https://www.youtube.com/watch?v=XolJx4GfMmg&t=63s IDE에서 dictionary를 표로 렌더링해주는 기능을 바람 현재도 코드폴딩, 인레이 힌트, HTML 렌더링된 docstring 등 그 유사한 기능의 일부가 존재함 https://x.com/efortis/status/1922427544470438381
  • 우리가 plain text보다 더 추상적인 무언가를 지금 당장 받아들이지 못하는 한계가 있다고 정의함 시도해도 결국 plain text 프로젝션으로 다운그레이드됨 Morse Code에서 Unicode까지 누적된 '거대한 룩업테이블(GLUT)'이 현재의 상징 해독 문화를 낳았다고 봄 추상화 레벨을 올리면 애플리케이션엔 더 적합한 심볼세트가 만들어지지만, 이에 맞는 도구는 나오지 않음 결국 그냥 텍스트 전송으로 변환해서 파싱하게 됨 (CSV나 Markdown 등) XML도 전용 에디터가 나오곤 하지만, 사람들은 결국 plain text로 편집하고 싶어함 그러나 문자 인코딩 문제나 특수문자 때문에 완벽하게 긍정적이진 않음
  • 저장된 artifact와 우리가 실제 보는 코드 프로젝션이 왜 같아야 하는지 종종 의문이 생김 git diff조차 IR(중간표현) 프로젝션으로 확인할 수 있으면 좋겠음 treesitter 같은 AST 툴 등장 덕분에, 사람이 더 효율적으로 AST나 IR을 다루는 인터페이스를 상상해보게 됨 예로 f#의 ordered compilation 구조는 코드리뷰를 단순화하는데 도움됨 반면, 자유로운 순서를 허용하는 언어나 구조에선 작은 diff 하나 확인하려고 여러 곳을 오가며 변화의 전체 맥락을 파악해야 해 번거로움
  • eslint-config-airbnb 관련해서 불편한 점을 공유함 대표적인 이슈 #1271, #1122 기존 프로젝트에 airbnb config를 적용하려고 한 시간 넘게 삽질했고, 코드는 원래 완벽했음에도 불필요한 규칙 때문에 비생산적이었다고 느낌 결국 해당 규칙만 로컬에서 꺼버렸고, 이후 프로젝트엔 절대 쓰지 않음 이 예시는 잘못된 lint 룰이 얼마나 생산성을 망칠 수 있는지 보여줌