Best Practices für Engine-Entwickler

Einführung

Godot hat eine große Anzahl von Benutzern, die einen Beitrag leisten können, da sich das Projekt selbst hauptsächlich an Benutzer richtet, die programmieren können. Trotzdem verfügen nicht alle über die gleiche Erfahrung in großen Projekten oder in der Softwareentwicklung, was zu häufigen Missverständnissen und schlechten Praktiken beim Einbringen von Code in das Projekt führen kann.

Sprache

The scope of this document is to be a list of best practices for contributors to follow, as well as to create a language they can use to refer to common situations that arise in the process of submitting their contributions.

Während einige es nützlich finden mögen, dies auf die allgemeine Softwareentwicklung auszudehnen, wollen wir uns nur auf Situationen beschränken, die in unserem Projekt am häufigsten vorkommen.

Beiträge werden meistens als Fehlerkorrekturen, Verbesserungen oder neue Funktionen eingestuft. Um diese Idee zu abstrahieren, werden wir sie Lösungen nennen, weil sie immer versuchen, etwas zu lösen, das als Problem beschrieben werden kann.

Best Practices

#1: Das Problem steht immer an erster Stelle

Many contributors are extremely creative and just enjoy the process of designing abstract data structures, creating nice user interfaces, or simply love programming. Whatever the case may be, they come up with cool ideas, which may or may not be solving any real problems.

../../_images/best_practices1.png

Diese werden normalerweise als Lösungen auf der Suche nach einem Problem bezeichnet. In einer idealen Welt wären sie nicht schädlich, aber in Wirklichkeit benötigt man Zeit um den Code Zeit zu schreiben, er nimmt Platz als Quelle und Binärdatei ein und muss gewartet werden, sobald er existiert. Das Vermeiden von unnötigen Hinzufügungen wird in der Softwareentwicklung immer als bewährte Methode angesehen.

#2: Um das Problem zu lösen, muss es überhaupt existieren

Dies ist eine Abweichung der bisherigen Praxis. Etwas Unnötiges hinzuzufügen ist keine gute Idee, aber was ist notwendig und was nicht?

../../_images/best_practices2.png

Die Antwort auf diese Frage ist, dass das Problem existieren muss. Es darf keine Spekulation oder Überzeugung sein. Der Benutzer muss die Software bestimmungsgemäß verwenden, um etwas zu erstellen, das er benötigt. In diesem Prozess kann der Benutzer auf ein Problem stoßen, das eine Lösung erfordert, um fortzufahren oder um eine höhere Produktivität zu erzielen. In diesem Fall wird eine Lösung benötigt.

Der Glaube, dass Probleme in der Zukunft auftreten können und dass die Software zum Zeitpunkt ihres Auftretens bereit sein muss, sie zu lösen, wird als "Zukunftssicherheit" bezeichnet und ist durch folgende Gedankengänge gekennzeichnet:

  • Ich denke, es wäre nützlich für Benutzer, ...

  • Ich denke, Benutzer bräuchten irgendwann ...

Dies wird im Allgemeinen als schlechte Angewohnheit angesehen, da der Versuch Probleme zu lösen die derzeit nicht wirklich existieren, sehr oft zu Code führt der geschrieben, aber nie verwendet wird oder zu Code dessen Verwendung und Wartung erheblich komplexer ist als es sein muss.

#3: Das Problem muss komplex sein oder häufig auftreten

Software wurde entwickelt um Probleme zu lösen, aber wir können nicht erwarten, dass sie jedes Problem löst, das unter der Sonne existiert. Als Spiel-Engine löst Godot Probleme für Sie, sodass Sie Spiele besser und schneller machen können, sie entwickelt aber nicht das gesamte Spiel für Sie. Irgendwo muss eine Linie gezogen werden.

../../_images/best_practices3.png

Ob es sich lohnt, ein Problem zu lösen, hängt von der Schwierigkeit ab, mit der der Benutzer es umgehen muss. Diese Schwierigkeit kann ausgedrückt werden als:

  • Die Komplexität des Problems

  • Die Häufigkeit des Problems

Wenn das Problem für die meisten Benutzer zu komplex ist, um es zu lösen, muss die Software eine vorgefertigte Lösung dafür anbieten. Wenn der Benutzer das Problem leicht umgehen kann, ist es dagegen nicht erforderlich, eine solche Lösung anzubieten, und es liegt beim Benutzer, dies zu tun.

Die Ausnahme ist jedoch, wenn der Benutzer häufig genug auf dieses Problem stößt. dass es jedes Mal ärgerlich ist, diese einfache Lösung jedes Mal durchführen zu müssen. In diesem Fall muss die Software eine Lösung anbieten, um diesen Anwendungsfall zu vereinfachen.

Nach unserer Erfahrung ist es in den meisten Fällen offensichtlich, wann ein Problem komplex oder häufig ist. Es kann jedoch vorkommen, dass das Ziehen dieser Linie schwierig ist. Aus diesem Grund wird immer empfohlen, mit anderen Entwicklern zu diskutieren (nächster Punkt).

#4: Die Lösung muss mit anderen besprochen werden

It is often the case that when users stumble upon problems, they are only immersed in their project, so they will naturally try to solve the problem from their perspective, thinking only about their use case.

Aus diesem Grund berücksichtigen vom Benutzer vorgeschlagene Lösungen nicht immer andere Anwendungsfälle, die Entwicklern häufig bekannt sind, und sind daher häufig auf ihre eigenen Anforderungen ausgerichtet.

../../_images/best_practices4.png

Für Entwickler ist die Perspektive anders. Sie finden das Problem des Benutzers möglicherweise zu einzigartig, um eine Lösung zu rechtfertigen (anstelle eines Workarounds für den Benutzer), oder sie schlagen eine Teillösung (normalerweise einfacher) vor, die für einen größeren Bereich bekannter Probleme gilt, und lassen den restlichen Lösungsweg beim Benutzer.

In jedem Fall ist es wichtig, vor dem Versuch eines Beitrags die tatsächlichen Probleme mit den anderen Entwicklern oder Mitwirkenden zu besprechen, damit eine bessere Einigung über die Implementierung erzielt werden kann.

Die einzige Ausnahme ist in diesem Fall, wenn ein Codebereich einen eindeutigen Eigentümer hat (von den anderen Mitwirkenden vereinbart), der direkt mit den Benutzern spricht und über das meiste Wissen verfügt, um eine Lösung direkt zu implementieren.

Godots Philosophie ist es auch, die Benutzerfreundlichkeit und Wartung der absoluten Leistung vorzuziehen. Leistungsoptimierungen werden berücksichtigt, sie werden jedoch möglicherweise nicht akzeptiert, wenn sie die Verwendung zu schwierig machen oder wenn die Codebasis zu komplex wird.

#5: Für jedes Problem eine eigene Lösung

Für Programmierer ist es immer eine sehr erfreuliche Herausforderung, die optimalen Lösungen für Probleme zu finden. Manchmal gehen die Dinge jedoch über Bord und Programmierer werden versuchen, Lösungen zu finden, die so viele Probleme wie möglich lösen.

Die Situation wird sich oft verschlechtern, wenn (um diese Lösung noch fantastischer und flexibler erscheinen zu lassen) die reinen spekulationsbasierten Probleme (wie in Nr. 2 beschrieben) auch noch auftauchen.

../../_images/best_practices5.png

The main problem is that, in reality, it rarely works this way. Most of the time, writing an individual solution to each problem results in code that is simpler and more maintainable.

Darüber hinaus sind Lösungen für individuelle Probleme für die Benutzer besser, da sie etwas finden, das genau das tut, was sie benötigen, ohne ein komplexeres System lernen und sich merken müssen, welches sie nur für einfache Aufgaben benötigen.

Big and flexible solutions also have an additional drawback which is that, over time, they are rarely flexible enough for all users, who keep requesting more functions added (and making the API and codebase more and more complex).

#6: Auf gängige Anwendungsfälle eingehen, die Tür für seltenen Fälle aber geöffnet lassen

Dies ist eine Fortsetzung des vorherigen Punktes, der weiter erklärt, warum diese Art des Denkens und Entwerfens von Software bevorzugt wird.

Wie bereits erwähnt (in Punkt 2), ist es für uns (als Menschen, die Software entwickeln) sehr schwierig, alle zukünftigen Benutzeranforderungen tatsächlich zu verstehen. Der Versuch, sehr flexible Strukturen zu schreiben, die viele Anwendungsfälle gleichzeitig abdecken, ist oft ein Fehler.

Wir können uns etwas einfallen lassen das wir für brillant halten, aber wenn es tatsächlich verwendet wird werden wir feststellen, dass Benutzer niemals die Hälfte davon verwenden werden oder dass sie Funktionen benötigen, die nicht ganz unserem ursprünglichen Design entsprechen, was uns dazu zwingt es entweder zu verwerfen oder noch komplexer zu gestalten.

Die Frage ist dann: wie soll Software entworfen werden, die Benutzern genau das gibt, von dem wir wissen, dass sie es brauchen, aber auch flexibel genug ist, um ihnen auch das zu ermöglichen, von dem wir noch nicht wissen, dass sie es möglicherweise in Zukunft brauchen werden?

../../_images/best_practices6.png

Die Antwort auf diese Frage lautet: Um sicherzustellen, dass Benutzer weiterhin das tun können, was sie möchten, müssen wir ihnen Zugriff auf eine Low-Level-API gewähren, mit der sie das erreichen können, was sie möchten, auch wenn es mehr Arbeit für sie ist, weil es bedeutet eine bereits existierende Logik neu zu implementieren.

In realen Szenarien sind diese Anwendungsfälle ohnehin höchst selten und ungewöhnlich. Daher ist es sinnvoll, eine benutzerdefinierte Lösung zu schreiben. Aus diesem Grund ist es wichtig, den Benutzern weiterhin die grundlegenden Bausteine dafür bereitzustellen.

#Nr. 7: Lokale Lösungen bevorzugen

Wenn Sie nach einer Lösung für ein Problem suchen, sei es die Implementierung einer neuen Funktion oder die Behebung eines Fehlers, ist es manchmal am einfachsten, Daten oder eine neue Funktion in die Kernschichten des Codes einzufügen.

Das Hauptproblem hierbei ist, dass das Hinzufügen von Elementen zu den Kernschichten, die nur von einem einzigen weit entfernten Ort aus verwendet werden, nicht nur das Verständnis des Codes erschwert (da zweigeteilt), sondern auch die Kern-API größer und komplexer macht, also im Allgemeinen schwieriger zu verstehen.

Dies ist schlecht, da die Lesbarkeit und Sauberkeit der Kern-APIs angesichts der Menge an Code die darauf basiert, immer von äußerster Wichtigkeit ist und weil dies der Schlüssel für neue Mitwirkende als Ausgangspunkt für das Erlernen der Codebasis ist.

../../_images/best_practices7.png

Die häufigste Begründung dafür ist, dass es normalerweise weniger Code ist, einfach ein Codeschnipsel in die Kernebenen einzufügen.

Trotzdem wird diese Praxis nicht empfohlen. Im Allgemeinen sollte der Code für eine Lösung näher an der Ursache des Problems liegen, selbst wenn er mehr Code enthält, dupliziert, komplexer oder weniger effizient ist. Möglicherweise ist mehr Kreativität erforderlich, aber dieser Weg ist immer der empfohlene.

#8: Verwenden Sie keine komplexen Dosenlösungen für einfache Probleme

Nicht jedes Problem hat eine einfache Lösung, und oft ist es die richtige Wahl, eine Bibliothek eines Drittanbieters zu verwenden, um das Problem zu lösen.

As Godot requires to be shipped in a large amount of platforms, we can't link libraries dynamically. Instead, we bundle them in our source tree.

../../_images/best_practices8.png

Infolgedessen sind wir sehr wählerisch in Bezug auf das, was hinzugefügt wird und wir bevorzugen eher kleinere Bibliotheken (einzelne Header sind wünschenswert). Nur in Fällen, in denen es keine andere Wahl gibt, bündeln wir etwas Größeres.

Außerdem müssen die Bibliotheken eine ausreichend freizügige Lizenz verwenden, um in Godot aufgenommen zu werden. Einige Beispiele für akzeptable Lizenzen sind Apache 2.0, BSD, MIT, ISC und MPL 2.0. Insbesondere können wir keine Bibliotheken akzeptieren, die unter der GPL oder LGPL lizenziert sind, da diese Lizenzen das statische Linken in proprietärer Software (als die Godot in den meisten exportierten Projekten verteilt wird) effektiv verbieten. Diese Anforderung gilt auch für den Editor, da wir ihn auf lange Sicht möglicherweise auf iOS betreiben wollen. Da iOS kein dynamisches Linking unterstützt, ist statisches Linking die einzige Option auf dieser Plattform.