Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

引擎贡献者的最佳实践

前言

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: 这问题总是第一个

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 solve real problems.

../../_images/best_practices1.png

These are usually called solutions in search of a problem. In an ideal world, they would not be harmful but, in reality, code takes time to write, takes up space and requires maintenance once it exists. Avoiding the addition of anything unnecessary is always considered a good practice in software development.

#2: 为了解决这个问题, 它必须放在开始位置

以前有过类似的尝试. 添加任何不必要的东西不是一个好主意, 但什么是必要的, 什么不是?

../../_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 upon a problem that requires a solution to proceed, or in order to achieve greater productivity. In this case, a solution is needed.

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

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

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

这通常被认为是一个坏习惯, 因为试图解决当前不存在的问题往往会导致代码被编写但从未使用, 或者使用和维护起来比需要的复杂得多.

#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: 这个解决方案必须与其他人讨论

Often, users will be immersed in their own projects when they stumble upon problems. These users will naturally try to solve the problem from their perspective, thinking only about their own use case. As a result, user proposed solutions don't always contemplate all use cases and are often biased towards the user's own requirements.

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