best_practices_for_engine_contributors.rst 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. .. _doc_best_practices_for_engine_contributors:
  2. Best practices for engine contributors
  3. ======================================
  4. Introduction
  5. ------------
  6. Godot has a large amount of users who have the ability to contribute, given the
  7. project itself is aimed mainly at users with the ability to do programming.
  8. Despite this, not all of them have the same level of experience working in large
  9. projects or in software engineering, which can lead to common misunderstandings
  10. and bad practices during the process of contributing code to the project.
  11. Language
  12. --------
  13. The scope of this document is to be a list of best practices for contributors to
  14. follow, as well as to creating a language they can use to refer to common
  15. situations that arise in the process of submitting their contributions.
  16. While some may find it useful to extend this to general software development,
  17. our intention is to just restrict to situations that are most common in our
  18. project.
  19. Contributions are most of the time categorized as bug fixes, enhancements or new
  20. features. To abstract this idea, we will call them *Solutions*, because they
  21. always seek to solve something that can be described as a *Problem*.
  22. Best Practices
  23. --------------
  24. #1: The problem always comes first
  25. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  26. Many contributors are extremely creative and just enjoy the process of designing
  27. abstract data structures, creating nice user interfaces, or simply love
  28. programming. Whatever the case may be, they come up with cool ideas, which may
  29. not be actually solving any actual problems.
  30. .. image:: img/best_practices1.png
  31. These are usually called *Solutions in search of a problem*. In an ideal world,
  32. they would not be harmful but, in reality, code takes time to write, takes space
  33. as source and binary and requires maintenance once it exists. Avoiding the
  34. addition of anything unnecessary is always considered a good practice in
  35. software development.
  36. #2: To solve the problem, it has to exist in the first place
  37. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  38. This is a variation of the previous practice. Adding anything unnecessary is not
  39. a good idea, but what constitutes what is necessary and what isn't?
  40. .. image:: img/best_practices2.png
  41. The answer to this question is that the problem needs to *exist* before it can
  42. be actually solved. It must not be speculation or a belief. The user must be
  43. using the software as intended to create something they *need*. In this process,
  44. the user may stumble into a problem that requires a solution to proceed, or in
  45. order to achieve greater productivity. In this case, *a solution is needed*.
  46. Believing that problems may arise in the future and that the software needs to
  47. be ready to solve them by the time they appear is called *"Future proofing"* and
  48. its characterized by lines of thought such as:
  49. - I think it would be useful for users to...
  50. - I think users will eventually need to...
  51. This is generally considered a bad habit because trying to solve problems that
  52. *don't actually exist* in the present will often lead to code that will be
  53. written but never used, or that is considerably more complex to use and maintain
  54. than it needs to be.
  55. #3: The problem has to be complex or frequent
  56. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  57. Software is designed to solve problems, but we can't expect it to solve *every
  58. problem that exists under the sun*. As a game engine, Godot will solve problems
  59. for you, so it helps you to make games better and faster, but it won't make the
  60. *entire game* for you. A line must be drawn somewhere.
  61. .. image:: img/best_practices3.png
  62. Whether a problem is worth solving is determined by the difficulty the user has
  63. to work around it. This difficulty can be expressed as:
  64. - The complexity of the problem
  65. - The frequency the problem
  66. If the problem is *too complex* for most users to solve, the software must offer
  67. a ready-made solution for it. Likewise, if the problem is easy for the user to
  68. workaround, offering such a solution is unnecessary and it's up to the user to
  69. do it.
  70. The exception, however, is when the user stumbles into this problem *frequently
  71. enough* that having to do the simple solution every time becomes an annoyance.
  72. In this case, the software must offer a solution to simplify this use case.
  73. In our experience, in most cases it's usually obvious to tell when a problem is
  74. complex or frequent, but cases may arise where drawing this line is difficult.
  75. This is why discussing with other developers (next point) is always advised.
  76. #4: The solution must be discussed with others
  77. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  78. It is often the case that, when users stumble upon problems, they are only
  79. immersed in their own project, so they will naturally try to solve the problem
  80. from their own perspective, thinking only about their use case.
  81. Because of this, user proposed solutions don't always contemplate other use
  82. cases that developers are often aware of, so they are often biased towards their
  83. own requirements.
  84. .. image:: img/best_practices4.png
  85. For developers, the perspective is different. They may find the user's problem
  86. too unique to justify a solution (instead of a user workaround), or maybe they
  87. will suggest a partial (usually simpler or lower level) solution that applies to
  88. a wider range of known problems, and leave the rest of the solution up to the
  89. user.
  90. In any case, before attempting a contribution, it is important to discuss the
  91. actual problems with the other developers or contributors, so a better agreement
  92. on implementation can be reached.
  93. The only exception, in this case, is when an area of code has a clear owner
  94. (agreed by the other contributors), who talks to users directly and has the most
  95. knowledge to implement a solution directly.
  96. Also, Godot's philosophy is to favor ease of use and maintenance over absolute
  97. performance. Performance optimizations will be considered, but they may not
  98. be accepted if they make something too difficult to use or if they add too much
  99. complexity to the codebase.
  100. #5: To each problem, its own solution
  101. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  102. For programmers, it is always a most enjoyable challenge to find the most
  103. optimal solutions to problems. Things, however, may go overboard sometimes and
  104. programmers will try to come up with solutions that solve as many problems as
  105. possible.
  106. The situation will often take a turn for the worse when, in order to make this
  107. solution appear even more fantastic and flexible, the pure speculation-based
  108. problems (as described in #2) also make their appearance on stage.
  109. .. image:: img/best_practices5.png
  110. The main problem is that, in reality, it rarely works this way. Most of the
  111. time, just writing an individual solution to each problem results in code that
  112. is simpler and more maintainable.
  113. Additionally, solutions that target individual problems are better for the
  114. users, as they find something that does exactly what they need, without having
  115. to learn and remember a more complex system they will only need for simple
  116. tasks.
  117. Big and flexible solutions also have an additional drawback which is that, over
  118. time, they are rarely flexible enough for all users, which keep requesting more
  119. functions added (and making the API and codebase more and more complex).
  120. #6: Cater to common use cases, leave the door open for the rare ones
  121. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  122. This is a continuation of the previous point, which further explains why this
  123. way of thinking and designing software is preferred.
  124. As mentioned before (in point #2), it is very difficult for us (as human beings
  125. who design software) to actually understand all future user needs. Trying to
  126. write very flexible structures that cater to many use cases at once is often a
  127. mistake.
  128. We may come up with something we believe is brilliant, but when it's actually
  129. used, we will find that users will never even use half of it, or that they will
  130. require features that don't quite accommodate our original design, forcing us to
  131. either throw it away or make it even more complex.
  132. The question is then, how to design software that gives users what *we know they
  133. need*, but that is flexible enough to allow them to do *what we don't know they
  134. might need* in the future?
  135. .. image:: img/best_practices6.png
  136. The answer to this question is that, to ensure users still can do what they want
  137. to do, we need to give them access to a *low level API* that they can use to
  138. achieve what they want, even if it's more work for them because it means
  139. reimplementing some logic that already exists.
  140. In real-life scenarios, these use cases will be at most rare and uncommon
  141. anyway, so it makes sense a custom solution needs to be written. This is why
  142. it's important to still provide users the basic building blocks to do it.
  143. #7: Solutions must be local
  144. ~~~~~~~~~~~~~~~~~~~~~~~~~~~
  145. When looking for a solution to a problem, be it implementing a new feature or
  146. fixing a bug, sometimes the easiest path is to add data or a new function in the
  147. core layers of code.
  148. The main problem here is, adding something to the core layers that will only be
  149. used from a single location far away will not only make the code more difficult
  150. to follow (split in two), but also make the core API larger, more complex, more
  151. difficult to understand in general.
  152. This is bad, because readability and cleanness of core APIs is always of extreme
  153. importance given how much code relies on it, and because it's key for new
  154. contributors as a starting point to learning the codebase.
  155. .. image:: img/best_practices7.png
  156. The common reasoning for wanting to do this is that it's usually less code to
  157. simply add a hack in the core layers.
  158. Despite this, this practice is not advised. Generally, the code for a solution
  159. should be closer to where the problem originates, even if it involves more code,
  160. duplicated, more complex or is less efficient. More creativity might be needed,
  161. but this path is always the advised one.
  162. #8: Don't use complex canned solutions for simple problems
  163. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  164. Not every problem has a simple solution and, many times, the right choice is to
  165. use a third party library to solve the problem.
  166. As Godot requires to be shipped in a large amount of platforms, we just can't
  167. link libraries dynamically. Instead, we bundle them in our source tree.
  168. .. image:: img/best_practices8.png
  169. As a result, we are very picky with what goes in, and we tend to prefer smaller
  170. libraries (in fact, single header ones are our favorite). Only in cases where
  171. there is no other choice we end up bundling something larger.
  172. Also, libraries must use a permissive enough license to be included into Godot.
  173. Some examples of acceptable licenses are Apache 2.0, BSD, MIT, ISC, and MPL 2.0.
  174. In particular, we cannot accept libraries licensed under the GPL or LGPL since
  175. these licenses effectively disallow static linking in proprietary software
  176. (which Godot is distributed as in most exported projects). This requirement also
  177. applies to the editor, since we may want to run it on iOS in the long term.
  178. Since iOS doesn't support dynamic linking, static linking the only option on
  179. that platform.