Modeling solid or even soft bodies as simple spring-mass-damper systems is surprisingly straight forward and gets impressive results fast. Getting results that are numerically stable, is a bit of a step up from there. IIRC the step up to rigid body simulation was a tough one, but mainly because of the annoying collision detection & handling logic. It's a bonanza of trade offs and everything you do essentially has different classes of issues. Keeping track of both performance and stability is bit of a PITA. I'd guess that's also your main difference between doing just 2D vs 3D, all of the other math should be the exact same.
I remember we did a strange detour into Navier-Stokes equations and 2D fluid simulation towards the end, which I'd argue was a much bigger step up. I ended up the only one with a (very slow but working) solution, having asked a friend with a physics degree for help.
We did some little project assignments at the end where I implemented a simple height map based water surface simulation[1]. Another surprisingly simple little project I recommend trying, that also yields impressive results fast.
I recall another guy who implemented cloth simulation and ran into massive problems with the numeric stability. Turns out, doing straight forward spring-mass-damper systems with Euler integration literally explodes in your face all the time. Multi step Runge-Kutta is of course much more stable, but again, requires a lot of tweaking to get up to reasonable speed.
[1] https://ubm-twvideo01.s3.amazonaws.com/o1/vault/gdc08/slides...
I went down the route of fracture & deformation rather than fluid sim route, and ended up implementing [0] (or, some amount of it. As you mentioned, the numerical stability aspect is tough).
[0] https://escholarship.org/content/qt7ct56944/qt7ct56944_noSpl...
It's also an incredibly humbling experience to write one, and benchmark it against Box2D to see just how slow your implementation is.