See all glossary terms

Software architecture

Just like an architect designs a building by planning the structure and room layout, software architecture is about designing the structure of a program. This notion encompasses:
  • The code organization.
  • How different parts of the program interact with each other.
  • The patterns and guidelines that developers follow to build the program.
Software architecture concerns itself as much with the production of code as it does with the performance of code.
It's everything we do to ensure the code we write is easy to update, change, navigate, and builds fast.
It is also everything we do to ensure the code runs fast, has few bugs, and accomplishes its stated goals.

What makes good software architecture?

Good software architecture is all about trade-offs. Rather than "good" or "bad" software architecture, it is more productive to talk in terms of "fit for purpose."
Often, different goals of architecture can be incompatible. For example, we usually have to choose between production speed (how fast we can ship new features) and performance (how fast the program runs).
Good software architecture is about finding the right balance between different aspects of a program. Here are some of the most important ones:
  1. Simplicity: Considering all of its requirements, a program should stay as simple as possible. Simple means that the architecture should not make it harder for new programmers joining the project to understand the codebase.
  2. Consistency: Your code should be consistent in its design. Your structure should allow you to solve similar problems in similar ways. Consistency in your code style and structure makes collaboration easier.
  3. Flexibility: A good architecture should ideally facilitate changes to the code when needed without introducing bugs or breaking unrelated parts of the game.
  4. Scalability: If you know that you're working on a large project or one you'll need to maintain for a long time, your architecture should be able to grow with the project. Ideally, it should handle an increasing number of features, content, and players without needing a rewrite of the entire codebase. However, when programs grow big or live for a long time, note that rewriting large parts of the codebase is often necessary to support future growth.

Godot comes with many architectural tools built in

When you work with a game engine like Godot, the engine provides you with a wealth of architectural tools. This means that, as a developer, you can focus more on the creative aspects of your game rather than getting bogged down in the technical details, making your life much easier. This is why, generally, people use engines like Godot.
Even in the editor, Godot provides many patterns and tools to help you structure your project. Scenes, scripts, and resources promote composition. Signals are the engine's version of the observer pattern, and autoloads are a version of singletons. Many programming patterns you'll find online often require little extra code when applied to Godot.
So, when users of an engine like Godot talk about software architecture, they're usually referring to the high-level structure of their game. They're talking about how they organize their scenes, scripts, and other assets to make their game efficient to iterate on.
It does not mean that software architecture is simple: It takes experience to keep code manageable as a program scales and increases in complexity.
However, it's important to note that while many architectures and programming patterns you can learn about online have a place and use, they may have limited value when applied on top of a large game engine like Godot. For instance, implementing a different architecture, like ECS (Entity Component System) or the "clean code architecture," can conflict with the engine and unnecessarily complicate your code.
When you're a beginner, it is very hard to understand the reasons behind specific code structure advice and whether the recommendations apply to your case. Don't hesitate to try things out and experiment! When you see someone talking about a pattern, try it, play with it, and see if it works for you.
Just be careful not to turn patterns into dogmatic gospels. Some people think there are strict rules to follow regarding software architecture and that different patterns and approaches are right or wrong, but this is not how it works. Software engineering is all about being mindful of the problems of a given project and choosing the most appropriate tools and solutions.

How do you achieve good software architecture?

You should do your best to stay pragmatic and prioritize the software's goals. It is not possible to nail every aspect, so you'll need to make choices about where to focus your efforts. This is as true for 3-hour game jams as for AAA games that take years to make.
It's better to think of software architecture as an ongoing process rather than a plan you set early and blindly follow afterward. Building good software is a craft, and the needs of projects evolve as you work through them. Structures and patterns that make sense to start with on day 1 may not be the most appropriate anymore on day 100 when the program's design has evolved.
One piece of advice generally holds true: Keeping the software as lean as possible is a major plus. Lean means keeping the code as straightforward as possible. It also means not adding unnecessary features or optimizing pieces of code that you have yet to identify as a problem. In other words, it's trying to write the simplest code that solves your problems.
By keeping the codebase as small as possible, you facilitate a number of the other goals:
  1. Simplicity: with less code and features to keep track of, the codebase is easier to read and understand.
  2. Consistency: staying consistent over a small codebase is much easier.
  3. Flexibility: a few lines of simple code are easier to modify than complicated functions with many options. By keeping the code simple, it becomes easier to change.
  4. Scalability: Simple code is easier to trace and audit, making it easier to change and optimize when needed.
Here is Wil Shipley's similar advice in his "Be inflexible" blog post, and here is Jeff Atwood agreeing in "the best code is no code at all".

How to keep code simple

Simplicity is... Hard! As programmers, we tend to always over-complicate things. How can we do better?
We can have an aversion to rewriting code, as it feels like we're redoing work we've already done. But rewriting is a crucial part of software development. Like artists draw many sketches before painting a masterpiece, we often need to iterate on our code to find the most appropriate solution.
So, you should always consider rewriting code as a serious option. Your first attempt at building a feature is an exploratory process. It's very hard to understand the requirements of any software before building it and getting it tested, so your first draft is generally about figuring out your program's requirements and constraints.
At this stage, you generally shouldn't embarrass yourself with considerations other than making things work. Write the code as if you were going to delete it, as an artist may throw away a bunch of sketches.
It can also help to write the new code in an isolated prototype so you don't have to deal with the rest of your codebase and have it slow you down.
Once your code works, start rewriting. During this phase, you should simplify as much as possible. Try to remove options you put in, reduce potential arguments, reduce potential states. Your goal in this second phase is to:
  1. Remove branches: the more possible overlapping states, the more complexity. You want to have as few if as possible.
  2. Remove unused features: If there's something you wrote "for later," remove it; if there's something you are using, consider what would happen if you removed it.
  3. Remove indirections: keep your code readable like a book. If you need to jump around many files to understand a piece of code, consider whether it is unavoidable and truly benefits you.
  4. Reduce potential bugs: Try to predict which areas may lead to bugs, and try to write your code so that making mistakes is harder. Using strong typing helps in this regard.
  5. Document: Document why your code was written in a certain way when it's not obvious, and how it's designed and structured so that you keep this knowledge and avoid undoing necessary decisions later.
Then, integrate the feature you wrote in isolation from the rest of the software.