With the 2.1 version, Magento migrated the admin product edit page from a mostly PHP-based version to a UI component one. And adding some fieldsets, fields, modal boxes, etc requires mainly XML tweaks. But let's say you want to generate fields dynamically...

This example is very simple for the sake of comprehension.

Configure the "container" with XML

First thing to do is to still use the UI component XML to create a fieldset that will then encompass your dynamic fields.

Just create a My/Module/view/adminhtml/ui_component/product_form.xml file with this content:

<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">  
    <fieldset name="my_fieldset" class="My\Module\Ui\Component\Form\Fieldset">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="label" xsi:type="string" translate="true">More Product Links</item>
                <item name="sortOrder" xsi:type="number">1000</item>
            </item>
        </argument>
    </fieldset>
</form>  

Dynamically create fields with PHP

As you can see in the previous XML snippet, the my_fieldset fieldset has the My\Module\Ui\Component\Form\Fieldset attached to it. This is where we will write our custom logic to inject fields into this fieldset.

So here is the My\Module\Ui\Component\Form\Fieldset PHP class which, I think, don't need further explaination:

<?php  
namespace My\Module\Ui\Component\Form;


use Magento\Framework\View\Element\UiComponent\ContextInterface;  
use Magento\Framework\View\Element\UiComponentInterface;  
use Magento\Ui\Component\Form\FieldFactory;  
use Magento\Ui\Component\Form\Fieldset as BaseFieldset;

class Fieldset extends BaseFieldset  
{
    /**
     * @var FieldFactory
     */
    private $fieldFactory;

    public function __construct(
        ContextInterface $context,
        array $components = [],
        array $data = [],
        FieldFactory $fieldFactory)
    {
        parent::__construct($context, $components, $data);
        $this->fieldFactory = $fieldFactory;
    }

    /**
     * Get components
     *
     * @return UiComponentInterface[]
     */
    public function getChildComponents()
    {
        $fields = [
            [
                'label' => __('Field Label From Code'),
                'value' => __('Field Value From Code'),
                'formElement' => 'input',
            ],
            [
                'label' => __('Another Field Label From Code'),
                'value' => __('Another Field Value From Code'),
                'formElement' => 'input',
            ],
            [
                'label' => __('Yet Another Field Label From Code'),
                'value' => __('Yet Another Field Value From Code'),
                'formElement' => 'input',
            ]
        ];

        foreach ($fields as $k => $fieldConfig) {
            $fieldInstance = $this->fieldFactory->create();
            $name = 'my_dynamic_field_' . $k;

            $fieldInstance->setData(
                [
                    'config' => $fieldConfig,
                    'name' => $name
                ]
            );

            $fieldInstance->prepare();
            $this->addComponent($name, $fieldInstance);
        }

        return parent::getChildComponents();
    }
}