

The Aware Game Engineer 2
Be Careful What You Wish for: Is AI Actually Profitable, or Just a Cursed Wish?
🗓️
Does using AI language models (LLMs) really accelerate game development, or do they just create hidden technical debt that can hurt us later? We’re going to approach this as a hypothesis: LLMs can dramatically speed up development, but also risk amplifying technical debt. Let’s test this claim together by looking at real examples from our workflow.
I spent the last couple of months observing the impact of AI on No Rest for the Wicked. In this article, we're gonna take a deeper look at what type of tool LLM really is and how to use it properly.
* Any time I mention AI-tool or LLM in this article I don’t mean any specific tool. On a daily basis, I usually use Gemini and Cursor. The article doesn’t consider which model is better.

Djin Lamp of software development
You are working on your product. One day, you hear some info about an appealing new technology that’s gonna revolutionize the world - Artificial Intelligence or Large Language Model. You search for the first available free AI tool on the web.
You can ask the tool to implement a mechanic in your game (almost) for free. The result works surprisingly well. You feel like you just discovered the magical lamp that can fix all your development struggles.
Be Careful What You Wish Prompt for
You spend a couple of hours playing with the endless possibilities of the tool (Vibe-Coding). After some time, you stumble upon a small aspect of the game that the tool cannot implement. It’s small, quite trivial to implement a feature, but even though it’s not possible for your artificial friend to make it work properly. You try to take a look at the code yourself.
You realize you lost control of your product a few hours ago. What went wrong? I prompted for good.
You stomped into horrible AI technical debt. In fact, if not used consciously, AI is just a Technical Debt factory in the long term.
3.What can we do about the technical debt?
The good news is that we can control the amount of technical debt added to our games by AI tools.
The more attention and validation we put on the AI work, the more control we have over it. As a result, less technical debt is introduced into the project.
However there are places in the codebase where we can consciously introduce technical debt and it won’t potentially bite us back at all. To understand why, let’s visualize the game codebase as a graph.
Dependency graph

Each type defined in the game code can be represented as a node. Edges symbolize dependencies among the types. If type A refers to type B and its execution flow depends on B's state, we say Type A is dependent on type B. In a perfect world, the graph should resemble a tree. Just looking at the type, we can count and estimate how dependency-heavy it is.
Dependency-heavy types are much more likely to introduce crazy technical debt. Just because each problem that occurs is gonna be multiplied by the number of dependencies.
What that means, for instance, is: introducing mess to the Class A is gonna cost us significantly more than just maintaining that mess for system A, since all its dependencies are gonna be influenced. So when you decide to introduce some confusion, bugs, performance overhead, or whatever form of tech debt you can imagine, we're gonna introduce it to all dependent types as well.
Technical Debt spreads among all dependencies.
Multi-dependency nodes

We can’t use AI as a Djin Lamp here. You need to know what you're gonna do, and we can treat LLM as cheap replacements for repetitive tasks, as a researcher, or just as another (not-so-trusty) programmer who helps you validate your work. That means, in my opinion, that part of the codebase should be only affected by an actual developer’s work. No place here for vibe-coding, some crazy stuff. Each change needs to be validated. A programmer who pushes AI-gen code takes full responsibility, as if he had written it himself.
My most frequently used cases of the AI during work are:
Smart refactoring tool: I give precise instructions to the LLM, which I apply and validate step by step.
Documentation search engine: I use it as an efficient search tool for documentation.
Project search engine: I use it as an efficient search tool for the project.
Solution research: I use it when I need some entry ideas for the problem solution.
Be careful, though: these tools are imperfect and (depending on the language model) have a strong tendency to hallucinate. Be aware of what you do; in the end, you are the developer and the person who takes responsibility for the work it produces.
Most models try to provide you with a solution, even if that’s an obscure heuristic that only works in some lalaland. Sometimes these solutions seem to work properly, but in the end, they're gonna turn out to be little more than hacks.
Never treat AI output as an absolute truth. In the end, you take responsibility for what you push.
When working on No Rest, we tried to use the tools to prototype some gameplay mechanics. Even though we develop games on Unity, the real logic engine is Quantum. We were impressed with the AI's result without checking the code. It compiled, it worked, and looked like it’s just the right solution.
It turned out that the model completely misunderstood how the quantum engine works and tried to invent assumptions to fit its only found solution. In the end, a developer reviewed the hallucinations and separated them from actual, useful code. That’s why, to make it sensical, you need to know what’s allowed.
AI tools are only really profitable when you understand technology better than they do.
Low-dependency nodes (Leaves)

Technical debt that sits on Leaves is much cheaper. It doesn’t spread across the whole game. We can find multiple examples of such nodes. Some Editor, UI, or even gameplay scripts. There might be multiple well-encapsulated nodes at the lowest level of the dependency graph.
Let’s consider a scenario in which we decide to implement an editor tool with LLM. Let’s stick to Items. Some general Items Manager. Using AI allows us to immediately prototype the tool. The implementation might not be the best, most scalable, or most performant, but as long as we don’t need to look into the code, the technical debt is negligible.
AI often produces code that's not performant enough. That requires a programmer's attention and validation. The best way to go here is to identify the most pending problems using the 20/80 rule. The time we would need to spend on performance adjustments is usually a fraction of the time we would need to sacrifice for implementing the node on our own.
The condition of leaf nodes seems to be a reasonable criterion for when to use AI tools.
+ We get a cheap implementation of the tool
+ Technical debt is “encapsulated” since the system doesn’t have any dependencies
+ We saved implementation time and often sanity points
Leaf Nodes are the cheapest places to allow AI technical debt costs.
There is still a question about the future of the code/system. Let’s now use our skills from chapter 1: (Link to: What do game developers and Fortune Tellers have in common?), and let’s try to predict the future. Indeed, it might turn out that the node is gonna have some dependencies in the future, or even that it’s gonna need to be rebuilt into some multi-node system, what then?
We need to be careful; in some cases, we may need to reimplement the tool from scratch, and the cost could be painful. If we overlook the moment when the system grows too big, we will need to pay for reimplementation, migration, and retesting. What’s the golden rule here?
Treat the node as a Leaf only if there are no plans to extend it into a multi-dependency system.
Otherwise, we need to consider it as a regular or high-dependency node.
Use cases of AI in No Rest for the Wicked.
Our design team has found immense value in AI for rapid prototyping. Having these tools allows them to quickly mock up and test isolated ideas before handing them off to engineering for proper implementation. However, in the core codebase, there is no place for unsupervised 'vibe-coding' - every change must be rigorously validated by an engineer. No Rest development confirms the rules I was trying to share in this article. We saved lots of time and money by implementing some of our editor tools or UI with the help of AI, as well as paying a high price for allowing it in some multi-dependency nodes. Item Manager
7.1 Leaf Node: Item Manager
We rely on various internal tools, such as our Item Manager. Because it is an isolated utility, we used AI to handle almost all of the repetitive boilerplate and initial scaffolding from the very beginning. It doesn’t have any dependencies. It is frequently extended with new features or content. The only work we do on the tool is performance and validation to ensure that the usage of other APIs is not violated. LLM keeps doing well at implementing all the design needs, even though it’s a huge tool so far. It has over 20,000 lines of code. While the AI’s output isn't the most elegant or optimized - as is typical with highly verbose IMGUI scripts - it is completely functional and, crucially, has absolutely zero dependencies. Because it is strictly an internal-only tool, the technical debt is negligible. I am sure we saved lots of money here.

If you wanna hear more about the tools we use in the project, check the interview Thomas Mahler gave to Thomas Brush
7.2 Multi-dependency node: Action System.
The action system is the game's central gameplay system. Recently, I was assigned to the task of reworking some lambda-closure allocations in the system (We use managed C# for both Unity and Quantum). The problem was super old and came from the system's very first version. It wasn’t fixed so far, since it wasn’t introducing any game-breaking performance overhead, and touching it was risky - central gameplay code, lots of use cases.
The problem: The action execution method allocates memory for the OnBeforeActionExecuted callback. We used lambda closures so far. The problem is that it allocates memory. 60 use cases that needed to be completely rewritten.
It would be tempting to ask an LLM to fix the problem: “Hey Chad, optimize my allocations on the Action system, please. Thank you, I love you.” That’s a multi-dependency node, though, and as we said, we don’t use LLM as Djin Lamp here. We solve the problem, AI only helps execute it.
The solution: For the more inquisitive, here is the unsafe workaround for the closure allocation problem. Callback code snippet:
Requirements: callback must run synchronously.
InstanceData is a pointer to the data of the executed action, so the caller can do some work on it.
Example use case - Before (Closure Alloc)
Example use case - After (No Alloc)
If I were alone, I would need to spend another day rewriting almost 60 use cases by hand. AI helped me to rework all methods in 15 minutes. Code review took me another 15 minutes. I did some testing, and the core gameplay system refactor took only an hour. Thanks to AI, I skipped repetitive handwork. It helped me review and validate the problem-solution I developed. Profit.
Key takeaways
AI language models are powerful tools that can significantly speed up your work when used accurately.
Never treat AI output as an absolute truth.
Any AI-generated code should always be validated and reviewed by an actual software engineer.
Never use LLM, as if it’s a Djin Lamp on multi-dependency nodes.
Speed up work on multi-dependency nodes by using AI as a refactoring tool, search engine or solution researcher.
Leaf Nodes are the best places to sacrifice AI technical debt to enable rapid iteration on some features.
Before taking on debt, ensure your leaf nodes don’t become multi-layered systems in the near future.

