The main source of problems is often the design of something, that makes an apparently easy change complicated.
To give you an example, when I did a bit of Dominion programming for myself years ago, I just started with the base set. In that time, a supply pile during the game could easily be described by 2 things: The card it represents, and the number of cards still in it.
When you start with this and continue and do work, then everything is fine, until Dark Ages comes out and suddenly you have 2 piles that contain different cards (Ruins and Knights), later of course more (split piles, Castles).
What's easy to do and handle in real life becomes a problem in your code. In real life, you always had to put down 10 cards per pile, that doesn't change with Kniths. But in the code, you just had 2 things representing a pile: The card in it and the number of cards left in it. That has to change, you must now represent each card in a pile by its own thing (thats an object in todays programming paradigm).
That's some busywork, but usually doable. The big problem is that probably in some places in your code you have assumed that only one kind of card can come from a pile. Consider Talisman, a Treasure card that when it is in play and you buy a card costing $4 or less, you gain another copy of that card.
So, what you could have done in the Dominon world before Dark Ages is to check, when you buy a card, how many Talismans are in play (call that number X), and how many copies of the card you want to buy are still in the supply (call that number Y).
If X+1 is less than or equal to Y, then you give the player X+1 copies of the card from the pile (one bought and one each for the X Talismans). If not, then you give them the remaining Y copies of the card, emptying the pile. That would have worked pretty well.
When Dark Ages comes around, you implement the changes I outlined above. You will have to change how to represent a supply pile and probably have a function that gives a player the top card of a pile, because that is needed very often (gaining cards happens regularly). If you don't have a special function that gives a player several cards from a pile, you'll change the check I described above to give the aditional cards from Talisman out one at a time. Everything works, even after extended testing.
So, on the surface all is well, until Empires comes out a few years later. You implement the split piles like you did Knights and Ruins. But 1 day after the online implementation goes life, you get bug reports. One player is complaining that their opponent got a Fortune in addition to the Gladiator they bought. Another says they don't think it is correct that they got an Emporium with the Patrician they bought. You look up the games and see that in both cases the players had a Talisman in play.
So what happened? The old assumption that you only need to count the cards in a pile to handle Talisman is now incorrect. If you buy the last Patrician/Gladiator, Talsiman is not supposed to give you the next card in the pile, because it is differently named card!
That situation could not exist when you programmed Talisman, so you did not take it into account. Such kind of problems are the source for many bugs, one of them the loss of an Ariadne 5 rocket: https://www.bugsnag.com/blog/bug-day-ariane-5-disaster
What happened was that an assumption that was correct for Ariadne 4 rockets was no longer true for Ariadne 5 rockets!
What I want to show with this is changing things that appear easy may have consequences that are not easily understandable from an outside perspective, because that doesn't take into account how the current behavior has been implemented. Maybe the part of the program that plays out the Treasure cards for bots isn't really part of the bot code, but some other code that needs to exist anyway for other reasons. Then you have to make new code (because the old code is still needed).
Sometimes information needed is not available at the place that executes that code, or it is complicated to bring this infomation to the place (think of transmitting someone who is physically away from you a lengthy shopping list, in the days before mobile phones; usually you had to talk to them on a (fixed) phone and they had to write it down, if they had something to write with and on). Information flow in a program is restricted for good reasons, so sometimes things are harder to do than then they appear.