Skip to content

Latest commit

 

History

History
4148 lines (3293 loc) · 110 KB

README-koKR.md

File metadata and controls

4148 lines (3293 loc) · 110 KB

서론

롤 모델이 중요하다.
-- Officer Alex J. Murphy / RoboCop

나는 루비 개발자로서 항상 한 가지 문제로 괴로웠다. — 파이썬 개발자들은 훌륭한 프로그래밍 스타일 가이드(PEP-8)를 가지고 있는데, 우리에겐 코딩 스타일과 모범 사례를 가진 공식적인 가이드가 전혀 없다는 것이다. 그리고 나는 그것이야말로 문제라고 확신한다. 또한 루비의 훌륭한 해커 커뮤니티는 모두가 탐낼 만한 문서를 만들어낼 능력이 있다고 확신한다.

이 가이드는 사내 루비 코딩 가이드 라인으로 시작됐다. 그러나 몇몇 포인트에서 내가 작성한 가이드가 일반 루비 커뮤니티에도 도움이 되게끔 써서 이와 같은 중복 작업이 더는 없기를 바라며 작업하기로 했다. 그러나 루비 커뮤니티에 의해 자체적으로 수립된 예제, 숙어, 스타일에 관한 프로그래밍 방식 역시 많은 관련자에게 도움이 될 것이다.

가이드를 작성하기 시작한 때부터 본인은 여러 유수의 루비 커뮤니티로부터 소중한 피드백을 받았다. 여러 분들의 그러한 협력과 노고에 다시 한 번 감사의 말씀을 올린다. 많은 분들의 도움이 있었기에 가이드를 작성할 수 있었고, 그러한 상호작용을 통해 여타의 모든 루비 개발 관계자들에게 도움될 수 있으리라 본다.

한 가지 더, 레일즈에 관심 있는 사람이라면, 추가적으로 필요한 부분은 루비 온 레일스 스타일 가이드를 참고하면 된다.

루비 스타일 가이드

이 루비 스타일 가이드는 다른 루비 개발자와 유지가능한 코드를 작성하는 모범사례를 적용하는 것을 추천한다. 현실을 반영한 스타일 가이드는 사람들이 사용하지만, 사람들이 거부하는 이상을 추구하는 스타일 가이드는 아무도 사용하지 않게 될 수 있다. — 그게 아무리 좋다고 해도 말이다.

이 가이드는 관련된 규칙에 따라 여러 부분으로 나뉘어 있다.(자명하다고 판단된 것을 제외하고는)규칙을 적으며 근거를 덧붙이려 노력했다.

이 모든 규칙들이 갑자기 제시되지는 않았다. 이것들 대부분이 내 전문분야인 소프트웨어 엔지니어로서의 수많은 경험, 루비 커뮤니티원의 제안, 또한 높은 평가를 받고 있는 프로그래밍 리소스인 "Programming Ruby""The Ruby Programming Language"를 기반으로 하였다.

특정 스타일에 대해서 루비 커뮤니티의 명백한 합의를 얻지 못한 부분도 있다.(문자열 구문 따옴표, 해시문 내에서의 공백, 여러 줄에 걸친 메소드 체인에서의 점(.)위치 등) 이러한 시나리오에서는 모든 유명한 스타일들이 허용되므로, 어떤 것을 사용할지는 당신이 선택하고 적용하기 나름이다.

이 가이드 라인은 시간에 따라 발전한다. 새로운 규칙이 확인되고, 오래된 규칙은 루비가 변함에 따라 쓸모없어진다.

많은 프로젝트가 (대개 여기서 유래된) 그들만의 코딩 스타일 가이드 라인을 가진다. 상충하는 부분이 있을 경우, 그 프로젝트에선 프로젝트의 가이드 라인을 우선하라.

이 가이드는 Pandoc를 통해 PDF나 HTML로 복사해갈 수 있다.

RuboCop은 이 스타일 가이드에 기반한 코드 분석기다.

이 가이드의 번역은 다음의 언어들로 되어있다.

Table of Contents

소스 코드 레이아웃

거의 모든 사람들이 자신의 것을 제외한 모든 코딩 스타일이 번잡하고 가독성이 떨어진다고 확신한다. 앞 문장에서 "자신의 것을 제외한"을 없앤다면 아마도 맞는 말일지도...
-- Jerry Coffin (on indentation)

  • 소스 파일의 인코딩은 UTF-8을 사용하라. [link]

  • 탭은 들여쓰기 단위별로 공백 2칸을 사용하라.(소프트 탭 사용) 하드 탭 사용 안 함 [link]

    # 나쁜 예 - 공백 4칸
    def some_method
        do_something
    end
    
    # 좋은 예
    def some_method
      do_something
    end
  • Unix 스타일로 줄바꿈 하라.(*BSD/Solaris/Linux/OS X 사용자들은 기본으로 설정되어 있다. Windows 사용자에겐 특히 주의가 필요하다.) [link]

    • 만약 Git을 사용하고 있으면, 다음 설정을 추가함으로써 프로젝트가 Windows 줄바꿈 형식으로 강제 설정되는 것을 막을 수 있을 것이다.

      $ git config --global core.autocrlf true
  • 두 개 이상의 명령문과 표현식을 세미콜론(;)으로 나눠쓰지 말아라. 한 줄에 한 개씩 쓰는 것을 권장한다. [link]

    # 나쁜 예
    puts 'foobar'; # 불필요한 세미콜론
    
    puts 'foo'; puts 'bar' # 같은 줄에 표현식이 2개
    
    # 좋은 예
    puts 'foobar'
    
    puts 'foo'
    puts 'bar'
    
    puts 'foo', 'bar' # 이 경우는 puts가 두 변수 다 적용됨.
  • 본문이 없는 클래스는 한 줄 형식이 권장된다. [link]

    # 나쁜 예
    class FooError < StandardError
    end
    
    # 나쁘지 않은 예
    class FooError < StandardError; end
    
    # 좋은 예
    FooError = Class.new(StandardError)
  • 한 줄짜리 메소드를 작성하지 마라. 그런 방식이 비록 현장에서 많이 쓰이긴 하지만, 그러한 syntax 사용법이 익숙지 않아 좋아하지 않는 경우도 있기 때문이다. 어쨌든 한 줄에는 하나 이상의 메소드가 표현되어선 안 된다. [link]

    # 나쁜 예
    def too_much; something; something_else; end
    
    # 나쁘지 않은 예 - 처음의 ;(세미콜론)은 필요
    def no_braces_method; body end
    
    # 나쁘지 않은 예 - 두 번째의 ;는 선택사항
    def no_braces_method; body; end
    
    # 나쁘지 않은 예 - 문법은 맞지만, ;이 없으면 가독성이 떨어짐
    def some_method() body end
    
    # 좋은 예
    def some_method
      body
    end

    본문이 비어있는 메소드는 이 규칙에서 예외다.

    # 좋은 예
    def no_op; end
  • 연산자 전후, 콤마 뒤, 콜론과 세미콜론 뒤에 공백(space)을 써라. 공백은 루비 인터프리터에는 (대부분의 경우) 중요하지 않지만 적절하게 사용하면 읽기 쉬운 코드를 작성할 수 있다. [link]

    sum = 1 + 2
    a, b = 1, 2
    class FooError < StandardError; end

    유일한 예외는 지수 연산자다.

    # 나쁜 예
    e = M * c ** 2
    
    # 좋은 예
    e = M * c**2
  • (, [의 뒤, ], )의 앞에 공백을 쓰지 마라. {의 주변과 }의 앞에 공백을 써라. [link]

    # 나쁜 예
    some( arg ).other
    [ 1, 2, 3 ].each{|e| puts e}
    
    # 좋은 예
    some(arg).other
    [1, 2, 3].each { |e| puts e }

    {, } 전후의 공백은 구문을 명확하게 하기 위해 쓸 만한데, 그 이유는 문자열 보간법(string interpolation)을 사용할 때뿐만 아니라 블록이나 해시문에 사용될 수 있기 때문이다.

    해시문에서는 다음 두 가지 스타일이 허용된다. 첫 번째 방법이 조금 더 읽기 좋다.(그리고 루비 커뮤니티에서 확실히 더 인기가 있다.) 두 번째 방법은 블록과 해시를 시각적으로 구별화 수 있는 장점이 있다. 무엇이든 하나를 선택하면, 일관적으로 적용하자.

    # 좋은 예 - {뒤와 }앞에 공백
    { one: 1, two: 2 }
    
    # 좋은 예 - {뒤 와 }앞에 공백 없이
    {one: 1, two: 2}

    보간 표현식에서는 괄호 안에 패딩 공백이 없어야 한다.

    # 나쁜 예
    "From: #{ user.first_name }, #{ user.last_name }"
    
    # 좋은 예
    "From: #{user.first_name}, #{user.last_name}"
  • ! 뒤에 공백을 넣지 않는다. [link]

    # 나쁜 예
    ! something
    
    # 좋은 예
    !something
  • 레인지문에는 공백을 넣지 않는다. [link]

    # 나쁜 예
    1 .. 3
    'a' ... 'z'
    
    # 좋은 예
    1..3
    'a'...'z'
  • whencase와 같은 깊이로 들여 쓰자. "The RubyProgramming Language"와 "Programming Ruby"에서 사용하는 스타일이다. [link]

    # 나쁜 예
    case
      when song.name == 'Misty'
        puts 'Not again!'
      when song.duration > 120
        puts 'Too long!'
      when Time.now.hour > 21
        puts "It's too late"
      else
        song.play
    end
    
    # 좋은 예
    case
    when song.name == 'Misty'
      puts 'Not again!'
    when song.duration > 120
      puts 'Too long!'
    when Time.now.hour > 21
      puts "It's too late"
    else
      song.play
    end
  • 조건식의 결과를 변수에 대입하는 경우, 그 가지(case)의 일반적인 정렬을 유지하라. [link]

    # 나쁜 예 - 상당히 복잡함
    kind = case year
    when 1850..1889 then 'Blues'
    when 1890..1909 then 'Ragtime'
    when 1910..1929 then 'New Orleans Jazz'
    when 1930..1939 then 'Swing'
    when 1940..1950 then 'Bebop'
    else 'Jazz'
    end
    
    result = if some_cond
      calc_something
    else
      calc_something_else
    end
    
    # 좋은 예 - 어떻게 돌아가는지 분명함
    kind = case year
           when 1850..1889 then 'Blues'
           when 1890..1909 then 'Ragtime'
           when 1910..1929 then 'New Orleans Jazz'
           when 1930..1939 then 'Swing'
           when 1940..1950 then 'Bebop'
           else 'Jazz'
           end
    
    result = if some_cond
               calc_something
             else
               calc_something_else
             end
    
    # 좋은 예(가로 폭의 효율이 약간 더 좋음)
    kind =
      case year
      when 1850..1889 then 'Blues'
      when 1890..1909 then 'Ragtime'
      when 1910..1929 then 'New Orleans Jazz'
      when 1930..1939 then 'Swing'
      when 1940..1950 then 'Bebop'
      else 'Jazz'
      end
    
    result =
      if some_cond
        calc_something
      else
        calc_something_else
      end
  • 메소드 정의와, 단락이 논리적으로 구분될 때마다 사이에 빈줄을 넣어 분할하자. [link]

    def some_method
      data = initialize(options)
    
      data.manipulate!
    
      data.result
    end
    
    def some_method
      result
    end
  • 메소드 호출의 마지막 인수 뒤에 쉼표를 피한다. 인수가 여러 줄로 나누어져 있지 않을 때는 특히 피한다. [link]

    # 나쁜 예 - 쉽게 인수를 이동/추가/삭제할 수 있지만 여전히 권장하진 않는다.
    some_method(
      size,
      count,
      color,
    )
    
    # 나쁜 예
    some_method(size, count, color, )
    
    # 좋은 예
    some_method(size, count, color)
  • 메소드의 인수에 기본 값을 대입할 때에는, = 연산자 주변에 공백을 넣어라. [link]

    # 나쁜 예
    def some_method(arg1=:default, arg2=nil, arg3=[])
      # do something...
    end
    
    # 좋은 예
    def some_method(arg1 = :default, arg2 = nil, arg3 = [])
      # do something...
    end

    여러 루비 책들이 첫 번째 스타일을 권장하지만, 실제로는 두 번째 스타일이 좀 더 눈에 잘 띈다.(그리고 분명히 읽기 더 쉽다.)

  • 불필요하게 \로 줄을 바꾸는 것을 피하라. 실제로 프로그래밍할 때 긴 문자열을 자르는 경우 말고는 줄을 임의로 바꾸지 마라. [link]

    # 나쁜 예
    result = 1 - \
             2
    
    # 좋은 예(하지만 여전히 못생겼다.)
    result = 1 \
             - 2
    
    long_string = 'First part of the long string' \
                  ' and second part of the long string'
  • 메소드 연쇄가 여러 줄에 걸쳐서 이루어질 때, 일관된 스타일을 적용하라. 루비에서 여러 줄에 걸친 메소드 연쇄 표현 스타일은 크게 두 가지가 있는데, 둘 모두 괜찮은 방식이다. .의 위치에 따라 선두법(leading, A방식)과 후방법(trailing, B방식)이 있다. [link]

    • (A방식) 연쇄적인 메소드 호출이 서로 다른 줄에 걸쳐 연결되어 있을 때, 두 번째 라인에 .을 두라.

      # 나쁜 예 - 두 번째 줄을 이해하기 위해서 첫 번째 라인을 찾아야 함
      one.two.three.
        four
      
      # 좋은 예 - 두 번째 줄에서 어떤일이 일어나는지 즉시 알 수 있음
      one.two.three
        .four
    • (B방식) 연쇄적인 메소드 호출이 서로 다른 줄에 걸쳐 연결되어 있을 때, 이를 명확히 하기 위해 첫 번째 줄에 .을 포함시켜라.

      # 나쁜 예 - 메소드 연쇄가 이어지는지 알기 위해서 다음 줄을 읽을 필요가 있음
      one.two.three
        .four
      
      # 좋은 예 - 메소드 연쇄가 다음줄에서 이어지는지 한눈에 알 수 있음
      one.two.three.
        four

    양 쪽 스타일의 장점에 대한 논의는 여기에서 볼 수 있다.

  • 메소드 호출이 여러 줄에 걸쳐 일어날 경우 인수들을 적절히 정렬하라. 라인 길이 제한으로 인수들을 정렬하기 어려울 때에는 두 번째 인수부터 한 칸 들여쓰기를(single indent) 하되, 이 때 첫 번째 인수가 들여쓰기 이전에 줄바꿈이 없었는지 확인해야 된다.(아래 세 번째 예에서 to:'bob@example.com' (첫 번째 인수)의 줄이 넘어가면 네 번째 예처럼 작성하면 된다.) [link]

    # 처음 상태(줄이 너무 길다)
    def send_mail(source)
      Mailer.deliver(to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text)
    end
    
    # 나쁜 예(들여쓰기 두번)
    def send_mail(source)
      Mailer.deliver(
          to: 'bob@example.com',
          from: 'us@example.com',
          subject: 'Important message',
          body: source.text)
    end
    
    # 좋은 예
    def send_mail(source)
      Mailer.deliver(to: 'bob@example.com',
                     from: 'us@example.com',
                     subject: 'Important message',
                     body: source.text)
    end
    
    # 좋은 예(보통의 들여쓰기)
    def send_mail(source)
      Mailer.deliver(
        to: 'bob@example.com',
        from: 'us@example.com',
        subject: 'Important message',
        body: source.text
      )
    end
  • 여러 줄에 걸친 배열 요소들을 정리하라. [link]

    # 나쁜 예 - 단일 들여쓰기
    menu_item = ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam',
      'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']
    
    # 좋은 예
    menu_item = [
      'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam',
      'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam'
    ]
    
    # 좋은 예
    menu_item =
      ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam',
       'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']
  • 너무 큰 숫자는 밑줄을 이용하면 가독성이 좋아진다. [link]

    # 나쁜 예 - 0이 몇 개나 됩니까?
    num = 1000000
    
    # 좋은 예 - 사람이 해석하기에도 훨씬 쉽다.
    num = 1_000_000
  • 숫자 접두사는 소문자를 사용한다. 8진법에는 0o를, 16진법에는 0x를, 2진법에는 0b를 사용한다. 10진법 숫자에 0d 접두사를 사용하지 않는다. [link]

    # 나쁜 예
    num = 01234
    num = 0O1234
    num = 0X12AB
    num = 0B10101
    num = 0D1234
    num = 0d1234
    
    # 좋은 예 - 숫자와 접두사를 구분하기 쉽다.
    num = 0o1234
    num = 0x12AB
    num = 0b10101
    num = 1234
  • Rdoc과 API 문서의 컨벤션을 이용하라. def와 명령 블록 사이에 빈 줄을 넣지 마라. [link]

  • 한 행에는 80자까지만 쓴다. [link]

  • 줄 끝의 공백은 피한다. [link]

  • 모든 파일 끝에 줄바꿈을 넣는다. [link]

  • 블록 형태의 주석을 사용하지 마라. 앞에 공백이 들어가면 작동하지 않고, 일반 주석과 달리 쉽게 발견하기 어렵다. [link]

    # 나쁜 예
    =begin
    comment line
    another comment line
    =end
    
    # 좋은 예
    # comment line
    # another comment line

구문

  • ::는 상수(클래스나 모듈 포함)와 생성자(Array() 또는 Nokogiri::HTML() 같은)를 참조할 때만 사용하라. 일반 메소드 호출에서는 ::를 쓰지 마라. [link]

    # 나쁜 예
    SomeClass::some_method
    some_object::some_method
    
    # 좋은 예
    SomeClass.some_method
    some_object.some_method
    SomeModule::SomeClass::SOME_CONST
    SomeModule::SomeClass()
  • 매개변수가 있을 때 def와 괄호를 함께 사용하라. 받을 매개변수가 없을 때에는 괄호를 제거하라. [link]

    # 나쁜 예
    def some_method()
      # 내용 생략
    end
    
    # 좋은 예
    def some_method
      # 내용 생략
    end
    
    # 나쁜 예
    def some_method_with_parameters param1, param2
      # 내용 생략
    end
    
    # 좋은 예
    def some_method_with_parameters(param1, param2)
      # 내용 생략
    end
  • 메소드를 호출할 때 인자를 괄호로 감싼다. 특히 f((3 + 2) + 1)과 같이 첫 번째 인자가 여는 괄호(()로 시작할 때 주의한다. [link]

    # 나쁜 예
    x = Math.sin y
    # 좋은 예
    x = Math.sin(y)
    
    # 나쁜 예
    array.delete e
    # 좋은 예
    array.delete(e)
    
    # 나쁜 예
    temperance = Person.new 'Temperance', 30
    # 좋은 예
    temperance = Person.new('Temperance', 30)

    다음 경우에만 괄호를 생략한다.

    • 인자가 없는 메소드 호출:

      # 나쁜 예
      Kernel.exit!()
      2.even?()
      fork()
      'test'.upcase()
      
      # 좋은 예
      Kernel.exit!
      2.even?
      fork
      'test'.upcase
    • 내부 DSL을 구성하는 메소드(예: Rake, 레일스, RSpec):

      # 나쁜 예
      validates(:name, presence: true)
      # 좋은 예
      validates :name, presence: true
    • 루비에서 "키워드"로 취급되는 메소드:

      class Person
        # 나쁜 예
        attr_reader(:name, :age)
        # 좋은 예
        attr_reader :name, :age
      
        # 내용 생략
      end
      
      # 나쁜 예
      puts(temperance.age)
      # 좋은 예
      puts temperance.age
  • 선택적인 인자는 인자 목록의 마지막에 선언한다. 루비에서 선택적인 인자를 목록 앞쪽에 사용하면 의도치 않은 결과를 반환할 수 있다. [link]

    # 나쁜 예
    def some_method(a = 1, b = 2, c, d)
      puts "#{a}, #{b}, #{c}, #{d}"
    end
    
    some_method('w', 'x') # => '1, 2, w, x'
    some_method('w', 'x', 'y') # => 'w, 2, x, y'
    some_method('w', 'x', 'y', 'z') # => 'w, x, y, z'
    
    # 좋은 예
    def some_method(c, d, a = 1, b = 2)
      puts "#{a}, #{b}, #{c}, #{d}"
    end
    
    some_method('w', 'x') # => '1, 2, w, x'
    some_method('w', 'x', 'y') # => 'y, 2, w, x'
    some_method('w', 'x', 'y', 'z') # => 'y, z, w, x'
  • 변수를 선언할 때 병렬 대입(parallel assignment)을 피한다. 병렬 대입은 메소드 호출의 결과일 때, 스플랫 연산자와 사용할 때, 변수를 교환 대입할 때 사용한다. 병렬 대입은 일반 대입에 비해 가독성이 떨어진다. [link]

    # 나쁜 예
    a, b, c, d = 'foo', 'bar', 'baz', 'foobar'
    
    # 좋은 예
    a = 'foo'
    b = 'bar'
    c = 'baz'
    d = 'foobar'
    
    # 좋은 예 - 변수 교환 대입
    # 변수 교환 대입은 특별한 경우다.
    # 각 변수에 대입된 값을 교환할 수 있기 때문이다.
    a = 'foo'
    b = 'bar'
    
    a, b = b, a
    puts a # => 'bar'
    puts b # => 'foo'
    
    # 좋은 예 - 메소드 반환
    def multi_return
      [1, 2]
    end
    
    first, second = multi_return
    
    # 좋은 예 - 스플랫과 함께 사용
    first, *list = [1, 2, 3, 4] # first => 1, list => [2, 3, 4]
    
    hello_array = *'Hello' # => ["Hello"]
    
    a = *(1..3) # => [1, 2, 3]
  • 병렬 대입할 때 불필요한 뒤의 밑줄 변수를 피하라. 이름 있는 밑줄 변수는 문맥을 읽는데 도움이 되기 떄문에 밑줄 변수보다 권장된다. 뒤의 밑줄 변수는 대입의 왼쪽에 스프렛 변수가 정의 되고 스프렛 변수가 밑줄이 아닐 때만 필요하다. [link]

    # 나쁜 예
    foo = 'one,two,three,four,five'
    # 유용한 정보가 아닌 불필요한 대입
    first, second, _ = foo.split(',')
    first, _, _ = foo.split(',')
    first, *_ = foo.split(',')
    
    # 좋은 예
    foo = 'one,two,three,four,five'
    # 밑줄 변수는 마지막 밑줄 요소를 제외한 전 요소를 가져오고 싶다는걸 보여주기
    # 위해 필요하다.
    *beginning, _ = foo.split(',')
    *beginning, something, _ = foo.split(',')
    
    a, = foo.split(',')
    a, b, = foo.split(',')
    # 사용하지 않는 변수에 불필요한 대입이지만, 유용한 정보를 제공한다.
    first, _second = foo.split(',')
    first, _second, = foo.split(',')
    first, *_ending = foo.split(',')
  • for을 쓸 때에는 정확히 그 용법을 알고 있을 때에만 사용해야 한다. 대부분 반복자(iterator)가 for 대신 사용된다. for 구문은 each의 관점에서 실행되기 때문에 일종의 우회적인 방식을 사용하지만, each 용법과는 다른 점이 있다. 즉, foreach와는 다르게 새로운 영역을 생성하는 것이 아니기 때문에, for 구문 내부에서 정의된 변수들은 for 외부에서도 접근 가능하다. [link]

    arr = [1, 2, 3]
    
    # 나쁜 예
    for elem in arr do
      puts elem
    end
    
    # elem은 for문 밖에서도 접근 가능한 것에 주의
    elem # => 3
    
    # 좋은 예
    arr.each { |elem| puts elem }
    
    # elem을 each블록 밖에서는 접근할 수 없음
    elem # => NameError: undefined local variable or method `elem'
  • if/unless문의 내용이 여러 줄일 때에는 then을 쓰지 마라. [link]

    # 나쁜 예
    if some_condition then
      # 내용 생략
    end
    
    # 좋은 예
    if some_condition
      # 내용 생략
    end
  • 조건문의 내용이 여러 줄일 때, 조건식은 항상 if/unless와 같은 줄에 붙여 써라. [link]

    # 나쁜 예
    if
      some_condition
      do_something
      do_something_else
    end
    
    # 좋은 예
    if some_condition
      do_something
      do_something_else
    end
  • if/then/else/end 구문 보다, 삼항 연산자(?:)를 더욱 선호한다. 그게 좀 더 명쾌하고 간결하다. [link]

    # 나쁜 예
    result = if some_condition then something else something_else end
    
    # 좋은 예
    result = some_condition ? something : something_else
  • 삼항 연산자는 하나의 식마다 한 개의 표현식을 쓴다. 즉, 삼항 연산자는 중첩해서 써서는 안 된다. 그러한 경우는 if/else 구문을 사용하는 것이 좋다. [link]

    # 나쁜 예
    some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else
    
    # 좋은 예
    if some_condition
      nested_condition ? nested_something : nested_something_else
    else
      something_else
    end
  • if x; ...는 사용하지마라. 대신 삼항 연산자를 사용하라. [link]

    # 나쁜 예
    result = if some_condition; something else something_else end
    
    # 좋은 예
    result = some_condition ? something : something_else
  • ifcase은 결과를 반환하는 표현식이라는 사실을 이용하라. [link]

    # 나쁜 예
    if condition
      result = x
    else
      result = y
    end
    
    # 좋은 예
    result =
      if condition
        x
      else
        y
      end
  • 하나의 행의 경우에는 when x then ...을 이용하라. 대안인 when x:...는 루비 1.9에서 없어졌다. [link]

  • when x; ...문을 사용하지마라. 앞서 말한 규칙을 보라. [link]

  • not보다는 !를 사용하라. [link]

    # 나쁜 예 - 연산자 우선순위에 의해 소괄호가 요구됨
    x = (not something)
    
    # 좋은 예
    x = !something
  • !!의 사용은 피하라 [link]

    !!는 값을 boolean으로 만들지만, 제어 구문의 조건문에서 명시적으로 변환할 필요는 없으며 의도를 불명확하게 만들 뿐이다. nil 검사를 하고 싶다면 nil?을 사용하라.

    # 나쁜 예
    x = 'test'
    # 불분명한 nil 검사
    if !!x
      # 내용 생략
    end
    
    # 좋은 예
    x = 'test'
    if x
      # 내용 생략
    end
  • andor은 금지어다. 약간의 가독성 개선은 이로 발생하는 발견하기 어려운 미묘한 버그의 발생 가능성이 커지는 점에 비하면 그만한 가치가 없다. 대신에 boolean을 비교하고 싶다면 항상 &&||를 써라. 분기를 위해서라면 ifunless를 사용하라. &&||를 사용할 수도 있지만 상대적으로 가독성이 좋지 않다. [link]

    # 나쁜 예
    # boolean 식
    ok = got_needed_arguments and arguments_are_valid
    
    # 제어문
    document.save or fail(RuntimError, "Failed to save document!")
    
    # 좋은 예
    # boolean 식
    if some_condition && some_other_condition
      do_something
    end
    
    # 제어문
    fail(RuntimeError, "Failed to save document!") unless document.save
    
    # ok
    # 제어문
    document.save || fail(RuntimeError, "Failed to save document!")
  • 여러 줄의 조건문에는 ?:(삼항 연산자)를 피하라. 대신 if/unless를 사용하라. [link]

  • 구문이 한 줄일 때에는, 같은 줄 끝에 if/unless를 사용하는 것이 좋다. 또 다른 좋은 표현방법은 &&/||과 같은 제어문을 쓰는 것이다. [link]

    # 나쁜 예
    if some_condition
      do_something
    end
    
    # 좋은 예
    do_something if some_condition
    
    # 또 다른 좋은 예
    some_condition && do_something
  • 간단하지 않고 여러 줄에 걸친 구문 블록에는 한 줄 if/unless를 피하라. [link]

    # 나쁜 예
    10.times do
      # 내용 여러 줄 생략
    end if some_condition
    
    # 좋은 예
    if some_condition
      10.times do
        # 내용 여러 줄 생략
      end
    end
  • 중첩된 if/unless/while/until 조건의 사용을 피하라. 적절하게 &&/||를 사용하라. [link]

    # 나쁜 예
    do_something if other_condition if some_condition
    
    # 좋은 예
    do_something if some_condition && other_condition
  • if 뒤의 부정적인 조건보다는 unless(나 ||)가 더 좋다. [link]

    # 나쁜 예
    do_something if !some_condition
    
    # 나쁜 예
    do_something if not some_condition
    
    # 좋은 예
    do_something unless some_condition
    
    # 또 다른 좋은 방법
    some_condition || do_something
  • unless에는 else를 쓰지 마라. 긍정적인 경우가 앞에 오도록 다시 작성하라. [link]

    # 나쁜 예
    unless success?
      puts 'failure'
    else
      puts 'success'
    end
    
    # 좋은 예
    if success?
      puts 'success'
    else
      puts 'failure'
    end
  • 제어식의 조건 앞뒤에는 괄호를 사용하지 마라. [link]

    # 나쁜 예
    if (x > 10)
      # 내용 생략
    end
    
    # 좋은 예
    if x > 10
      # 내용 생략
    end
  • while/until 문의 내용이 여러 줄일 때에는, while/until condition do를 쓰지 마라. [link]

    # 나쁜 예
    while x > 5 do
      # 내용 생략
    end
    
    until x > 5 do
      # 내용 생략
    end
    
    # 좋은 예
    while x > 5
      # 내용 생략
    end
    
    until x > 5
      # 내용 생략
    end
  • 구문이 한 줄일 때에는, 같은 줄 끝에 while/until를 쓰는 것이 좋다. [link]

    # 나쁜 예
    while some_condition
      do_something
    end
    
    # 좋은 예
    do_something while some_condition
  • while에 부정적인 조건을 쓰기보단 until을 쓰는 것이 좋다. [link]

    # 나쁜 예
    do_something while !some_condition
    
    # 좋은 예
    do_something until some_condition
  • 무한 반복문이 필요할 때에는, while/until보다 Kernel#loop를 쓰는 것이 더 좋다. [link]

    # 나쁜 예
    while true
      do_something
    end
    
    until false
      do_something
    end
    
    # 좋은 예
    loop do
      do_something
    end
  • 종결조건을 나중에 판정하는 반복문을 쓸 때, begin/end/until 또는 begin/end/while보다는 Kernel#loopbreak를 사용하라. [link]

    # 나쁜 예
    begin
      puts val
      val += 1
    end while val < 0
    
    # 좋은 예
    loop do
      puts val
      val += 1
      break unless val < 0
    end
  • 옵션들이 해시라면 가장 바깥 중괄호를 생략한다. [link]

    # 나쁜 예
    user.set({ name: 'John', age: 45, permissions: { read: true } })
    
    # 좋은 예
    user.set(name: 'John', age: 45, permissions: { read: true })
  • 내장된 DSL의 일부인 메소드들에 대해 바깥의 중괄호와 괄호를 생략한다. [link]

    class Person < ActiveRecord::Base
      # 나쁜 예
      validates(:name, { presence: true, length: { within: 1..10 } })
    
      # 좋은 예
      validates :name, presence: true, length: { within: 1..10 }
    end
  • 블록의 연산이 메소드 호출뿐이라면 프록 발동 단축(proc invocation shorthand)을 사용한다. [link]

    # 나쁜 예
    names.map { |name| name.upcase }
    
    # 좋은 예
    names.map(&:upcase)
  • 한 줄짜리 블록에서는 do...end보다 {...}가 권장된다. 여러 줄짜리 블록에 대해 {...}를 사용하는 것은 피하라.(줄이 많으면 항상 보기에 안 좋다.) "제어 흐름"과 "메소드 정의"에는 항상 do...end를 사용하라.(예. Rakefile이나 특정 DSL 내에서) 연속적(chaining)일 때는 do...end를 피하라. [link]

    names = %w[Bozhidar Steve Sarah]
    
    # 나쁜 예
    names.each do |name|
      puts name
    end
    
    # 좋은 예
    names.each { |name| puts name }
    
    # 나쁜 예
    names.select do |name|
      name.start_with?('S')
    end.map { |name| name.upcase }
    
    # 좋은 예
    names.select { |name| name.start_with?('S') }.map(&:upcase)

    어떤 사람은 {...}를 사용하는 여러 줄에 걸친 연쇄(chaining)다 보기에 괜찮다고 주장할 수도 있겠지만 정말 그러한지는 다음 사항들에 대해 자문해봐야 한다. 그렇게 작성된 코드가 가독성이 좋은가? 또 블록 내부의 내용이 깔끔하게 메소드로 추출 가능한가? 등에 대해서 말이다.

  • 다른 블록에 인수만 넘기는 블록문 쓰는 것을 피하기 위해, 명시적으로 블록 인수를 사용하는 것을 고려해보라. 블록이 proc으로 변하면서, 성능에 영향을 주는 것을 조심해야 한다. [link]

    require 'tempfile'
    
    # 나쁜 예
    def with_tmp_dir
      Dir.mktmpdir do |tmp_dir|
        Dir.chdir(tmp_dir) { |dir| yield dir }  # block just passes arguments
      end
    end
    
    # 좋은 예
    def with_tmp_dir(&block)
      Dir.mktmpdir do |tmp_dir|
        Dir.chdir(tmp_dir, &block)
      end
    end
    
    with_tmp_dir do |dir|
      puts "dir is accessible as a parameter and pwd is set: #{dir}"
    end
  • 제어문에 불필요한 return을 피하라. [link]

    # 나쁜 예
    def some_method(some_arr)
      return some_arr.size
    end
    
    # 좋은 예
    def some_method(some_arr)
      some_arr.size
    end
  • 불필요한 self를 피하라.(이건, self write accessor를 호출할 때나, 예약어 뒤에 오는 메소드, 또는 오버로드 가능한 연산자만 필요하다.) [link]

    # 나쁜 예
    def ready?
      if self.last_reviewed_at > self.last_updated_at
        self.worker.update(self.content, self.options)
        self.status = :in_progress
      end
      self.status == :verified
    end
    
    # 좋은 예
    def ready?
      if last_reviewed_at > last_updated_at
        worker.update(content, options)
        self.status = :in_progress
      end
      status == :verified
    end
  • 당연하지만 지역 변수와 메소드가 같은 것을 의미하지 않을 때, 지역변수로 메소드를 가리는 것을 피하라. [link]

    class Foo
      attr_accessor :options
    
      # 괜찮은 예
      def initialize(options)
        self.options = options
        # option과 self.option은 여기서 같다.
      end
    
      # 나쁜 예
      def do_something(options = {})
        unless options[:when] == :later
          output(self.options[:message])
        end
      end
    
      # 좋은 예
      def do_something(params = {})
        unless params[:when] == :later
          output(options[:message])
        end
      end
    end
  • 배정문이 괄호 안에 싸인 경우를 제외하고는, 조건식에서 =(배정 연산자)를 써서 값을 반환하지 마라. 이것은 조건문에서 안전하게 대입하기라 불리는 루비 사용자 사이에서는 상당히 유명한 관용 표현이다. [link]

    # 나쁜 예(+ 주의)
    if v = array.grep(/foo/)
      do_something(v)
      # 생략
    end
    
    # 좋은 예(MRI would still complain, but RuboCop won't)
    if (v = array.grep(/foo/))
      do_something(v)
      # 생략
    end
    
    # 좋은 예
    v = array.grep(/foo/)
    if v
      do_something(v)
      # 생략
    end
  • 가능하면 생략된 스타일의 자체 대입 연산자를 사용하라. [link]

    # 나쁜 예
    x = x + y
    x = x * y
    x = x**y
    x = x / y
    x = x || y
    x = x && y
    
    # 좋은 예
    x += y
    x *= y
    x **= y
    x /= y
    x ||= y
    x &&= y
  • 초기화가 안된 변수를 초기화 할 때에는 ||=를 사용하라. [link]

    # 나쁜 예
    name = name ? name : 'Bozhidar'
    
    # 나쁜 예
    name = 'Bozhidar' unless name
    
    # 좋은 예 - name이 nil이나 false가 아니면 'Bozhidar'로 초기화한다.
    name ||= 'Bozhidar'
  • boolean 변수에 대해서는 ||=로 초기화하지 마라.(현재 변수가 false일 때, 무슨 일이 일어날지 생각하라.) [link]

    # 나쁜 예 - enabled가 false 일 때도 true로 대입
    enabled ||= true
    
    # 좋은 예
    enabled = true if enabled.nil?
  • 존재여부를 모르는 전처리 변수에는 &&=를 쓴다. &&=를 사용하면 값이 존재할 때만 값을 바꾸기 때문에 값 존재 여부를 확인하는 if는 없어도 된다. [link]

    # 나쁜 예
    if something
      something = something.downcase
    end
    
    # 나쁜 예
    something = something ? something.downcase : nil
    
    # 괜찮은 예
    something = something.downcase if something
    
    # 좋은 예
    something = something && something.downcase
    
    # 더 좋은 예
    something &&= something.downcase
  • case 동등(equality) 연산자 ===를 사용하는 것을 피하라. 왜냐하면 그 이름에서 말해주듯, 이 연산자는 암묵적으로 case의(동등 여부뿐만 아니라) 상태를 판별하는 데에 사용되도록 고안되었기 때문에, 외부에서 본다면 약간 혼란스러운 코드가 된다. [link]

    # 나쁜 예
    Array === something
    (1..100) === 7
    /something/ === some_string
    
    # 좋은 예
    something.is_a?(Array)
    (1..100).include?(7)
    some_string =~ /something/
  • ==로 할수 있을 때에는 eql?을 사용하지 마라. eql?이 제공하는 엄격한 비교는 대부분 필요 없다. [link]

    # 나쁜 예 - eql?은 문자열에서는 ==와 같음
    'ruby'.eql? some_str
    
    # 좋은 예
    'ruby' == some_str
    1.0.eql? x # Integer와 Float 1의 식별하기 위해 eql?을 사용하는 것은 괜찮다.
  • 펄 스타일의 특수 변수($:, $; 등) 사용을 피하라. 그들은 상당히 기괴해 보이고, one-liner 스크립트 외에서 사용된 경우에는 읽기 힘들다. English 라이브러리에서 제공하는 인간 친화적인 alias를 사용하라. [link]

    # 나쁜 예
    $:.unshift File.dirname(__FILE__)
    
    # 좋은 예
    require 'English'
    $LOAD_PATH.unshift File.dirname(__FILE__)
  • 메소드 이름과 괄호 사이에 공백을 넣지 마라. [link]

    # 나쁜 예
    f (3 + 2) + 1
    
    # 좋은 예
    f(3 + 2) + 1
  • 항상 -w 옵션과 함께 루비 인터프리터를 실행하라. 그러면 위의 규칙들을 잊어버렸을 때 경고할 것이다. [link]

  • 중첩 메소드 선언을 사용하는 대신, 람다를 사용한다. 중첩 메소드 선언은 사실 밖의 메소드와 같은 범위(예를 들어 클래스)에서 메소드를 생성한다. 더욱이, "중첩된 메소드"는 선언이 들어있는 메소드가 호출될 떄마다 재정의된다. [link]

    # 나쁜 예
    def foo(x)
      def bar(y)
        # 몸통 생략
      end
    
      bar(x)
    end
    
    # 좋은 예 - 위와 같지만, foo가 호출될 때마다 bar가 매번 재정의되지 않는다
    def bar(y)
      # 몸통 생략
    end
    
    def foo(x)
      bar(x)
    end
    
    # 좋은 예
    def foo(x)
      bar = ->(y) { ... }
      bar.call(x)
    end
  • 한 줄짜리 본문 블록에 새로운 lambda 구문을 사용하라. lambda 메소드는 여러 줄이 있는 블록에 써라. [link]

    # 나쁜 예
    l = lambda { |a, b| a + b }
    l.call(1, 2)
    
    # 맞지만, 보기에 매우 어색한 예
    l = ->(a, b) do
      tmp = a * 7
      tmp * b / 50
    end
    
    # 좋은 예
    l = ->(a, b) { a + b }
    l.call(1, 2)
    
    l = lambda do |a, b|
      tmp = a * 7
      tmp * b / 50
    end
  • 매개변수와 함께 새로운 람다(stabby lambda)를 사용할 때에는 매개변수 괄호를 생략하지 않는다. [link]

    # 나쁜 예
    l = ->x, y { something(x, y) }
    
    # 좋은 예
    l = ->(x, y) { something(x, y) }
  • 매개변수 없이 새로운 람다(stabby lambda)를 사용할 때에는 매개변수 괄호를 생략한다. [link]

    # 나쁜 예
    l = ->() { something }
    
    # 좋은 예
    l = -> { something }
  • Proc.new보다는 proc을 권장한다. [link]

    # 나쁜 예
    p = Proc.new { |n| puts n }
    
    # 좋은 예
    p = proc { |n| puts n }
  • lambda나 proc에서 proc[]이나 proc.()보다 proc.call()을 권장한다. [link]

    # 나쁜 예 - Enumeration 접근과 비슷해 보임
    l = ->(v) { puts v }
    l[1]
    
    # 역시 나쁜 예 - 잘 쓰지 않는 구문
    l = ->(v) { puts v }
    l.(1)
    
    # 좋은 예
    l = ->(v) { puts v }
    l.call(1)
  • 사용하지 않는 블록 인수나, 지역변수에는 _를 앞에 붙여라.(설명이 좀 없더라도) _만 쓰는 것도 가능하다. 이 컨벤션은 루비 인터프리터와 Robocop과 같은 툴에 의해 인지되고, 사용하지 않는 변수에 대한 경고를 숨긴다. [link]

    # 나쁜 예
    result = hash.map { |k, v| v + 1 }
    
    def something(x)
      unused_var, used_var = something_else(x)
      # 생략
    end
    
    # 좋은 예
    result = hash.map { |_k, v| v + 1 }
    
    def something(x)
      _unused_var, used_var = something_else(x)
      # 생략
    end
    
    # 좋은 예
    result = hash.map { |_, v| v + 1 }
    
    def something(x)
      _, used_var = something_else(x)
      # 생략
    end
  • STDOUT/STDERR/STDIN보다는 $stdout/$stderr/$stdin을 사용하라. STDOUT/STDERR/STDIN은 상수이며, 루비에서의 상수는 실제로 재대입 할 수 있다.(다른 스트림으로 리디렉션도 가능) 물론 재대입하면 인터프리터의 경고가 나올 것이다. [link]

  • $stderr.puts보다는 warn을 사용하라. 좀 더 간결하고 명확할 뿐만 아니라 warn을 사용하면 필요에 따라 경고를 숨길 수도 있다.(-W0를 통해 경고 수준을 0으로 함으로써) [link]

  • 기괴한 String#%메소드보다는 sprintf와 alias인 format사용이 더 좋다. [link]

    # 나쁜 예
    '%d %d' % [20, 10]
    # => '20 10'
    
    # 좋은 예
    sprintf('%d %d', 20, 10)
    # => '20 10'
    
    # 좋은 예
    sprintf('%{first} %{second}', first: 20, second: 10)
    # => '20 10'
    
    format('%d %d', 20, 10)
    # => '20 10'
    
    # 좋은 예
    format('%{first} %{second}', first: 20, second: 10)
    # => '20 10'
  • 문자 인수와 함께 쓰는 기괴한 Array#* 보다 Array#join의 사용을 선호한다. [link]

    # 나쁜 예
    %w[one two three] * ', '
    # => 'one, two, three'
    
    # 좋은 예
    %w[one two three].join(', ')
    # => 'one, two, three'
  • 변수가 배열인지 모르겠지만 그것을 Array로 취급하고 싶을 때에는 명시적인 Array 체크나 [*var] 대신에 Array()를 사용하라. [link]

    # 나쁜 예
    paths = [paths] unless paths.is_a? Array
    paths.each { |path| do_something(path) }
    
    # 나쁜 예(항상 새로운 배열 인스턴스를 만듦)
    [*paths].each { |path| do_something(path) }
    
    # 좋은 예(좀 더 가독성이 높은)
    Array(paths).each { |path| do_something(path) }
  • 복잡한 비교논리를 사용하는 것 대신에 가능하면 범위나 Comparable#between?을 사용하라. [link]

    # 나쁜 예
    do_something if x >= 1000 && x <= 2000
    
    # 좋은 예
    do_something if (1000..2000).include?(x)
    
    # 좋은 예
    do_something if x.between?(1000, 2000)
  • ==로 비교하기 보다 서술형 메소드(predicate methods)를 사용하는 것이 더 좋다. 숫자 비교는 괜찮다. [link]

    # 나쁜 예
    if x % 2 == 0
    end
    
    if x % 2 == 1
    end
    
    if x == nil
    end
    
    if x == 0
    end
    
    # 좋은 예
    if x.even?
    end
    
    if x.odd?
    end
    
    if x.nil?
    end
    
    if x.zero?
    end
  • boolean 값을 다룰 때가 아니면, 명시적으로 'nil이 아닌 것'을 체크하는 것을 피하라. [link]

    # 나쁜 예
    do_something if !something.nil?
    do_something if something != nil
    
    # 좋은 예
    do_something if something
    
    # 좋은 예 - boolean을 다룸
    def value_set?
      !@some_boolean.nil?
    end
  • BEGIN 블록 사용을 피하라. [link]

  • END 블록 사용을 피하라. 차라리 Kernel#at_exit를 써라. [link]

    # 나쁜 예
    END { puts 'Goodbye!' }
    
    # 좋은 예
    at_exit { puts 'Goodbye!' }
  • flip-flop 사용을 피하라. [link]

  • 제어문에서 중첩된 조건문은 피하라. [link]

    유효하지 않은 데이터가 들어갈 수도 있는 경우, 보호 구문을 권장한다. 보호 구문은 함수에서 가능한 한 빨리 탈출할 수 있게 하는 함수 제일 처음에 있는 조건문을 말한다.

    # 나쁜 예
    def compute_thing(thing)
      if thing[:foo]
        update_with_bar(thing[:foo])
        if thing[:foo][:bar]
          partial_compute(thing)
        else
          re_compute(thing)
        end
      end
    end
    
    # 좋은 예
    def compute_thing(thing)
      return unless thing[:foo]
      update_with_bar(thing[:foo])
      return re_compute(thing) unless thing[:foo][:bar]
      partial_compute(thing)
    end

    반복문 내에서는 조건문 블록 대신에 next를 선호한다.

    # 나쁜 예
    [0, 1, 2, 3].each do |item|
      if item > 1
        puts item
      end
    end
    
    # 좋은 예
    [0, 1, 2, 3].each do |item|
      next unless item > 1
      puts item
    end
  • collect보다는 map을, detect보다는 find를, find_all보다는 select를, inject보다는 reduce를, length보다는 size를 권장한다. 이것이 무리한 요구는 아니다. 다른 alias를 써서 가독성이 좋아진다면, 그것도 괜찮다. 메소드의 시적인 표현은 Smalltalk 언어로 부터 물려받은 것으로, 일반적인 다른 프로그래밍 언어와는 다르다. select를 사용하는 이유는 reject와 함께 쓰일 때 find_all보다 좀 더 잘 어울리고 이름 자체가 충분한 설명이 되기 때문이다. [link]

  • size를 대신해서 count를 쓰지 마라. Array외의 Enumerable객체들은 크기를 결정하기 위해 모든 콜렉션을 반복한다. [link]

    # 나쁜 예
    some_hash.count
    
    # 좋은 예
    some_hash.size
  • map + flatten의 조합을 사용하는 것 대신 flat_map을 사용하라. 이것은 2차원 이상의 배열에는 적용되지는 않는다. 다시 말해서, users.first.songs == ['a', ['b','c']]의 경우는 flat_map보다는 map + flatten를 사용하라. flat_map은 1차원의 배열만 1차원으로 만드는 반면 flatten은 모든 것을 1차원 배열로 만든다. [link]

    # 나쁜 예
    all_songs = users.map(&:songs).flatten.uniq
    
    # 좋은 예
    all_songs = users.flat_map(&:songs).uniq
  • reverse.each보다는 reverse_each를 써라. include Enumerable을 사용하는 몇몇 클래스에서 좀 더 효율적인 구현을 사용하기 떄문이다. 최악 경우 클래스에서 특별한 구현을 사용하지 않는다고 해도, Enumerable에서 상속된 일반적인 구현은 reverse.each 정도의 효율을 낸다. [link]

    # 나쁜 예
    array.reverse.each { ... }
    
    # 좋은 예
    array.reverse_each { ... }

네이밍

프로그래밍에서 정말 어려운 것은 캐시무효화와 이름 짓기뿐이다.
-- Phil Karlton

  • 식별자는 영어로 쓴다. [link]

    # 나쁜 예 - ascii 문자가 아닌 식별자
    заплата = 1_000
    
    # 나쁜 예 - 식별자가 (키릴문자 대신에)라틴어 문자로 적힌 불가리어 단어다.
    zaplata = 1_000
    
    # 좋은 예
    salary = 1_000
  • 심볼,메소드,변수에 대해 snake_case를 써라. [link]

    # 나쁜 예
    :'some symbol'
    :SomeSymbol
    :someSymbol
    
    someVar = 5
    var_10  = 10
    
    def someMethod
      # 생략
    end
    
    def SomeMethod
      # 생략
    end
    
    # 좋은 예
    :some_symbol
    
    some_var = 5
    var10    = 10
    
    def some_method
      # 생략
    end
  • 심볼이나 메소드, 변수명에서 숫자를 띄우지 마라. [link]

    # 나쁜 예
    :some_sym_1
    
    some_var_1 = 1
    
    def some_method_1
      # some code
    end
    
    # 좋은 예
    :some_sym1
    
    some_var1 = 1
    
    def some_method1
      # some code
    end
  • 클래스와 모듈에 대해서는 CamelCase(카멜표기)를 사용하라. (HTTP,RFC,XML와 같은 약어는 대문자로 유지하라) [link]

    # 나쁜 예
    class Someclass
      # 생략
    end
    
    class Some_Class
      # 생략
    end
    
    class SomeXml
      # 생략
    end
    
    class XmlSomething
      # 생략
    end
    
    # 좋은 예
    class SomeClass
      # 생략
    end
    
    class SomeXML
      # 생략
    end
    
    class XMLSomething
      # 생략
    end
  • 파일 이름은 snake_case로 써라. 예. hello_world.rb [link]

  • 디렉토리 이름은 snake_case로 써라. 예. lib/hello_world/hello_world.rb [link]

  • 되도록이면 하나의 소스파일은 하나의 클래스/모듈을 갖도록 하라. 파일명은 CamelCase로 적힌 클래스/모듈이름을 snake_case로 바꾼 것으로 하라. [link]

  • 다른 상수들은 SCREAMING_SNAKE_CASE을 사용하라. [link]

    # 나쁜 예
    SomeConst = 5
    
    # 좋은 예
    SOME_CONST = 5
  • 서술형 메소드(boolean 값을 반환하는 메소드)의 이름은 꼭 물음표로 끝나야 한다. (예를 들어 Array#empty?) 메소드가 boolean 값을 반환하는게 아니라면, 물음표로 끝나선 안 된다. [link]

  • 단정 메소드에 is, does, can 같은 보조 전치사를 붙이는 것을 피하라. 이런 단어는 장황할 뿐만 아니라 empty?, include? 같은 루비 핵심 라이브러리의 이진 메소드의 스타일과 다르기도 하다. [link]

    # 나쁜 예
    class Person
      def is_tall?
        true
      end
    
      def can_play_basketball?
        false
      end
    
      def does_like_candy?
        true
      end
    end
    
    # 좋은 예
    class Person
      def tall?
        true
      end
    
      def basketball_player?
        false
      end
    
      def likes_candy?
        true
      end
    end
  • 잠재적으로 위험한 메소드의(예를 들어, self나 인수를 수정하는 메소드와 exit!(finalizer를 실행하지 않음) 등) 이름은 위험한 메소드의 안전한 버전이 존재할 때는 느낌표로 끝낸다. [link]

    # 나쁜 예 - 'safe'한 메소드가 없다.
    class Person
      def update!
      end
    end
    
    # 좋은 예
    class Person
      def update
      end
    end
    
    # 좋은 예
    class Person
      def update!
      end
    
      def update
      end
    end
  • 가능하면 bang(위험한) 관점에서 non-bang(안전한)메소드를 정의하라. [link]

    class Array
      def flatten_once!
        res = []
    
        each do |e|
          [*e].each { |f| res << f }
        end
    
        replace(res)
      end
    
      def flatten_once
        dup.flatten_once!
      end
    end
  • 이항 연산자를 정의할 때에는, 매개변수 이름을 other로 하라. (<<[]는 의미가 달라지므로 이 규칙에서 제외된다.) [link]

    def +(other)
      # 내용 생략
    end

주석

좋은 코드는 가장 좋은 문서이다. 주석을 추가하려고 할 때 스스로에게 물어보라. "이 주석이 필요 없으려면 코드를 어떻게 만들면 될까?" 코드를 더 잘 작성하여 문서를 명확하게 하자.
-- Steve McConnell

  • 스스로 문서화가 되는 코드를 작성하고 이 단락은 무시하도록 하자. 진짜로! [link]

  • 주석은 영어로 작성한다. [link]

  • #뒤에 한 칸 띄고 주석 내용을 작성한다. [link]

  • 한 단어 이상의 주석은 대문자 표기법과 구두점 규칙을 사용한다. 마침표 뒤에는 공백을 사용한다. [link]

  • 불필요한 주석은 피한다. [link]

    # 나쁜 예
    counter += 1 # Increments counter by one.
  • 주석은 최신 상태로 유지한다. 코드내용과 맞지 않는 주석은 없는 것이 낫다. [link]

좋은 주석은 좋은 유머와 같다. 설명이 필요없다.
— 오래된 프로그래머 격언, Russ Olsen

  • 나쁜 코드에 대해서 주석을 달지 마라. 코드가 스스로 설명할 수 있도록 리펙토링 하라.("한다, 안 한다가 있을 뿐이야. 해보는 건 없어." Yoda) [link]

주석 어노테이션

  • 어노테이션은 관련된 코드 바로 위에 작성한다. [link]

  • 어노테이션 키워드는 콜론과 공백 다음에 설명을 작성한다. [link]

  • 만약 설명이 여러 줄인 경우 다음 줄은 #을 쓰고 세 칸 들여쓰기 한다.(한 칸은 일반적으로 하고, 두 칸은 들여쓰기 목적) [link]

    def bar
      # FIXME: This has crashed occasionally since v3.2.1. It may
      #   be related to the BarBazUtil upgrade.
      baz(:quux)
    end
  • 설명이 명확하지 않고 중복되는 경우 예외적으로 라인 뒤쪽에 어노테이션을 작성한다.(규칙은 아님) [link]

    def bar
      sleep 100 # OPTIMIZE
    end
  • TODO는 빠진 기능을 나중에 추가해야 할 때 사용한다. [link]

  • FIXME는 코드가 깨져 고쳐야 할 때 사용한다. [link]

  • OPTIMIZE는 코드가 느리거나 비효율적이라서 성능상 문제가 생기는 경우에 사용한다. [link]

  • HACK은 코드에 냄새가 있는 것 같아 이야기해 보고 리팩토링이 필요한 경우 사용한다. [link]

  • REVIEW는 의도한 대로 동작하는지 확인해야 하는 경우에 사용한다. 예를 들어: REVIEW: Are we sure this is how the client does X currently? [link]

  • 다른 어노테이션 키워드들은 필요에 따라 작성하고 README 같은 곳에 정리해둔다. [link]

매직 코멘트

  • 매직 코멘트는 모든 코드와 문서 위에 두어라. 매직 코멘트는 소스 파일에 쉬뱅(#!)을 사용하는 경우에만 그 밑에 두어라. [link]

    # 좋은 예
    # frozen_string_literal: true
    # Some documentation about Person
    class Person
    end
    
    # 나쁜 예
    # Some documentation about Person
    # frozen_string_literal: true
    class Person
    end
    # 좋은 예
    #!/usr/bin/env ruby
    # frozen_string_literal: true
    App.parse(ARGV)
    
    # 나쁜 예
    # frozen_string_literal: true
    #!/usr/bin/env ruby
    App.parse(ARGV)
  • 여러 개의 매직 코멘트를 사용하는 경우에는 한 줄에 하나의 매직 코멘트만 사용하라. [link]

    # 좋은 예
    # frozen_string_literal: true
    # encoding: ascii-8bit
    
    # 나쁜 예
    # -*- frozen_string_literal: true; encoding: ascii-8bit -*-
  • 매직 코멘트와 코드, 문서 사이에 빈 줄을 사이에 두어라. [link]

    # 좋은 예
    # frozen_string_literal: true
    
    # Some documentation about Person
    class Person
      # 생략
    end
    
    # 나쁜 예
    # frozen_string_literal: true
    # Some documentation about Person
    class Person
      # 생략
    end

클래스와 모듈

  • 클래스 정의는 일관된 구조를 사용한다. [link]

    class Person
      # extend와 include가 먼저 나온다.
      extend SomeModule
      include AnotherModule
    
      # inner classes
      CustomError = Class.new(StandardError)
    
      # 상수는 다음에 나온다.
      SOME_CONSTANT = 20
    
      # 그 뒤로 attribute 매크로가 온다.
      attr_reader :name
    
      # 다른 매크로들이 있다면 그 다음에 나온다.
      validates :name
    
      # public 클래스 메소드가 다음 줄에 온다.
      def self.some_method
      end
    
      # 초기화는 클래스 메소드와 다른 인스턴스 메소드 사이에 온다.
      def initialize
      end
    
      # 다른 public 인스턴스 메소드가 뒤에 온다.
      def some_method
      end
    
      # protected와 private 메소드는 마지막 근처에 모아둔다.
      protected
    
      def some_protected_method
      end
    
      private
    
      def some_private_method
      end
    end
  • 한 줄 이상의 클래스는 클래스 안에 선언하지 않는다. 이런 경우에는 클래스 이름의 폴더를 만들고 각각의 inner 클래스를 별도의 파일로 분리해 보는 것도 좋다. [link]

    # 나쁜 예
    
    # foo.rb
    class Foo
      class Bar
        # 안에 30개의 메소드
      end
    
      class Car
        # 안에 20개의 메소드
      end
    
      # 안에 30개의 메소드
    end
    
    # 좋은 예
    
    # foo.rb
    class Foo
      # 안에 30개의 메소드
    end
    
    # foo/bar.rb
    class Foo
      class Bar
        # 안에 30개의 메소드
      end
    end
    
    # foo/car.rb
    class Foo
      class Car
        # 안에 20개의 메소드
      end
    end
  • 클래스 메소드만 가지는 클래스는 모듈로 선언하는 것이 더 좋다. 클래스는 인스턴스를 생성해서 사용할 때만 사용한다. [link]

    # 나쁜 예
    class SomeClass
      def self.some_method
        # 내용 생략
      end
    
      def self.some_other_method
        # 내용 생략
      end
    end
    
    # 좋은 예
    module SomeModule
      module_function
    
      def some_method
        # 내용 생략
      end
    
      def some_other_method
        # 내용 생략
      end
    end
  • 모듈의 인스턴스 메소드를 클래스 메소드로 바꿀 때는 extend