Empfohlene Vorgehensweise für Engine-Mitarbeiter

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

Der Umfang dieses Dokuments besteht darin, eine Liste bewährter Verfahren für die Mitwirkenden zu erstellen und eine Sprache zu erstellen, mit der sie auf häufige Situationen verweisen können, die bei der Einreichung ihrer Beiträge auftreten.

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.

Bewährte Techniken

#1: Das Problem steht immer an erster Stelle

Viele Mitwirkende sind äußerst kreativ und genießen es einfach, abstrakte Datenstrukturen zu entwerfen, nette Benutzeroberflächen zu erstellen oder einfach nur zu programmieren. Was auch immer der Fall sein mag, sie kommen mit coolen Ideen, die möglicherweise keine tatsächlichen Probleme lösen.

../../_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: To solve the problem, it has to exist in the first place

This is a variation of the previous practice. Adding anything unnecessary is not a good idea, but what constitutes what is necessary and what isn't?

../../_images/best_practices2.png

The answer to this question is that the problem needs to exist before it can be actually solved. It must not be speculation or a belief. The user must be using the software as intended to create something they need. In this process, the user may stumble into a problem that requires a solution to proceed, or in order to achieve greater productivity. In this case, a solution is needed.

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 ...

This is generally considered a bad habit because trying to solve problems that don't actually exist in the present will often lead to code that will be written but never used, or that is considerably more complex to use and maintain than it needs to be.

#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 das Problem für den Benutzer leicht zu umgehen ist, ist es ebenfalls 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

Es ist häufig der Fall, dass Benutzer, wenn sie auf Probleme stoßen, nur in ihr eigenes Projekt eintauchen. Daher versuchen sie natürlich, das Problem aus ihrer eigenen Perspektive zu lösen, indem sie nur über ihren Anwendungsfall nachdenken.

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

Das Hauptproblem ist, dass es in Wirklichkeit selten so funktioniert. In den meisten Fällen führt das Schreiben einer individuellen Lösung für jedes Problem zu Code, der einfacher und wartbarer ist.

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.

Große und flexible Lösungen haben auch den zusätzlichen Nachteil, dass sie im Laufe der Zeit selten flexibel genug für alle Benutzer sind, die immer mehr Funktionen anfordern (und die API und Codebasis immer komplexer machen).

#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 man Software entwirft, die Benutzern das gibt, was wir wissen, dass sie brauchen, aber flexibel genug ist, um ihnen zu ermöglichen, was wir nicht wissen, dass sie möglicherweise in Zukunft brauchen.

../../_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.

#7: Lösungen müssen lokal sein

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.

Da Godot auf einer großen Anzahl von Plattformen laufen muss, können wir Bibliotheken einfach nicht dynamisch verknüpfen. Stattdessen bündeln wir sie mit unseren Quelldateien.

../../_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.

Also, libraries must use a permissive enough license to be included into Godot. Some examples of acceptable licenses are Apache 2.0, BSD, MIT, ISC, and MPL 2.0. In particular, we cannot accept libraries licensed under the GPL or LGPL since these licenses effectively disallow static linking in proprietary software (which Godot is distributed as in most exported projects). This requirement also applies to the editor, since we may want to run it on iOS in the long term. Since iOS doesn't support dynamic linking, static linking the only option on that platform.