Up to date

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

Meojres practicas para los contribuyentes al motor

Introducción

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.

Lenguaje

El objetivo de este documento es proporcionar una lista de mejores prácticas para que los colaboradores las sigan, así como crear un lenguaje que puedan utilizar para referirse a situaciones comunes que surgen en el proceso de enviar sus contribuciones.

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.

Las contribuciones se categorizan la mayor parte del tiempo como correcciones de errores, mejoras o nuevas características. Para abstraer esta idea, las llamaremos Soluciones, porque siempre buscan resolver algo que puede describirse como un Problema.

Mejores Prácticas

#1: El problema siempre viene primero

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: Para resolver el problema, este debe existir en primer lugar

Esta es una variación de la práctica anterior. Agregar cualquier cosa innecesaria no es una buena idea, pero ¿qué constituye lo que es necesario y lo que no lo es?

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

Creer que pueden surgir problemas en el futuro y que el software debe estar listo para resolverlos en el momento en que aparezcan se conoce como "Future proofing", y se caracteriza por líneas de pensamiento como:

  • Creo que sería útil para los usuarios...

  • Creo que los usuarios querrán eventualmente...

Este es generalmente considerado un mal hábito porque intentar resolver problemas que en realidad no existen en el presente a menudo lleva a código que se escribe pero nunca se usa, o que es considerablemente más complejo de usar y mantener de lo que debería ser.

#3: El problema debe ser complejo o frecuente

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:

  • La complejidad del problema

  • La frecuencia del problema

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: La solución debe discutirse con otros

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.

Además, la filosofía de Godot es favorecer la facilidad de uso y el mantenimiento sobre el rendimiento absoluto. Las optimizaciones de rendimiento serán consideradas, pero es posible que no sean aceptadas si dificultan demasiado el uso o si agregan demasiada complejidad al código.

#5: A cada problema, su propia solución

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.

La situación a menudo empeorará cuando, para hacer que esta solución parezca aún más fantástica y flexible, también aparezcan los problemas basados en especulaciones puras (como se describe en el punto #2).

../../_images/best_practices5.png

El problema principal es que, en realidad, rara vez funciona de esa manera. La mayoría de las veces, escribir una solución individual para cada problema resulta en un código más simple y más fácil de mantener.

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: Satisfacer los casos de uso comunes, dejar la puerta abierta para los casos raros

Esto es una continuación del punto anterior, que explica aún más por qué esta forma de pensar y diseñar software es preferible.

Como se mencionó antes (en el punto #2), es muy difícil para nosotros (como seres humanos que diseñan software) comprender realmente todas las necesidades futuras de los usuarios. Intentar escribir estructuras muy flexibles que satisfagan muchas situaciones de uso a la vez suele ser un error.

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

La respuesta a esta pregunta es que, para asegurar que los usuarios aún puedan hacer lo que desean, necesitamos brindarles acceso a una API de bajo nivel que puedan utilizar para lograr lo que desean, incluso si requiere más trabajo por parte de ellos porque significa reimplementar alguna lógica que ya existe.

En escenarios del mundo real, estos casos de uso serán como mucho raros e infrecuentes, por lo que tiene sentido que se necesite escribir una solución personalizada. Es por eso que es importante seguir proporcionando a los usuarios los bloques de construcción básicos para hacerlo.

#7: Prefiere soluciones locales

Cuando buscas una solución para un problema, ya sea implementar una nueva característica o corregir un error, a veces el camino más fácil es agregar datos o una nueva función en las capas centrales del código.

El principal problema aquí es que agregar algo a las capas centrales que solo se usará desde un lugar lejano no solo hará que el código sea más difícil de seguir (dividido en dos), sino que también hará que la API central sea más grande, más compleja y más difícil de entender en general.

Esto es malo porque la legibilidad y limpieza de las API centrales siempre son de suma importancia, dado que mucho código depende de ellas, y también es clave para los nuevos colaboradores como punto de partida para aprender el código base.

../../_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: No utilices soluciones complejas para problemas simples

No todos los problemas tienen una solución simple y, muchas veces, la opción correcta es utilizar una biblioteca de terceros para resolver el problema.

Como Godot debe ser distribuido en una gran cantidad de plataformas, no podemos enlazar bibliotecas dinámicamente. En su lugar, las incluimos en nuestro árbol de código fuente.

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