좋은 코드의 6계명: 시간이 지나도 변치 않는 코드 작성

게시 됨: 2022-03-11

인간은 약 반세기 동안 컴퓨터 프로그래밍의 예술과 과학과 씨름해 왔습니다. 대부분의 예술 및 과학과 비교할 때, 컴퓨터 과학은 여러 면에서 여전히 유아에 불과하며 벽으로 걸어 들어가고, 발에 걸려 넘어지고, 때때로 테이블 위에 음식을 던집니다.

상대적으로 젊었기 때문에 "좋은 코드"의 적절한 정의가 무엇인지에 대한 합의가 아직 이루어지지 않았다고 생각합니다. 그 정의가 계속해서 발전하고 있기 때문입니다. 어떤 사람들은 "좋은 코드"가 100% 테스트 커버리지를 가진 코드라고 말할 것입니다. 다른 사람들은 그것이 초고속이고 킬러 성능을 가지고 있으며 10년 된 하드웨어에서 충분히 실행될 것이라고 말할 것입니다.

이것들은 모두 소프트웨어 개발자들에게 칭찬할 만한 목표이지만, 저는 감히 그 혼합에 또 다른 목표인 유지보수 가능성을 던집니다. 특히 "좋은 코드"는 조직에서 쉽고 쉽게 유지 관리할 수 있는 코드이며(작성자뿐만 아니라!) 그것이 작성된 스프린트보다 더 오래 지속됩니다. 미국과 해외에서 대기업과 중소기업에서 엔지니어로 경력을 쌓았고 유지보수가 가능하고 "좋은" 소프트웨어와 상관관계가 있는 것 같습니다.

그냥 "작동"하는 코드에 안주하지 마십시오. 우수한 코드를 작성하십시오.
트위터

계명 #1: 다른 사람의 코드가 당신을 대우하기를 원하는 방식으로 당신의 코드를 대우하십시오

나는 당신의 코드에 대한 주요 청중이 컴파일러/컴퓨터가 아니라 다음 사람이 코드를 읽고, 이해하고, 유지하고, 향상시켜야 한다고 쓴 최초의 사람과는 거리가 멀다. ). 보수를 받을 가치가 있는 모든 엔지니어는 "작동하는" 코드를 생성할 수 있습니다. 뛰어난 엔지니어를 구별하는 것은 비즈니스를 장기적으로 지원하는 유지 관리 가능한 코드를 효율적으로 작성할 수 있고 간단하고 명확하고 유지 관리 가능한 방식으로 문제를 해결할 수 있는 기술을 보유하고 있다는 것입니다.

모든 프로그래밍 언어에서 좋은 코드나 나쁜 코드를 작성할 수 있습니다. 좋은 코드를 작성하는 데 얼마나 도움이 되는지(어쨌든 최상위 기준 중 하나 이상이어야 함)로 프로그래밍 언어를 판단한다고 가정하면 모든 프로그래밍 언어는 사용(또는 남용) 방식에 따라 "좋음" 또는 "나쁨"이 될 수 있습니다. ).

많은 사람들이 "깨끗한" 것으로 간주되고 읽을 수 있는 언어의 예는 Python입니다. 언어 자체는 어느 정도의 공백 규율을 시행하고 내장 API는 풍부하고 상당히 일관적입니다. 즉, 형언할 수 없는 몬스터를 생성하는 것이 가능하다. 예를 들어, 클래스를 정의하고 런타임 동안 해당 클래스의 모든 메서드를 정의/재정의/정의 취소할 수 있습니다(종종 원숭이 패치라고도 함). 이 기술은 기껏해야 일관되지 않은 API로 이어지며 최악의 경우 괴물을 디버그하는 것이 불가능합니다. 누군가는 순진하게 생각할 수 있습니다. "물론이지만 아무도 그렇게하지 않습니다!" 불행히도 그들은 그렇게 하며 API의 핵심으로 원숭이 패치를 광범위하게 사용하는 상당한(그리고 인기 있는!) 라이브러리를 만나기 전에 pypi를 탐색하는 데 오랜 시간이 걸리지 않습니다. 최근에 객체의 네트워크 상태에 따라 전체 API가 변경되는 네트워킹 라이브러리를 사용했습니다. 예를 들어 client.connect() 를 호출하고 때때로 HostNotFound 또는 NetworkUnavailable 대신 MethodDoesNotExist 오류가 발생한다고 상상해 보십시오.

계명 #2: 좋은 코드는 부분적으로나 전체적으로나 쉽게 읽고 이해할 수 있습니다

좋은 코드는 부분적으로나 전체적으로나 다른 사람이 쉽게 읽고 이해할 수 있습니다(미래의 작성자는 물론 "내가 정말 그렇게 썼나요?" 증후군을 피하기 위해 노력함).

"부분적으로"라는 말은 코드에서 일부 모듈이나 기능을 열면 나머지 코드베이스 전체를 읽을 필요 없이 그것이 하는 일을 이해할 수 있어야 한다는 의미입니다. 가능한 한 직관적이고 자체 문서화되어야 합니다.

코드베이스의 다른(겉보기에는 관련이 없어 보이는) 부분의 동작에 영향을 미치는 미세한 세부 사항을 지속적으로 참조하는 코드는 모든 문장 끝에 각주나 부록을 참조해야 하는 책을 읽는 것과 같습니다. 당신은 첫 페이지를 통과하지 못할 것입니다!

"로컬" 가독성에 대한 몇 가지 다른 생각:

  • 잘 캡슐화된 코드는 모든 수준에서 관심사를 분리하여 더 읽기 쉬운 경향이 있습니다.

  • 이름이 중요합니다. 두뇌가 생각을 형성하고 실제적이고 신중한 생각을 변수 및 메서드 이름에 넣는 빠른 생각과 느린 시스템 2 방식을 활성화합니다. 몇 초만 더 투자하면 상당한 이익을 얻을 수 있습니다. 이름이 좋은 변수는 코드를 훨씬 더 직관적으로 만들 수 있지만 변수 이름을 잘못 지정하면 가짜와 혼동을 일으킬 수 있습니다.

  • 똑똑함이 적입니다. 멋진 기술, 패러다임 또는 연산(예: 목록 이해 또는 삼항 연산자)을 사용할 때 코드가 더 짧을 뿐만 아니라 읽기 쉽게 만드는 방식으로 사용하도록 주의하십시오.

  • 일관성은 좋은 것입니다. 중괄호를 배치하는 방법뿐만 아니라 작업 측면에서도 스타일의 일관성은 가독성을 크게 향상시킵니다.

  • 우려의 분리. 주어진 프로젝트는 코드베이스의 다양한 지점에서 수많은 지역적으로 중요한 가정을 관리합니다. 코드베이스의 각 부분을 가능한 한 적은 수의 문제에 노출시키십시오. 사람 개체가 때때로 null 성을 가질 수 있는 사람 관리 시스템이 있다고 가정합니다. 사람 개체를 표시하는 페이지에 코드를 작성하는 사람에게는 정말 어색할 수 있습니다! 그리고 "우리 코드베이스가 가지고 있는 어색하고 분명하지 않은 가정"에 대한 핸드북을 유지하지 않는 한(나는 그렇지 않다는 것을 압니다) 디스플레이 페이지 프로그래머는 성이 null일 수 있고 아마도 null 포인터가 있는 코드를 작성할 것이라는 것을 알지 못할 것입니다. 성이 null인 경우는 예외입니다. 대신 코드베이스의 다른 부분이 서로 상호 작용하는 데 사용하는 잘 설계된 API 및 계약을 사용하여 이러한 경우를 처리합니다.

계명 #3: 좋은 코드에는 관리 상태를 명확하게 만들기 위해 잘 설계된 레이아웃과 아키텍처가 있습니다.

국가는 적입니다. 왜요? 모든 응용 프로그램에서 가장 복잡한 단일 부분이며 매우 신중하고 신중하게 처리해야 하기 때문입니다. 일반적인 문제에는 데이터베이스 불일치, 새 데이터가 모든 곳에서 반영되지 않는 부분적인 UI 업데이트, 순서가 잘못된 작업 또는 모든 곳에서 if 문과 분기가 있는 복잡한 코드로 인해 읽기 어렵고 코드 유지 관리가 더 어려워지는 마비될 정도로 복잡한 코드가 포함됩니다. 주의를 기울여 처리할 받침대에 상태를 놓고 상태에 액세스하고 수정하는 방법과 관련하여 매우 일관되고 신중하면 코드베이스가 크게 단순화됩니다. 일부 언어(예: Haskell)는 프로그래밍 및 구문 수준에서 이를 시행합니다. 외부 상태에 액세스하지 않는 순수 함수 라이브러리와 외부 순수 기능을 참조하는 상태 저장 코드의 작은 표면 영역이 있는 경우 코드베이스의 명확성이 얼마나 향상될 수 있는지 놀랄 것입니다.

계명 #4: 좋은 코드는 바퀴를 재발명하는 것이 아니라 거인의 어깨 위에 서 있습니다

잠재적으로 바퀴를 재발명하기 전에 해결하려는 문제가 얼마나 일반적인지 또는 수행하려는 기능이 얼마나 일반적인지 생각하십시오. 누군가가 이미 활용할 수 있는 솔루션을 구현했을 수 있습니다. 적절하고 가능한 경우 그러한 옵션에 대해 생각하고 조사할 시간을 가지십시오.

즉, 완전히 합리적인 반론은 종속성이 단점 없이 "무료"로 제공되지 않는다는 것입니다. 몇 가지 흥미로운 기능을 추가하는 타사 또는 오픈 소스 라이브러리를 사용하면 해당 라이브러리에 전념하고 의존하게 됩니다. 그것은 큰 약속입니다. 거대한 라이브러리이고 약간의 기능만 필요한 경우 예를 들어 Python 3.x로 업그레이드하는 경우 전체 라이브러리를 업데이트해야 하는 부담을 정말로 원하십니까? 또한 버그가 발생하거나 기능을 향상시키려는 경우 수정 또는 향상을 제공하기 위해 작성자(또는 공급업체)에 의존하거나 오픈 소스인 경우( 잠재적으로 상당한) 코드베이스에서 모호한 기능을 수정하거나 수정하려는 시도에 완전히 익숙하지 않습니다.

확실히 의존하는 코드를 더 잘 사용할수록 유지 관리에 시간을 투자해야 할 가능성이 줄어듭니다. 결론은 자신의 연구를 수행하고 외부 기술을 포함할지 여부와 해당 기술이 스택에 얼마나 많은 유지 관리를 추가할지에 대해 스스로 평가하는 것이 가치가 있다는 것입니다.

다음은 프로젝트에서 현대에 재창조하지 말아야 할 몇 가지 일반적인 예입니다(이것이 귀하의 프로젝트가 아닌 경우).

데이터베이스

프로젝트에 필요한 CAP를 파악한 다음 올바른 속성을 가진 데이터베이스를 선택하십시오. 데이터베이스는 더 이상 MySQL만을 의미하지 않으며 다음 중에서 선택할 수 있습니다.

  • "기존" 스키마 SQL: Postgres / MySQL / MariaDB / MemSQL / Amazon RDS 등
  • 주요 가치 저장소: Redis / Memcache / Riak
  • NoSQL: 몽고DB/카산드라
  • 호스팅된 DB: AWS RDS / DynamoDB / AppEngine 데이터 저장소
  • 헤비 리프팅: Amazon MR / Hadoop(Hive/Pig) / Cloudera / Google Big Query
  • 미친 물건: Erlang의 Mnesia, iOS의 핵심 데이터

데이터 추상화 계층

대부분의 경우 사용하기로 선택한 데이터베이스에 원시 쿼리를 작성하지 않아야 합니다. 대부분의 경우 DB와 애플리케이션 코드 사이에 라이브러리가 있어 동시 데이터베이스 세션을 관리하는 문제와 기본 코드에서 스키마 세부 정보를 분리합니다. 최소한 애플리케이션 코드 중간에 원시 쿼리나 SQL 인라인이 있어서는 안 됩니다. 오히려, 그것을 함수로 감싸고 정말로 명백한 파일(예: "queries.py")이라고 하는 파일에 모든 함수를 중앙 집중화하십시오. 예를 들어 users = load_users() 와 같은 줄은 users users = db.query(“SELECT username, foo, bar from users LIMIT 10 ORDER BY ID”) 보다 읽기가 훨씬 쉽습니다. 또한 이러한 유형의 중앙 집중화를 통해 쿼리에서 일관된 스타일을 유지하기가 훨씬 쉬워지고 스키마가 변경될 경우 쿼리를 변경하기 위해 이동할 위치의 수가 제한됩니다.

활용을 고려할 기타 공통 라이브러리 및 도구

  • 대기열 또는 Pub/Sub 서비스. AMQP 공급자, ZeroMQ, RabbitMQ, Amazon SQS 중에서 선택하십시오.
  • 저장. 아마존 S3, 구글 클라우드 스토리지
  • 모니터링: Graphite/Hosted Graphite, AWS Cloud Watch, New Relic
  • 로그 수집/집계. Loggly, Splunk

오토 스케일링

  • 자동 스케일링. Heroku, AWS Beanstalk, AppEngine, AWS Opsworks, Digital Ocean

계명 #5: 시냇물을 건너지 마십시오!

프로그래밍 디자인, pub/sub, 배우, MVC 등을 위한 많은 좋은 모델이 있습니다. 가장 좋아하는 것을 선택하고 고수하십시오. 서로 다른 종류의 데이터를 처리하는 서로 다른 종류의 논리는 코드베이스에서 물리적으로 격리되어야 합니다(다시 말하지만, 이러한 관심 분리 개념 및 미래 독자의 인지 부하 감소). 예를 들어 UI를 업데이트하는 코드는 UI에 들어갈 내용을 계산하는 코드와 물리적으로 구별되어야 합니다.

계명 #6: 가능하면 컴퓨터가 일을 하게 하라

컴파일러가 코드의 논리적 오류를 포착하고 잘못된 동작, 버그 또는 전면적인 충돌을 방지할 수 있다면 우리는 이를 절대적으로 이용해야 합니다. 물론 일부 언어에는 이를 다른 언어보다 쉽게 ​​만드는 컴파일러가 있습니다. 예를 들어 Haskell에는 프로그래머가 대부분의 노력을 코드를 컴파일하는 데 소비하게 하는 엄격한 컴파일러가 있습니다. 일단 컴파일되면 "그냥 작동합니다". 강력한 형식의 함수형 언어로 작성된 적이 없는 사용자에게는 이것이 우스꽝스럽거나 불가능해 보일 수 있지만 내 말을 그대로 받아들이지는 마십시오. 진지하게, 이러한 링크 중 일부를 클릭하면 런타임 오류가 없는 세상에서 사는 것이 절대적으로 가능합니다. 그리고 그것은 정말로 마술적입니다.

물론 모든 언어에 많은(또는 어떤 경우에는!) 컴파일 시간 검사에 적합한 컴파일러나 구문이 있는 것은 아닙니다. 그렇지 않은 경우 몇 분 정도 시간을 내어 프로젝트에서 어떤 선택적 엄격성 검사를 활성화할 수 있는지 조사하고 이것이 의미가 있는지 평가하십시오. 관대한 런타임을 가진 언어에 대해 최근에 사용한 몇 가지 일반적인 것들의 짧고 포괄적이지 않은 목록은 다음과 같습니다.

  • Python: pylint, pyflakes, warnings, emacs의 경고
  • 루비: 경고
  • 자바스크립트: jslint

결론

이것은 "좋은"(즉, 쉽게 유지 관리할 수 있는) 코드를 생성하기 위한 계명의 완전하거나 완벽한 목록이 결코 아닙니다. 그렇긴 하지만, 내가 미래에 선택해야 하는 모든 코드베이스가 이 목록에 있는 개념의 절반만 따랐다면, 나는 훨씬 더 적은 수의 백발을 갖게 될 것이고 내 인생이 끝날 때 5년을 더 추가할 수도 있을 것입니다. 그리고 확실히 일이 더 즐겁고 스트레스가 덜하다는 것을 알게 될 것입니다.