Programming Standards and Guidelines - alexitsios/Calamity GitHub Wiki
The terms Standards
and Guidelines
are used interchangeably throughout this document.
What is this?
- An important document for programmers to review and reference throughout development.
- It's important to note that these are
Guidelines
, not rules. - It's a tool to help us all work together efficiently.
- Any member of the team may suggest changes to the guidelines at any point during development.
- Suggested changes will be discussed and finalized after consensus with the team.
- Let's make something that works best for all of us.
Programming guidelines help ensure that code is consistent and easy to understand, regardless of which team member wrote it. This leads to faster code comprehension and improved productivity, especially for team members with less experience. The goal is to be able to read and understand code at a glance. It should be easily understandable even by those who do not know programming. The guidelines also serve as a good starting point for new team members to quickly understand our code structure. Additionally, adhering to programming guidelines can reduce the time spent on reading and understanding code, which can ultimately lower maintenance costs and development time. Overall, programming guidelines benefit both individuals and the team as a whole.
Benefits of programming standards:
- Improves ease of team integration and code comprehension.
- Enhances code quality, efficiency, and ease of maintenance.
- Simplifies code complexity.
- Decreases development cost and time.
Drawbacks of not establishing clear programming standards:
- Each team member may adopt their own programming styles.
- Creates a more complex codebase structure.
- Difficulties in maintaining and debugging code.
- Increased development time and time for code comprehension.
- Can result in reduced team motivation and cause burnout.
Code refactoring:
- Don't go out of your way to change the existing code base to fit the guidelines.
- Apply guidelines to every new line of code and every area of the code you work with moving forward.
- The goal is not to undo finished work or waste time redoing what's already done.
- Code must conform to guidelines for new pull requests to be merged.
- Review the code architecture page for a comprehensive breakdown of our systems.
- S - Single Responsibility Principle (SRP)
- O - Open-Closed Principle (OCP)
- L - Liskov Substitution Principle (LSP)
- I - Interface Segregation Principle (ISP)
- D - Dependency Inversion Principle (DIP)
- Improved details of SOLID Principles will be added soon.
The Model-View-Controller (MVC) is a software design pattern that is used to separate concerns and responsibilities of different parts of a software application. It is a widely used pattern in web development, desktop application development, and game development.
In the MVC pattern, the application is divided into three main components:
- Model: The Model represents the data and the business logic of the application. It defines how the data is stored, how it is accessed, and how it is manipulated. It is responsible for managing the application state and responding to changes in the data.
- View: The View is responsible for rendering the data from the Model to the user interface. It is the component that the user interacts with and provides a way for the user to interact with the application.
- Controller: The Controller is responsible for managing the communication between the Model and the View. It receives input from the user and updates the Model accordingly. It also updates the View when the Model changes.
By separating the responsibilities of the application into these three components, the MVC pattern makes it easier to maintain and evolve the application over time. It also makes the code more modular and easier to test, as each component can be tested independently of the others.
- Utilize Unity's component system and our ScriptableObject architecture for a Composition over Inheritance design pattern.
- Use namespaces for related systems. Group systems by similar-functionality.
- Our namespace prefix is
Calamity
, e.g.namespace Calamity.NameOfAFramework{}
- Our namespace prefix is
- Denote the access modifier scope before declarations. Keep it
protected
,private
, andpublic
, generally. - Default access to
private
. If it can be private, keep it private. Only allow the lowest level of access needed. - Serialize private data and set values through the Unity inspector when appropriate to make systems that are malleable by designers.
- To expose private fields to the Inspector use
[SerializeField]
. - To hide a public field use
[HideInInspector]
.
- To expose private fields to the Inspector use
- When working with data that won't be exposed to the Inspector, prefer
properties
overpublic
fields for classes that will handle their own set methods, e.g.
//Another script cares about this,
public float boo; // Standard public variable
//Can be read by anything but only set locally
public float Boo {get; private set; } // Property
// Private serialized-fields can be extended with Public duplicates with a get accessor as a workaround if needed.
[SerializeField] private float _boo = "";
public float Boo { get { return _boo; } }
- Use descriptive names. Don't abbreviate for classes, methods, and variables. Describe things accurately so anyone can understand at a glance.
- Rename things often, whenever needed to more accurately describe something.
- Do so with right click and “rename” in Visual Studio. This will update all references.
- Add [FormerlySerializedAs] attribute when renaming serialized fields. Avoids breaking references in the Inspector.
- Requires using UnityEngine.Serialization;
- Use enums and constants with descriptive names instead of magic strings and numbers, e.g.
// Unclear 'Magic' variables (bad)
float gravity = 9.8f;
float jumpHeight = 2.5f * gravity;
// Use descriptive constants instead (good)
const float EarthGravity = 9.8f;
float jumpHeight = 2.5f * EarthGravity;
- Use Unity’s Update() method and its variants minimally. Every call is expensive.
- Debug.Log() and its variants should be used for debugging purposes, not as a print window for development.
- Use object pooling to reduce garbage collection and improve performance.
- Use static variables sparingly. If there's an easy alternative, use it instead.
- Variables and fields that can be made const should always be made const.
- If const isn’t possible, readonly might be a suitable alternative.
- Use strings minimally. Ideally, strings should only be used for printing to the UI.
- If we keep strings contained to a limited area and filter all output strings through a single access point, it will help later if we consider doing regional localization.
- Avoid Boxing variables as objects, e.g.
int x = 42;
object obj = x; // boxing occurs here
Naming conventions:
- Use PascalCase for class names, methods, enums, constants, static variables, and public variables, e.g.
PlayerController
. - Use camelCase for private variable names, e.g.
moveSpeed
. - Prefix private fields with an underscore, e.g.
_health
. - Don't use snake_case, e.g.
jump_height
Code formatting:
- Use tabs for indentation.
- Use one statement per line.
- Use braces on a new line, e.g.
private void ExampleMethod()
{
// code here
}
Commenting:
- Use inline comments sparingly and only when necessary.
- Use XML documentation comments for public methods and fields, e.g.
/// <summary>
/// An example method to demonstrate XML documentation using the summary, param, and returns tags.
/// </summary>
/// <param name="x">First value to sum.</param>
/// <param name="y">Second value to sum.</param>
/// <returns>Sum of x and y as an int value.</returns>
public int SumValues(int x, int y)
{
int z = x + y;
return z;
}