somethinglikegames.de
Bluesky Logo

Categories


Tags

A fictional list of export formats; next to the T3D format is a smiley with a raised eyebrow

Before I get to the main topic, I’d first like to explain how this article came about.
During the summer semester of 2026, I had the opportunity to teach the elective course “Technical Art” as part of the “Virtual Worlds and Game Technologies” program at Offenburg University of Applied Sciences. Throughout the course, we covered various topics that might be of interest to technical artists. As part of their final assessment, all students were required to write a term paper on a project of their own in the context of technical art. Although grading was based exclusively on the written documentation, I had advised them to also submit the actual project files. That way, if a description in the text was unclear, I could check directly within the project. I was really pleased to see how diverse the projects turned out to be. Everything was represented, from workflow tools to real-time VFX. One of the projects, which was implemented using the Unreal Engine, included a file with the extension .t3d as an attachment.

T3D 🤨 I had never heard of this file format before. A quick search revealed that T3D used to be a text-based exchange format for Unreal Engine, but it can no longer be imported since Unreal Engine 4.13. However, the student had clearly created his project using Unreal Engine 5.7. So how could a file format that’s no longer supported have been exported in the first place? 🤔 The answer is surprisingly simple: Even current versions of Unreal Engine can still export numerous assets as T3D. All you have to do is open the export dialog in the Content Browser via Asset Actions → Export.

A few weeks ago, I told this story at the GAMESTATE Festival BW to a gameplay programmer who works extensively with the Unreal Engine. His immediate reaction was: “You should write an article about that.

As you can see, I’ve taken this suggestion on board. However, this article is not intended to be a critique of a single, forgotten file format.

Because, actually, T3D isn’t the problem at all.

T3D is merely a symptom.

Big software forgets

That’s exactly why I found the T3D case so interesting. A single forgotten export dialog isn’t a problem in and of itself. It only becomes problematic as an indication of how forgetful large software systems become over time. Not in the literal sense, of course. What I mean is that with every new feature, every refactoring, and every personnel change, a small piece of context is lost. Eventually, no one knows anymore why certain parts of the software even exist.

Unreal Engine has been evolving for decades now. Unreal Engine 5 wasn’t built entirely from scratch, but evolved from Unreal Engine 4, which in turn was built on Unreal Engine 3. Consequently, there are inevitably components whose origins date back more than ten years. At the same time, Epic Games is releasing new features at an impressive pace. A look at the public roadmap shows just how many features are added with each version. However, the bigger a system gets, the harder it becomes to keep track of all the legacy code and historical decisions.

The T3D export is a good example of this. In Unreal Engine 4.12, importing T3D files caused the editor to crash under certain circumstances. The initial solution in Unreal Engine 4.13 was simply to remove T3D as a selectable file format in the import dialog. However, using the file filter “*.*” still allowed the import to be forced along with the crash.

Only then was the import completely disabled. The patch notes state:

The user can no longer attempt to import non-FBX and non-OBJ files when importing into a level.

The related issue UE-32075 also mentions that exporting T3D files remains possible.

This issue was closed on July 5, 2016. Today, more than ten years later, this export feature still exists in Unreal Engine 5.7 and even in 5.8. The real problem here isn’t the export feature itself. What’s truly sad is that apparently no one even notices that it still exists. This is exactly what’s typical of big software systems that have grown over many years.

How I Know This Pattern

Before I began to focus more and more on game development, I worked for several years in traditional software development. There, I was part of a team responsible for a software system that had already been in existence for over ten years when I joined the project. None of the original developers were still with the company. On average, each team member was responsible for about 100,000 lines of code. While “lines of code” isn’t a meaningful metric for complexity, it does give an idea of just how big the system had become by then.
Automated tests were virtually nonexistent. The database layer was still based on a thesis written by one of the original developers, because at the time there were no established ORM tools in the Java ecosystem. So the team had simply developed its own.

The entire project was a prime example of legacy code that had aged poorly. Every change carried the risk of unexpected side effects. New features took longer and longer to implement because we first had to understand what impact a change might have elsewhere. Eventually, we managed to convince management to invest a portion of our development resources specifically in technical modernization. Over the course of several years, we gradually replaced the most problematic areas. The effort required was considerable. The results, however, were equally significant. As software quality improved, the error rate decreased. At the same time, maintainability improved, and many performance issues were resolved in the process.

Technical debt is not a bug

Now that we’ve seen how even a seemingly insignificant export dialog can remain unnoticed for years, the question arises: How do such situations arise in the first place? An obvious answer would be that someone made a mistake back then. But it’s probably not that simple.

In software development, the term “technical debt” is often used. However, it doesn’t automatically refer to bad code, incompetent developers, or a flawed architecture. In fact, technical debt often arises from perfectly understandable decisions.
Let’s imagine a project is on the verge of an important release. A new feature works in principle, but it really needs to be thoroughly reworked. Perhaps automated test coverage is missing, perhaps an existing interface needs to be fundamentally rethought, or the code contains some unsightly workarounds. All of these issues take time—time that is no longer available before the release.
The decision is often: Let’s release the feature in its current form for now and deal with it later. It is precisely at this moment that technical debt arises.

The term was coined by software developer Ward Cunningham, who deliberately chose it as an analogy for financial debt. After all, a loan isn’t inherently a bad thing. On the contrary, it enables investments that might otherwise not be possible at all. A loan only becomes problematic when you’re perpetually paying only the interest and never paying off the actual debt.
Technical debt works in much the same way. In the short term, it can be a sensible decision. A game has to be released on time, a customer is waiting for a feature, or a demo needs to be ready for a trade show. In such situations, it’s often more sensible to choose a pragmatic solution than to spend months working on the perfect architecture.
It only becomes difficult when the plan to clean things up later is constantly postponed.
With every additional shortcut, the system’s complexity increases just a little bit. New developers have to understand more and more historical baggage. Changes become riskier because no one can say for sure what side effects they’ll trigger. Developing new features takes longer, even though the team actually wanted to get faster.

I witnessed exactly this kind of development in the legacy project I described earlier. The system wasn’t difficult to maintain because the original developers had done a poor job. Many of their decisions made perfect sense at the time. The problem was rather that the project had continued to grow over many years without enough time being invested to regularly reevaluate those decisions and adapt them to new circumstances.

The T3D export also falls into this exact category for me. The fact that this export function still exists today doesn’t necessarily mean that someone made a wrong decision ten years ago. Presumably, there were simply more important problems to solve back then. What’s more interesting is that the function is still there ten years later–not because it’s needed, but because apparently no one has had a reason to remove it.

Technical debt, therefore, does not arise the moment a pragmatic decision is made. It arises when pragmatic decisions persist over time and no one can find the time to undo them.

Software quality means more than just having few bugs

So far, we’ve focused a lot on technical debt. But how can you actually tell when the quality of a piece of software is suffering? After all, the Unreal Engine works exceptionally well. It renders impressive graphics, offers countless tools, and is used for games, movies, and simulations. So a missing export dialog doesn’t suddenly make it bad software.

Why, then, should the T3D export be a quality issue at all?
The answer is that software quality encompasses much more than the mere absence of errors. Quality models such as ISO/IEC 25010 describe various characteristics that can be used to assess how well software can be developed, used, and maintained over the long term.

In the case of the T3D example, not all quality characteristics are equally relevant. The primary focus is on maintainability. Many of the problems described earlier (technical debt, legacy code, or forgotten features) can ultimately be traced back to this single quality characteristic.

Maintainability

Maintainability describes how easily software can be analyzed, modified, and verified. Especially in the case of long-lived software systems, it determines whether new requirements can be implemented efficiently or whether every change becomes a risk.

Does anyone even understand this feature anymore?

The first step in any change is to understand the existing system. Why does a feature exist? Who uses it? Which components depend on it?
These are precisely the questions that are difficult to answer when exporting to T3D. Is the feature merely a forgotten relic from the days of Unreal Engine 3, or are there still internal tools or workflows that rely on it? Without this knowledge, even the analysis itself becomes a challenge.

ISO 25010 refers to this characteristic as “analysability.” The easier it is to assess the impact of a change, the better the maintainability of the software.

Could you remove the T3D export tomorrow?

Suppose Epic Games decided to permanently remove the ability to export as a T3D file in Unreal Engine 5.9. Would that really be just a minor change? Maybe. Or maybe not.
There may still be plugins, build pipelines, or internal tools that rely on this very feature. There may even be users who rarely use the export feature but still depend on it.
The harder these questions are to answer, the riskier even small changes become. Developers begin to prefer leaving problematic areas untouched because no one can predict with certainty what side effects a change might entail.

This is precisely what the sub-feature “modifiability” describes. Good software is not characterized by the fact that it never needs to be changed, but by the fact that changes can be made with reasonable effort.

How would you even know that nothing broke?

Even if the T3D export could be removed without any problems, the question remains: How can we be sure that doing so won’t cause any other issues? Are there automated tests for this feature? Are there integration or regression tests that would detect potential side effects? Or would we have to rely on a user eventually reporting a bug?

ISO 25010 summarizes this aspect under the term “testability.” The easier it is to automatically verify changes, the lower the risk of unexpected side effects.
Especially in large software systems, testability therefore becomes a crucial component of good maintainability.

Usability

However, software quality affects not only developers but also their users. A user opens the Unreal Engine export dialog and discovers the T3D file format there. The obvious assumption is: If I can export this format, there’s probably a useful use case for it. However, this expectation is not met. Support for importing this format was removed over ten years ago.
The export dialog therefore communicates something different from the software’s actual behavior. For experienced Unreal developers, this may be nothing more than a minor quirk. For beginners, however, it can cause unnecessary confusion.

Usability doesn’t just mean that a feature exists; it also means that its operation is understandable and doesn’t raise false expectations.

Reliability

At first glance, reliability seems to have little to do with the T3D example. After all, the Unreal Engine no longer crashes due to T3D, since importing this file format is no longer possible. However, this quality characteristic becomes more interesting when you take a broader view.
The more difficult it is to analyze, modify, and test a software system, the more cautious its developers become. Refactorings are postponed, legacy issues persist, and known problems are often circumvented rather than resolved in a sustainable manner.
Poor maintainability thus also affects reliability in the long term. After all, the more difficult changes become, the greater the likelihood that errors will creep in unnoticed or that known problems will persist indefinitely.

The T3D export is therefore not the actual problem. Rather, it is a minor symptom of how difficult it becomes to maintain the quality of a software system at a high level over many years.

The Price of Versatility

Almost every successful software product evolves over time beyond its original intended purpose. New target audiences are added, existing features are expanded, and at the same time, as little of the existing functionality as possible should be disrupted. With each of these decisions, the system’s complexity grows.

Unreal Engine illustrates this development particularly well. Originally developed as an engine for the game Unreal, it has long since evolved into a universal platform. Today, it is used in the following areas, among others:

  • Game Development
  • Film Production
  • Virtual Production
  • Architecture and Archviz
  • Automotive
  • UEFN
  • Simulation
  • Digital Twins

Each of these application areas comes with its own set of requirements. While game developers may prioritize improvements to Lumen or Niagara, film studios might be looking for new features for virtual production. In the automotive industry, topics such as CAD data or high-resolution visualizations likely play a greater role. At the same time, all user groups expect existing workflows to continue functioning and for changes to their established processes to be kept to a minimum.

Added to this are requirements such as plugin compatibility, backward compatibility, and an API that is as stable as possible. All of these aspects make sense on their own. Taken together, however, they result in the software becoming more complex with each new version. The key point here is that this complexity rarely stems from a single major decision. It grows incrementally:

  • With every new feature
  • With every additional target audience
  • With every decision to retain existing behavior for compatibility reasons

This trend is by no means unusual. It is often the price that successful software pays for its versatility.

Feature Development vs. Product Quality

Ideally, new features increase a product’s value. At the same time, however, they almost always increase its complexity as well.

Let’s imagine two sprint reviews:

Sprint A:
  • A new Niagara feature
  • A new Lumen feature
  • New Animation nodes
Sprint B:
  • 18.000 lines deleted
  • Five obsolete APIs removed
  • Test coverage increased
  • Build time cut in half

Which sprint is more impressive? For most people, it’s probably Sprint A, even though Sprint B may have been the more important one in the long run.

The Real Insight

The T3D export is a small example of a pattern that can be observed in nearly every long-lasting software project.

Technical debt rarely results from poor developers or bad decisions. Most often, it arises from compromises. A feature needs to be finished on time, a deadline is approaching, or an existing solution is good enough. The problem arises only when these compromises are never questioned again.

This is precisely why software quality is not a state that is ever fully achieved. It is an ongoing process. Old decisions must be reviewed regularly, features that are no longer needed must be removed, and technical debt must be consciously reduced. Otherwise, the system’s complexity continues to grow insidiously, often so slowly that it isn’t noticed until changes suddenly become disproportionately expensive.

This is precisely where software development resembles the Broken Windows Theory. Put simply, it states that small signs of neglect encourage further neglect. Applied to software, therefore, the problem isn’t the single forgotten feature itself, but the signal it sends. It shows that apparently no one feels responsible for this part of the system anymore.

End

What’s truly astonishing about this whole story isn’t actually that a student accidentally exported a ten-year-old file format. He had no way of knowing that this format could no longer be reimported. What’s surprising, rather, is that even the engine itself apparently lost this knowledge over the course of its development.

Once they reach a certain size, nearly all long-lived software systems face the same challenges, regardless of whether they’re business applications, game engines, or operating systems. Since this can affect all systems, I’m particularly fond of a principle from the Boy Scouts that Robert C. Martin adapted to software development in Clean Code:

Leave the campground cleaner than you found it.

Perfection is virtually impossible to achieve in a large software system. But if every developer leaves the code in the section they’re working on just a little bit better than they found it, the system will improve on its own over time.

So what does this mean? Software quality depends less on whether technical debt accumulates and more on whether we recognize it and are willing to pay it off step by step.