Once I had finished what I considered to be a release-ready version of my accounting/logistics mod (Factory Ledger version 0.8.0), I began working with it to verify its behaviour.
The first thing I examined were the transaction accounts: Receiving, Transition and Shipping. I performed a full reset. After resetting, all inventory should obviously be zero. Then I checked the receiving side. That looked fine. I could clearly see the three basic materials — coal, iron~ and copper ore — flowing into the system. The accounts became positive accordingly. Good. That was exactly what I had expected.
Next, I looked at the shipping side, i.e. the outbound side. This naturally takes a while until the system ramps up because all the intermediate buffers have to be filled first: copper has to move from raw resources through multiple processing steps before finished products appear. The shipping was OK, too. The number of utility science packs shipped is the same as the number produced.
As a side note, one could probably optimise this ramp-up with some kind of priority control, but that is a separate optimisation topic and not relevant here.
![]() |
| Head of the movement table in Excel. |
After letting the system run for a while, I ended up with around 200,000 transaction records. I exported these and loaded them into Excel. As the factory ledger's output format is semicolon-separated, I simply converted it to tabs and replaced the equals signs. Then, it could be pasted directly into Excel. After that, the columns need to be labelled properly, and once they are clean, analysis becomes straightforward.
![]() |
8 furnesses M10 to M17 for copper-plates |
With this part of my data in the Excel sheet, I created a pivot table, which is also shown on the right-hand side. In the rows, you can see the different inserters, each identified by a number, for example 115, 117, and so on. In the columns, I have copper ore and copper plates, always showing the give and the take.
As in accounting, this should be balanced. Everything that goes in should also go out. This is logistics. There is no creation out of nothing, and there are no black holes where things disappear. So the system should be balanced.
But if you look at the table, you can see that this is not the case. There are inserters that give more material than they take: they put material away, but they take less. And there is also the opposite effect. Some inserters give only a small amount but take much more. This is very difficult to understand.
Examining the code explained why.
A transaction was logged when the last item was placed onto a belt or when the last item was taken. From a physical perspective, however, these processes often consist of multiple actions. A stack inserter may pick up several items at once, but then place them onto a belt one by one over many ticks.
The code, however, created only a single booking for this entire process, and this booking used the remaining item count at the final moment when the event was emitted. As a result, if an inserter took 12 items from a chest but placed them onto the belt one by one, the final GIVE event often recorded a count of 1, because only the last item was still in the inserter’s hand at that moment. The corresponding TAKE event, however, still showed 12. From an accounting perspective, this is clearly wrong.
This issue did not become apparent by just thinking about the problem or by reading the code. It emerged through a discussion with ChatGPT. I described the observed imbalance in the data and the behavior visible in the pivot table. ChatGPT then analyzed the relevant Factorio API events and their semantics. This led to the insight that different internal events are involved in taking and placing items, and that my logging logic was tied to the wrong moment in time. Based on this understanding, I decided to change the implementation to fix the issue.
Factorio simulates physics. Accounting does not.
link to factorio mod portal (under development take newest) |
This approach deliberately separates three layers that should never be conflated:
-
Physical simulation: Tick-by-tick, item-by-item behavior (belts, delays, blocking, partial unloading).
-
Logical transport operation: “Inserter moved X items from A to B,” regardless of how long that took or how many micro-steps were involved.
-
Accounting / analysis data: Stable, aggregate transaction records that can be summed, balanced, and meaningfully analyzed in tools like Excel.
Once this separation is enforced in the code, the transaction data becomes consistent and trustworthy. Quantities balance. Pivot tables make sense. And higher-level analysis — throughput, bottlenecks, abnormal delays — can be built on top of the accounting layer, instead of being confused with it.
The bug was not a Factorio quirk, but my modeling error: accounting logic must not follow physics too closely. It has to abstract it.
Transparency note:
I am responsible for the ideas, concepts and overall direction of this work. I define the questions, goals and constraints. Throughout the process, I work closely with AI systems in an assistant capacity.
They help me to think through problems, read and interpret APIs, validate assumptions, write and refine code, and improve technical explanations. This support is especially effective in areas where speed, breadth and systematic analysis matter.
This does not replace authorship or responsibility. The division of labour is clear: I act as the spiritus rector, setting intent, structure and judgement, while the AI system acts as a highly capable assistant, helping to translate these ideas into concrete implementations and texts.
Blog: , Seite:
Version: 1.4 April 2025, Kontakt: E-Mail Martin Wölker
Pirmasens, Germany, 2018-,
ausgelesen am: , Licence
CC BY


Kommentare
Kommentar veröffentlichen