Every dev picks up a pile of rules early on. Write tests first. Never commit to main. Keep functions under 20 lines. DRY everything. Comment your code. 100% coverage or it doesn't count.
Some of those rules stuck. Others got quietly dropped somewhere along the way, usually after a project where following them made things worse.
I'm curious about the ones you dropped.
- What was the rule?
- What happened that made you stop?
- What do you do instead now?
Bonus points if you still follow it sometimes and can explain when.
Two small ground rules so this stays useful:
- be specific (a real situation beats "it depends"), and if you disagree with someone's answer, reply and say why. The arguments in threads like this are usually the best part 😄
I'll go first in the comments.

Top comments (2)
Mine's "make it reusable from the start."
I build the specific thing first and let it be a little messy. With Mochi, my Linux desktop pet, I got one actual pet working (its animations, moods, and little behaviors) before thinking about reuse at all.
Once it worked, I pulled the systems that had proven themselves out into their own runtime, Deskling: animation, the state machine, the event bus, movement and boundaries, dragging, scheduling. I didn't rewrite any of it from scratch. I extracted code that already worked, and by then I knew which parts deserved to be reusable because Mochi was already using them.
Where I do design for reuse now is Deskling itself, since the whole point is letting other people make pets. Community pets ship as data-only
.desklingpackages with no arbitrary code, so "reusable" there means "safe for strangers to share." That's a much more specific problem than "maybe I'll need this someday."Curious if anyone went the other way and got burned by NOT abstracting early.
One I quietly stopped following is “abstract it early so it can be reused later.”
I used to see two similar pieces of code and immediately think they should become a shared service or utility. The problem is that similar code doesn’t always mean the same concept. I’ve had cases where two API flows looked almost identical at first, then their validation and error handling started evolving differently. The shared abstraction ended up needing flags and special cases just to keep both behaviors alive.
Now I’m fine with a little duplication until I see the same rule changing for the same reason. For example, if two endpoints both calculate pricing but one is for checkout and the other is for internal reporting, I’d rather keep them separate until the domain relationship is actually proven.
For me, DRY became less about removing repeated code and more about avoiding duplicated knowledge. The abstraction should earn its place in the architecture, not get promoted because two functions happen to look alike.