Code Architecture Considerations
Pitching Code Architecture Standards
Recently I have tried to emphasise conforming to code architecture standards such as the SOLID principles and the common closure + common reuse principle in an effort to curb the technical debt of the upcoming title for BigBoiGames. This has prompted me to revisit the Clean Architecture book written by Robert C. Martin and take consideration the principles they suggest and how to apply it in our Unity environment.
“The primary purpose of architecture is to support the life cycle of the system. Good architecture makes the system easy to understand, easy to develop, easy to maintain and easy to deploy. The ultimate goal is to minimize the lifetime cost of the system and to maximise programmer productivity.” (Pg. 137)
The Pitching
This was met with mixed opinions, with some members of the team readily willing to conform to these principles, and others either indifferent or hesitant due to not seeing the benefits of the extra work and caution compared to how they normally write code.
This presented some work to cover: How to convey the benefits of conforming to these standards.
The Benefits of Standards
I firmly believe that the SOLID principles alone are an extremely useful tool (when used sensibly) causing careful considerations which ultimately lead to future extensibility of the product with a small cost in time. An example of this would be the Open-Closed Principle and Dependency Inversion.
Through carefully architecting our classes and interfaces we are able to create code that achieves the current behaviours we are looking for in our game while also leaving the opportunity to for example create a new implementation of an interface and switch out the interface we are utilsing thereby changing the behaviour of a component such as a weapon.
Without careful consideration, guided by the SOLID principles which often work in harmony - this could become problematic and lead to a developer creating far more code, dependencies and thus technical debt than if they had simply kept to the standardisation of their code.
Open-Closed and Dependency Inversion Elaborated Example
To elaborate, imagine a Character which must be equipped with a weapon. For simplicitys sake the capabilities of this can be reduced to Attack, Defend and Power-Up.
Thus we can create a
public interface IWeapon
which contains the definition of members:
Attack();
Defend();
PowerUp();
This may then be implemented by an abstract class of weapon, which may be inherited by a class named MeleeWeapon. Just as easy as that we have created a melee specific behaviour, but better still - if we decide suddenly that we would like the character to use a ranged weapon such as a bow all we must do is allow the player to switch the implementation of IWeapon for example in a function
private void SwitchWeapon(IWeapon weaponType)
within the Character class.
Design Patterns in Game Development
Another important consideration the team had to take into account was design patterns, which have become important additions to the arsenal in structuring our codebase and trying to ensure effective use of the implementations we create. Ultimately aiding in a pleasant programming environment with a managable amount of technical debt.
The Singleton, Observer and Command pattern come to mind after the team making an effort to place these patterns where appropriate.
The Dangers of Code Architecting
I believe that code architecture can be a bit of a rabbit hole, leading some developers to take principles and patterns too far and applying where there is no need to apply or to an extent that simply harms productivity - the opposite of what we are trying to do!
An example of this would be the CQRS pattern in backend applications, while there are certainly applications that benefit from this architectural decision, there are ultimately APIs which do not require this strict and overkill pattern which will just cause more problems than it ‘solves’.
Ultimately the final decisions lay between the programmer, the reviewer and common sense to ensure the codebase does not go over the top and reverse the good effects that architecting with patterns and principles can do!
Future Considerations
Another thing I have picked up from the Code Architecture book is the code coupling section, a means to streamline the management of dependencies throughout the codebase. While not directly applicable to my team, yet. I believe it would be useful to research these methods and how they would integrate in a Unity environment early on as the project grows and the business vision expands.