One of the more useful things about building software for a business you actually run is that bugs stop being abstract very quickly.
Senado Games sells across multiple marketplaces, and the management system I have been building increasingly acts as the layer that tries to make sense of them. Stock exists in the MIS, but it also exists as listings on eBay and elsewhere. Those views need to agree closely enough that I can make decisions from the system rather than constantly checking each marketplace by hand.
Recently I was working on post-sale reconciliation: when an item sells on one marketplace, the system should identify the product and make sure the stock position on the other channels is still sensible. Initially this is deliberately an action-oriented workflow rather than fully automatic stock adjustment. I want the system to surface what needs attention before I let it make every decision itself.
While doing that work, an older problem surfaced in the eBay catalogue synchronisation.
Two similar methods with very different meanings
The eBay integration had two ways of obtaining active listings.
One was intended for a relatively narrow live-data view. It looks at listings within a short end-time window and is useful when the application wants a current operational snapshot.
The other performs a much broader scan across multiple windows. That is the method intended for catalogue synchronisation, because an eBay listing can remain active for a long time.
At some point the catalogue sync had drifted onto the narrow live-data method.
Technically, both methods returned active listings. The code still looked plausible. The application still ran. But the semantics were wrong.
A long-lived listing outside the narrow window could disappear from the result set. The synchronisation process could then conclude that a listing it already knew about was no longer active and mark it as ended.
That is a much more interesting class of bug than an exception or a failed API call. Nothing necessarily crashes. Instead, the system confidently records something that isn’t true.
The fix was small; understanding the boundary wasn’t
The production fix was straightforward: catalogue synchronisation went back to using the full active-listing scan.
The more useful lesson was why the mistake was possible.
Both operations deal with “active eBay listings”, but they answer different questions:
- Live data: what listings are relevant to the operational view I need right now?
- Catalogue synchronisation: what listings exist that my local catalogue must continue to recognise?
Those aren’t interchangeable queries, even though their return types can be identical.
This is something I keep running into as the MIS grows. A method that is technically capable of supplying some data is not necessarily appropriate for every business process that consumes that data. The time horizon, completeness guarantees and purpose of a query are part of its contract, even if Java’s type system cannot express them.
The naming helps, but documentation and tests matter too.
AI changes the development loop, not the responsibility
AI-assisted development is now part of how I work on the MIS. I use tools such as Codex and Claude to help investigate problems, implement changes and work through tests, while I provide the business context, decide what the system should actually do and review the results against the way the business operates.
It can make the development loop considerably faster, but this bug is also a useful example of what AI does not remove. A narrow active-listing query can look perfectly reasonable in isolation. Understanding that catalogue synchronisation requires a much broader view depends on the semantics of the business process, not simply on producing code that compiles and passes data between compatible types.
If anything, faster AI-assisted development makes explicit contracts and meaningful tests more important. Code can now be produced and changed very quickly; the responsibility for deciding whether it represents the right behaviour has not gone away.
Tests can drift as well
Fixing the sync also exposed another form of drift: some of the unit tests no longer accurately represented the production code.
The tests were stubbing an older repository lookup rather than the method now used by the service. They also contained stubs for calls that were no longer exercised on the paths under test, which matters when using Mockito with strict stubbing.
More importantly, a test needed to represent the current product-resolution behaviour properly: an existing marketplace listing should resolve back to the product it has already been matched to rather than accidentally creating or resolving something differently.
So the change became partly a catalogue-sync fix and partly a test realignment.
I don’t regard that as merely tidying tests after changing production code. Tests are another description of how the system is supposed to behave. If that description has drifted far enough from reality, it stops protecting the behaviour that matters.
Why I haven’t fully automated stock reconciliation
The wider feature that led me here is post-sale stock reconciliation.
The obvious end state is attractive: sell something on eBay and automatically reduce the corresponding available stock everywhere else. No manual intervention, no overselling, no duplicated admin.
I’m deliberately not jumping straight there.
Marketplace inventory has enough awkward cases — fulfilment stock, held stock, listing mismatches, delayed marketplace data and imperfect product mappings — that automatic correction can amplify a bad assumption very quickly.
For now, the MIS can identify an individual sale, reconcile the relevant product across marketplaces and create an action where intervention is needed. That gives me a useful intermediate stage: automate the detection and the boring investigation, while keeping the consequential decision visible.
As confidence in those rules grows, individual actions can become automatic.
That feels like a better automation path than starting with “make everything automatic” and then adding exceptions after it causes trouble.
The useful part of running your own software
None of this is particularly exotic engineering. It is Java services, repository lookups, marketplace APIs, tests and some fairly ordinary business rules.
What makes it useful is the feedback loop.
I can build a feature, use it while actually running the shop, discover where the model differs from reality, and feed that straight back into the software. The resulting system is increasingly shaped by operational edge cases rather than an imagined specification.
This particular bug reinforced two things for me.
First, data retrieval methods have business semantics beyond their signatures. “Give me active listings” is incomplete unless you also know active over what horizon and for what purpose.
Second, automation earns its way into an operational system. Automating observation and reconciliation first, then automating actions once the rules have proved themselves, is often the safer progression.
The next stage is to keep tightening that post-sale workflow until the manual actions become boringly predictable. At that point, they are probably ready to disappear.


