Not a defintive answer, just some input.
We (the paragraphs maintainers) are working on reaching feature and user experience parity (or be better, of course) with the Field Collection module, specifically for non-typical paragraph scenarios (single paragraph type, single cardinality, default paragrapht type, ..). There are discussions on officially deprecating field collections in favor of Paragraphs, this might or might not happen. So I'd recommend to not use FC, but I'm obviously biased there.
The latest Paragraphs dev version has support for setting a default paragraph type now, which means that it will by default show the form fields for that already, and you don't need to add a type. The add button should only be shown if you allow more than one field, which is the default for paragraph fields but can easily be changed.
Revisions should work fine in IEF as well, if you use an Entity Reference Revisions Field. If not, then you'll have to report a but, but I assume the maintainers are actively working on that as it is a critical element of Commerce 2.x.
One important difference between the two is Paragraphs are by definition a composite of the parent node, that means they have a parent reference and not re-usable, in case that is a requirement. IEF doesn't have that and allows to search and reference existing entities.
The advantage of custom entitiy types (with base classes or traits to re-use base field definitions) is that they're way more efficient, Paragraphs/IEF both result in separate entities that need to be saved, and all the configurable fields need to be stored in separate tables. With custom entity types, you can add all the fields you need as base fields. We're constantly working on requiring less code for custom entities, listings can be done with views with none or very little custom code, widgets work for base fields too, so anything you can do with configurable fields can be done with base fields as well. One major pain point right now is a revision UI, but we're making improvings there as well and entity.module provides some basic user interfaces there as well, diff.module is also working on supporting all entity types.
Yet another option would be to define base fields/configurable fields in code. That is possible as well, but I personally wouldn't really recommend it, there are still some unfinished areas there, especially uninstalling/removing those fields is really hard, as the delete/purge process only works for configurable fields. You could technically also maintain configurable fields with code and sync them between multiple bundles.