어떤 경우에 코드가 적을수록 좋지 않습니까? [닫은]


55

나는 최근에 직장에서 일부 코드를 리팩터링했으며, 내가 잘했다고 생각했습니다. 980 줄의 코드를 450으로 떨어 뜨리고 클래스 수를 반으로 줄였습니다.

이것을 내 동료들에게 보여줄 때 일부는 이것이 개선이라는 데 동의하지 않았습니다.

그들은 "몇 줄의 코드가 반드시 더 나은 것은 아니다"고 말했다.

사람들이 정말로 긴 줄을 쓰거나 모든 것을 하나의 방법으로 작성하여 몇 줄을 저장하는 극단적 인 경우가있을 수 있지만 이것이 내가 한 것이 아닙니다. 내 생각에 코드는 크기가 절반이므로 이해하기 쉽고 체계적으로 구성되어 있습니다.

왜 누군가가 작업을 수행하는 데 필요한 코드의 두 배로 작업하고 싶어하는지 이해하기 위해 고심하고 있습니다. 누군가가 내 동료와 같은 느낌을 느끼고 더 적은 코드를 더 많이 사용하는 좋은 사례를 만들 수 있는지 궁금합니다. ?


145
코드 크기는 줄이나 문자 수가 아닌 코드를 읽고 이해해야하는 시간으로 측정됩니다.
Bergi

13
작성된 귀하의 질문은 너무 광범위합니다. 대신 변경 한 사항에 대한 새로운 내용을 작성하는 것이 좋습니다.
jpmc26

8
빠른 역 제곱근 알고리즘을 고려하십시오 . 변수의 이름을 적절하게 지정하여 완전한 Newton 방법을 구현하면 더 많은 코드 줄이 포함되어 있어도 훨씬 명확하고 읽기 쉽습니다. (이 특별한 경우 스마트 코드를 사용하는 것은 성능 문제로 정당화 될 수 있습니다).
Maciej Piechotka

65
codegolf.stackexchange.com : 귀하의 질문에 답변하기위한 전체 스택 교환 사이트가 있습니다 . :)
Federico Poloni

답변:


124

얇은 사람이 과체중 사람보다 반드시 건강하지는 않습니다.

980 줄의 어린이 이야기는 450 줄의 물리학 논문보다 읽기 쉽습니다.

코드의 품질을 결정하는 많은 속성이 있습니다. Cyclomatic ComplexityHalstead Complexity 와 같은 일부는 단순히 계산됩니다 . 응집력 , 가독성, 이해도, 확장 성, 견고성, 정확성, 자체 문서화, 청결도, 테스트 가능성 등과 같은 다른 것들도 더 느슨하게 정의됩니다 .

예를 들어 코드의 전체 길이를 줄이면서 불필요하게 복잡성을 추가하고 코드를 더 암호로 만들 수 있습니다.

긴 코드 조각을 작은 방법 으로 나누면 유익 할 수있는만큼 해로울 수 있습니다 .

리팩토링 노력이 바람직하지 않은 결과를 낳았다 고 생각하는 이유에 대한 구체적인 피드백을 동료에게 제공하도록 요청하십시오.


1
@PiersyP는 단지 FYI입니다. 좋은 리팩토링에 대해 제가 배운 지침 중 하나는 순환 복잡성 이 원래의 제곱근 으로 감소해야한다는 것 입니다.
MA Hanin

4
@PiersyP, 나는 또한 코드가 코드보다 나쁘거나 나쁘다고 말하지 않습니다. 외부인으로서 나는 정말 말할 수 없습니다. 또한 동료가 보수적이고 지나치게 변경 사항을 검토하고 검증하는 데 노력을 기울이지 않았기 때문에 변경 사항을 두려워 할 수도 있습니다. 그래서 추가 피드백을 요청하도록 제안했습니다.
MA Hanin

6
잘 하셨어요. 여러분은 어딘가에 "올바른"무게가 있다는 것을 알게되었습니다 (정확한 숫자는 다를 수 있음). @Neil의 원래 게시물조차도 "사람이 더 무거울 때"가 아니라 "과체중"이라고 말하며, 이는 프로그래밍과 마찬가지로 스위트 스폿이 있기 때문입니다. "올바른 크기"이상으로 코드를 추가하는 것은 어수선하고 그 이하의 행을 제거하면 간결성을 위해 이해력을 희생 할뿐입니다. 그 시점이 정확히 어디인지 아는 것은 어려운 일입니다.
AC

1
꼭 필요하지 않다고해서 가치가 없다는 의미는 아닙니다.
Chris Wohlert

1
@Neil 당신은 일반적으로 옳지 만 당신이 암시하는 피하기 어려운 "균형"은 객관적으로 말하는 신화의 것입니다 . 모든 사람들은 "좋은 균형"이 무엇인지에 대해 다른 생각을 가지고 있습니다. 분명히, OP는 자신이 좋은 일을했다고 생각했지만 그의 동료들은 그렇지 않았다. 그러나 나는 그들이 코드를 작성할 때 "올바른 균형"을 가졌다 고 스스로 확신했다.
code_dredd

35

흥미롭게도, 동료와 나는 현재 코드 라인이 동일하게 유지되지만 클래스와 함수의 수를 두 배 미만으로 늘리는 리 팩터의 중간에 있습니다. 좋은 예가 있습니다.

우리의 경우에는 실제로 2 개가되어야 할 추상화 계층이 1 개있었습니다. 모든 것이 UI 레이어에 들어갔습니다. 이를 두 개의 계층으로 나누면 모든 것이 더 응집력이 높아지고 개별 조각을 테스트하고 유지 관리하는 것이 훨씬 간단 해집니다.

동료를 괴롭히는 코드 의 크기 가 아니라 다른 것입니다. 그들이 그것을 표현할 수 없다면, 이전 구현을 본 적이없는 것처럼 코드를 직접보고 비교하기보다는 자체 장점으로 평가하십시오. 때로는 긴 리팩터링을 할 때 원래 목표를 잊어 버리고 너무 멀리 가져갑니다. 중요한 "큰 그림"을보고 조언을 소중히 여기는 페어 프로그래머의 도움을 받아 다시 제자리에 놓으십시오.


1
예, UI를 다른 것과 확실히 분리하면 항상 가치가 있습니다. 원래 목표를 잃어버린 것에 대한 귀하의 요점에 대해서는 어느 정도 동의하지만, 더 나은 것으로 또는 더 나은 길로 다시 디자인 할 수도 있습니다. 진화에 대한 오래된 논쟁 ( "날개의 일부가 무엇입니까?")과 같이, 개선을 위해 시간이 걸리지 않으면 상황이 개선되지 않습니다. 당신은 도중에 잘 때까지 어디로 가고 있는지 항상 알 수 없습니다. 나는 동료들이 왜 불안한지 알아 내려고 노력하는 데 동의하지만, 아마도 그것은 당신의 문제가 아니라 "그들의 문제"일 것입니다.

18

알버트 아인슈타인 (Albert Einstein)으로 여겨지는 인용구는 다음과 같습니다.

모든 것을 가능한 한 단순하게 만드십시오.

물건을 다듬을 때 선외로 가면 코드를 읽기가 더 어려워 질 수 있습니다. "쉬운 / 읽기 어려운"은 매우 주관적인 용어 일 수 있으므로, 이것이 의미하는 바를 정확하게 설명하겠습니다. 숙련 된 개발자가 "이 코드의 기능"을 결정하는 데 어려움이 얼마나 있을까요? 전문화 된 도구의 도움없이 소스 만 살펴보면됩니다.

Java 및 Pascal과 같은 언어는 자세한 설명으로 유명하지 않습니다. 사람들은 종종 특정 구문 요소를 가리키며 "컴파일러의 작업을 더 쉽게하기 위해 거기에 있습니다"라고 비난 적으로 말합니다. 이것은 "단지"부분을 제외하고는 다소 사실입니다. 명확한 정보가 많을수록 컴파일러뿐만 아니라 인간이 코드를 읽고 이해하기가 더 쉽습니다.

내가 말하면 정수라는 것이 var x = 2 + 2;즉시 분명합니다 x. 그러나 내가 말하면 var foo = value.Response;, 그것이 무엇을 foo나타내는 지 또는 그 속성과 기능이 무엇 인지 는 훨씬 명확하지 않습니다 . 컴파일러가 쉽게 추론 할 수 있어도 사람에게 훨씬 더인지적인 노력을 기울입니다.

사람들이 읽을 수 있도록, 그리고 기계가 실행할 수 있도록 프로그램을 작성해야합니다. (이론적으로,이 인용문은 극도로 읽기 어려운 악명 높은 언어로 쓰여진 교과서에서 나옵니다!) 중복되는 것을 제거하는 것이 좋습니다. 프로그램을 작성하는 데 꼭 필요한 것은 아니지만 진행 상황을 파악하십시오.


7
var시간 독서의 대부분이 코드를 이해하기 때문에 일반적으로 아무 것도 변경하지 않는 특정 변수의 실제 유형을 알고, 추상화의 특정 수준에서 행동을 알아내는 포함하기 때문에 예 단순화 특히 좋은 것이 아니다 (그것만 낮은 추상화를 이해하는 데 도움이됩니다). 예를 들면 - 더 좋은 예는 하나의 복잡한 문장들로 숙청 간단한 코드의 여러 줄 것 if ((x = Foo()) != (y = Bar()) && CheckResult(x, y)) grok 수 시간이 걸립니다, 그리고 유형을 알고 x하거나 y조금에 도움이되지 않습니다.
벤 Cottrell

15

더 긴 코드는 더 읽기 쉽습니다. 일반적으로 반대이지만 많은 예외가 있습니다. 그중 일부는 다른 답변에 요약되어 있습니다.

그러나 다른 각도에서 보자. 우리는 회사의 문화, 코드 기반 또는 로드맵에 대한 추가 지식 없이도 2 개의 코드를 보는 대부분의 숙련 된 프로그래머가 새 코드를 우수한 것으로 간주합니다. 그럼에도 불구하고 새로운 코드를 반대해야 할 많은 이유가 있습니다. 간결하게하기 위해 "새 코드를 비판하는 사람들" Pecritenc이라고합니다 .

  • 안정. 이전 코드가 안정적인 것으로 알려진 경우 새 코드의 안정성을 알 수 없습니다. 새 코드를 사용하려면 여전히 테스트해야합니다. 어떤 이유로 적절한 테스트를 사용할 수없는 경우 변경이 큰 문제입니다. 테스트가 가능하더라도 Pecritenc은 노력이 코드의 (사소한) 개선 가치가 없다고 생각할 수 있습니다.
  • 성능 / 확장. 이전 코드는 더 잘 확장되었을 수 있으며 Pecritenc은 클라이언트와 기능이 곧 쌓이면서 성능이 문제가 될 것이라고 가정합니다.
  • 확장 성. 이전 코드는 Pecritenc이 곧 추가 할 것으로 예상되는 일부 기능을 쉽게 소개 할 수있었습니다.
  • 정통. 이전 코드는 회사 코드베이스의 다른 5 개 장소에서 사용되는 재사용 패턴을 가질 수 있습니다. 동시에 새로운 코드는 회사의 절반 만이 시점에서 들어 본 멋진 패턴을 사용합니다.
  • 돼지에 립스틱. Pecritenc은 이전 코드와 새 코드가 모두 쓰레기이거나 관련이 없다고 생각할 수 있으며, 따라서 이들 코드를 비교할 때 무의미합니다.
  • 자부심. Pecritenc은 코드의 원작자 일 수 있으며 사람들이 코드를 크게 변경하는 것을 좋아하지 않습니다. 그는 개선이 가벼운 모욕으로 보일 수도 있습니다. 왜냐하면 그들이 더 잘했음을 암시하기 때문입니다.

4
'Pecritenc'의 경우 +1이며 사전 팩토링 전에 미리 고려해야 할 합리적 이의 제기에 대한 요약 요약입니다.

1
'확장 성'에 +1-원본 코드에 향후 프로젝트에 사용하기위한 함수 나 클래스가있을 수 있다고 생각했기 때문에 추상화는 중복되거나 불필요 해 보였지만 단일 프로그램의 맥락에서만 가능했습니다.
대런 벨소리

또한 문제의 코드는 중요 코드가 아닐 수 있으므로 코드를 정리하기 위해 엔지니어링 리소스를 낭비하는 것으로 간주됩니다.
Erik Eidt

@nocomprende 당신이 합리적이고 사전에 고려한 사전 요소를 사용한 이유는 무엇입니까? Pecritenc와 비슷한 방법일까요?
Milind R

@MilindR 아마도 선입견, 편견 또는 개인적 취향일까요? 또는, 아무 이유도없이, 공모 조건을 혼란스럽게하는 보조 인자들의 우주적 합류. 정말 모르겠습니다. 당신 은요?

1

어떤 종류의 코드가 더 좋은지는 프로그래머의 전문 지식과 그들이 사용하는 도구에 달려 있습니다. 예를 들어, 다음은 일반적으로 잘 작성되지 않은 코드로 간주되는 것이 상속을 최대한 활용하는 잘 작성된 객체 지향 코드보다 일부 상황에서 더 효과적인 이유입니다.

(1) 일부 프로그래머는 객체 지향 프로그래밍을 직관적으로 파악하지 못합니다. 소프트웨어 프로젝트에 대한 은유가 전기 회로라면 많은 코드 복제가 필요합니다. 많은 클래스에서 거의 동일한 메소드를 보려고합니다. 그들은 당신이 집에서 느끼게합니다. 그리고 부모 클래스 나 조부모 클래스에서 메소드를 찾아보아야하는 프로젝트가 진행 상황을 적대적으로 느낄 수 있습니다. 부모 클래스의 작동 방식을 이해하고 현재 클래스의 차이점을 이해하고 싶지 않습니다. 현재 클래스의 작동 방식을 직접 이해하려고하며 정보가 여러 파일에 혼동되어 있다는 사실을 알게됩니다.

또한 특정 클래스의 특정 문제를 해결하려는 경우 기본 클래스에서 직접 문제를 해결하거나 현재 관심있는 클래스의 메서드를 덮어 쓸지 여부를 생각하고 싶지 않을 수 있습니다. 상속 없이는 의식적인 결정을 내릴 필요가 없습니다. 기본값은 비슷한 클래스에서 비슷한 문제가 버그로보고 될 때까지 무시하는 것입니다. 반대.

(2) 일부 프로그래머는 디버거를 많이 사용합니다. 일반적으로 나는 코드 상속 측면에서 확고하고 복제 방지 측면에서도 확실하지만 객체 지향 코드를 디버깅 할 때 (1)에서 설명한 좌절감을 공유합니다. 코드 실행을 수행하면 동일한 객체에 남아 있어도 때때로 (조상) 클래스 사이를 계속 뛰어 다닙니다. 또한 잘 작성된 코드에서 중단 점을 설정하면 도움이되지 않을 때 트리거 될 가능성이 높아 지므로 조건부 (실제적인 경우)로 만들거나 관련 트리거 전에 수동으로 여러 번 계속하는 데 노력해야 할 수도 있습니다.


3
"조부모 수업"! haw haw! 아담과 이브 수업을 조심하십시오. (그리고 하나님의 수업은 물론) 그 전에는 형식이 없었으며 무효였습니다.

1

그것은 전적으로 달려 있습니다. 부울 변수를 함수 매개 변수로 허용하지 않지만 대신 enum각 옵션에 대한 전용이 필요한 프로젝트를 진행했습니다 .

그래서,

enum OPTION1 { OPTION1_OFF, OPTION1_ON };
enum OPTION2 { OPTION2_OFF, OPTION2_ON };

void doSomething(OPTION1, OPTION2);

보다 더 장황하다

void doSomething(bool, bool);

하나,

doSomething(OPTION1_ON, OPTION2_OFF);

보다 더 읽기 쉽다

doSomething(true, false);

컴파일러는 두 코드에 대해 동일한 코드를 생성해야하므로 더 짧은 형식을 사용하여 얻을 수있는 것은 없습니다.


0

응집력이 문제가 될 수 있다고 말하고 싶습니다.

예를 들어 웹 응용 프로그램에서 모든 제품을 색인하는 관리 페이지가 있다고 가정 해 봅시다. 모든 제품은 홈페이지 상황에서 사용하는 코드와 동일한 코드 (인덱스)입니다.

DRY를 유지하고 매끄럽게 유지하기 위해 모든 것을 부분 화하기로 결정한 경우 사용자 탐색이 관리자인지 아닌지에 관한 많은 조건을 추가하고 불필요한 내용으로 코드를 어지럽히면 읽을 수 없게됩니다. 디자이너!

따라서 이와 같은 상황에서는 코드가 거의 동일하더라도 다른 것으로 확장 될 수 있고 사용 사례가 약간 변경 될 수 있기 때문에 조건과 if를 추가하여 각각을 따라가는 것은 좋지 않습니다. 따라서 DRY 개념을 버리고 코드를 유지 관리 가능한 부분으로 나누는 것이 좋은 전략입니다.


0
  • 적은 코드가 더 많은 코드와 동일한 작업을 수행하지 않는 경우. 단순성을위한 리팩토링은 좋지만이 솔루션이 충족하는 문제 공간을 지나치게 단순화하지 않도록주의해야합니다. 980 줄의 코드는 450 개보다 많은 코너 케이스를 처리 할 수 ​​있습니다.
  • 더 적은 코드가 더 많은 코드만큼 정상적으로 실패하지 않는 경우. "불필요한"try-catch 및 기타 오류 처리를 제거하기 위해 코드에서 몇 가지 "ref *** toring"작업을 수행했습니다. 피할 수없는 결과는 오류와 사용자가 할 수있는 일, 앱이 다운되거나 YSODed에 대한 좋은 메시지가 담긴 대화 상자를 표시하는 것이 었습니다.
  • 적은 코드가 더 많은 코드보다 유지 관리 / 확장 성이 적을 때. 코드의 간결성을위한 리팩토링은 종종 LoC의 이익을 위해 "불필요한"코드 구성을 제거합니다. 문제는 병렬 인터페이스 선언, 추출 된 메소드 / 서브 클래스 등과 같은 코드 구성이이 코드가 현재 수행하는 것보다 더 많은 작업을 수행하거나 다르게 수행해야하는 경우 필요하다는 것입니다. 극단적으로 문제 정의가 약간 변경되면 특정 문제에 맞게 조정 된 특정 솔루션이 전혀 작동하지 않을 수 있습니다.

    일례; 정수 목록이 있습니다. 이러한 각 정수는 목록에서 하나를 제외하고 중복 값을 갖습니다. 알고리즘은 그 짝을 이루지 않은 값을 찾아야합니다. 일반적인 경우의 해결책은 목록에서 속이없는 숫자를 찾을 때까지 모든 숫자를 다른 숫자와 비교하는 것입니다. 이는 N ^ 2 시간 연산입니다. 해시 테이블을 사용하여 히스토그램을 작성할 수도 있지만 공간이 매우 비효율적입니다. 그러나 비트 XOR 연산을 사용하여 선형 시간과 상수 공간으로 만들 수 있습니다. 실행중인 "전체"(0으로 시작)에 대해 모든 정수를 XOR하고 마지막에 실행 합계는 짝을 이루지 않은 정수의 값이됩니다. 매우 우아합니다. 요구 사항이 변경되고 목록에서 둘 이상의 숫자가 쌍을 이루지 않거나 정수에 0이 포함될 수 있습니다. 이제 프로그램은 가비지 또는 모호한 결과를 반환합니다 (0을 반환하면 모든 요소가 쌍을 이루거나 쌍을 이루지 않은 요소가 0을 의미합니까?). 이는 실제 프로그래밍에서 "영리한"구현의 문제입니다.

  • 더 적은 코드가 더 많은 코드보다 자체 문서화가 적을 때. 코드 자체를 읽고 수행중인 작업을 결정하는 것은 팀 개발에 중요합니다. 주니어 개발자에게 아름답게 수행하는 brain-f *** 알고리즘을 제공하고 출력을 약간 수정하기 위해 조정하도록 요청하는 것은 그리 멀지 않습니다. 많은 선임 개발자들도 그 상황에 어려움을 겪을 것입니다. 주어진 시간에 코드가 무엇을하고 있는지, 무엇이 잘못 될 수 있는지 이해하는 것은 실무팀 개발 환경의 핵심입니다. 심지어 솔로이기도합니다. 파킨슨 병도 치료할 수있는 기능으로 돌아 왔을 때 암을 치료하는 방법은 오랫동안 사라질 것이다.)

0

컴퓨터 코드는 여러 가지 작업을 수행해야합니다. 이런 일을하지 않는 "미니멀리스트"코드는 좋은 코드가 아닙니다.

예를 들어, 컴퓨터 프로그램은 가능한 모든 (또는 최소한 가능한 모든 경우) 다루어야합니다. 하나의 코드가 하나의 "기본 사례"만 다루고 다른 하나는 무시한다면 코드가 간단하더라도 좋은 코드는 아닙니다.

컴퓨터 코드는 "확장 가능"해야합니다. 암호 코드는 하나의 특수 응용 프로그램에서만 작동 할 수 있지만 길지만 개방형 프로그램은 새 응용 프로그램을 더 쉽게 추가 할 수 있습니다.

컴퓨터 코드는 분명해야합니다. 다른 응답자가 시연 한 것처럼, 하드 코어 코더는 작업을 수행하는 한 줄의 "알고리즘"유형 함수를 생성 할 수 있습니다. 그러나 한 프로그래머는 일반 프로그래머에게 명확 해지기 전에 5 개의 "문장"으로 나눠야했습니다.


의무는 보는 사람의 눈에있다.

-2

계산 성능. 파이프 라이닝 또는 코드의 실행 부분을 병렬로 최적화하는 경우, 예를 들어 1에서 400으로 루프하지 않고 1에서 50으로 루프하고 각 루프에 유사한 코드의 8 개의 인스턴스를 배치하는 것이 좋습니다. 나는 이것이 당신의 상황에 해당한다고 가정하지는 않지만 더 많은 행이 더 나은 (성능 측면) 예입니다.


4
좋은 컴파일러는 일반 프로그래머보다 특정 컴퓨터 아키텍처에서 루프를 풀리는 방법을 잘 알고 있어야하지만 일반적인 요점은 유효합니다. 한 번 Cray 고성능 라이브러리에서 행렬 곱셈 루틴의 소스 코드를 살펴 보았습니다. 행렬은 세 개의 중첩 루프와 총 약 6 줄의 코드입니다. 잘못되었습니다-그 라이브러리 루틴은 약 1100 줄의 코드와 왜 그렇게 길 었는지 설명하는 비슷한 수의 주석 줄을 실행했습니다!
alephzero

1
@alephzero 와우, 나는 그 코드를보고 싶습니다 .Cray Cray 일뿐입니다.

@alephzero, 좋은 컴파일러는 많은 것을 할 수 있지만 슬프게도 모든 것을 할 수는 없습니다. 밝은면은 그것들이 프로그래밍을 계속 흥미롭게하는 것들입니다!
한스 얀센

2
@alephzero 사실, 좋은 행렬 곱셈 코드는 약간의 시간을 줄이지 않고 (즉, 일정한 인자로 줄입니다), 점근 적 복잡성이 다른 완전히 다른 알고리즘을 사용합니다. 예를 들어 Strassen 알고리즘 은 대략 O (n ^ 2.8) O (n ^ 3)보다는.
Arthur Tacca
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.