The appeal of Craft CMS is its ability to flex into most any structure your project needs. Whether it's selling digital products, documenting a library of assets, or a simple blog—Craft can easily handle it.
However, to use a cheesy quote: "With great power comes great responsibility." And this applies to Craft when harnessing that flexibility. It's easy to reach the end of a project with a significant number of fields and sections that aren't actually necessary.
Looking back over the last (almost) decade of building Craft sites, I've found that there are certain principles that seem to keep the 'bloat' to a minimum.
In no particular order:
In my experience, this is the #1 source of bloat for most Craft projects. You can easily increase the complexity of the site (without need) by adding includes, 'logic layers', and embeds.
For all but the most complex sites, it's simply not needed. If we apply the principle of "simple as possible, but no simpler" to our components, we'll be able to reuse them wherever needed, instead of limiting their usage to only the specific area intended by the designer.
You'll find that most of these principles are squarely-aimed at making maintenance and future work easier. Even if you're building an MVP (which realistically will be production for longer than you'd wish), the time spent building the initial project is paltry compared to the time spent 'in-retainer', or maintaining the site and adding features.
If that's true, any change or approach you can do to make that time more effective is worth the effort.
This principle is no exception—when you're debugging, it's invaluable to quickly find uses of a field without first finding the field's handle. Of course, there are plugins that display this for you. Of course, you can view the source of the form page—however those are two steps you'll need to do every. single. time. you need to debug something or find something about a field.
Even a few seconds multiplied many times matters.
Entry types require fields to be manually added to each type for any action you take. Again, this is another area where it doesn't seem like it'd matter. I mean, you're only going to be doing a bunch of field work during the initial build, right?
Unfortunately, that's rarely the case, and you'll begin to regret those beautifully-separate types when you have to constantly manage fields for each one.
For developers coming from older systems (like ExpressionEngine), this is a challenging principle to internalize. The desire to overspecify fields (whether via name or functionality) is common for many developers that don't have experience on the other side of a project—managing 75-100 fields results in needless complication, and when combined with the previous two principles, can result in code bloat by developers "being safe" and simply creating another field instead of taking the risk of using an existing field.
In my experience I've found that it's useful to have a 'Common' Field Group, into which a few "base" fields are added. These fields should be added to any content that will be visible in search results or displayed in aggregated lists:
They're simple fields: a Title, Photo, and Summary field. However, having the ability to know that any Entry you're working with in Twig will always have these fields gives you powerful assumptions when writing markup. Search results pages become much easier; SEO defaults become a breeze.