22 Punkte von GN⁺ 2024-09-04 | 5 Kommentare | Auf WhatsApp teilen
  • Bei der Wartung einer unbekannten Codebasis verbringt man viel Zeit damit, nach Strings zu suchen
  • Selbst in Projekten, die man allein geschrieben hat, muss man vieles suchen, etwa Funktionsnamen, Fehlermeldungen und Klassennamen
  • Wenn die Suche nicht gut funktioniert, findet man Referenzen in der Codebasis nicht und läuft Gefahr, sie als unnötig einzustufen
  • Aus solchen Situationen wurden einige Regeln abgeleitet, mit denen sich die Greppability einer Codebasis erhalten lässt

Bezeichner nicht aufspalten

  • Bezeichner aufzuspalten oder dynamisch zusammenzusetzen ist keine gute Idee
  • Angenommen, es gibt zwei Datenbanktabellen namens shipping_addresses und billing_addresses; dann mag es attraktiv erscheinen, den Tabellennamen je nach Bestelltyp dynamisch zusammenzubauen
const getTableName = (addressType: 'shipping' | 'billing') => {  
    return `${addressType}_addresses`  
}  
  • Das wirkt zwar DRY, ist aber schlecht für die Wartbarkeit. Wer in der Codebasis nach dem Tabellennamen shipping_addresses sucht, könnte diese Stelle übersehen
  • Es ist besser, Bezeichner fest zu codieren
  • Für bessere Durchsuchbarkeit refaktorisierter Code:
const getTableName = (addressType: 'shipping' | 'billing') => {  
    if (addressType === 'shipping') {  
        return 'shipping_addresses'  
    }  
    if (addressType === 'billing') {  
        return 'billing_addresses'  
    }  
    throw new TypeError('addressType must be billing or shipping')  
}  
  • Dasselbe gilt für Spaltennamen, Objektfelder und Methoden-/Funktionsnamen (in JavaScript lassen sich Methodennamen leicht dynamisch zusammenbauen)

Im gesamten Stack dieselben Namen verwenden

  • Feldnamen an Anwendungsgrenzen nicht ändern, nur um einem Benennungsschema zu entsprechen
  • Ein typisches Beispiel: Identifikatoren im PostgreSQL-Stil mit snake_case nach JavaScript zu holen und in camelCase umzuwandeln, ist keine gute Idee
  • Das erschwert die Suche. Um alles zu finden, muss man statt nach einem String nach zwei suchen
const getAddress = async (id: string) => {  
    const address = await getAddressById(id)  
    return {  
        streetName: address.street_name,  
        zipCode: address.zip_code,  
    }  
}  
  • Es ist besser, das Objekt direkt zurückzugeben
const getAddress = async (id: string) => {  
    return await getAddressById(id)  
}  

Flach ist besser als verschachtelt

  • Inspiriert vom Zen of Python gilt beim Umgang mit Namespaces meist: flach ist besser als verschachtelt, ob bei Ordnern oder Objektstrukturen
  • Wenn es zwei Möglichkeiten für die Struktur einer Übersetzungsdatei gibt:
{  
    "auth": {  
        "login": {  
            "title": "Login",  
            "emailLabel": "Email",  
            "passwordLabel": "Password",  
        },  
        "register": {  
            "title": "Register",  
            "emailLabel": "Email",  
            "passwordLabel": "Password",  
        }  
    }  
}  
{  
    "auth.login.title": "Login",  
    "auth.login.emailLabel": "Email",   
    "auth.login.passwordLabel": "Password",  
    "auth.register.title": "Login",  
    "auth.register.emailLabel": "Email",  
    "auth.register.passwordLabel": "Password",  
}  
  • Die zweite Option ist vorzuziehen. Die Keys lassen sich leicht finden und mit t('auth.login.title') referenzieren
  • Betrachtet man die Struktur von React-Komponenten:
./components/AttributeFilterCombobox.tsx  
./components/AttributeFilterDialog.tsx  
./components/AttributeFilterRating.tsx  
./components/AttributeFilterSelect.tsx  
  • Diese Struktur ist vorzuziehen gegenüber
./components/attribute/filter/Combobox.tsx  
./components/attribute/filter/Dialog.tsx  
./components/attribute/filter/Rating.tsx  
./components/attribute/filter/Select.tsx  
  • Denn aus Sicht der Suche kann man so nach dem vollständigen Komponenten-Namen mit Namespace wie AttributeFilterCombobox suchen statt nach einem allgemeinen Namen wie Dialog

Meinung von GN⁺

  • Dieser Blogbeitrag erklärt sehr gut, wie wichtig die Suche nach Bezeichnern bei der Pflege einer Codebasis ist
  • Bezeichner dynamisch zusammenzubauen oder Namen an Anwendungsgrenzen zu ändern, erschwert die Wartung von Code. Bezeichner sollten konsistent und eindeutig sein
  • Stattdessen sind fest codierte Bezeichner und flache Namespaces aus Sicht der Durchsuchbarkeit besser
  • Es lohnt sich, diese Prinzipien in Projekten anzuwenden, um Lesbarkeit und Wartbarkeit des Codes zu verbessern
  • Zusätzlich zu den vom Autor vorgeschlagenen Regeln gibt es viele weitere Wege, die Codequalität zu erhöhen, etwa Self-Documenting Code und sinnvolle Kommentare

5 Kommentare

 
nowpark 2024-09-06

Bitte konvertiere es in den vollständigen Pfad von json – ich lasse auch noch ein Tool da, das es greppbar macht!

https://de.news.hada.io/topic?id=3159

 
botplaysdice 2024-09-05

Gut … Greppability also …

 
ahwjdekf 2024-09-04

Es scheint auch nützlich zu sein, möglichst viele hilfreiche Informationen in einer einzigen Zeile unterzubringen.

 
roxie 2024-09-09

Klingt gut.

 
GN⁺ 2024-09-04
Hacker-News-Kommentare
  • Das Suchen nach Symbolen wie Funktionsnamen und Klassennamen ist schwächer als die Verwendung von Werkzeugen, die die Syntax des Codes verstehen

    • Allein die Funktionen „Gehe zur Definition“ und „Verwendungen finden“ können den Bedarf an Textsuche stark verringern
    • In den letzten zehn Jahren habe ich hauptsächlich nur nach für Nutzer sichtbaren Zeichenketten gesucht
    • Solche Beiträge bedeuten, dass der Autor Zeit investieren sollte, um bessere Werkzeuge für seine Sprache zu lernen
    • Schon eine gute IDE kann viel Zeit sparen
  • Es wäre nützlich, wenn grep einen Modus für „supergroßzügige Groß-/Kleinschreibungsignorierung“ hätte

    • Zum Beispiel, um bei der Suche nach „FooBar|first_name“ auf alle Schreibweisen zu matchen
    • Es ist schwer, sich einen Fall vorzustellen, in dem so eine Funktion nicht der Standard sein sollte
  • Zustimmung zum Thema Greppability

    • Im Schwedischen gibt es tatsächlich Wörter wie „grep-bar“ oder „grep-barhet“
    • „greppbar“ bedeutet „verständlich“, und „greppbarhet“ bedeutet „Verständlichkeit“
  • Beim Entwurf von Hamilton war es ein Ziel, Funktionsdefinitionen und ihre nachgelagerten Verwendungen leicht grepbar zu machen

    • In der Python-Welt der Datentransformation ist es leicht, Codebasen zu bauen, bei denen grep kaum hilft
  • „greppable“ ist kein Wort bzw. Konzept, das eigenständig oft verwendet wird

    • Ich nutze es seit Langem als Organisationsprinzip
    • Es ist eine der besten Methoden, Code zu strukturieren
  • Ich habe einmal ein komplexes Beispiel mit bedingter String-Interpolation gesehen

    • Als ich neu im Projekt war, habe ich viel zu lange gebraucht, um die drei Wörter zu finden, die ich in der UI gesehen hatte
    • Später habe ich diesen Code vollständig in Zeichenketten umgewandelt, die sich leicht grepbar durchsuchen lassen
  • Viele Coding-Styles und Werkzeuge halten String-Konstanten unabhängig von der Zeilenlänge in einer einzelnen Zeile

    • Damit man eine Zeichenkette in der Programmausgabe sehen und im Code nach genau derselben Zeichenkette suchen kann
  • Rust, Javascript und Lisp stellen Schlüsselwörter vor Funktionsdefinitionen, was die Suche erleichtert

    • C hat solche Schlüsselwörter nicht, daher kann man nur nach dem Funktionsnamen suchen
    • Einige C-Coding-Regeln teilen Definitionen auf zwei Zeilen auf, um die Suche zu erleichtern
  • Ich stimme der Greppability zu, bin aber dagegen, Namen über Grenzen hinweg gleich zu halten

    • Wenn ein Symbol nur in einer einzigen Domäne existiert, reduziert das die kognitive Last
  • Gute Durchsuchbarkeit von Code ist sinnvoll, aber die Beispiele erhöhen absichtlich die Fehleranfälligkeit

    • Wenn man String-Bedingungen hinzufügt, entsteht die Möglichkeit von Inkonsistenzen zwischen Eingabe und Ausgabe
    • Wenn man Dictionaries abflacht, steigt die Wahrscheinlichkeit von Tippfehlern
    • Tippfehler kommen häufig vor und sind schwer zu beheben, wenn sie in mehrere Codebasen kopiert wurden