If you make your contracts upgradeable, you violate a core blockchain principle that code is immutable, and add an additional layer that users have to trust will not get exploited.
Three years ago, Consensys security researchers brought this issue to the forefront when this blog written by Steve Marx was published. In this, the upgradeability of code itself was considered as a bug.
There are a bunch of examples in which upgrading the smart contracts has led to introduction of new bugs such as that in the case of 190 Million dollar Nomad bridge hack. See this incident analysis by Certik which gives a thorough breakdown of this issue, termed by samczsun as a “Chaotic“ hack.
The a16z guys wrote an article on the same and made a tool to detect metamorphic and thus the upgradeable smart contracts. Well, for some reason their tool takes forever to load XD
Securitywise, the project's version 1 which is say time tested and more secure, if upgraded directly to new version 2 with new features, results in all the time tested security shield gone all the way to zero.
Another problem is that if you don’t use upgradeable contracts and the contracts have a bug in it, you can never fix that bug. And an exploit on it in inevitable unless you have some features of rescuing tokens or migration to another contract.
A good solution could be to use upgradeable contracts for a limited time such as 1 year, in which you only use upgradeable contract to just fix any existing bugs and not to introduce any new features. Then renounce the upgradeability feature after 1 year.
If you want to add new features, deploy new contracts entirely for them. Uniswap v1, v2 and v3 do a great job in demonstrating the fact that you don't need upgradeability to develop secure and robust Defi projects.
MakerDAO also uses a mixed approach as well when it comes to using the Upgradeability of smart contracts. Their approach is well explained in this video.
When using upgradeable contracts make sure that you avoid writing your own custom proxy contract from scratch. It’s because it could lead to storage collision such as in the case of the 6 Million dollar Audius hack in 2022. Instead use the proxy patterns and contracts written by Openzeppelin as they are quite secure and are well documented to avoid any storage collision.
In addition to that, you can refer this audit checklist made by Pashov, but it is not complete as the author says. Also keep in mind that each upgradeable pattern can have its own security considerations and pitfalls to look into.
Also lease don’t tell me that you would be getting any new upgrades and changes to the code audited. Because there are good chances that the auditing team would have already forgotten a substantial chunk of your code! They would rather skim and skip most of the code than take the pain to go through all over the code again XD (sry Sirtik!)
Make your contracts upgradeable only if you really really need to, and cannot do without it. Otherwise by adding the upgradeability, you would be introducing sheer degradeability.
And don’t trust the code if it is upgradeable :)