Most of the built-in workflow functions can be customized with Condition code or Additional actions code, so if you can use a built-in script, you should.
Example 1. Where to put your scripts?
For each type of workflow function, the plugin will provide the current
transientVars in the script binding. That means you can refer to them using these variables, without declaring them.
If you use Intellij IDEA, you can add a dynamic property for these variables of the correct type. Alternatively you can just redeclare it with type information:
import com.atlassian.jira.issue.Issue; Issue issue = issue
Post-functions execute after the transition has been validated. Here is the place where you can pass on a message to a downstream system, send custom notifications or modify the issue etc.
The order of the post functions is important. For example a post function that changes the description of the issue - issue.setDescription("A new description") - should be placed before the Update change history for an issue and store the issue in the database step and a fast track transition an issue after the Fire Event step.