Avoid redundant database roundtrips

If You are validating coupon, why pass coupon_id? Just pass a coupon struct. This way, no need to call db.

And You could pattern match on Coupon to be sure You are working with a valid struct.

validate_coupon(%Coupon{} = coupon)

This would put side effect (loading from db) outside the function, making it pure.

And if You return the updated coupon, You could chain…

coupon_id
|> load_from_db()
|> validate_coupon()
|> increase_counter()