I2C's reputation for not being reliable has a handful of common causes that come up again and again. For the most part, prevented by ordinary design practices. The general advice of reducing bus speed does not necessarily prevent it, rather it makes it easier to apply fixes if needed.
Causes on the hardware side
- false falling edges on SCK from crosstalk between SDA and SCL (i.e. when SDA is transitioning, SCL supposed to remain high but sees a glitch). This is an issue when cables length gets into multiple meters. Can mitigate by reducing dV/dt via controlled rate driver,
or series resistance (forming RC vs bulk capacitance); constant-current pull-up; higher VDD; and even cable construction (SDA/SCL wire position not adjacent, PVC vs PPE inner insulation - lower dielectric constant insulation better). But really, i2c shouldn't be going into cables of any significant length.
- false/double falling edges on SCK from t-line effects. Fix: reduce dV/dt of the falling edges, and terminate the active end, with some series resistance (33-100 ohm range). If long line, perhaps use twisted pairs (each signal vs GND, not SDA/SCL together!). Note that due to open drain drivers, series termination only effective when line is pulled low.
- false falling edges on SCK from ground bounce. In this case, excessive pull-up current can actually be the root cause (open drain activates, dI/dt raises GND locally vs signal line) and attempting to fix the problem with stronger pull up would make it worse. Likewise, making lines longer also increase dI/dt due to larger cap. Either way, this issue is likely a matter of bypass/grounding. Constant-current pull-up can be involved in the fix, if the situation is simultaneously constrained by rise time vs total capacitance, because it CC-pullup can reduce the amount of current needed to achieve the rise time. Root cause could also be just (particularly bad) general supply noise.
- Excessive rise time due to capacitance. In this case, stronger pull-up is the right thing to do. Again, think twice if using i2c with long cables.
The above can happen in combination. In case of adding series resistance, be aware that it will raise the "low" signal level by I_pullup*R_series, so don't overdo it.
On the firmware side
- Some devices need min and/or max delays, between bytes, between multi-byte words, between sessions, or when starting(?). One bad experience I had with OEM sensors in a niche market, the device vendor didn't fully document these timing requirements and I had to deduce them -- massively frustrating. The "wrong" delay caused the confused device to pull the bus low and then remain in that state.