Hmm, good question! I hadn't really considered warning.
For my usecases I think I typically would want to raise to ensure, for example, that tests loudly fail if such a violation is detected. Similarly, at runtime if I am unexpectedly violating the annotation, I probably have a bug that is potentially getting the system into an unexpected state which I would like to raise so the operation is aborted and it gets loudly reported (i.e. to Sentry) for me to fix.
However, I can definitely see people wanting just a warning so it doesn't actively crash things in prod while still being detectable.
Perhaps something like:
field :foo, :string, writable: [when: :never, on_violation: :warn]
Where :when could have values :always, :never, and :insert (like today) and :on_violation could have values :nothing, :warn, and :raise.
Such an approach would also allow Ecto to continue to accept writable: :never and internally translate it to writable: [when: :never, on_violation: :nothing] (or on_violation: :warn if we wanted to change the default behavior to warn instead) in a backwards compatible manner.
WDYT?