Das Guard Clause Entwurfsmuster

Es besteht das Vorurteil, dass eine Methode/ Funktion generell nur einen Ausstiegspunkt (exit point) haben sollte. Demnach gelten mehrere Ausstiegspunkte als bad practice.
Als Beispiel für eine Methode mit einem einzigen Ausstiegspunkt (am Ende), berechnet warranty_days die Anzahl der verbleibenden Garantie in Tagen:

class Product
  attr_accessor :sold_at

  DEFAULT_WARRANTY_DAYS = 180
  SECONDS_PER_DAY = 24 * 60 * 60

  def warranty_days
    if sold_at.nil?
      DEFAULT_WARRANTY_DAYS
    else
      days_since_sold < DEFAULT_WARRANTY_DAYS ? (DEFAULT_WARRANTY_DAYS - days_since_sold) : 0
    end
  end

  private

  def days_since_sold
    ((Time.now - sold_at).to_f / SECONDS_PER_DAY).round
  end
end

Allerdings stammt diese Meinung noch aus alten C/ C++ Zeiten. Damals wurde zu Beginn der Funktion Speicher alloziiert und am Ende wieder freigegeben. Bei mehreren Ausstiegspunkten innerhalb der Funktion, konnte es schon mal passieren, den Speicher eben nicht korrekt freigegeben zu haben.
Bei modernen Sprachen (z.B. Ruby) kümmert sich allerdings die automatische Speicherbereinigung (garbage collection) darum. Das Argument ist also veraltet.
Demgegenüber steht das sogenannte Guard Clause Entwurfsmuster. Die Aufgabe eines Guard (Wächter) ist es, innerhalb der Methode nachfolgende Logik abzusichern. Grund dafür können unerwartete Übergabeparameterwerte oder auch unpassende Zustände des Objektes sein.
Das Beispiel noch einmal, aber dieses Mal mit 2 Wächtern:

class Product
  attr_accessor :sold_at

  DEFAULT_WARRANTY_DAYS = 180
  SECONDS_PER_DAY = 24 * 60 * 60

  def warranty_days
    return DEFAULT_WARRANTY_DAYS if sold_at.nil? # 1. guard
    return 0 if days_since_sold > DEFAULT_WARRANTY_DAYS # 2. guard
    DEFAULT_WARRANTY_DAYS - days_since_sold
  end

  private

  def days_since_sold
    ((Time.now - sold_at).to_f / SECONDS_PER_DAY).round
  end
end

Sie erhöhen selbst bei diesem einfachen Beispiel die Lesbarkeit. Debuggen wird ebenfalls stark erleichtert, da für einen debug Pfad, nicht zwangsläufig der gesamte Code gelesen werden muss.
Die sogenannten frühen Ausstiege ermöglichen zusätzlich die Reduzierung von if/else Verschachtelungen auf einfache if Bedingungen. Mit nachgestellten if Bedingungen, kann die Logik dann in die erste Einrückungsebene wandern, was oft dazu führt, dass Kernlogik sichtbarer wird.
Selbst ternäre Operatoren werden dann zwangsläufig seltener verwendet (siehe 2. Wächter in dem Beispiel).
Auch OR Verknüpfungen lassen sich mit einem frühen Austieg vereinfachen.
Dass Szenario für ein Beispiel ist die Prüfung der Kreditwürdigkeit in einer Bank. Zunächst das Original:

def creditworthy? person
  customers.include?(person) || (person.adult? && person.country_citizen?)
end

Die Guard Clause Pattern Alternative:

def creditworthy? person
  return true if customers.include?(person)
  person.adult? && person.country_citizen?
end