Custom Metadata Type records are considered Metadata, and as such can't be inserted or updated directly via standard DML operations. Trying to do so will result in a TypeException with a message like: 

> System.TypeException: DML operation INSERT not allowed on ISVNamespace__MetadataTypeName__mdt

The [`Metadata`][1] namespace was added to Apex in [Summer'17/v40.0][2] and provides the ability to deploy certain types of metadata asynchronously without having to resort the to Metadata API. 

There is a good example of how this works in the [Metadata Operations - Deploy Metadata][3] documentation.

    public class CreateMetadata{
      public void updateAndDeployMetadata() {
        // Setup custom metadata to be created in the subscriber org.
        Metadata.CustomMetadata customMetadata =  new Metadata.CustomMetadata();
        customMetadata.fullName = 'ISVNamespace__MetadataTypeName.MetadataRecordName';
    
        Metadata.CustomMetadataValue customField = new Metadata.CustomMetadataValue();
        customField.field = 'customField__c';
        customField.value = 'New value';
    
        customMetadata.values.add(customField);
    
        Metadata.DeployContainer mdContainer = new Metadata.DeployContainer();
        mdContainer.addMetadata(customMetadata);
    
        // Setup deploy callback, MyDeployCallback implements
        // the Metadata.DeployCallback interface (code for
        // this class not shown in this example)
        MyDeployCallback callback = new MyDeployCallback();
    
        // Enqueue custom metadata deployment
        Id deployRequestId = Metadata.Operations.enqueueDeployment(mdContainer, callback);
      }
    }

Note that this is an asynchronous operation and you need to provide a callback Apex class that implements the Metadata.DeployCallback interface.

This would be something like:

    public class MyDeployCallback implements Metadata.DeployCallback {
        public void handleResult(Metadata.DeployResult result,
                                 Metadata.DeployCallbackContext context) {
            Id jobId = context.getCallbackJobId();

            
            switch on (result.status) {
                when Succeeded {
                    // Deployment was successful
                }
                when SucceededPartial {
                    // The deployment succeeded, but some components might not have been successfully deployed. Check Metadata.DeployResult for more details.
                }
                when Failed {
                    // Deployment was not successful
                }
                when Canceled {
    
                }
                when Pending, InProgress, Canceling {
                    // Queued or state changing
                }
            }
        }
    }

The [DeployResult][4] that comes back via the callback has a number of useful properties that correspond to monitoring an API based Metadata deployment result.


  [1]: https://developer.salesforce.com/docs/atlas.en-us.208.0.apexcode.meta/apexcode/apex_metadata.htm
  [2]: https://releasenotes.docs.salesforce.com/en-us/summer17/release-notes/rn_apex_metadata.htm
  [3]: https://developer.salesforce.com/docs/atlas.en-us.208.0.apexcode.meta/apexcode/apex_class_Metadata_Operations.htm
  [4]: https://developer.salesforce.com/docs/atlas.en-us.208.0.apexcode.meta/apexcode/apex_class_Metadata_DeployResult.htm#apex_class_Metadata_DeployResult