[RUBY] Der Standpunkt bei der Codeüberprüfung einer Webanwendung (Rails)

Als ich den Code überprüfte, hatte ich das Gefühl, dass ich immer auf ähnliche Punkte hingewiesen habe, und fasste die Standpunkte zusammen, die mir wichtig sind. Es gibt einige Perspektiven, die nicht von der verwendeten Sprache abhängen, aber da ich bei der Arbeit hauptsächlich Rails verwende, gibt es auch Perspektiven, die für Ruby und Rails spezifisch sind.

Vorerst habe ich kurz geschrieben, woran ich im Moment denken kann, aber ich werde es hinzufügen, wenn ich mich an den Standpunkt erinnere, der mir in Zukunft am Herzen liegt. Es ist gut, weiterhin sowohl Entwicklungs- als auch Qiita-Artikel (CD) zu liefern, ohne von Anfang an nach Perfektion zu streben.

Einrückungen und Codierungsregeln

Verwenden Sie statische Analysewerkzeuge, um statische Teile Ihres Codes zu überprüfen, z. B. die folgenden Einrückungen und Codierungsregeln. Es ist Zeitverschwendung für den Menschen, visuell zu prüfen, und es kommt zu Undichtigkeiten und Unschärfen. Darüber hinaus werden Sie von den Details abgelenkt und übersehen die Punkte, auf die hingewiesen werden sollte.

Rubocop ist ein bekanntes statisches Analysewerkzeug in Ruby. Die Einführung von Funktionen, die eine solche Entwicklung unterstützen, wird in der Anfangsentwicklung häufig verschoben, es ist jedoch sehr schwierig, statische Analysewerkzeuge in die Mitte einzufügen. Wenn Sie versuchen, es später hinzuzufügen, erhalten Sie Code, der die Regeln lockert oder befreit. Fügen Sie ihn daher in der ersten Entwicklungsphase hinzu. Persönlich denke ich, dass es am besten ist, es fest zu setzen und sich nach Bedarf zu entspannen.

Sind Sie zu sehr mit der Überprüfung des statischen Codes beschäftigt?

Es ist ein widersprüchliches Thema, da ich gerade geschrieben habe, dass die statische Codeprüfung vor einer Zeit verwendet werden sollte, aber die statische Codeprüfung ist gelockert oder eine Ausnahme, wenn es schwierig ist, Korrekturen vorzunehmen, um die Angabe der statischen Codeprüfung zu korrigieren. Es ist besser einzustellen. Eine übliche Methode sind überstrichene Methoden. Wenn die Methode eine bestimmte Anzahl von Zeilen überschreitet, wird sie als geteilt bezeichnet. Wenn es jedoch angemessen ist, dass die Verarbeitung eine einzelne Einheit ist, ist sie nur schwer zu verstehen, sodass ich denke, dass es nicht erforderlich ist, sie zu erzwingen.

Ist der HTTP-Statuscode angemessen?

Mit Rails funktioniert es oft ohne Probleme, auch wenn der Statuscode nicht mit der Methode oder Antwort übereinstimmt. Daher versuche ich zu überprüfen, ob der Statuscode ordnungsgemäß verwendet wird. Ich denke, dass die detaillierte Verwendung je nach Person unterschiedlich ist, aber ich werde meine Interpretation über den Hauptstatuscode schreiben.

2XX Dies ist der Statuscode, wenn die Anforderung normal verarbeitet wird.

201 Created Es wird verwendet, wenn eine Ressource durch POST- oder PUT-Anforderung generiert wird. Bei Verwendung von 201 wird die generierte Ressource häufig im Antworttext zurückgegeben. Wenn Sie nichts zurückgeben müssen, sollten Sie 204 No Content verwenden.

204 No Content Es wird häufig verwendet, wenn eine Ressource durch eine DELETE-Anforderung gelöscht wird, aber es wird auch verwendet, wenn selbst in POST oder PUT kein Antworttext vorhanden ist. Wenn Sie dies verwenden, geben Sie keinen Wert in den Antworttext ein.

200 OK Wenn Sie eine andere als die oben genannte normale Antwort zurückgeben möchten, sollten Sie grundsätzlich 200 OK verwenden.

3XX-Serie

Dies ist der Statuscode, wenn Sie zu einer anderen Seite umleiten möchten.

301 Moved Permanently Verwenden Sie 301, wenn Sie dauerhaft auf eine andere Seite umleiten möchten. Wenn Sie beispielsweise eine Webseite migrieren, wird sie verwendet, um von der URL der alten Seite zur neuen Seite umzuleiten. Es ist auch als SEO-Maßnahme nützlich. Wenn Sie 301 angeben, indiziert die Suchmaschine die neue URL für Sie.

303 See Other Ich war mir dessen bis vor kurzem nicht bewusst, aber wenn Sie nach dem Registrieren / Aktualisieren der Ressource zur Detailseite der Ressource umleiten möchten, verwenden Sie 303. Das Muster ist wie folgt. Registrieren Sie neue Ressourcen bei POST / resources und leiten Sie zu der Seite (GET / resources /: id) weiter, auf der Ressourceninformationen angezeigt werden

302 Found Verwenden Sie im Gegensatz zu 301 302, wenn Sie vorübergehend auf eine andere Seite umleiten möchten. Wenn Sie "redirect_to" in Rails verwenden, ist es standardmäßig 302, aber unter Berücksichtigung des Umleitungszwecks hat 302 nur wenige Verwendungszwecke, und ich denke, dass es oft besser ist, es an 301 oder 303 zu verteilen.

4XX-Serie

Dies ist der Statuscode, wenn ein vom Client verursachter Fehler auftritt.

404 Not Found Der Statuscode, der verwendet wird, wenn die Seite nicht gefunden werden kann. Da "404" oft auf dem Bildschirm angezeigt wird, denke ich, dass viele Leute es wissen.

Für manche Menschen mag es eine andere Idee sein, aber ich versuche, 404 und 200 richtig zu verwenden, wenn ich bedenke, ob die fehlende Ressource für den Kunden abnormal oder normal ist. Hier sind einige konkrete Beispiele, um die Vorstellung zu erleichtern.

401 Unauthorized Verwenden Sie diese Option, wenn für eine Aktion, für die eine Authentifizierung erforderlich ist, keine gültigen Anmeldeinformationen übergeben werden.

403 Forbidden Es wird verwendet, wenn die Authentifizierung durchgeführt wird, die Nutzungsberechtigung jedoch nicht ausreicht. Zum Beispiel, wenn ein schreibgeschützter Benutzer versucht, eine Ressource zu aktualisieren.

Bei der Verwendung des 403 ist jedoch eines zu beachten. Wenn Sie auf eine Ressource zugreifen, die für einen Benutzer privat ist. In diesem Fall möchten Sie 403 verwenden, wenn Sie an das System denken, da Sie keine Zugriffsberechtigung haben. Wenn Sie jedoch 403 zurückgeben, wird der Benutzer darüber informiert, dass die Ressource vorhanden ist. In einem solchen Fall ist es besser, die Existenz selbst nicht zu informieren, daher ist es besser, 404 mit der Bedeutung zurückzugeben, dass sie nicht existiert.

400 Bad Request Es wird verwendet, wenn eine ungültige Anforderung eingeht, z. B. wenn die erforderlichen Parameter nicht angegeben sind. Wenn es nicht für 401, 403, 404 gilt, verwende ich normalerweise 400.

410 Gone Es wird verwendet, wenn auf die URL zugegriffen wird, die dauerhaft gelöscht werden soll, z. B. nach Ablauf des Zeitraums und Löschen der Seite auf der zeitlich begrenzten Kampagnenseite. Es ist eine Schande, aber in letzter Zeit bleiben viele Seiten auch nach Ablauf der Kampagnenperiode unbeaufsichtigt, und es wird zu einem Geräusch bei der Suche, was ärgerlich ist. Ich möchte, dass Sie die Aufgabe haben, es nach Ablauf der Frist zu löschen und ordnungsgemäß auszuführen.

5XX-Serie

Dies ist der Statuscode, wenn ein serverbedingter Fehler auftritt.

500 Internal Server Error Verwenden Sie diese Option, wenn auf dem Server ein unerwarteter Fehler auftritt. Ich denke nicht, dass es üblich ist, dass ein Programm explizit einen 500-Fehler generiert. Wenn es also einen Teil gibt, der explizit einen 500-Fehler zurückgibt, ist es meiner Meinung nach besser, ihn so zu ändern, dass ein entsprechender Fehler zurückgegeben wird.

503 Service Unavailable Es wird verwendet, wenn die Wartungsseite angezeigt wird, da aufgrund regelmäßiger Wartung kein normaler Zugriff möglich ist.

Ist die Fehlermeldung an den Client angemessen?

Möglicherweise möchten Sie eine Fehlermeldung an den Client zurückgeben, wenn ein Fehler auftritt. Stellen Sie jedoch sicher, dass die Meldung korrekt ist. Die Sichtweise ist, ob Sie die Fehlermeldung erhalten und Maßnahmen ergreifen können. Ein häufiges schlechtes Beispiel ist ein Muster, das eine Nachricht zurückgibt, in der kein Element wie "Ungültiges Eingabeelement" angegeben ist, wenn eine Eingabebedingung auf einem Bildschirm mit mehreren Eingabeelementen nicht erfüllt ist. Damit wissen Sie nicht, welcher Artikel falsch ist und Sie wissen nicht, was Sie tun sollen. Lassen Sie uns das Element und die Fehlerursache wie "Bitte geben Sie den Wert des xx-Elements als numerischen Wert ein" mitteilen.

Es gibt jedoch Fälle, in denen es NG ist, den Benutzer im Detail zu informieren. Wenn Sie beispielsweise auf dem Anmeldebildschirm eine vorhandene ID angeben, in der Sie die ID / das Kennwort eingeben und bei falschem Kennwort "falsches Kennwort" zurückgeben, wird der Benutzer darüber informiert, dass die ID vorhanden ist. .. Mit dieser Implementierung kann ein böswilliger Benutzer eine geeignete ID eingeben, um eine Liste der vorhandenen IDs zu erstellen. Der Anmeldebildschirm sollte eine Nachricht zurückgeben, die nicht erkennen kann, was falsch ist, z. B. "Ungültige Anmeldeinformationen", selbst wenn die ID nicht vorhanden ist oder nur das Kennwort unterschiedlich ist.

Die API-Antwort gibt einen aussagekräftigen Wert zurück

Ich denke, dass moderne Webanwendungen oft separate Front- und Backends haben. Infolgedessen bietet das Backend häufig Funktionen als API. Wenn Sie eine API erstellen und ein aufgezähltes Element zurückgeben, sollte es zurückgegeben werden, damit die Bedeutung nur anhand des Werts verstanden werden kann. Zum Beispiel, wenn Sie folgenden Status haben:

enum status {
  todo: 1,
  in_progress: 2,
  done: 3
}

Wenn Sie dies an die Rezeption zurücksenden möchten, sollten Sie anstelle der Nummer (1, 2, 3) den Namen (z. B. "do to do") zurückgeben. Wenn Sie einen numerischen Wert wie "status = 1" zurückgeben, was ist 1, wenn Sie die Antwort sehen? Wenn Sie jedoch den Namen wie "status =" todo "" zurückgeben, können Sie die Bedeutung nur durch einen Blick darauf verstehen.

Ist die Reihenfolge des Arrays eindeutig?

Überprüfen Sie bei der Rückgabe eines Arrays, ob die Reihenfolge des Arrays eindeutig ist.

Im Folgenden wird das durch user_id eingegrenzte Überprüfungsarray zurückgegeben. Da jedoch keine Reihenfolge angegeben ist, ist nicht sicher, in welcher Reihenfolge sie zurückgegeben werden. Es ist in Ordnung, wenn es sicher ist, dass der Anrufer sich nicht um die Bestellung kümmert, aber geben Sie die Reihenfolge an, damit die Bestellung festgelegt wird.

def my_reviews(user_id)
  Review.where(user_id: user_id)
end

Unten wird die absteigende Reihenfolge von created_at angegeben. Wenn dies der Fall ist, können Sie anscheinend die neu erstellten in der richtigen Reihenfolge erhalten.

def my_reviews(user_id)
  Review.where(user_id: user_id).order(created_at: :desc)
end

Es gibt jedoch immer noch Probleme damit. Die Reihenfolge ist ungewiss, wenn created_at dieselbe Überprüfung hat. Um die Reihenfolge sicher zu bestimmen, ist es gut zu prüfen, ob die in der Reihenfolge angegebenen Spalten wie unten gezeigt eindeutig sind. Dieses Mal habe ich die absteigende Reihenfolge der ID für die zweite Sortierung angegeben, um sie eindeutig zu machen.

def my_reviews(user_id)
  # 1. created_In absteigender Reihenfolge von um anordnen. Allerdings erstellt_at hat keine eindeutigen Einschränkungen, daher kann es am selben Tag sein
  # 2.Wenn es am selben Tag ist, ordnen Sie sie in absteigender Reihenfolge der ID an. Die ID ist eindeutig und kann daher nicht denselben Wert haben
  # =>Die Reihenfolge ist eindeutig festgelegt
  Review.where(user_id: user_id).order(created_at: :desc, id: :desc)
end

Was andere reibungslos lesen und verstehen können

Während der Codeüberprüfung werden möglicherweise Implementierungen angezeigt, deren Verständnis lange dauert oder die irreführend sind. Eine solche Implementierung sollte so einfach wie möglich sein, da jeder, der diesen Code in Zukunft sieht, dasselbe empfinden und den Aufwand für das Lesen von Code erhöhen und die Wahrscheinlichkeit des Einbettens von Fehlern erhöhen wird. Wenn Sie es nicht einfach machen können, können Sie einen Kommentar hinzufügen. Wenn Sie ein Framework wie Rails verwenden, können Sie falsch verstehen, dass Sie es außerhalb des Frameworks verwenden, wie unten gezeigt. Lassen Sie uns irreführenden Code mit einer hohen Wahrscheinlichkeit loswerden, dass später keine Fehler mehr auftreten.

class Review
  belongs_to :user
  enum status {
    open: 0,
    close: 1,
  }
end

class User
  #× Obwohl es sich um die Standardzuordnung handelt, wird sie durch Angabe von Bedingungen eingegrenzt
  has_many :reviews, -> { open }

  #○ Wenn Sie eine Bedingung festlegen möchten, verwenden Sie einen Namen, der sie versteht.
  has_many :open_reviews, -> { open }, class_name: 'Review'
end

class Hoge
  def fuga(user_id)
    user = User.find user_id
    #Wenn Sie nur hier suchen, besteht eine hohe Wahrscheinlichkeit, dass Sie fälschlicherweise glauben, alle Bewertungen des Benutzers erhalten zu haben.
    user.reviews
  end
end

YAGNI Wie viele von Ihnen vielleicht wissen, gibt es das Wort YAGNI. Siehe Wikipedia für Details. https://ja.wikipedia.org/wiki/YAGNI

Wie oben erwähnt, besteht kaum eine Chance, dass Sie sich eine Funktion vorstellen und implementieren können, die Sie noch nicht verwendet haben, und sie unverändert verwenden. Seien Sie vorsichtig, wenn Sie den Code sehen, den Sie schreiben, um sich die Zukunft vorzustellen. Es ist schmerzhaft, den implementierten Code entfernen zu lassen, aber es ist oft besser, ihn zu entfernen. Wenn Sie es verlassen, kann es später stören oder Sie müssen es möglicherweise reparieren, obwohl Sie es während des Refactorings nicht verwendet haben, was Ihre Produktivität beeinträchtigen kann.

Reduzieren Sie den Umfang der Methoden und Konstanten

Machen Sie Methoden und Konstanten, die nicht von außen aufgerufen werden, privat. Wenn es öffentlich ist, ist der Einflussbereich beim Ändern der Methode oder Konstante die gesamte Quelle. Wenn es jedoch privat ist, müssen Sie sich nur um dieselbe Klasse kümmern, sodass es recht einfach ist, den Einflussbereich der Quellmodifikation wie Refactoring zu untersuchen. Werden.

Ist die Verantwortung angemessen?

Jede Klasse hat ihre eigenen Verantwortlichkeiten. Überprüfen Sie, ob Ihre Verantwortlichkeiten eingehalten werden.

Betrachten Sie beispielsweise die Aktion, die Ihr Konto aktiviert.

Da die zum Zeitpunkt der Aktivierung ausgeführte Verarbeitung dem Kontomodell bekannt sein sollte, für das das Konto zuständig ist, ist es besser, sie mit der folgenden Richtlinie zu implementieren.

class AccountController
  #× Der Controller weiß, was zum Zeitpunkt der Aktivierung zu tun ist
  def activate
    account = Account.find_by(token: params[:token])
    account.status = :activated
    account.activated_at = Time.now
    account.token = nil
    account.save!
  end

  #○ Der Controller weiß zum Zeitpunkt der Aktivierung nicht, was zu tun ist, und fragt das Kontomodell
  def activate
    account = Account.find_by(token: params[:token])
    account.activate!
  end
end

class Account
  def activate!
    status = :activated
    activated_at = Time.now
    token = nil
    save!
  end
end

includes, eager_load, preload Wenn Sie sich über diese Unterschiede nicht sicher sind, lesen Sie bitte den folgenden Qiita-Artikel, den Sie mindestens einmal gesehen haben sollten, wenn Sie Rails verwenden. https://qiita.com/k0kubun/items/80c5a5494f53bb88dc58

Includes ist eine bequeme Existenz, die Preload und eifriges Laden richtig verwendet. Als ich anfing, Rails zu verwenden, dachte ich, es wäre besser, Includes zu verwenden, aber die aktuelle Idee ist das Gegenteil. Ich versuche, Basic Preload oder Zeal_load zu verwenden. Dies liegt daran, dass wir meiner Meinung nach wissen sollten, ob wir bei der Implementierung beitreten sollen oder nicht. Einschlüsse, die je nach Implementierung möglicherweise beitreten oder nicht, sind schwer zu kontrollieren und können zu unerwartetem Verhalten führen. Daher ist es besser, sie nicht zu verwenden.

Gibt es eine nutzlose Verbindung?

ActiveRecord ist sehr nützlich, aber es scheint weit vom Rails-Ingenieur entfernt zu sein. Wenn Sie selbst SQL schreiben, sind Sie bereit, SQL auszugeben, das Sie nicht schreiben würden. Unter ihnen ist das Muster des vergeblichen Beitritts besonders zu sehen. Ein konkretes Beispiel ist unten gezeigt. Eine Methode find_by_organization, die die organisatoin_id angibt, um die Benutzer abzurufen, die zu dieser Organisation gehören.

class User
  has_many :user_organization

  def self.find_by_organization(organization_id)
    #× Organisationstabellen müssen nicht verbunden werden
    # select * from users inner join user_organizations on users.id = user_organizations.user_id inner join organizations on user_organizations.organization_id = organizations.id where organizations.id = #{organization_id}
    joins(user_organization: :organization).merge(Organization.where(id: organization_id))

    # ○ user_Nur Organisationen werden kombiniert
    # select * from users inner join user_organizations on users.id = user_organizations.user_id where user_organizations.organization_id = #{organization_id}
    joins(:user_organization).merge(UserOrganization.where(organization_id: organization_id))
  end
end

class Organization
  has_many :user_organization
end

class UserOrganization
  belongs_to :user
  belongs_to :organization
end

Recommended Posts

Der Standpunkt bei der Codeüberprüfung einer Webanwendung (Rails)
Punkte, die Sie beim persönlichen Umgang mit Variablen beachten sollten
Dinge, die Sie beim Schreiben von Code in Java beachten sollten
So beheben Sie das Problem, dass beim Stoppen der Webanwendung kein Protokollierungsprotokoll ausgegeben wird
Informationen zu Arten der Codeabdeckung
[Rails] Sprechen Sie darüber, wie Sie auf den Rückgabewert von where achten
Informationen zum Testen von WEB-Anwendungen mit DynamoDB und zum Automatisieren der Bereitstellung in Fargate