clean_graffity

프로그래밍은 단순히 로직을 작성하는 작업이 아닙니다. 개발자 간의 아이디어와 의도를 코드로 소통하는 효율적인 커뮤니케이션의 과정입니다.

명확하고 일관된 코드는 팀 협업을 원활히 하고, 유지보수 비용을 줄이며, 더 나은 제품으로 거듭나는 기반이 됩니다.

이번 포스팅에서는 클린 코드와 협업의 중요성을 엔지니어링 비용 관점에서 살펴보겠습니다.

프로그래밍은 커뮤니케이션이다.

위 문구를 여러 의미로 해석할 수 있습니다. 대부분은 다음과 같은 의미로 해석됩니다.

“코드를 통한 컴퓨터와의 소통”

이를 달성하기 위해선 여러가지 수단이 있습니다. Assembly부터 C, C++, Java, Python, JavaScript, Go, Rust, etc…

하지만, 대부분의 프로그래밍 팀은 문제를 해결하고 목적을 달성하기 위해 특정 프로그래밍 언어를 정하고 기술 스택을 정합니다.

컴퓨터와의 소통을 위해서라면 팀의 각자가 그 어떤 방법이던 상관없을 터, 어떤 이유일까요?

그 이유는 프로그래밍 언어와 그 기술 스택들을 통해 팀과 소통하기 때문입니다.

즉, 컴퓨터가 특정 프로그래밍 언어로 하여금 동작하도록 팀이 합의한 것입니다.

코드는 읽는 것이 핵심 - 코딩 시간의 80%는 코드 읽기

엔지니어링은 비용 대비 효과입니다. 여기서 비용은 시간도 포함됩니다.

소프트웨어 엔지니어링에서 가장 중요한 것은, 기존 코드의 “유지보수”입니다. 여기서 “유지보수” 행위의 80%가 코드를 읽는 행위입니다.

즉, 코드를 읽는 시간을 줄이면 가장 핵심인 “유지보수” 비용을 크게 줄일 수 있는 것입니다.

그렇다면 코드를 읽는 시간은 어떻게 줄일 수 있을까요? 간단히 2가지 원칙만 소개해보겠습니다.

DRY원칙 - 반복 최소화

아래 double이라는 비동기 함수를 여러번 실행시키는 두 코드를 살펴보겠습니다.

const double = (n: number): Promise<number> =>
  new Promise((res) => setTimeout(() => res(n * 2), 2000));

double(10)
  .then((r1) => double(r1))
  .then((r2) => double(r2))
  .then((r3) => console.log("Final Result (Promise):", r3))
  .catch(console.error);
const double = (n: number): Promise<number> =>
  new Promise((res) => setTimeout(() => res(n * 2), 2000));

async function doubleThirdTime() {
  try {
    const result = await double(await double(await double(10)));
    console.log("Final Result (Async/Await):", result);
  } catch (e) {
    console.error(e);
  }
};

doubleThirdTime();

분명 동일하게 작동하는 코드이지만, 그 형태는 완전하게 다릅니다.

읽으면 확실하게 알 수 있지만, 읽어야하는 수고 즉 비용이 발생합니다.

같은 기능의 코드라면 클린코드를 위한 DRY 원칙에 따라 굳이 다시 반복할 필요도 없고, 다시 읽어야할 필요도 없습니다.

선언형 코드 - 코드가 아닌 이름으로 소통한다

아래 코드를 살펴보겠습니다.

const numbers = [1, 2, 3, 4, 5, 6];
const results: number[] = [];

for (let i = 0; i < numbers.length; i++) {
  if (numbers[i] % 2 === 0) {
    results.push(numbers[i] ** 2);
  }
}

console.log("Results (Imperative):", results);

반복문을 구체적으로 정의해주고 있습니다. 여기서 읽고 이해해야되는 것들을 간단하게 정리해보겠습니다.

  • 반복문 index 변수는 무엇인지, 언제까지 어떻게 반복할지
  • 내부 코드의 내용 읽기
  • 코드의 내용을 바탕으로 코드의 의도 파악하기

이 코드는 반복문 정의를 모두 읽어야 반복문 내부 코드의 맥락을 이해할 수 있습니다.

그러고나서 내부 코드를 읽어 내용을 파악하고 그 맥락을 읽어 이 코드의 의도를 파악합니다.

이 코드는 꽤나 읽고 이해해야하는 부분이 많습니다.

이 코드를 리팩토링해보겠습니다.

const numbers = [1, 2, 3, 4, 5, 6];
const results = numbers.filter((n) => n % 2 === 0).map((n) => n ** 2);

console.log("Results (Declarative):", results);

반복문에 대한 정의 대신 filter라는 함수가 있습니다.

이 filter함수는 numbers라는 Array를 1개씩 iteration하겠다. 라는 의미를 전달하고 있습니다.

게다가 “filter”라는 이름으로 반복문 내부 코드의 의도까지 모두 전달하고 있습니다.

따라서, 우리가 이 코드에서 읽고 이해해야하는 것은 다음과 같습니다.

  • numbers Array 요소들이 어떻게 “filter”될 것인가.

즉, 오로지 반복문 내에서 실행되는 내용만 알면됩니다.

좋은 코드는 좋은 글과 같다. - 좋은 네이밍이 중요한 이유

코드를 잘 작성하기 위해 소통하는 것도 중요하지만, 코드 그 자체를 잘 작성하는 것도 중요합니다.

코드도 결국 특정 규칙에 맞춰 Text를 적는 행위이기 때문에, 좋은 코드는 좋은 글과 같다고 할 수 있습니다.

소설 『카라마조프 가의 형제들』은 철학적 깊이와 심리적 통찰로 유명하지만, 동일한 인물을 지칭하는 여러 대명사와 별명 때문에 난해한 소설로도 악명이 높습니다.

예를 들어, 주인공 중 한 명인 알렉세이(알료샤)는 상황과 맥락에 따라 다음과 같이 불립니다:

  • 알료샨카
  • 리오샤 (?)
  • 수도사 (?)
  • 순수한 사람 (?)
  • 카라마조프의 영혼 (?)
  • 묵묵한 심판자 (…?)
  • 등등등……

독자들은 이 이름들이 모두 동일한 인물을 가리킨다는 점을 이해하기 위해 끊임없이 맥락을 추론해야 합니다.

코드에서도 복잡한 소설과 동일하게 명확하지 않은 여러 대명사(혹은 변수명)가 혼란을 초래할 수 있습니다.

이런 혼란을 막고 읽기 쉬운 코드를 유지하기 위해 각 프로그램 언어들만의 컨벤션 네이밍들을 잘 따르고 도메인 용어들을 명확히 정의해야 합니다.

정리

  • 프로그래밍은 커뮤니케이션이다.

    코드는 컴퓨터와 팀원, 그리고 미래의 나와 소통하는 매체로, 명확성과 일관성이 핵심입니다.

  • 좋은 코드는 좋은 글과 같다.

    명확한 네이밍과 구조를 통해 코드의 의도를 쉽게 전달하며, 읽는 사람의 부담을 최소화해야 합니다.

더 자세한 내용은 아래 자료를 참고해주세요.

정말 좋은 책들이니 읽어보시길 추천드리겠습니다.

팀의 궁극적 목표 - 말하지 않아도 알아요

perfect_team

보통 빠르고 효율적으로 상황을 해결하는 팀은 눈빛과 손짓만으로 동료와 소통하고 말하지 않아도 서로의 역할을 수행하고 서로를 보완하는 팀입니다.

그들은 평소에 사인을 합의하고 수도없이 연습을 합니다. 그렇게 팀만의 패턴을 만들고 실전에서도 망설임 없이 신속하게 문제를 처리할 수 있습니다.

프로그래밍에서도 동일합니다. 패턴이 친숙해지면 직관적이면서 정확하고 빠르게 움직일 수 있습니다. (ref. 직관적 프로그래밍)

즉, 말하지 않아도 아는 찰떡궁합 팀이 되는 것입니다.

커뮤니케이션도 비용이다

모든 커뮤니케이션에는 비용이 따릅니다.

이는 시간, 에너지, 그리고 감정적 자원의 소모로 이어지며, 팀의 효율성을 낮출 수 있습니다. 예를 들어:

  • 코드 리뷰: 명확하지 않은 네이밍과 PR 설명으로 로직 설명을 위한 추가 논의로 시간이 낭비됩니다.
  • 회의: 많은 회의가 목적 없이 길어지면서, 핵심 업무 시간을 잡아먹습니다.
  • 메시지 교환: 중요하지 않은 메시지가 계속 전달되면, 집중력을 방해하며 업무 전환 비용(context switching)이 발생 합니다.
  • 불필요한 설명: 팀의 기본 원칙이 합의되지 않으면 매번 비슷한 내용을 설명하거나 논의해야 하는 비효율이 발생합니다.

이런 커뮤니케이션 비용은 팀의 속도를 늦추고, 더 중요한 비즈니스 임팩트에 집중하지 못하게 만듭니다.

합의된 원칙의 힘

“말하지 않아도 통하는 팀”은 공통된 원칙과 패턴에 의해 움직입니다.

공통된 원칙과 패턴으로 불필요한 설명을 줄이고, 핵심적인 일에 더 많은 자원을 투자할 수 있습니다.

  • 공통의 규칙과 기준
    • 코드 리뷰 규칙, 브랜치 네이밍 방식, CI/CD 파이프라인 구성 등 팀 내 기본 원칙을 명확히 정의합니다.
    • 모든 팀원이 이 원칙을 따르도록 교육하고 습관화하는 것이 중요합니다.
  • 명확한 R&R
    • 각자의 권한과 역할이 명확히 정해져 있다면, 추가적인 지시 없이도 자신의 책임을 알고 움직일 수 있습니다.
    • 팀원들은 서로의 강점과 약점을 이해하고, 필요할 때 자연스럽게 서로를 보완합니다.
  • 도구와 자동화
    • 린트 규칙, 자동화된 테스트, 배포 파이프라인 등 불필요한 커뮤니케이션을 줄이는 도구와 자동화 시스템을 구축합니다.

원칙은 최소한으로

원칙의 힘은 강력하지만, 지켜야 하는 게 많아질수록 커뮤니케이션 비용도 증가합니다.

따라서, 최소한의 정말 중요한 원칙만을 설정하고 그 원칙을 지키는 것이 중요합니다.

혹은, 합의된 원칙을 자동화해 커뮤니케이션 비용을 줄이는 것도 좋은 방법입니다.

정리

아래는 포스팅의 주요 내용을 정리한 내용입니다.

  • 코드의 본질: 타인에게 보여주기 위한 것
    • 그렇기에 클린코드 원칙을 지켜 항상 읽기 쉬우면서도 관리하기 좋은 코드를 상시 유지해야합니다.
    • 깨진 유리창의 법칙 : 더러운 코드는 더러운 코드를 부른다. 따라서 상시 깨끗하게 유지해야한다.
  • 불필요한 커뮤니케이션은 큰 비용이다.
    • 명확한 규칙과 기준을 설정해 불필요한 설명 비용을 줄이기 위해 노력해야합니다.
    • 정말 중요한 최소한의 원칙을 설정하고, 합의된 원칙을 자동화하여 커뮤니케이션 비용을 줄일 수 있습니다.
    • 자동화도구 활용: eslint, prettier, commitlint, husky

이 원칙들을 잘 실천해 불필요한 커뮤니케이션 비용을 줄이고 비즈니스 임팩트에 집중, 시장에 insight를 전달할 수 있는 멋진 팀이 되었으면 좋겠습니다.

ref