Answer
The discipline is to default to the most restrictive modifier and widen only when needed, because every public or protected member becomes a contract you must support. package-private is underused but excellent for collaborating classes that shouldn't expose internals to the world. protected is the subtle one: it's part of the inheritance contract, so a protected field becomes a commitment to every future subclass author and ties your hands when refactoring — prefer protected methods over protected fields.
Common mistakes
- Making everything public by default.
- Forgetting that protected also grants package access.