Software architecture is an art of compromise
Have you ever been involved in a Salesforce project where, as a builder or as an architect, you don't have complete control over the architecture decisions? Instead, they are being forced upon you by an external system or platform or vendor (and sometimes even business) because they own the engagement layer or they are the de facto System of Record (SoR) or System of Reference (SoF).
Over time, and with hard-earned experience, you begin to understand that implementation decisions are made within the context of the constraints that you work with. Your decision to use code over config, point-to-point vs middleware, off-the-shelf vs bespoke depends on the maturity of your team, organisation and processes, but it also depends on the nature of the constraints forced on the team by external actors.
You are forced to use Apex over Flow or a managed package because the external system wants you to respond in less than 10 ms, and no matter how hard you try, you can't eke out that kind of performance without apex. You chose point-to-point over middleware because the external system is unable (due to technology or personnel limitation) to talk to a middleware.
It is essential that you identify and catalogue these restrictions upfront. Don't stumble into them; instead, seek them out. It will help you plan and communicate any compromises you may have to make to your design.
Developing on top of the customer 360 Platform (any platform, really) is a series of compromises and (at times) intentional sub-optimal decisions that allow you to build to realistic timelines and deliver value early on and gather feedback.
Compromise in architecture is not just necessary, it is inevitable, so learn to expect it and plan for it.