Some additional thoughts:
Re: Deletes are expensive
1. How often is a delete from a code table performed (hint if there are <100, <1000 entries?) There may be no need for optimization
2. If you are doing a lot of deletes as in a DW feed, then you can use disable/enable check/no check to improve performance. Then TRUNCATE is also an option.
Re: DRI not necessary
1. At least create the DRI with the NO CHECK option to allow tools that read metadata to understand the joins. There is no performance argument against a NO CHECK constraint. Most DB tools nowadays use this information.
2. At some point in Development and Testing, you should turn appropriate DRI on to keep persons honest. So I would advocate turning to NO CHECK any constraints in PRODUCTION that you feel cause performance concerns.
3. If developers instinctively know about integrity, why do some have no clue when they get an RI constraint violation? I frequently use Triggers vs DRI to give a more precise error message including the value of the FK column that caused the problem, and the name of the PK attribute. Some developers STILL email the message/stack trace to me and ask what it means. This way, I can answer the question without doing any research, and accomplishing a training goal in the same breath
1. Choose only between creating a disabled or enabled constraint. Even if you use RI triggers or SPs.
2. Perform Performance Tuning by eliminating constraints the same way you add indexes- on an as needed basis. Wait until you have a problem, then disable the constraint/trigger.
2. Consider temporarily disabling constraints for certain high volume activities. Ditto for special use indexes as another poster mentions.