Well inputs and outputs are usually predetermined. If you would for example want to change an output it would basically mean you changed your mind where you want the transaction be sent (to a new address) this would technically be a new transaction and it can still be done.
For example before the transaction is confirmed you can create a new transaction from the same inputs and send it to a new address using a higher fee, thus making the old transaction invalid. This can also be an issue in case of a stale block occurring when the tx is sent back to the mempool.
This video by Anton makes a good explanation of tx malleability:
You can also learn more in detail how segwit changes transaction structure to prevent the malleability issue in the Bitcoin Programing course in the academy