Code contributions should adhere to the following quality standards to maintain the consistency, reliability, and maintainability of the Kanda SDK. These standards ensure that all code is tested, backward compatible, and supports cross-platform functionality.
Conventions and Patterns
Follow the Kanda SDK style and structure conventions. Detailed guidelines are available in:
Premature Complexity
Code is not an asset, but a “debt”. The asset is the value that the code brings.
Strive to reduce the amount of dead code and unused features in the codebase. These bring complexity and make it harder to understand the code.
Some rules-of-thumb:
- If something might be needed in the future, implement it in the future, not now.
- If something is not used, delete it (be aware of backwards compatibility when relevant).
Predictable code makes future improvements and features simpler to add.
Example:
public class UserDataManager {
public void SaveUserData(string data) {
string encryptedData = EncryptData(data);
SaveToFile(encryptedData);
}
private string EncryptData(string data) {
return data;
}
private void SaveToFile(string data) {
}
}
public class UserDataManager {
public void SaveUserData(string data) {
SaveToFile(data);
}
private void SaveToFile(string data) {
}
}
Manual Testing
Manual testing in the Unity editor is essential during development. For larger changes, ensure to test in actual builds, including login and lobby, for all target platforms.
Example:
public class FeatureTester : MonoBehaviour {
void Start() {
SimulateLogin();
TestLobbyConnection();
}
private void SimulateLogin() {
Debug.Log("Simulating login...");
}
private void TestLobbyConnection() {
Debug.Log("Testing lobby connection...");
}
}
Automated Testing
Automated testing is crucial for long-term quality assurance. Tests help reveal bugs in your code and ensure that refactoring or related changes by other developers do not break existing functionality.
When adding or changing functionality in the Kanda SDK, cover critical cases with unit tests to ensure logic integrity.
Example:
[Test]
public void UserPreferenceManager_ShouldSaveAndLoadPreferences() {
UserPreferenceManager manager = new UserPreferenceManager();
manager.SavePreference("theme", "dark");
string value = manager.LoadPreference("theme");
Assert.AreEqual("dark", value);
}
Error Handling
Consider how your code might fail due to external reasons and handle these scenarios appropriately.
Use Unity Assertions for upfront validation of data passed to your code. Surround risky logic with "try blocks" to handle errors gracefully.
Example:
public class DataLoader {
public void LoadData(string path) {
Assert.IsFalse(string.IsNullOrEmpty(path), "Path should not be null or empty");
try {
string data = File.ReadAllText(path);
ProcessData(data);
} catch (FileNotFoundException ex) {
Debug.LogError("File not found: " + ex.Message);
} catch (Exception ex) {
Debug.LogError("Error loading data: " + ex.Message);
}
}
private void ProcessData(string data) {
}
}
- Note
- Assertions and exceptions might not work as expected in Burst-compiled code
Boy Scout Principle
When working with legacy code, strive to leave it in a better state than you found it.
If cleaning up a class requires significant rework, perform it first and submit as a separate pull request to simplify reviews.
While complete coverage of legacy code with tests is not required, try make sure critical paths are covered.
Example:
public class LegacyUserManager {
public void UpdateUser(string id, string data) {
}
}
public class UserManager {
public void UpdateUser(string userId, string newData) {
ValidateUserId(userId);
SaveUserData(userId, newData);
}
private void ValidateUserId(string userId) {
if (string.IsNullOrEmpty(userId)) {
throw new ArgumentException("User ID cannot be null or empty");
}
}
private void SaveUserData(string userId, string newData) {
}
}
[Test]
public void UserManager_ShouldUpdateUserCorrectly() {
UserManager manager = new UserManager();
manager.UpdateUser("123", "new data");
Assert.Pass();
}
Backward Compatibility
Code changes should be backward compatible. Implement changes in a way that allows them to co-exist with previous implementations, using composition, feature flags, strategy patterns, or similar methods.
Component composition example:
public struct Player : IComponentData {
public int health;
}
public struct Regeneration : IComponentData {
public int regenerationRate;
}
public struct UpdatePlayerSystem : ISystem {
public void OnUpdate(ref SystemState state) {
foreach (var (player, entity) in SystemAPI.Query<Player>().WithNone<Regeneration>()) {
player.health += 10;
EntityManager.SetComponentData(entity, player);
}
foreach (var (player, regeneration, entity) in SystemAPI.Query<Player, Regeneration>()) {
player.health += regeneration.regenerationRate;
EntityManager.SetComponentData(entity, player);
}
}
}
Feature flag example:
public class FeatureToggle {
public bool NewFeatureEnabled { get; set; }
public void ExecuteFeature() {
if (NewFeatureEnabled) {
ExecuteNewFeature();
} else {
ExecuteOldFeature();
}
}
private void ExecuteNewFeature() {
}
private void ExecuteOldFeature() {
}
}
Strategy pattern example:
public interface IMovementStrategy {
void Move(Transform transform);
}
public class WalkMovement : IMovementStrategy {
public void Move(Transform transform) {
transform.Translate(Vector3.forward * Time.deltaTime);
}
}
public class FlyMovement : IMovementStrategy {
public void Move(Transform transform) {
transform.Translate(Vector3.up * Time.deltaTime);
}
}
public class PlayerMovement : MonoBehaviour {
private IMovementStrategy movementStrategy;
private void Start() {
movementStrategy = new WalkMovement();
}
private void Update() {
movementStrategy.Move(transform);
}
public void SetMovementStrategy(IMovementStrategy newStrategy) {
movementStrategy = newStrategy;
}
}
public class GameController : MonoBehaviour {
public PlayerMovement playerMovement;
private void Start() {
playerMovement.SetMovementStrategy(new WalkMovement());
playerMovement.SetMovementStrategy(new FlyMovement());
}
}
Common Backward Compatibility Smells
- Automated tests are failing.
- Changing or adding core components.
- Adding special cases or enabled-by-default features to widely used components.
Explicitly test for backward compatibility if you encounter these smells to ensure the old functionality remains operational or has a clear migration path.
Cross-Platform Support
The Kanda SDK supports PC and VR default. New features should ideally support all platforms. If not, specify this in your PR and ensure the feature is disabled by default on unsupported platforms.
If extending a core platform system (e.g., VR input), extend other platform systems similarly to prevent feature disparity and hard-to-debug issues.
By following these quality standards, we can maintain a high level of code quality and reliability across the Kanda SDK.
Example:
public class PlatformFeatureHandler {
public void Execute() {
#if UNITY_STANDALONE
ExecutePCFeature();
#elif UNITY_ANDROID
ExecuteVRFeature();
#endif
}
private void ExecutePCFeature() {
}
private void ExecuteVRFeature() {
}
}