Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

引擎贡献者的最佳实践

前言

Godot has a large amount of users who have the ability to contribute because the project itself is aimed mainly at users who can code. That being said, not all of them have the same level of experience working in large projects or in software engineering, which can lead to common misunderstandings and bad practices during the process of contributing code to the project.

语言

本文档的范围是罗列让贡献者遵循的最佳实践,并且创建一种语言,供他们用来指代提交贡献过程中出现的常见情况。

While a generalized list of software development best practices might be useful, we'll focus on the situations that are most common in our project.

贡献大部分时间都归类为错误修复, 增强功能或新功能. 为了抽象化这个想法, 我们将它们称为 解决方案, 因为它们总是寻求解决可以描述为 问题 的问题.

最佳实践

#1:问题第一

许多贡献者非常有创造力,他们就是享受设计抽象数据结构、创建漂亮的用户界面,或者就是单纯喜欢编程。不管是什么情况,他们都会想出很酷的想法,但这些想法可能并不能真正解决任何实际问题。

../../_images/best_practices1.png

这种情况通常被称为为解决方案寻找问题。在理想情况下并没有害处,但在现实世界中,代码需要花费时间去编写、需要占用空间、出现后就需要维护。在软件开发领域,避免添加任何不必要的东西永远是最佳实践。

#2:只能解决存在的问题

这是上一条的变体。添加不必要的东西是不好的,但是如何判断是否必要呢?

../../_images/best_practices2.png

答案是问题需要先存在,然后才能解决。问题不能是推测或者观点。用户必须正常使用软件,才能创造出他们需要的东西。在这个过程中,用户可能会遇到一些问题,需要有解决方案才能继续,或者达到更高的生产力。这种情况下就需要解决方案

坚信将来可能会出现问题,并且软件需要在问题出现时就已准备好解决它们,这被称为“保护未来”,其特点是思路如下:

  • 我想这对于用户来说是有用的....

  • 我认为用户最终需要...

通常认为这是不好的习惯,因为试图解决当前不存在的问题的结果往往是代码写好了,但是从来没人用,或者是无论用起来还是维护起来都比实际需要的要复杂地多。

#3:问题应该比较复杂或频繁出现

Software is designed to solve problems, but we can't expect it to solve every problem that exists under the sun. As a game engine, Godot will help you make games better and faster, but it won't make an entire game for you. A line must be drawn somewhere.

../../_images/best_practices3.png

Whether a problem is worth solving is determined by the effort that is required to work around it. The required effort depends on:

  • 问题的复杂性

  • 问题发生的频率

If the problem is too complex for most users to solve, then the software should offer a ready-made solution for it. Likewise, if the problem is easy for the user to work around, offering such a solution is unnecessary.

The exception, however, is when the user encounters a problem frequently enough that having to do the simple solution every time becomes an annoyance. In this case, the software should offer a solution to simplify the use case.

It's usually easy to tell if a problem is complex or frequent, but it can be difficult. This is why discussing with other developers (next point) is always advised.

#4:必须与其他人讨论解决方案

当用户偶尔发现问题时,他们往往会沉浸在自己的项目中。自然而然地,他们会尝试从他们自己的角度来解决问题,并只关注他们自己的情况。因此,用户提出的解决方案通常偏向于用户自己的需求,考虑不到所有情况。

../../_images/best_practices4.png

For developers, the perspective is different. They may find the user's problem too unique to justify a solution (instead of a workaround), or they might suggest a partial (usually simpler or lower level) solution that applies to a wider range of known problems and leave the rest of the solution up to the user.

In any case, before attempting to contribute, it is important to discuss the actual problems with the other developers or contributors, so a better agreement on implementation can be reached.

The only exception is when an area of code has a clear agreed upon owner, who talks to users directly and has the most knowledge to implement a solution directly.

另外,Godot的理念是支持易用性和维护性, 而不是绝对性能. 性能优化将被考虑, 但是如果它们使得某些东西太难使用或者给代码库增加了太多的复杂性, 那么它们可能不会被接受.

#5:不同的问题用不同的解决方案

For programmers, it is always a most enjoyable challenge to find the most optimal solutions to problems. It is possible to go overboard, though. Sometimes, contributors will try to come up with solutions that solve as many problems as possible.

当为了使这个解决方案显得更加奇妙和灵活, 纯粹的基于推测的问题(如#2所述)也出现在舞台上时, 情况往往会变得更糟.

../../_images/best_practices5.png

主要问题是,在现实中,它很少以这种方式工作。大多数情况下,只要为每个问题编写一个单独的解决方案,代码就会变得更简单,更易于维护。

Additionally, solutions that target individual problems are better for the users. Targeted solutions allow users find something that does exactly what they need, without having to learn a more complex system they will only need for simple tasks.

Big and flexible solutions also have an additional drawback which is that, over time, they are rarely flexible enough for all users. Users end up requesting more and more functionality which ends up making the API and codebase more and more complex.

#6:满足常见用例,为罕见用例敞开大门

这是前一点的延续, 这进一步解释了为什么这种思维方式和设计软件是首选.

如前所述(第2点), 我们(作为设计软件的人)很难真正理解所有未来的用户需求. 试图同时编写满足许多用例的非常灵活的结构常常是一个错误.

We may come up with something we believe is brilliant but later find out that users will never even use half of it or that they require features that don't quite fit into our original design, forcing us to either throw it away or make it even more complex.

The question is then, how do we design software that both allows users to do what we know they need to do now and allows them to do what we don't yet know they'll need to do in the future?

../../_images/best_practices6.png

这个问题的答案是, 为了确保用户仍然可以做他们想做的事情, 我们需要让他们访问一个 "低级API", 他们可以用它来实现他们想要的, 即使这对他们来说是更大的工作, 因为这意味着重新实现一些已经存在的逻辑.

在现实场景中, 这些用例无论如何都是非常罕见的, 所以需要编写一个定制的解决方案是有意义的. 这就是为什么仍然要为用户提供基本的构建块来实现这一点很重要.

#7:局部解决方案优先

当寻找一个问题的解决方法时, 例如实现新特性或者修复一个BUG, 有时最简单的方式是在核心代码层中, 添加数据或是一个新的代码函数.

这里的主要问题是, 在核心层中添加一些只在很远的地方使用的东西, 不仅会使代码更难理解(一分为二), 而且还会使核心API更大, 更复杂, 更难理解.

这是不好的, 因为核心api的可读性和干净性总是非常重要的, 因为它是新贡献者学习代码库的一个起点.

../../_images/best_practices7.png

A common reason for wanting to do this is that it's usually less code to simply add a hack in the core layers.

Doing so is not advised. Generally, the code for a solution should be closer to where the problem originates, even if it involves additional, duplicated, more complex, or less efficient code. More creativity might be needed, but this path is always the advised one.

#8:不要用复杂的现成解决方案来处理简单的问题

并非每个问题都有一个简单的解决方案, 很多时候, 正确的选择是使用第三方库来解决问题.

由于 Godot 需要在大量的平台上发布,我们无法对库进行动态链接。相反,我们将它们捆绑在我们的源代码树中。

../../_images/best_practices8.png

As a result, we are very picky with what goes in, and we tend to prefer smaller libraries (single header ones are our favorite). We will only bundle something larger if there is no other choice.

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 is the only option on that platform.