코드 품질은 단순히 실행되어 의도한 기능을 수행하는지를 넘어, 코드의 견고함을 의미합니다. 고품질 코드는 효율성, 유지보수성, 가독성, 재사용성으로 구분되며, 반대로 저품질 코드는 취약하고 해석하기 어렵고 시간이 지남에 따라 기술 부채가 누적되기 쉽습니다.
높은 코딩 표준은 소프트웨어 개발 과정에서, 상업용 주방 운영에서의 적절한 사전 준비와 "깔끔하게 작업하기"에 해당하는 개념입니다. 코드 품질을 높이는 실천은 단기적으로 더 나은 기능을 제공할 수 있지만, 장기적으로는 문제 감소, 개발 속도 향상, 유지보수 비용 절감이라는 더 큰 이점을 제공합니다.
고품질 코드의 장기적인 이점은 소프트웨어 개발 생명주기의 세부 사항에 익숙하지 않은 경영진에게 프로그래머가 설명하기 어려울 수 있습니다. 최적의 코드가 제공하는 전체적인 이점과 비즈니스 우선순위의 즉각적인 압박을 균형 있게 맞추는 일은 종종 복잡한 절충을 수반합니다. 그럼에도 불구하고 2022년에 39개의 독점 프로덕션 코드베이스를 대상으로 한 연구에서는 여러 결과 중 다음과 같은 내용을 제시했습니다1.
서둘러 작성된 코드로 인한 기술 부채는 개발자 시간의 최대 42%를 낭비합니다.
저품질 코드는 고품질 코드보다 결함을 15배 더 많이 발생시킵니다.
저품질 코드에서 문제를 해결하는 데는 평균적으로 고품질 코드보다 124% 더 많은 시간이 소요됩니다.
고품질 코드는 코드베이스를 이해하고, 리팩터링, 디버깅 및 새로운 기능 추가의 용이성과 속도를 높입니다. 명확하고 일관되며 잘 작성된 코드는 개발 팀 간의 협업을 더욱 원활하게 하고 코드 변경의 복잡성과 문제를 줄입니다. 이는 강력한 소프트웨어 품질뿐만 아니라 우수한 개발자 경험과 사용자 경험도 함께 이끕니다.
코드가 컴파일되고 런타임에서 목적을 성공적으로 수행하는지만으로는 전체적인 품질을 판단하기에 충분하지 않습니다. 코드를 작성하는 일은 작업을 올바르게 완료하는 단 하나의 방법만 존재하는 크로스워드 퍼즐을 푸는 것과는 다릅니다. 하나의 코딩 문제에는 수많은 해결 방법이 존재하는 경우가 많습니다. 따라서 기능성은 허용 가능한 코드의 단지 에 불과합니다. 고품질 코드의 가치는 그 주변 컨텍스트에 미치는 2차적인 효과에서 드러납니다.
가장 중요하고 흥미로운 AI 뉴스에 대한 선별된 인사이트를 확인하세요. 주간 Think 뉴스레터를 구독하세요. IBM 개인정보 보호정책을 참조하세요.
고품질 코드는 비교적 추상적인 개념입니다. 코드의 전체적인 품질은 특정한 개별적이고 객관적인 코드 품질 지표(이러한 지표는 많이 존재하지만)뿐만 아니라, 코드가 어떻게 작성되었는지와 더 큰 코드베이스 내에서 어떻게 상호작용하는지에 의해 결정됩니다.
고품질 코드의 일반적인 특성은 다음과 같습니다.
가독성: 코드 가독성은 유지보수, 디버깅 및 시간과 팀을 아우르는 협업에 필수적입니다. 다른 팀원이 코드를 쉽게 이해할 수 있나요? 몇 년 후 작업하는 다른 프로그래머가 별도의 설명 없이도 코드를 정확하게 해석할 수 있나요?
유지보수성: 코드 복잡성은 코드 유지보수성과 반비례하는 경우가 많습니다. 코드를 쉽게 테스트할 수 있어 팀이 효율적으로 보안 취약점 평가 및 최적화 기회를 평가할 수 있나요? 핵심 기능을 손상시키지 않고 새로운 기능을 추가할 만큼 충분히 견고한가요, 아니면 대규모 리팩터링 없이는 수정하기 어려울 정도로 취약한가요? 유지보수성을 우선시하면 초기에는 추가적인 수고가 들 수 있지만, 이후에는 상당한 시간과 에너지를 절약할 수 있습니다.
효율성: 잘 작성된 코드는 시스템의 지연 시간과 리소스 소비를 줄일 수 있습니다. 예를 들어, 신중하게 선택한 데이터 구조는 특정 기능을 수행하는 데 필요한 CPU 연산 횟수를 최소화할 수 있습니다. 신중하게 설계된 데이터 캐싱 전략은 중복된 데이터베이스 쿼리와 네트워크 요청을 제거하여 비용이 큰 입출력(I/O) 작업을 줄이고, 사용하지 않는 메모리를 즉시 해제함으로써 불필요한 RAM 증가를 방지합니다.
신뢰성: 결함이 적고 코드 변경에 견고한 구조는 장애와 다운타임의 빈도를 줄입니다. 신뢰성은 사용자 경험과 신뢰뿐만 아니라, 조직이 의존하는 핵심 시스템의 안정성에도 필수적입니다.
2023년 Harvard Data Science Review에 기고한 Calvin University, Amherst College, Columbia 소속 연구자들은 좋은 코드를 위한 규범적 프레임워크인 “The Four C’s.”를 제안했습니다2.
정확성: 코드는 의도한 대로 동작합니다. 저자들은 이 자명한 요소에 대해 두 가지 보조 명제를 강조했습니다. “첫째, 정확성은 좋은 코드를 위한 필수 조건이지만 충분 조건은 아닙니다. 둘째, 다른 목표들은 정확성을 뒷받침하고 강화합니다.”
명확성: 코드를 읽고 작성하는 누구나 코드의 의도를 이해할 수 있으며, 필요에 따라 직관적으로 수정할 수 있습니다.
포괄성: 코드의 확산, 중복, 불필요한 의존성을 피합니다. 적절한 포괄성은 무엇보다도 “재사용 가능한 코드를 함수로 묶고, 여러 파일이나 프로젝트에서 사용하는 코드를 모듈이나 패키지에 유지하는 것”을 포함합니다.
일관성: 코드베이스는 스타일, 네이밍 규칙, 주석, 들여쓰기 및 기타 관행에서 내부적인 일관성을 유지해야 합니다.
현대의 소프트웨어 개발이 에이전틱 코딩 어시스턴트인 IBM Bob과 같은 툴에 의해 점점 더 주도됨에 따라, 포괄성과 일관성은 자동화된 툴의 효율성과 정확성을 극대화하는 데 특히 중요합니다. 그럼에도 불구하고 IBM Bob과 같은 차세대 플랫폼이 실시간으로 AI 리팩터링 기회를 식별하는 기능은 이러한 수준의 스타일 규율을 유지하는 데 필요한 노력을 줄여줍니다.
각 프로그래밍 언어와 사용 사례마다 고유한 특성과 세부적인 고려 사항이 있지만, 어떤 상황에서도 적용 가능한 보편적인 코드 품질 모범 사례가 존재합니다.
고품질 코드를 이해하는 한 가지 방법은 이 글 후반에서 다룰 나쁜 코드의 특징을 최대한 피하는 코드로 생각하는 것입니다.
보다 규범적인 접근을 위해, 앞서 언급한 Harvard Data Science Review(HDSR) 논문은 코드 품질을 보장하기 위한 일련의 가이드라인을 제시합니다. 이 가이드라인은 표면적으로는 데이터 과학자의 요구에 최적화되어 있지만, 대부분은 모든 코딩 분야에 적용할 수 있습니다.
강력한 네이밍 규칙은 코드 가독성과 일관성에 필수적입니다. 저자들은 다음과 같은 실천 방안을 제시합니다.
이름의 길이는 그 범위에 비례해야 합니다. 용어의 최초 정의와 사용 사이의 거리가 시간, 코드 줄 수, 조직 구조 측면에서 클수록, 그 이름이 역할을 명확하게 전달하는 것이 더욱 중요합니다.
약어 목록을 유지하세요. 짧거나 한 글자 변수 이름은 코드를 간결하게 만드는 데 도움이 될 수 있지만, 프로젝트에 익숙하지 않은 사람에게는 이해하기 어려워질 수 있습니다. 일종의 “용어집”을 유지하면 이러한 상충 관계를 완화할 수 있습니다.
대문자 사용을 일관되게 유지하세요. 두 이름이 대소문자 차이로만 구분되는 경우는 피하는 것이 바람직합니다.
모호한 이름은 피하세요. 이는 코드 해석의 난이도를 크게 높입니다.
자연스럽게 정렬되는 파일 네이밍 규칙을 선택하세요. 예를 들어 날짜와 시간에는 ISO 8601 표준을 사용할 수 있으며, 숫자 앞에 0을 채워 모든 숫자의 자릿수를 동일하게 맞출 수 있습니다.
명확하고 일관된 네이밍 규칙은 AI 코딩 어시스턴트에 프롬프트를 작성하는 과정을 단순화하고, 출력의 정확도를 높이는 데에도 도움이 됩니다. 예를 들어 특정 유형의 변수를 검사하거나 특정 날짜 범위의 모든 파일을 탐색하도록 에이전트에 프롬프트를 작성하는 대신( 이 경우 AI 에이전트가 어떤 변수나 파일이 조건을 충족하는지 컨텍스트를 기반으로 확률적으로 추론해야 할 수 있습니다) 특정 숫자로 시작하는 모든 파일이나 특정 이름을 가진 모든 변수를 탐색하도록 명시적으로 프롬프트를 작성할 수 있습니다.
강력한 네이밍 규칙을 정립하는 것 외에도, 포괄적인 스타일 가이드는 공백과 들여쓰기 사용, 주석과 데이터 유형, 그리고 “코딩 방언”과 같은 서식 요소를 표준화해야 합니다. 다양한 프로그래밍 언어에 대해 검증된 다양한 스타일 가이드는 GitHub에서 확인할 수 있으며, 이 큐레이션 목록과 같은 자료가 있습니다.
스타일 가이드는 AI 코딩 어시스턴트에게도 중요한 참고 자료로, 특정 작업의 컨텍스트를 제공하거나 AI 에이전트의 시스템 프롬프트 일부로 활용될 수 있습니다.
툴킷과 라이브러리를 활용하는 것은 코드 재사용성과 효율성을 높이는 자연스러운 방법으로, 다양한 팀 간 워크플로와 결과를 표준화하고 코드 생성 속도를 전반적으로 높이는 데 도움을 줍니다. 코드가 복잡한 타사 시스템과의 상호작용을 중재하거나 반복적이고 이미 해결된 문제를 처리해야 할 때 특히 유용합니다.
그러나 툴킷에 지나치게 의존하면 불필요한 비대화를 초래하고 외부 의존성을 도입하여 코드의 견고성과 유지보수성을 저하시킬 수 있습니다. 또한 코드의 기본 로직을 추상화하여 가독성을 떨어뜨리는 경향이 있습니다. HDSR 저자들은 이상적인 균형을 다음과 같이 간단히 설명합니다. “우리는 툴킷을 가능한 한 단순하게 만들고 싶지만, 그보다 더 단순하게 만들고 싶지는 않습니다.”
동일한 코드 블록을 반복해서 복사, 붙여넣기 및 수정하고 있다면, 해당 코드를 하나의 함수로 묶는 것이 더 효과적일 수 있습니다. 그 함수의 매개변수는 각 상황마다 달라지는 요소를 반영할 수 있습니다. 이렇게 하면 코드를 정리하고 유지보수를 단순화할 수 있으며, 코드 블록의 모든 중복을 개별적으로 수정하는 대신 한 곳에서 한 번에 모든 인스턴스를 수정할 수 있습니다.
일관성 검사는 잠재적인 데이터 또는 시스템 상태가 사전에 정의된 논리 규칙과 규약을 준수하는지를 확인하는 자동화된 검증으로, 코드에서 예기치 않은 충돌과 모순을 방지하고 대응하는 데 도움이 됩니다. 이러한 자동화된 테스트는 일반적으로 CI/CD 파이프라인(지속적 통합/지속적 배포)의 표준적이고 핵심적인 구성 요소입니다.
이는 코드 테스트 가능성의 중요성을 잘 보여주는 대표적인 사례입니다. 코드가 지나치게 복잡하거나 강하게 결합된 의존성이 많다면, 모든 함수를 완전히 검증하는 단위 테스트를 설계하는 것은 어렵거나 불가능합니다.
버전 관리 시스템은 팀 간의 일관성, 품질 관리, 협업 및 코드 리뷰 프로세스를 촉진하는 데 도움이 됩니다. AI 기반 코딩 프레임워크를 사용할 때 (특히 코드베이스를 자동으로 수정할 수 있는 경우) 원치 않거나 부정적인 변경 사항을 쉽게 되돌릴 수 있는 수단을 반드시 확보해야 합니다. 예를 들어 IBM Bob은 워크스페이스 파일을 체크포인트로 자동 버전 관리하여 필요할 때 코드 변경 사항을 쉽게 되돌릴 수 있도록 합니다.
일반적으로 나쁜 코드는 읽기와 유지보수가 어렵고, 변경과 새로운 기능에 취약하며, 비효율적이고 신뢰성이 낮습니다. 이러한 코드는 종종 불필요한 의존성을 포함하며, 서로 다른 모듈이 얽혀 있어 하나를 변경할 때 다른 것을 깨뜨리지 않기 위해 추가 작업이 필요합니다. 적절한 문서화가 부족하고 구조가 잘 정리되어 있지 않으며 일관되고 논리적인 구조가 결여된 상태로, 흔히 “스파게티 코드.”라고 불립니다.
나쁜 코드는 단순히 코딩 역량 부족 때문만이 아니라, 잘못된 인센티브와 조직 구조의 결과이기도 합니다. 코드 품질을 희생하면서 기능 출시를 지나치게 우선시하면 시장 출시 속도는 빨라지지만, 향후 더 큰 문제와 기술 부채를 초래하게 됩니다.
나쁜 코드도 종종 작동한다는 점(적어도 일시적으로는)을 기억하는 것이 중요합니다. 만약 그렇지 않았다면 기술 부채가 쌓이지 않았을 것이며, 명백히 잘못된 코드는 반드시 수정되어야 하기 때문입니다. Martin Fowler와 Kent Beck이 1999년에 처음 출판하고 이후 여러 차례 개정한 대표적인 저서 Refactoring: Improving the Design of Existing Code에서는 이러한 나쁜 코드를 설명하기 위해 코드 스멜이라는 용어를 사용했습니다. 이는 일반적으로 버그는 아니며 프로그램의 동작을 직접적으로 방해하지는 않지만, 설계상의 약점과 코드 품질 문제를 나타내며 향후 개발 속도를 늦추거나 버그를 유발할 수 있습니다.
Fowler와 Beck이 제시한 주의해야 할 코드 스멜 목록에는 다음과 같은 예가 포함됩니다.
긴 함수(긴 메서드): 너무 많은 코드 라인을 포함하는 메서드입니다.
거대한 클래스: 너무 많은 역할을 수행하려 하고 변수도 과도하게 포함하여 응집성이 부족한 클래스입니다.
원시 타입 집착: 특화된 작은 객체 대신 원시 데이터 타입을 사용하는 경우입니다.
불분명한 이름: 함수나 변수 이름이 부적절하여 실제 의도를 숨기는 경우입니다.
데이터 뭉치: 여러 곳에서 항상 함께 나타나는 변수 그룹입니다.
게으른 요소: 존재할 만큼 충분한 역할을 하지 않는 클래스나 함수입니다.
긴 매개변수 목록: 올바르게 동작하기 위해 너무 많은 인수를 요구하는 함수입니다.
샷건 수술: 하나의 변경이 여러 흩어진 모듈을 동시에 수정해야 하는 경우입니다(본질적으로 거대한 클래스와 반대 개념입니다).
중복 코드: 여러 위치에 동일하거나 매우 유사한 코드 구조가 존재하는 경우입니다.
설명, 예시, 참고 문헌이 포함된 코드 스멜의 종합 목록은 여기에서 확인할 수 있습니다. 이러한 징후가 존재한다는 것은 일반적으로 리팩터링이 필요하다는 신호입니다. 이러한 문제와 그로 인해 발생하는 복잡성에 대해 조직 전반에서 충분히 이해하는 것은 개발 팀 전반에 걸쳐 공통된 품질 기준을 수립하는 데 도움이 됩니다.
코드 품질 측정은 항상 정성적 평가와 정량적 평가를 모두 포함해야 합니다. 순환 복잡도와 같은 객관적 지표는 유용할 수 있지만, 적절한 컨텍스트 없이 사용하면 오해를 불러일으킬 수 있습니다.
예를 들어 팀이 자동화된 테스트 스위트를 작성하고, 코드가 다양한 단위 테스트 전반에서 100% 코드 커버리지를 달성할 수 있습니다. 그러나 코드가 실제로 필요한 대로 완전히 작동하는지를 진정으로 검증하는 데 필요한 의미 있는 검증이 테스트 스위트에 부족하다면, 그 테스트 커버리지에서 오는 잘못된 확신은 오히려 해를 끼칠 수 있습니다.
마찬가지로, 견고한 리뷰 구조에는 수동 코드 리뷰와 AI 코드 리뷰가 모두 포함되어야 합니다. IBM Bob과 같은 최신 에이전틱 코딩 툴은 실시간으로 광범위한 정적 코드 분석과 리팩터링을 수행할 수 있지만, 개발자의 구체적인 요구와 의도를 전달하는 사용자 지정 규칙과 사용자 지정 모드를 통해 그 효과가 크게 향상됩니다. 인간은 완전하지 않고 AI도 오류가 없지는 않지만, 서로를 보완하는 것이 모든 잠재적 문제가 적절한 컨텍스트에서 검토되었음을 확인하는 가장 확실한 방법입니다.
코드 품질은 항상 컨텍스트에 따라 달라진다는 점을 기억해야 합니다. 팀의 한 프로그래머가 의도한 기능을 깔끔하게 수행하는 정교하고 효율적인 알고리즘이나 코드 블록을 작성했다고 가정해 보겠습니다. 만약 해당 문제를 모두가 이미 익숙한 표준 내장 라이브러리 함수로 효과적으로 해결할 수 있었다면, 그 정교한 코드는 오히려 불필요한 복잡성과 인지 부담을 추가하는 품질 문제입니다.
안전한 의도 인식 개발을 지원하는 AI 파트너인 Bob으로 소프트웨어 제공을 가속화하세요.
코드 작성, 디버깅, 코드 리팩토링 또는 코드 완성에 소요되는 시간을 최소화하고, 더 많은 시간을 혁신에 집중할 수 있도록 지원하는 신뢰할 수 있는 AI 기반 도구로 소프트웨어 개발 노력을 최적화하세요.
AI로 핵심 워크플로와 운영을 새롭게 혁신해 경험과 실시간 의사 결정, 비즈니스 가치를 극대화하세요.
1. “Code red: the business impact of code quality – a quantitative study of 39 proprietary production codebases,” Proceedings of the International Conference on Technical Debt (Association for Computing Machinery Digital Libtary를 통해 액세스), 2022년 8월 16일
2. “Fostering Better Coding Practices for Data Scientists,” Harvard Data Science Review, 2023년 7월 27일