diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index ca2daee0c..5e4fc315d 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -227,15 +227,6 @@ updates:
     schedule:
       day: sunday
       interval: weekly
-  - package-ecosystem: gomod
-    directory: /internal/metric
-    labels:
-      - dependencies
-      - go
-      - "Skip Changelog"
-    schedule:
-      day: sunday
-      interval: weekly
   - package-ecosystem: gomod
     directory: /internal/tools
     labels:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 73e1f723a..30b433344 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 
 ## [Unreleased]
 
+### ⚠️ Notice ⚠️
+
+This update is a breaking change of the unstable Metrics API. Code instrumented with the `go.opentelemetry.io/otel/metric` <= v0.27.0 will need to be modified.
+
 ### Added
 
 - Added support to configure the span limits with environment variables.
@@ -24,6 +28,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 
 - For tracestate's members, prepend the new element and remove the oldest one, which is over capacity (#2592)
 - Add event and link drop counts to the exported data from the `oltptrace` exporter. (#2601)
+- The metrics API has been significantly changed. (#2587)
 - Unify path cleaning functionally in the `otlpmetric` and `otlptrace` config. (#2639)
 - Change the debug message from the `sdk/trace.BatchSpanProcessor` to reflect the count is cumulative. (#2640)
 
diff --git a/bridge/opencensus/aggregation.go b/bridge/opencensus/aggregation.go
index 3d88f7589..99d2b07af 100644
--- a/bridge/opencensus/aggregation.go
+++ b/bridge/opencensus/aggregation.go
@@ -21,8 +21,8 @@ import (
 
 	"go.opencensus.io/metric/metricdata"
 
-	"go.opentelemetry.io/otel/metric/number"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/number"
 )
 
 var (
diff --git a/bridge/opencensus/exporter.go b/bridge/opencensus/exporter.go
index bdc22eced..d40ddc9d6 100644
--- a/bridge/opencensus/exporter.go
+++ b/bridge/opencensus/exporter.go
@@ -27,13 +27,13 @@ import (
 
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
+	"go.opentelemetry.io/otel/metric/instrument"
 	"go.opentelemetry.io/otel/metric/unit"
 	"go.opentelemetry.io/otel/sdk/instrumentation"
 	"go.opentelemetry.io/otel/sdk/metric/export"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/resource"
 )
 
@@ -170,17 +170,17 @@ func convertDescriptor(ocDescriptor metricdata.Descriptor) (sdkapi.Descriptor, e
 		// Includes TypeGaugeDistribution, TypeCumulativeDistribution, TypeSummary
 		return sdkapi.Descriptor{}, fmt.Errorf("%w; descriptor type: %v", errConversion, ocDescriptor.Type)
 	}
-	opts := []metric.InstrumentOption{
-		metric.WithDescription(ocDescriptor.Description),
+	opts := []instrument.Option{
+		instrument.WithDescription(ocDescriptor.Description),
 	}
 	switch ocDescriptor.Unit {
 	case metricdata.UnitDimensionless:
-		opts = append(opts, metric.WithUnit(unit.Dimensionless))
+		opts = append(opts, instrument.WithUnit(unit.Dimensionless))
 	case metricdata.UnitBytes:
-		opts = append(opts, metric.WithUnit(unit.Bytes))
+		opts = append(opts, instrument.WithUnit(unit.Bytes))
 	case metricdata.UnitMilliseconds:
-		opts = append(opts, metric.WithUnit(unit.Milliseconds))
+		opts = append(opts, instrument.WithUnit(unit.Milliseconds))
 	}
-	cfg := metric.NewInstrumentConfig(opts...)
+	cfg := instrument.NewConfig(opts...)
 	return sdkapi.NewDescriptor(ocDescriptor.Name, ikind, nkind, cfg.Description(), cfg.Unit()), nil
 }
diff --git a/bridge/opencensus/exporter_test.go b/bridge/opencensus/exporter_test.go
index 8fe689347..79e195c1f 100644
--- a/bridge/opencensus/exporter_test.go
+++ b/bridge/opencensus/exporter_test.go
@@ -26,15 +26,15 @@ import (
 
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/metrictest"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
+	"go.opentelemetry.io/otel/metric/instrument"
 	"go.opentelemetry.io/otel/metric/unit"
 	"go.opentelemetry.io/otel/sdk/instrumentation"
 	"go.opentelemetry.io/otel/sdk/metric/controller/controllertest"
 	"go.opentelemetry.io/otel/sdk/metric/export"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/metrictest"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/resource"
 )
 
@@ -400,8 +400,8 @@ func TestConvertDescriptor(t *testing.T) {
 				"foo",
 				sdkapi.GaugeObserverInstrumentKind,
 				number.Int64Kind,
-				metric.WithDescription("bar"),
-				metric.WithUnit(unit.Bytes),
+				instrument.WithDescription("bar"),
+				instrument.WithUnit(unit.Bytes),
 			),
 		},
 		{
@@ -416,8 +416,8 @@ func TestConvertDescriptor(t *testing.T) {
 				"foo",
 				sdkapi.GaugeObserverInstrumentKind,
 				number.Float64Kind,
-				metric.WithDescription("bar"),
-				metric.WithUnit(unit.Milliseconds),
+				instrument.WithDescription("bar"),
+				instrument.WithUnit(unit.Milliseconds),
 			),
 		},
 		{
@@ -432,8 +432,8 @@ func TestConvertDescriptor(t *testing.T) {
 				"foo",
 				sdkapi.CounterObserverInstrumentKind,
 				number.Int64Kind,
-				metric.WithDescription("bar"),
-				metric.WithUnit(unit.Dimensionless),
+				instrument.WithDescription("bar"),
+				instrument.WithUnit(unit.Dimensionless),
 			),
 		},
 		{
@@ -448,8 +448,8 @@ func TestConvertDescriptor(t *testing.T) {
 				"foo",
 				sdkapi.CounterObserverInstrumentKind,
 				number.Float64Kind,
-				metric.WithDescription("bar"),
-				metric.WithUnit(unit.Dimensionless),
+				instrument.WithDescription("bar"),
+				instrument.WithUnit(unit.Dimensionless),
 			),
 		},
 		{
diff --git a/example/prometheus/main.go b/example/prometheus/main.go
index 8a1248a35..12148d259 100644
--- a/example/prometheus/main.go
+++ b/example/prometheus/main.go
@@ -25,7 +25,7 @@ import (
 	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/exporters/prometheus"
 	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/global"
+	"go.opentelemetry.io/otel/metric/instrument"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
 	controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
@@ -35,6 +35,9 @@ import (
 
 var (
 	lemonsKey = attribute.Key("ex.com/lemons")
+
+	// TODO Bring back Global package
+	meterProvider metric.MeterProvider
 )
 
 func initMeter() {
@@ -54,7 +57,9 @@ func initMeter() {
 	if err != nil {
 		log.Panicf("failed to initialize prometheus exporter %v", err)
 	}
-	global.SetMeterProvider(exporter.MeterProvider())
+	// TODO Bring back Global package
+	// global.SetMeterProvider(exporter.MeterProvider())
+	meterProvider = exporter.MeterProvider()
 
 	http.HandleFunc("/", exporter.ServeHTTP)
 	go func() {
@@ -67,23 +72,33 @@ func initMeter() {
 func main() {
 	initMeter()
 
-	meter := global.Meter("ex.com/basic")
+	// TODO Bring back Global package
+	// meter := global.Meter("ex.com/basic")
+	meter := meterProvider.Meter("ex.com/basic")
 	observerLock := new(sync.RWMutex)
 	observerValueToReport := new(float64)
 	observerLabelsToReport := new([]attribute.KeyValue)
-	cb := func(_ context.Context, result metric.Float64ObserverResult) {
+
+	gaugeObserver, err := meter.AsyncFloat64().Gauge("ex.com.one")
+	if err != nil {
+		log.Panicf("failed to initialize instrument: %v", err)
+	}
+	_ = meter.RegisterCallback([]instrument.Asynchronous{gaugeObserver}, func(ctx context.Context) {
 		(*observerLock).RLock()
 		value := *observerValueToReport
 		labels := *observerLabelsToReport
 		(*observerLock).RUnlock()
-		result.Observe(value, labels...)
-	}
-	_ = metric.Must(meter).NewFloat64GaugeObserver("ex.com.one", cb,
-		metric.WithDescription("A GaugeObserver set to 1.0"),
-	)
+		gaugeObserver.Observe(ctx, value, labels...)
+	})
 
-	histogram := metric.Must(meter).NewFloat64Histogram("ex.com.two")
-	counter := metric.Must(meter).NewFloat64Counter("ex.com.three")
+	histogram, err := meter.SyncFloat64().Histogram("ex.com.two")
+	if err != nil {
+		log.Panicf("failed to initialize instrument: %v", err)
+	}
+	counter, err := meter.SyncFloat64().Counter("ex.com.three")
+	if err != nil {
+		log.Panicf("failed to initialize instrument: %v", err)
+	}
 
 	commonLabels := []attribute.KeyValue{lemonsKey.Int(10), attribute.String("A", "1"), attribute.String("B", "2"), attribute.String("C", "3")}
 	notSoCommonLabels := []attribute.KeyValue{lemonsKey.Int(13)}
@@ -94,12 +109,9 @@ func main() {
 	*observerValueToReport = 1.0
 	*observerLabelsToReport = commonLabels
 	(*observerLock).Unlock()
-	meter.RecordBatch(
-		ctx,
-		commonLabels,
-		histogram.Measurement(2.0),
-		counter.Measurement(12.0),
-	)
+
+	histogram.Record(ctx, 2.0, commonLabels...)
+	counter.Add(ctx, 12.0, commonLabels...)
 
 	time.Sleep(5 * time.Second)
 
@@ -107,12 +119,8 @@ func main() {
 	*observerValueToReport = 1.0
 	*observerLabelsToReport = notSoCommonLabels
 	(*observerLock).Unlock()
-	meter.RecordBatch(
-		ctx,
-		notSoCommonLabels,
-		histogram.Measurement(2.0),
-		counter.Measurement(22.0),
-	)
+	histogram.Record(ctx, 2.0, notSoCommonLabels...)
+	counter.Add(ctx, 22.0, notSoCommonLabels...)
 
 	time.Sleep(5 * time.Second)
 
@@ -120,12 +128,8 @@ func main() {
 	*observerValueToReport = 13.0
 	*observerLabelsToReport = commonLabels
 	(*observerLock).Unlock()
-	meter.RecordBatch(
-		ctx,
-		commonLabels,
-		histogram.Measurement(12.0),
-		counter.Measurement(13.0),
-	)
+	histogram.Record(ctx, 12.0, commonLabels...)
+	counter.Add(ctx, 13.0, commonLabels...)
 
 	fmt.Println("Example finished updating, please visit :2222")
 
diff --git a/exporters/otlp/otlpmetric/exporter.go b/exporters/otlp/otlpmetric/exporter.go
index 36c41cce7..caf21eaf2 100644
--- a/exporters/otlp/otlpmetric/exporter.go
+++ b/exporters/otlp/otlpmetric/exporter.go
@@ -20,9 +20,9 @@ import (
 	"sync"
 
 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/metrictransform"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/export"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/resource"
 )
 
diff --git a/exporters/otlp/otlpmetric/exporter_test.go b/exporters/otlp/otlpmetric/exporter_test.go
index 31e3f3e68..b8ba3dce9 100644
--- a/exporters/otlp/otlpmetric/exporter_test.go
+++ b/exporters/otlp/otlpmetric/exporter_test.go
@@ -29,16 +29,16 @@ import (
 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric"
 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/metrictransform"
 	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/metrictest"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/instrumentation"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
 	"go.opentelemetry.io/otel/sdk/metric/export"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/metrictest"
+	"go.opentelemetry.io/otel/sdk/metric/number"
 	"go.opentelemetry.io/otel/sdk/metric/processor/processortest"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/resource"
 	commonpb "go.opentelemetry.io/proto/otlp/common/v1"
 	metricpb "go.opentelemetry.io/proto/otlp/metrics/v1"
diff --git a/exporters/otlp/otlpmetric/internal/metrictransform/metric.go b/exporters/otlp/otlpmetric/internal/metrictransform/metric.go
index e1ba26dcc..002bb64a9 100644
--- a/exporters/otlp/otlpmetric/internal/metrictransform/metric.go
+++ b/exporters/otlp/otlpmetric/internal/metrictransform/metric.go
@@ -24,10 +24,10 @@ import (
 	"sync"
 	"time"
 
-	"go.opentelemetry.io/otel/metric/number"
 	"go.opentelemetry.io/otel/sdk/instrumentation"
 	"go.opentelemetry.io/otel/sdk/metric/export"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/number"
 	"go.opentelemetry.io/otel/sdk/resource"
 	commonpb "go.opentelemetry.io/proto/otlp/common/v1"
 	metricpb "go.opentelemetry.io/proto/otlp/metrics/v1"
diff --git a/exporters/otlp/otlpmetric/internal/metrictransform/metric_test.go b/exporters/otlp/otlpmetric/internal/metrictransform/metric_test.go
index 7b5b801eb..d8f2a1c8b 100644
--- a/exporters/otlp/otlpmetric/internal/metrictransform/metric_test.go
+++ b/exporters/otlp/otlpmetric/internal/metrictransform/metric_test.go
@@ -25,14 +25,14 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric/metrictest"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
 	"go.opentelemetry.io/otel/sdk/metric/export"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/metrictest"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 	commonpb "go.opentelemetry.io/proto/otlp/common/v1"
 	metricpb "go.opentelemetry.io/proto/otlp/metrics/v1"
 )
diff --git a/exporters/otlp/otlpmetric/internal/otlpmetrictest/data.go b/exporters/otlp/otlpmetric/internal/otlpmetrictest/data.go
index cc3f78715..2da921f14 100644
--- a/exporters/otlp/otlpmetric/internal/otlpmetrictest/data.go
+++ b/exporters/otlp/otlpmetric/internal/otlpmetrictest/data.go
@@ -20,13 +20,13 @@ import (
 	"time"
 
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric/metrictest"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/instrumentation"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
 	"go.opentelemetry.io/otel/sdk/metric/export"
+	"go.opentelemetry.io/otel/sdk/metric/metrictest"
+	"go.opentelemetry.io/otel/sdk/metric/number"
 	"go.opentelemetry.io/otel/sdk/metric/processor/processortest"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 // OneRecordReader is a Reader that returns just one
diff --git a/exporters/otlp/otlpmetric/internal/otlpmetrictest/otlptest.go b/exporters/otlp/otlpmetric/internal/otlpmetrictest/otlptest.go
index 53af9d556..524cb7745 100644
--- a/exporters/otlp/otlpmetric/internal/otlpmetrictest/otlptest.go
+++ b/exporters/otlp/otlpmetric/internal/otlpmetrictest/otlptest.go
@@ -25,12 +25,12 @@ import (
 
 	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric"
-	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
+	"go.opentelemetry.io/otel/metric/instrument"
 	controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/number"
 	processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/selector/simple"
 	metricpb "go.opentelemetry.io/proto/otlp/metrics/v1"
 )
@@ -63,34 +63,37 @@ func RunEndToEndTest(ctx context.Context, t *testing.T, exp *otlpmetric.Exporter
 		case sdkapi.CounterInstrumentKind:
 			switch data.nKind {
 			case number.Int64Kind:
-				metric.Must(meter).NewInt64Counter(name).Add(ctx, data.val, labels...)
+				c, _ := meter.SyncInt64().Counter(name)
+				c.Add(ctx, data.val, labels...)
 			case number.Float64Kind:
-				metric.Must(meter).NewFloat64Counter(name).Add(ctx, float64(data.val), labels...)
+				c, _ := meter.SyncFloat64().Counter(name)
+				c.Add(ctx, float64(data.val), labels...)
 			default:
 				assert.Failf(t, "unsupported number testing kind", data.nKind.String())
 			}
 		case sdkapi.HistogramInstrumentKind:
 			switch data.nKind {
 			case number.Int64Kind:
-				metric.Must(meter).NewInt64Histogram(name).Record(ctx, data.val, labels...)
+				c, _ := meter.SyncInt64().Histogram(name)
+				c.Record(ctx, data.val, labels...)
 			case number.Float64Kind:
-				metric.Must(meter).NewFloat64Histogram(name).Record(ctx, float64(data.val), labels...)
+				c, _ := meter.SyncFloat64().Histogram(name)
+				c.Record(ctx, float64(data.val), labels...)
 			default:
 				assert.Failf(t, "unsupported number testing kind", data.nKind.String())
 			}
 		case sdkapi.GaugeObserverInstrumentKind:
 			switch data.nKind {
 			case number.Int64Kind:
-				metric.Must(meter).NewInt64GaugeObserver(name,
-					func(_ context.Context, result metric.Int64ObserverResult) {
-						result.Observe(data.val, labels...)
-					},
-				)
+				g, _ := meter.AsyncInt64().Gauge(name)
+				_ = meter.RegisterCallback([]instrument.Asynchronous{g}, func(ctx context.Context) {
+					g.Observe(ctx, data.val, labels...)
+				})
 			case number.Float64Kind:
-				callback := func(v float64) metric.Float64ObserverFunc {
-					return metric.Float64ObserverFunc(func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(v, labels...) })
-				}(float64(data.val))
-				metric.Must(meter).NewFloat64GaugeObserver(name, callback)
+				g, _ := meter.AsyncFloat64().Gauge(name)
+				_ = meter.RegisterCallback([]instrument.Asynchronous{g}, func(ctx context.Context) {
+					g.Observe(ctx, float64(data.val), labels...)
+				})
 			default:
 				assert.Failf(t, "unsupported number testing kind", data.nKind.String())
 			}
diff --git a/exporters/otlp/otlpmetric/otlpmetricgrpc/example_test.go b/exporters/otlp/otlpmetric/otlpmetricgrpc/example_test.go
index f37c39095..fe0866b7a 100644
--- a/exporters/otlp/otlpmetric/otlpmetricgrpc/example_test.go
+++ b/exporters/otlp/otlpmetric/otlpmetricgrpc/example_test.go
@@ -24,8 +24,7 @@ import (
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric"
 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
-	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/global"
+	"go.opentelemetry.io/otel/metric/instrument"
 	controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
 	processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
 	"go.opentelemetry.io/otel/sdk/metric/selector/simple"
@@ -54,7 +53,8 @@ func Example_insecure() {
 		controller.WithExporter(exp),
 		controller.WithCollectPeriod(2*time.Second),
 	)
-	global.SetMeterProvider(pusher)
+	// TODO Bring back Global package
+	// global.SetMeterProvider(pusher)
 
 	if err := pusher.Start(ctx); err != nil {
 		log.Fatalf("could not start metric controoler: %v", err)
@@ -68,14 +68,16 @@ func Example_insecure() {
 		}
 	}()
 
-	meter := global.Meter("test-meter")
+	// TODO Bring Back Global package
+	// meter := global.Meter("go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc_test")
+	meter := pusher.Meter("go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc_test")
 
 	// Recorder metric example
-	counter := metric.Must(meter).
-		NewFloat64Counter(
-			"an_important_metric",
-			metric.WithDescription("Measures the cumulative epicness of the app"),
-		)
+
+	counter, err := meter.SyncFloat64().Counter("an_important_metric", instrument.WithDescription("Measures the cumulative epicness of the app"))
+	if err != nil {
+		log.Fatalf("Failed to create the instrument: %v", err)
+	}
 
 	for i := 0; i < 10; i++ {
 		log.Printf("Doing really hard work (%d / 10)\n", i+1)
@@ -113,7 +115,8 @@ func Example_withTLS() {
 		controller.WithExporter(exp),
 		controller.WithCollectPeriod(2*time.Second),
 	)
-	global.SetMeterProvider(pusher)
+	// TODO Bring back Global package
+	// global.SetMeterProvider(pusher)
 
 	if err := pusher.Start(ctx); err != nil {
 		log.Fatalf("could not start metric controoler: %v", err)
@@ -128,14 +131,15 @@ func Example_withTLS() {
 		}
 	}()
 
-	meter := global.Meter("test-meter")
+	// TODO Bring back Global package
+	// meter := global.Meter("go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc_test")
+	meter := pusher.Meter("go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc_test")
 
 	// Recorder metric example
-	counter := metric.Must(meter).
-		NewFloat64Counter(
-			"an_important_metric",
-			metric.WithDescription("Measures the cumulative epicness of the app"),
-		)
+	counter, err := meter.SyncFloat64().Counter("an_important_metric", instrument.WithDescription("Measures the cumulative epicness of the app"))
+	if err != nil {
+		log.Fatalf("Failed to create the instrument: %v", err)
+	}
 
 	for i := 0; i < 10; i++ {
 		log.Printf("Doing really hard work (%d / 10)\n", i+1)
@@ -170,7 +174,8 @@ func Example_withDifferentSignalCollectors() {
 		controller.WithExporter(exp),
 		controller.WithCollectPeriod(2*time.Second),
 	)
-	global.SetMeterProvider(pusher)
+	// TODO Bring back Global package
+	// global.SetMeterProvider(pusher)
 
 	if err := pusher.Start(ctx); err != nil {
 		log.Fatalf("could not start metric controoler: %v", err)
@@ -184,14 +189,15 @@ func Example_withDifferentSignalCollectors() {
 		}
 	}()
 
-	meter := global.Meter("test-meter")
+	// TODO Bring back Global package
+	// meter := global.Meter("go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc_test")
+	meter := pusher.Meter("go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc_test")
 
 	// Recorder metric example
-	counter := metric.Must(meter).
-		NewFloat64Counter(
-			"an_important_metric",
-			metric.WithDescription("Measures the cumulative epicness of the app"),
-		)
+	counter, err := meter.SyncFloat64().Counter("an_important_metric", instrument.WithDescription("Measures the cumulative epicness of the app"))
+	if err != nil {
+		log.Fatalf("Failed to create the instrument: %v", err)
+	}
 
 	for i := 0; i < 10; i++ {
 		log.Printf("Doing really hard work (%d / 10)\n", i+1)
diff --git a/exporters/prometheus/prometheus.go b/exporters/prometheus/prometheus.go
index 085955262..7d31514fb 100644
--- a/exporters/prometheus/prometheus.go
+++ b/exporters/prometheus/prometheus.go
@@ -29,12 +29,12 @@ import (
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/instrumentation"
 	controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
 	"go.opentelemetry.io/otel/sdk/metric/export"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/resource"
 )
 
diff --git a/exporters/prometheus/prometheus_test.go b/exporters/prometheus/prometheus_test.go
index b05d92321..587c8633e 100644
--- a/exporters/prometheus/prometheus_test.go
+++ b/exporters/prometheus/prometheus_test.go
@@ -26,7 +26,7 @@ import (
 
 	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/exporters/prometheus"
-	"go.opentelemetry.io/otel/metric"
+	"go.opentelemetry.io/otel/metric/instrument"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
 	controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
@@ -107,9 +107,12 @@ func TestPrometheusExporter(t *testing.T) {
 	require.NoError(t, err)
 
 	meter := exporter.MeterProvider().Meter("test")
-	upDownCounter := metric.Must(meter).NewFloat64UpDownCounter("updowncounter")
-	counter := metric.Must(meter).NewFloat64Counter("counter")
-	histogram := metric.Must(meter).NewFloat64Histogram("histogram")
+	upDownCounter, err := meter.SyncFloat64().UpDownCounter("updowncounter")
+	require.NoError(t, err)
+	counter, err := meter.SyncFloat64().Counter("counter")
+	require.NoError(t, err)
+	histogram, err := meter.SyncFloat64().Histogram("histogram")
+	require.NoError(t, err)
 
 	labels := []attribute.KeyValue{
 		attribute.Key("A").String("B"),
@@ -124,9 +127,13 @@ func TestPrometheusExporter(t *testing.T) {
 
 	expected = append(expected, expectCounter("counter", `counter{A="B",C="D",R="V"} 15.3`))
 
-	_ = metric.Must(meter).NewInt64GaugeObserver("intgaugeobserver", func(_ context.Context, result metric.Int64ObserverResult) {
-		result.Observe(1, labels...)
+	gaugeObserver, err := meter.AsyncInt64().Gauge("intgaugeobserver")
+	require.NoError(t, err)
+
+	err = meter.RegisterCallback([]instrument.Asynchronous{gaugeObserver}, func(ctx context.Context) {
+		gaugeObserver.Observe(ctx, 1, labels...)
 	})
+	require.NoError(t, err)
 
 	expected = append(expected, expectGauge("intgaugeobserver", `intgaugeobserver{A="B",C="D",R="V"} 1`))
 
@@ -148,15 +155,23 @@ func TestPrometheusExporter(t *testing.T) {
 
 	expected = append(expected, expectGauge("updowncounter", `updowncounter{A="B",C="D",R="V"} 6.8`))
 
-	_ = metric.Must(meter).NewFloat64CounterObserver("floatcounterobserver", func(_ context.Context, result metric.Float64ObserverResult) {
-		result.Observe(7.7, labels...)
+	counterObserver, err := meter.AsyncFloat64().Counter("floatcounterobserver")
+	require.NoError(t, err)
+
+	err = meter.RegisterCallback([]instrument.Asynchronous{counterObserver}, func(ctx context.Context) {
+		counterObserver.Observe(ctx, 7.7, labels...)
 	})
+	require.NoError(t, err)
 
 	expected = append(expected, expectCounter("floatcounterobserver", `floatcounterobserver{A="B",C="D",R="V"} 7.7`))
 
-	_ = metric.Must(meter).NewFloat64UpDownCounterObserver("floatupdowncounterobserver", func(_ context.Context, result metric.Float64ObserverResult) {
-		result.Observe(-7.7, labels...)
+	upDownCounterObserver, err := meter.AsyncFloat64().UpDownCounter("floatupdowncounterobserver")
+	require.NoError(t, err)
+
+	err = meter.RegisterCallback([]instrument.Asynchronous{upDownCounterObserver}, func(ctx context.Context) {
+		upDownCounterObserver.Observe(ctx, -7.7, labels...)
 	})
+	require.NoError(t, err)
 
 	expected = append(expected, expectGauge("floatupdowncounterobserver", `floatupdowncounterobserver{A="B",C="D",R="V"} -7.7`))
 
@@ -196,10 +211,8 @@ func TestPrometheusStatefulness(t *testing.T) {
 
 	ctx := context.Background()
 
-	counter := metric.Must(meter).NewInt64Counter(
-		"a.counter",
-		metric.WithDescription("Counts things"),
-	)
+	counter, err := meter.SyncInt64().Counter("a.counter", instrument.WithDescription("Counts things"))
+	require.NoError(t, err)
 
 	counter.Add(ctx, 100, attribute.String("key", "value"))
 
diff --git a/exporters/stdout/stdoutmetric/example_test.go b/exporters/stdout/stdoutmetric/example_test.go
index c367b7670..1250a463a 100644
--- a/exporters/stdout/stdoutmetric/example_test.go
+++ b/exporters/stdout/stdoutmetric/example_test.go
@@ -21,7 +21,7 @@ import (
 	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
 	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/global"
+	"go.opentelemetry.io/otel/metric/instrument/syncint64"
 	controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
 	processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
 	"go.opentelemetry.io/otel/sdk/metric/selector/simple"
@@ -33,13 +33,15 @@ const (
 )
 
 var (
-	meter = global.GetMeterProvider().Meter(
-		instrumentationName,
-		metric.WithInstrumentationVersion(instrumentationVersion),
-	)
+	// TODO Bring back Global package
+	// meter = global.GetMeterProvider().Meter(
+	// 	instrumentationName,
+	// 	metric.WithInstrumentationVersion(instrumentationVersion),
+	// )
+	meter metric.Meter
 
-	loopCounter = metric.Must(meter).NewInt64Counter("function.loops")
-	paramValue  = metric.Must(meter).NewInt64Histogram("function.param")
+	loopCounter syncint64.Counter
+	paramValue  syncint64.Histogram
 
 	nameKey = attribute.Key("function.name")
 )
@@ -80,7 +82,18 @@ func InstallExportPipeline(ctx context.Context) func() {
 	if err = pusher.Start(ctx); err != nil {
 		log.Fatalf("starting push controller: %v", err)
 	}
-	global.SetMeterProvider(pusher)
+	// TODO Bring back Global package
+	// global.SetMeterProvider(pusher)
+	meter = pusher.Meter(instrumentationName, metric.WithInstrumentationVersion(instrumentationVersion))
+
+	loopCounter, err = meter.SyncInt64().Counter("function.loops")
+	if err != nil {
+		log.Fatalf("creating instrument: %v", err)
+	}
+	paramValue, err = meter.SyncInt64().Histogram("function.param")
+	if err != nil {
+		log.Fatalf("creating instrument: %v", err)
+	}
 
 	return func() {
 		if err := pusher.Stop(ctx); err != nil {
@@ -92,7 +105,7 @@ func InstallExportPipeline(ctx context.Context) func() {
 func Example() {
 	ctx := context.Background()
 
-	// Registers a meter Provider globally.
+	// TODO: Registers a meter Provider globally.
 	cleanup := InstallExportPipeline(ctx)
 	defer cleanup()
 
diff --git a/exporters/stdout/stdoutmetric/metric.go b/exporters/stdout/stdoutmetric/metric.go
index 56e449de2..c816c035b 100644
--- a/exporters/stdout/stdoutmetric/metric.go
+++ b/exporters/stdout/stdoutmetric/metric.go
@@ -22,10 +22,10 @@ import (
 	"time"
 
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/instrumentation"
 	"go.opentelemetry.io/otel/sdk/metric/export"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/resource"
 )
 
diff --git a/exporters/stdout/stdoutmetric/metric_test.go b/exporters/stdout/stdoutmetric/metric_test.go
index e9153fcfd..33e2831db 100644
--- a/exporters/stdout/stdoutmetric/metric_test.go
+++ b/exporters/stdout/stdoutmetric/metric_test.go
@@ -101,7 +101,8 @@ func TestStdoutTimestamp(t *testing.T) {
 
 	require.NoError(t, cont.Start(ctx))
 	meter := cont.Meter("test")
-	counter := metric.Must(meter).NewInt64Counter("name.lastvalue")
+	counter, err := meter.SyncInt64().Counter("name.lastvalue")
+	require.NoError(t, err)
 
 	before := time.Now()
 	// Ensure the timestamp is after before.
@@ -137,7 +138,8 @@ func TestStdoutTimestamp(t *testing.T) {
 func TestStdoutCounterFormat(t *testing.T) {
 	fix := newFixture(t)
 
-	counter := metric.Must(fix.meter).NewInt64Counter("name.sum")
+	counter, err := fix.meter.SyncInt64().Counter("name.sum")
+	require.NoError(t, err)
 	counter.Add(fix.ctx, 123, attribute.String("A", "B"), attribute.String("C", "D"))
 
 	require.NoError(t, fix.cont.Stop(fix.ctx))
@@ -148,7 +150,8 @@ func TestStdoutCounterFormat(t *testing.T) {
 func TestStdoutLastValueFormat(t *testing.T) {
 	fix := newFixture(t)
 
-	counter := metric.Must(fix.meter).NewFloat64Counter("name.lastvalue")
+	counter, err := fix.meter.SyncFloat64().Counter("name.lastvalue")
+	require.NoError(t, err)
 	counter.Add(fix.ctx, 123.456, attribute.String("A", "B"), attribute.String("C", "D"))
 
 	require.NoError(t, fix.cont.Stop(fix.ctx))
@@ -159,7 +162,8 @@ func TestStdoutLastValueFormat(t *testing.T) {
 func TestStdoutHistogramFormat(t *testing.T) {
 	fix := newFixture(t, stdoutmetric.WithPrettyPrint())
 
-	inst := metric.Must(fix.meter).NewFloat64Histogram("name.histogram")
+	inst, err := fix.meter.SyncFloat64().Histogram("name.histogram")
+	require.NoError(t, err)
 
 	for i := 0; i < 1000; i++ {
 		inst.Record(fix.ctx, float64(i)+0.5, attribute.String("A", "B"), attribute.String("C", "D"))
@@ -181,7 +185,8 @@ func TestStdoutNoData(t *testing.T) {
 			t.Parallel()
 
 			fix := newFixture(t)
-			_ = metric.Must(fix.meter).NewFloat64Counter(fmt.Sprint("name.", aggName))
+			_, err := fix.meter.SyncFloat64().Counter(fmt.Sprint("name.", aggName))
+			require.NoError(t, err)
 			require.NoError(t, fix.cont.Stop(fix.ctx))
 
 			require.Equal(t, "", fix.Output())
@@ -243,7 +248,8 @@ func TestStdoutResource(t *testing.T) {
 			ctx := context.Background()
 			fix := newFixtureWithResource(t, tc.res)
 
-			counter := metric.Must(fix.meter).NewFloat64Counter("name.lastvalue")
+			counter, err := fix.meter.SyncFloat64().Counter("name.lastvalue")
+			require.NoError(t, err)
 			counter.Add(ctx, 123.456, tc.attrs...)
 
 			require.NoError(t, fix.cont.Stop(fix.ctx))
diff --git a/internal/metric/async.go b/internal/metric/async.go
deleted file mode 100644
index 94801b7ff..000000000
--- a/internal/metric/async.go
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright The OpenTelemetry Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package metric // import "go.opentelemetry.io/otel/internal/metric"
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"sync"
-
-	"go.opentelemetry.io/otel"
-	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric/sdkapi"
-)
-
-//nolint:revive // ignoring missing comments for exported error in an internal package
-var ErrInvalidAsyncRunner = errors.New("unknown async runner type")
-
-// AsyncCollector is an interface used between the MeterImpl and the
-// AsyncInstrumentState helper below.  This interface is implemented by
-// the SDK to provide support for running observer callbacks.
-type AsyncCollector interface {
-	// CollectAsync passes a batch of observations to the MeterImpl.
-	CollectAsync(labels []attribute.KeyValue, observation ...sdkapi.Observation)
-}
-
-// AsyncInstrumentState manages an ordered set of asynchronous
-// instruments and the distinct runners, taking into account batch
-// observer callbacks.
-type AsyncInstrumentState struct {
-	lock sync.Mutex
-
-	// errorOnce will use the otel.Handler to report an error
-	// once in case of an invalid runner attempting to run.
-	errorOnce sync.Once
-
-	// runnerMap keeps the set of runners that will run each
-	// collection interval.  Singletons are entered with a real
-	// instrument each, batch observers are entered with a nil
-	// instrument, ensuring that when a singleton callback is used
-	// repeatedly, it is executed repeatedly in the interval, while
-	// when a batch callback is used repeatedly, it only executes
-	// once per interval.
-	runnerMap map[asyncRunnerPair]struct{}
-
-	// runners maintains the set of runners in the order they were
-	// registered.
-	runners []asyncRunnerPair
-
-	// instruments maintains the set of instruments in the order
-	// they were registered.
-	instruments []sdkapi.AsyncImpl
-}
-
-// asyncRunnerPair is a map entry for Observer callback runners.
-type asyncRunnerPair struct {
-	// runner is used as a map key here.  The API ensures
-	// that all callbacks are pointers for this reason.
-	runner sdkapi.AsyncRunner
-
-	// inst refers to a non-nil instrument when `runner` is a
-	// AsyncSingleRunner.
-	inst sdkapi.AsyncImpl
-}
-
-// NewAsyncInstrumentState returns a new *AsyncInstrumentState, for
-// use by MeterImpl to manage running the set of observer callbacks in
-// the correct order.
-func NewAsyncInstrumentState() *AsyncInstrumentState {
-	return &AsyncInstrumentState{
-		runnerMap: map[asyncRunnerPair]struct{}{},
-	}
-}
-
-// Instruments returns the asynchronous instruments managed by this
-// object, the set that should be checkpointed after observers are
-// run.
-func (a *AsyncInstrumentState) Instruments() []sdkapi.AsyncImpl {
-	a.lock.Lock()
-	defer a.lock.Unlock()
-	return a.instruments
-}
-
-// Register adds a new asynchronous instrument to by managed by this
-// object.  This should be called during NewAsyncInstrument() and
-// assumes that errors (e.g., duplicate registration) have already
-// been checked.
-func (a *AsyncInstrumentState) Register(inst sdkapi.AsyncImpl, runner sdkapi.AsyncRunner) {
-	a.lock.Lock()
-	defer a.lock.Unlock()
-
-	a.instruments = append(a.instruments, inst)
-
-	// asyncRunnerPair reflects this callback in the asyncRunners
-	// list.  If this is a batch runner, the instrument is nil.
-	// If this is a single-Observer runner, the instrument is
-	// included.  This ensures that batch callbacks are called
-	// once and single callbacks are called once per instrument.
-	rp := asyncRunnerPair{
-		runner: runner,
-	}
-	if _, ok := runner.(sdkapi.AsyncSingleRunner); ok {
-		rp.inst = inst
-	}
-
-	if _, ok := a.runnerMap[rp]; !ok {
-		a.runnerMap[rp] = struct{}{}
-		a.runners = append(a.runners, rp)
-	}
-}
-
-// Run executes the complete set of observer callbacks.
-func (a *AsyncInstrumentState) Run(ctx context.Context, collector AsyncCollector) {
-	a.lock.Lock()
-	runners := a.runners
-	a.lock.Unlock()
-
-	for _, rp := range runners {
-		// The runner must be a single or batch runner, no
-		// other implementations are possible because the
-		// interface has un-exported methods.
-
-		if singleRunner, ok := rp.runner.(sdkapi.AsyncSingleRunner); ok {
-			singleRunner.Run(ctx, rp.inst, collector.CollectAsync)
-			continue
-		}
-
-		if multiRunner, ok := rp.runner.(sdkapi.AsyncBatchRunner); ok {
-			multiRunner.Run(ctx, collector.CollectAsync)
-			continue
-		}
-
-		a.errorOnce.Do(func() {
-			otel.Handle(fmt.Errorf("%w: type %T (reported once)", ErrInvalidAsyncRunner, rp))
-		})
-	}
-}
diff --git a/internal/metric/global/benchmark_test.go b/internal/metric/global/benchmark_test.go
deleted file mode 100644
index 05cab62d2..000000000
--- a/internal/metric/global/benchmark_test.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright The OpenTelemetry Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package global_test
-
-import (
-	"context"
-	"testing"
-
-	"go.opentelemetry.io/otel/attribute"
-	internalglobal "go.opentelemetry.io/otel/internal/metric/global"
-	metricglobal "go.opentelemetry.io/otel/metric/global"
-)
-
-func BenchmarkGlobalInt64CounterAddNoSDK(b *testing.B) {
-	// Compare with BenchmarkGlobalInt64CounterAddWithSDK() in
-	// ../../sdk/metric/benchmark_test.go to see the overhead of the
-	// global no-op system against a registered SDK.
-	internalglobal.ResetForTest()
-	ctx := context.Background()
-	sdk := metricglobal.Meter("test")
-	labs := []attribute.KeyValue{attribute.String("A", "B")}
-	cnt := Must(sdk).NewInt64Counter("int64.counter")
-
-	b.ResetTimer()
-
-	for i := 0; i < b.N; i++ {
-		cnt.Add(ctx, 1, labs...)
-	}
-}
diff --git a/internal/metric/global/internal_test.go b/internal/metric/global/internal_test.go
deleted file mode 100644
index 44e1904ae..000000000
--- a/internal/metric/global/internal_test.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright The OpenTelemetry Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package global_test
-
-import (
-	"os"
-	"testing"
-
-	ottest "go.opentelemetry.io/otel/internal/internaltest"
-	"go.opentelemetry.io/otel/internal/metric/global"
-)
-
-// Ensure struct alignment prior to running tests.
-func TestMain(m *testing.M) {
-	fieldsMap := global.AtomicFieldOffsets()
-	fields := make([]ottest.FieldOffset, 0, len(fieldsMap))
-	for name, offset := range fieldsMap {
-		fields = append(fields, ottest.FieldOffset{
-			Name:   name,
-			Offset: offset,
-		})
-	}
-	if !ottest.Aligned8Byte(fields, os.Stderr) {
-		os.Exit(1)
-	}
-
-	os.Exit(m.Run())
-}
diff --git a/internal/metric/global/meter.go b/internal/metric/global/meter.go
deleted file mode 100644
index 77781ead1..000000000
--- a/internal/metric/global/meter.go
+++ /dev/null
@@ -1,287 +0,0 @@
-// Copyright The OpenTelemetry Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package global // import "go.opentelemetry.io/otel/internal/metric/global"
-
-import (
-	"context"
-	"sync"
-	"sync/atomic"
-	"unsafe"
-
-	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/internal/metric/registry"
-	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
-)
-
-// This file contains the forwarding implementation of MeterProvider used as
-// the default global instance.  Metric events using instruments provided by
-// this implementation are no-ops until the first Meter implementation is set
-// as the global provider.
-//
-// The implementation here uses Mutexes to maintain a list of active Meters in
-// the MeterProvider and Instruments in each Meter, under the assumption that
-// these interfaces are not performance-critical.
-//
-// We have the invariant that setDelegate() will be called before a new
-// MeterProvider implementation is registered as the global provider.  Mutexes
-// in the MeterProvider and Meters ensure that each instrument has a delegate
-// before the global provider is set.
-//
-// Metric uniqueness checking is implemented by calling the exported
-// methods of the api/metric/registry package.
-
-type meterKey struct {
-	InstrumentationName    string
-	InstrumentationVersion string
-	SchemaURL              string
-}
-
-type meterProvider struct {
-	delegate metric.MeterProvider
-
-	// lock protects `delegate` and `meters`.
-	lock sync.Mutex
-
-	// meters maintains a unique entry for every named Meter
-	// that has been registered through the global instance.
-	meters map[meterKey]*meterEntry
-}
-
-type meterImpl struct {
-	delegate unsafe.Pointer // (*metric.MeterImpl)
-
-	lock       sync.Mutex
-	syncInsts  []*syncImpl
-	asyncInsts []*asyncImpl
-}
-
-type meterEntry struct {
-	unique sdkapi.MeterImpl
-	impl   meterImpl
-}
-
-type instrument struct {
-	descriptor sdkapi.Descriptor
-}
-
-type syncImpl struct {
-	delegate unsafe.Pointer // (*sdkapi.SyncImpl)
-
-	instrument
-}
-
-type asyncImpl struct {
-	delegate unsafe.Pointer // (*sdkapi.AsyncImpl)
-
-	instrument
-
-	runner sdkapi.AsyncRunner
-}
-
-var _ metric.MeterProvider = &meterProvider{}
-var _ sdkapi.MeterImpl = &meterImpl{}
-var _ sdkapi.InstrumentImpl = &syncImpl{}
-var _ sdkapi.AsyncImpl = &asyncImpl{}
-
-func (inst *instrument) Descriptor() sdkapi.Descriptor {
-	return inst.descriptor
-}
-
-// MeterProvider interface and delegation
-
-func newMeterProvider() *meterProvider {
-	return &meterProvider{
-		meters: map[meterKey]*meterEntry{},
-	}
-}
-
-func (p *meterProvider) setDelegate(provider metric.MeterProvider) {
-	p.lock.Lock()
-	defer p.lock.Unlock()
-
-	p.delegate = provider
-	for key, entry := range p.meters {
-		entry.impl.setDelegate(key, provider)
-	}
-	p.meters = nil
-}
-
-func (p *meterProvider) Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter {
-	p.lock.Lock()
-	defer p.lock.Unlock()
-
-	if p.delegate != nil {
-		return p.delegate.Meter(instrumentationName, opts...)
-	}
-
-	cfg := metric.NewMeterConfig(opts...)
-	key := meterKey{
-		InstrumentationName:    instrumentationName,
-		InstrumentationVersion: cfg.InstrumentationVersion(),
-		SchemaURL:              cfg.SchemaURL(),
-	}
-	entry, ok := p.meters[key]
-	if !ok {
-		entry = &meterEntry{}
-		// Note: This code implements its own MeterProvider
-		// name-uniqueness logic because there is
-		// synchronization required at the moment of
-		// delegation.  We use the same instrument-uniqueness
-		// checking the real SDK uses here:
-		entry.unique = registry.NewUniqueInstrumentMeterImpl(&entry.impl)
-		p.meters[key] = entry
-	}
-	return metric.WrapMeterImpl(entry.unique)
-}
-
-// Meter interface and delegation
-
-func (m *meterImpl) setDelegate(key meterKey, provider metric.MeterProvider) {
-	m.lock.Lock()
-	defer m.lock.Unlock()
-
-	d := new(sdkapi.MeterImpl)
-	*d = provider.Meter(
-		key.InstrumentationName,
-		metric.WithInstrumentationVersion(key.InstrumentationVersion),
-		metric.WithSchemaURL(key.SchemaURL),
-	).MeterImpl()
-	m.delegate = unsafe.Pointer(d)
-
-	for _, inst := range m.syncInsts {
-		inst.setDelegate(*d)
-	}
-	m.syncInsts = nil
-	for _, obs := range m.asyncInsts {
-		obs.setDelegate(*d)
-	}
-	m.asyncInsts = nil
-}
-
-func (m *meterImpl) NewSyncInstrument(desc sdkapi.Descriptor) (sdkapi.SyncImpl, error) {
-	m.lock.Lock()
-	defer m.lock.Unlock()
-
-	if meterPtr := (*sdkapi.MeterImpl)(atomic.LoadPointer(&m.delegate)); meterPtr != nil {
-		return (*meterPtr).NewSyncInstrument(desc)
-	}
-
-	inst := &syncImpl{
-		instrument: instrument{
-			descriptor: desc,
-		},
-	}
-	m.syncInsts = append(m.syncInsts, inst)
-	return inst, nil
-}
-
-// Synchronous delegation
-
-func (inst *syncImpl) setDelegate(d sdkapi.MeterImpl) {
-	implPtr := new(sdkapi.SyncImpl)
-
-	var err error
-	*implPtr, err = d.NewSyncInstrument(inst.descriptor)
-
-	if err != nil {
-		// TODO: There is no standard way to deliver this error to the user.
-		// See https://github.com/open-telemetry/opentelemetry-go/issues/514
-		// Note that the default SDK will not generate any errors yet, this is
-		// only for added safety.
-		panic(err)
-	}
-
-	atomic.StorePointer(&inst.delegate, unsafe.Pointer(implPtr))
-}
-
-func (inst *syncImpl) Implementation() interface{} {
-	if implPtr := (*sdkapi.SyncImpl)(atomic.LoadPointer(&inst.delegate)); implPtr != nil {
-		return (*implPtr).Implementation()
-	}
-	return inst
-}
-
-// Async delegation
-
-func (m *meterImpl) NewAsyncInstrument(
-	desc sdkapi.Descriptor,
-	runner sdkapi.AsyncRunner,
-) (sdkapi.AsyncImpl, error) {
-
-	m.lock.Lock()
-	defer m.lock.Unlock()
-
-	if meterPtr := (*sdkapi.MeterImpl)(atomic.LoadPointer(&m.delegate)); meterPtr != nil {
-		return (*meterPtr).NewAsyncInstrument(desc, runner)
-	}
-
-	inst := &asyncImpl{
-		instrument: instrument{
-			descriptor: desc,
-		},
-		runner: runner,
-	}
-	m.asyncInsts = append(m.asyncInsts, inst)
-	return inst, nil
-}
-
-func (obs *asyncImpl) Implementation() interface{} {
-	if implPtr := (*sdkapi.AsyncImpl)(atomic.LoadPointer(&obs.delegate)); implPtr != nil {
-		return (*implPtr).Implementation()
-	}
-	return obs
-}
-
-func (obs *asyncImpl) setDelegate(d sdkapi.MeterImpl) {
-	implPtr := new(sdkapi.AsyncImpl)
-
-	var err error
-	*implPtr, err = d.NewAsyncInstrument(obs.descriptor, obs.runner)
-
-	if err != nil {
-		// TODO: There is no standard way to deliver this error to the user.
-		// See https://github.com/open-telemetry/opentelemetry-go/issues/514
-		// Note that the default SDK will not generate any errors yet, this is
-		// only for added safety.
-		panic(err)
-	}
-
-	atomic.StorePointer(&obs.delegate, unsafe.Pointer(implPtr))
-}
-
-// Metric updates
-
-func (m *meterImpl) RecordBatch(ctx context.Context, labels []attribute.KeyValue, measurements ...sdkapi.Measurement) {
-	if delegatePtr := (*sdkapi.MeterImpl)(atomic.LoadPointer(&m.delegate)); delegatePtr != nil {
-		(*delegatePtr).RecordBatch(ctx, labels, measurements...)
-	}
-}
-
-func (inst *syncImpl) RecordOne(ctx context.Context, number number.Number, labels []attribute.KeyValue) {
-	if instPtr := (*sdkapi.SyncImpl)(atomic.LoadPointer(&inst.delegate)); instPtr != nil {
-		(*instPtr).RecordOne(ctx, number, labels)
-	}
-}
-
-func AtomicFieldOffsets() map[string]uintptr {
-	return map[string]uintptr{
-		"meterProvider.delegate": unsafe.Offsetof(meterProvider{}.delegate),
-		"meterImpl.delegate":     unsafe.Offsetof(meterImpl{}.delegate),
-		"syncImpl.delegate":      unsafe.Offsetof(syncImpl{}.delegate),
-		"asyncImpl.delegate":     unsafe.Offsetof(asyncImpl{}.delegate),
-	}
-}
diff --git a/internal/metric/global/meter_test.go b/internal/metric/global/meter_test.go
deleted file mode 100644
index 2062ecf8b..000000000
--- a/internal/metric/global/meter_test.go
+++ /dev/null
@@ -1,253 +0,0 @@
-// Copyright The OpenTelemetry Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package global_test
-
-import (
-	"context"
-	"errors"
-	"testing"
-
-	"github.com/stretchr/testify/require"
-
-	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/internal/metric/global"
-	"go.opentelemetry.io/otel/metric"
-	metricglobal "go.opentelemetry.io/otel/metric/global"
-	"go.opentelemetry.io/otel/metric/metrictest"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
-)
-
-var Must = metric.Must
-
-var asInt = number.NewInt64Number
-var asFloat = number.NewFloat64Number
-
-func TestDirect(t *testing.T) {
-	global.ResetForTest()
-
-	ctx := context.Background()
-	meter1 := metricglobal.Meter("test1", metric.WithInstrumentationVersion("semver:v1.0.0"))
-	meter2 := metricglobal.Meter("test2", metric.WithSchemaURL("hello"))
-
-	library1 := metrictest.Library{
-		InstrumentationName:    "test1",
-		InstrumentationVersion: "semver:v1.0.0",
-	}
-	library2 := metrictest.Library{
-		InstrumentationName: "test2",
-		SchemaURL:           "hello",
-	}
-
-	labels1 := []attribute.KeyValue{attribute.String("A", "B")}
-	labels2 := []attribute.KeyValue{attribute.String("C", "D")}
-	labels3 := []attribute.KeyValue{attribute.String("E", "F")}
-
-	counter := Must(meter1).NewInt64Counter("test.counter")
-	counter.Add(ctx, 1, labels1...)
-	counter.Add(ctx, 1, labels1...)
-
-	histogram := Must(meter1).NewFloat64Histogram("test.histogram")
-	histogram.Record(ctx, 1, labels1...)
-	histogram.Record(ctx, 2, labels1...)
-
-	_ = Must(meter1).NewFloat64GaugeObserver("test.gauge.float", func(_ context.Context, result metric.Float64ObserverResult) {
-		result.Observe(1., labels1...)
-		result.Observe(2., labels2...)
-	})
-
-	_ = Must(meter1).NewInt64GaugeObserver("test.gauge.int", func(_ context.Context, result metric.Int64ObserverResult) {
-		result.Observe(1, labels1...)
-		result.Observe(2, labels2...)
-	})
-
-	second := Must(meter2).NewFloat64Histogram("test.second")
-	second.Record(ctx, 1, labels3...)
-	second.Record(ctx, 2, labels3...)
-
-	provider := metrictest.NewMeterProvider()
-	metricglobal.SetMeterProvider(provider)
-
-	counter.Add(ctx, 1, labels1...)
-	histogram.Record(ctx, 3, labels1...)
-	second.Record(ctx, 3, labels3...)
-
-	provider.RunAsyncInstruments()
-
-	measurements := metrictest.AsStructs(provider.MeasurementBatches)
-
-	require.EqualValues(t,
-		[]metrictest.Measured{
-			{
-				Name:    "test.counter",
-				Library: library1,
-				Labels:  metrictest.LabelsToMap(labels1...),
-				Number:  asInt(1),
-			},
-			{
-				Name:    "test.histogram",
-				Library: library1,
-				Labels:  metrictest.LabelsToMap(labels1...),
-				Number:  asFloat(3),
-			},
-			{
-				Name:    "test.second",
-				Library: library2,
-				Labels:  metrictest.LabelsToMap(labels3...),
-				Number:  asFloat(3),
-			},
-			{
-				Name:    "test.gauge.float",
-				Library: library1,
-				Labels:  metrictest.LabelsToMap(labels1...),
-				Number:  asFloat(1),
-			},
-			{
-				Name:    "test.gauge.float",
-				Library: library1,
-				Labels:  metrictest.LabelsToMap(labels2...),
-				Number:  asFloat(2),
-			},
-			{
-				Name:    "test.gauge.int",
-				Library: library1,
-				Labels:  metrictest.LabelsToMap(labels1...),
-				Number:  asInt(1),
-			},
-			{
-				Name:    "test.gauge.int",
-				Library: library1,
-				Labels:  metrictest.LabelsToMap(labels2...),
-				Number:  asInt(2),
-			},
-		},
-		measurements,
-	)
-}
-
-type meterProviderWithConstructorError struct {
-	metric.MeterProvider
-}
-
-type meterWithConstructorError struct {
-	sdkapi.MeterImpl
-}
-
-func (m *meterProviderWithConstructorError) Meter(iName string, opts ...metric.MeterOption) metric.Meter {
-	return metric.WrapMeterImpl(&meterWithConstructorError{m.MeterProvider.Meter(iName, opts...).MeterImpl()})
-}
-
-func (m *meterWithConstructorError) NewSyncInstrument(_ sdkapi.Descriptor) (sdkapi.SyncImpl, error) {
-	return sdkapi.NewNoopSyncInstrument(), errors.New("constructor error")
-}
-
-func TestErrorInDeferredConstructor(t *testing.T) {
-	global.ResetForTest()
-
-	ctx := context.Background()
-	meter := metricglobal.GetMeterProvider().Meter("builtin")
-
-	c1 := Must(meter).NewInt64Counter("test")
-	c2 := Must(meter).NewInt64Counter("test")
-
-	provider := metrictest.NewMeterProvider()
-	sdk := &meterProviderWithConstructorError{provider}
-
-	require.Panics(t, func() {
-		metricglobal.SetMeterProvider(sdk)
-	})
-
-	c1.Add(ctx, 1)
-	c2.Add(ctx, 2)
-}
-
-func TestImplementationIndirection(t *testing.T) {
-	global.ResetForTest()
-
-	// Test that Implementation() does the proper indirection, i.e.,
-	// returns the implementation interface not the global, after
-	// registered.
-
-	meter1 := metricglobal.Meter("test1")
-
-	// Sync: no SDK yet
-	counter := Must(meter1).NewInt64Counter("interface.counter")
-
-	ival := counter.Measurement(1).SyncImpl().Implementation()
-	require.NotNil(t, ival)
-
-	_, ok := ival.(*metrictest.Sync)
-	require.False(t, ok)
-
-	// Async: no SDK yet
-	gauge := Must(meter1).NewFloat64GaugeObserver(
-		"interface.gauge",
-		func(_ context.Context, result metric.Float64ObserverResult) {},
-	)
-
-	ival = gauge.AsyncImpl().Implementation()
-	require.NotNil(t, ival)
-
-	_, ok = ival.(*metrictest.Async)
-	require.False(t, ok)
-
-	// Register the SDK
-	provider := metrictest.NewMeterProvider()
-	metricglobal.SetMeterProvider(provider)
-
-	// Repeat the above tests
-
-	// Sync
-	ival = counter.Measurement(1).SyncImpl().Implementation()
-	require.NotNil(t, ival)
-
-	_, ok = ival.(*metrictest.Sync)
-	require.True(t, ok)
-
-	// Async
-	ival = gauge.AsyncImpl().Implementation()
-	require.NotNil(t, ival)
-
-	_, ok = ival.(*metrictest.Async)
-	require.True(t, ok)
-}
-
-func TestRecordBatchMock(t *testing.T) {
-	global.ResetForTest()
-
-	meter := metricglobal.GetMeterProvider().Meter("builtin")
-
-	counter := Must(meter).NewInt64Counter("test.counter")
-
-	meter.RecordBatch(context.Background(), nil, counter.Measurement(1))
-
-	provider := metrictest.NewMeterProvider()
-	metricglobal.SetMeterProvider(provider)
-
-	meter.RecordBatch(context.Background(), nil, counter.Measurement(1))
-
-	require.EqualValues(t,
-		[]metrictest.Measured{
-			{
-				Name: "test.counter",
-				Library: metrictest.Library{
-					InstrumentationName: "builtin",
-				},
-				Labels: metrictest.LabelsToMap(),
-				Number: asInt(1),
-			},
-		},
-		metrictest.AsStructs(provider.MeasurementBatches))
-}
diff --git a/internal/metric/global/metric.go b/internal/metric/global/metric.go
deleted file mode 100644
index 896c6dd1c..000000000
--- a/internal/metric/global/metric.go
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright The OpenTelemetry Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package global // import "go.opentelemetry.io/otel/internal/metric/global"
-
-import (
-	"sync"
-	"sync/atomic"
-
-	"go.opentelemetry.io/otel/metric"
-)
-
-type meterProviderHolder struct {
-	mp metric.MeterProvider
-}
-
-var (
-	globalMeter = defaultMeterValue()
-
-	delegateMeterOnce sync.Once
-)
-
-// MeterProvider is the internal implementation for global.MeterProvider.
-func MeterProvider() metric.MeterProvider {
-	return globalMeter.Load().(meterProviderHolder).mp
-}
-
-// SetMeterProvider is the internal implementation for global.SetMeterProvider.
-func SetMeterProvider(mp metric.MeterProvider) {
-	delegateMeterOnce.Do(func() {
-		current := MeterProvider()
-
-		if current == mp {
-			// Setting the provider to the prior default is nonsense, panic.
-			// Panic is acceptable because we are likely still early in the
-			// process lifetime.
-			panic("invalid MeterProvider, the global instance cannot be reinstalled")
-		} else if def, ok := current.(*meterProvider); ok {
-			def.setDelegate(mp)
-		}
-	})
-	globalMeter.Store(meterProviderHolder{mp: mp})
-}
-
-func defaultMeterValue() *atomic.Value {
-	v := &atomic.Value{}
-	v.Store(meterProviderHolder{mp: newMeterProvider()})
-	return v
-}
-
-// ResetForTest restores the initial global state, for testing purposes.
-func ResetForTest() {
-	globalMeter = defaultMeterValue()
-	delegateMeterOnce = sync.Once{}
-}
diff --git a/internal/metric/global/registry_test.go b/internal/metric/global/registry_test.go
deleted file mode 100644
index 0d92c044e..000000000
--- a/internal/metric/global/registry_test.go
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright The OpenTelemetry Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package global
-
-import (
-	"context"
-	"errors"
-	"testing"
-
-	"github.com/stretchr/testify/require"
-
-	"go.opentelemetry.io/otel/internal/metric/registry"
-	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/metrictest"
-	"go.opentelemetry.io/otel/metric/sdkapi"
-)
-
-type (
-	newFunc func(name, libraryName string) (sdkapi.InstrumentImpl, error)
-)
-
-var (
-	allNew = map[string]newFunc{
-		"counter.int64": func(name, libraryName string) (sdkapi.InstrumentImpl, error) {
-			return unwrap(MeterProvider().Meter(libraryName).NewInt64Counter(name))
-		},
-		"counter.float64": func(name, libraryName string) (sdkapi.InstrumentImpl, error) {
-			return unwrap(MeterProvider().Meter(libraryName).NewFloat64Counter(name))
-		},
-		"histogram.int64": func(name, libraryName string) (sdkapi.InstrumentImpl, error) {
-			return unwrap(MeterProvider().Meter(libraryName).NewInt64Histogram(name))
-		},
-		"histogram.float64": func(name, libraryName string) (sdkapi.InstrumentImpl, error) {
-			return unwrap(MeterProvider().Meter(libraryName).NewFloat64Histogram(name))
-		},
-		"gauge.int64": func(name, libraryName string) (sdkapi.InstrumentImpl, error) {
-			return unwrap(MeterProvider().Meter(libraryName).NewInt64GaugeObserver(name, func(context.Context, metric.Int64ObserverResult) {}))
-		},
-		"gauge.float64": func(name, libraryName string) (sdkapi.InstrumentImpl, error) {
-			return unwrap(MeterProvider().Meter(libraryName).NewFloat64GaugeObserver(name, func(context.Context, metric.Float64ObserverResult) {}))
-		},
-	}
-)
-
-func unwrap(impl interface{}, err error) (sdkapi.InstrumentImpl, error) {
-	if impl == nil {
-		return nil, err
-	}
-	if s, ok := impl.(interface {
-		SyncImpl() sdkapi.SyncImpl
-	}); ok {
-		return s.SyncImpl(), err
-	}
-	if a, ok := impl.(interface {
-		AsyncImpl() sdkapi.AsyncImpl
-	}); ok {
-		return a.AsyncImpl(), err
-	}
-	return nil, err
-}
-
-func TestRegistrySameInstruments(t *testing.T) {
-	for _, nf := range allNew {
-		ResetForTest()
-		inst1, err1 := nf("this", "meter")
-		inst2, err2 := nf("this", "meter")
-
-		require.NoError(t, err1)
-		require.NoError(t, err2)
-		require.Equal(t, inst1, inst2)
-
-		SetMeterProvider(metrictest.NewMeterProvider())
-
-		require.Equal(t, inst1, inst2)
-	}
-}
-
-func TestRegistryDifferentNamespace(t *testing.T) {
-	for _, nf := range allNew {
-		ResetForTest()
-		inst1, err1 := nf("this", "meter1")
-		inst2, err2 := nf("this", "meter2")
-
-		require.NoError(t, err1)
-		require.NoError(t, err2)
-
-		if inst1.Descriptor().InstrumentKind().Synchronous() {
-			// They're equal because of a `nil` pointer at this point.
-			// (Only for synchronous instruments, which lack callacks.)
-			require.EqualValues(t, inst1, inst2)
-		}
-
-		SetMeterProvider(metrictest.NewMeterProvider())
-
-		// They're different after the deferred setup.
-		require.NotEqual(t, inst1, inst2)
-	}
-}
-
-func TestRegistryDiffInstruments(t *testing.T) {
-	for origName, origf := range allNew {
-		ResetForTest()
-
-		_, err := origf("this", "super")
-		require.NoError(t, err)
-
-		for newName, nf := range allNew {
-			if newName == origName {
-				continue
-			}
-
-			other, err := nf("this", "super")
-			require.Error(t, err)
-			require.NotNil(t, other)
-			require.True(t, errors.Is(err, registry.ErrMetricKindMismatch))
-			require.Contains(t, err.Error(), "by this name with another kind or number type")
-		}
-	}
-}
diff --git a/internal/metric/global/state_test.go b/internal/metric/global/state_test.go
deleted file mode 100644
index 526eac7fa..000000000
--- a/internal/metric/global/state_test.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright The OpenTelemetry Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package global_test
-
-import (
-	"testing"
-
-	internalglobal "go.opentelemetry.io/otel/internal/metric/global"
-	metricglobal "go.opentelemetry.io/otel/metric/global"
-)
-
-func TestResetsOfGlobalsPanic(t *testing.T) {
-	internalglobal.ResetForTest()
-	tests := map[string]func(){
-		"SetMeterProvider": func() {
-			metricglobal.SetMeterProvider(metricglobal.GetMeterProvider())
-		},
-	}
-
-	for name, test := range tests {
-		shouldPanic(t, name, test)
-	}
-}
-
-func shouldPanic(t *testing.T, name string, f func()) {
-	defer func() {
-		if r := recover(); r == nil {
-			t.Errorf("calling %s with default global did not panic", name)
-		}
-	}()
-
-	f()
-}
diff --git a/internal/metric/go.mod b/internal/metric/go.mod
deleted file mode 100644
index 7e92e2dcb..000000000
--- a/internal/metric/go.mod
+++ /dev/null
@@ -1,73 +0,0 @@
-module go.opentelemetry.io/otel/internal/metric
-
-go 1.16
-
-require (
-	github.com/stretchr/testify v1.7.0
-	go.opentelemetry.io/otel v1.4.1
-	go.opentelemetry.io/otel/metric v0.27.0
-)
-
-replace go.opentelemetry.io/otel => ../..
-
-replace go.opentelemetry.io/otel/metric => ../../metric
-
-replace go.opentelemetry.io/otel/internal/metric => ./
-
-replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus
-
-replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing
-
-replace go.opentelemetry.io/otel/example/jaeger => ../../example/jaeger
-
-replace go.opentelemetry.io/otel/example/namedtracer => ../../example/namedtracer
-
-replace go.opentelemetry.io/otel/example/opencensus => ../../example/opencensus
-
-replace go.opentelemetry.io/otel/example/otel-collector => ../../example/otel-collector
-
-replace go.opentelemetry.io/otel/example/passthrough => ../../example/passthrough
-
-replace go.opentelemetry.io/otel/example/prometheus => ../../example/prometheus
-
-replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin
-
-replace go.opentelemetry.io/otel/exporters/prometheus => ../../exporters/prometheus
-
-replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace
-
-replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc
-
-replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp
-
-replace go.opentelemetry.io/otel/exporters/jaeger => ../../exporters/jaeger
-
-replace go.opentelemetry.io/otel/exporters/zipkin => ../../exporters/zipkin
-
-replace go.opentelemetry.io/otel/internal/tools => ../tools
-
-replace go.opentelemetry.io/otel/sdk => ../../sdk
-
-replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric
-
-replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric
-
-replace go.opentelemetry.io/otel/trace => ../../trace
-
-replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric
-
-replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc
-
-replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric
-
-replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace
-
-replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp
-
-replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test
-
-replace go.opentelemetry.io/otel/example/fib => ../../example/fib
-
-replace go.opentelemetry.io/otel/schema => ../../schema
-
-replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../exporters/otlp/internal/retry
diff --git a/internal/metric/go.sum b/internal/metric/go.sum
deleted file mode 100644
index 4f1776cd9..000000000
--- a/internal/metric/go.sum
+++ /dev/null
@@ -1,18 +0,0 @@
-github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=
-github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
-github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
-github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
-github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/metric/config.go b/metric/config.go
index 3f722344f..621e4c5fc 100644
--- a/metric/config.go
+++ b/metric/config.go
@@ -14,65 +14,6 @@
 
 package metric // import "go.opentelemetry.io/otel/metric"
 
-import (
-	"go.opentelemetry.io/otel/metric/unit"
-)
-
-// InstrumentConfig contains options for metric instrument descriptors.
-type InstrumentConfig struct {
-	description string
-	unit        unit.Unit
-}
-
-// Description describes the instrument in human-readable terms.
-func (cfg *InstrumentConfig) Description() string {
-	return cfg.description
-}
-
-// Unit describes the measurement unit for a instrument.
-func (cfg *InstrumentConfig) Unit() unit.Unit {
-	return cfg.unit
-}
-
-// InstrumentOption is an interface for applying metric instrument options.
-type InstrumentOption interface {
-	// ApplyMeter is used to set a InstrumentOption value of a
-	// InstrumentConfig.
-	applyInstrument(InstrumentConfig) InstrumentConfig
-}
-
-// NewInstrumentConfig creates a new InstrumentConfig
-// and applies all the given options.
-func NewInstrumentConfig(opts ...InstrumentOption) InstrumentConfig {
-	var config InstrumentConfig
-	for _, o := range opts {
-		config = o.applyInstrument(config)
-	}
-	return config
-}
-
-type instrumentOptionFunc func(InstrumentConfig) InstrumentConfig
-
-func (fn instrumentOptionFunc) applyInstrument(cfg InstrumentConfig) InstrumentConfig {
-	return fn(cfg)
-}
-
-// WithDescription applies provided description.
-func WithDescription(desc string) InstrumentOption {
-	return instrumentOptionFunc(func(cfg InstrumentConfig) InstrumentConfig {
-		cfg.description = desc
-		return cfg
-	})
-}
-
-// WithUnit applies provided unit.
-func WithUnit(unit unit.Unit) InstrumentOption {
-	return instrumentOptionFunc(func(cfg InstrumentConfig) InstrumentConfig {
-		cfg.unit = unit
-		return cfg
-	})
-}
-
 // MeterConfig contains options for Meters.
 type MeterConfig struct {
 	instrumentationVersion string
@@ -80,18 +21,18 @@ type MeterConfig struct {
 }
 
 // InstrumentationVersion is the version of the library providing instrumentation.
-func (cfg *MeterConfig) InstrumentationVersion() string {
+func (cfg MeterConfig) InstrumentationVersion() string {
 	return cfg.instrumentationVersion
 }
 
 // SchemaURL is the schema_url of the library providing instrumentation.
-func (cfg *MeterConfig) SchemaURL() string {
+func (cfg MeterConfig) SchemaURL() string {
 	return cfg.schemaURL
 }
 
 // MeterOption is an interface for applying Meter options.
 type MeterOption interface {
-	// ApplyMeter is used to set a MeterOption value of a MeterConfig.
+	// applyMeter is used to set a MeterOption value of a MeterConfig.
 	applyMeter(MeterConfig) MeterConfig
 }
 
diff --git a/metric/doc.go b/metric/doc.go
index 4baf0719f..bd6f43437 100644
--- a/metric/doc.go
+++ b/metric/doc.go
@@ -19,49 +19,5 @@ OpenTelemetry API.
 This package is currently in a pre-GA phase. Backwards incompatible changes
 may be introduced in subsequent minor version releases as we work to track the
 evolving OpenTelemetry specification and user feedback.
-
-Measurements can be made about an operation being performed or the state of a
-system in general. These measurements can be crucial to the reliable operation
-of code and provide valuable insights about the inner workings of a system.
-
-Measurements are made using instruments provided by this package. The type of
-instrument used will depend on the type of measurement being made and of what
-part of a system is being measured.
-
-Instruments are categorized as Synchronous or Asynchronous and independently
-as Adding or Grouping. Synchronous instruments are called by the user with a
-Context. Asynchronous instruments are called by the SDK during collection.
-Adding instruments are semantically intended for capturing a sum. Grouping
-instruments are intended for capturing a distribution.
-
-Adding instruments may be monotonic, in which case they are non-decreasing
-and naturally define a rate.
-
-The synchronous instrument names are:
-
-  Counter:           adding, monotonic
-  UpDownCounter:     adding
-  Histogram:         grouping
-
-and the asynchronous instruments are:
-
-  CounterObserver:       adding, monotonic
-  UpDownCounterObserver: adding
-  GaugeObserver:         grouping
-
-All instruments are provided with support for either float64 or int64 input
-values.
-
-An instrument is created using a Meter. Additionally, a Meter is used to
-record batches of synchronous measurements or asynchronous observations. A
-Meter is obtained using a MeterProvider. A Meter, like a Tracer, is unique to
-the instrumentation it instruments and must be named and versioned when
-created with a MeterProvider with the name and version of the instrumentation
-library.
-
-Instrumentation should be designed to accept a MeterProvider from which it can
-create its own unique Meter. Alternatively, the registered global
-MeterProvider from the go.opentelemetry.io/otel package can be used as a
-default.
 */
 package metric // import "go.opentelemetry.io/otel/metric"
diff --git a/metric/example_test.go b/metric/example_test.go
new file mode 100644
index 000000000..92e0e2cd9
--- /dev/null
+++ b/metric/example_test.go
@@ -0,0 +1,116 @@
+// Copyright The OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package metric_test
+
+import (
+	"context"
+	"fmt"
+	"runtime"
+	"time"
+
+	"go.opentelemetry.io/otel/metric/instrument"
+	"go.opentelemetry.io/otel/metric/instrument/syncfloat64"
+	"go.opentelemetry.io/otel/metric/nonrecording"
+	"go.opentelemetry.io/otel/metric/unit"
+)
+
+//nolint:govet // Meter doesn't register for go vet
+func ExampleMeter_synchronous() {
+	// In a library or program this would be provided by otel.GetMeterProvider().
+	meterProvider := nonrecording.NewNoopMeterProvider()
+
+	workDuration, err := meterProvider.Meter("go.opentelemetry.io/otel/metric#SyncExample").SyncInt64().Histogram(
+		"workDuration",
+		instrument.WithUnit(unit.Milliseconds))
+	if err != nil {
+		fmt.Println("Failed to register instrument")
+		panic(err)
+	}
+
+	startTime := time.Now()
+	ctx := context.Background()
+	// Do work
+	// ...
+	workDuration.Record(ctx, time.Since(startTime).Milliseconds())
+
+}
+
+//nolint:govet // Meter doesn't register for go vet
+func ExampleMeter_asynchronous_single() {
+	// In a library or program this would be provided by otel.GetMeterProvider().
+	meterProvider := nonrecording.NewNoopMeterProvider()
+	meter := meterProvider.Meter("go.opentelemetry.io/otel/metric#AsyncExample")
+
+	memoryUsage, err := meter.AsyncInt64().Gauge(
+		"MemoryUsage",
+		instrument.WithUnit(unit.Bytes),
+	)
+	if err != nil {
+		fmt.Println("Failed to register instrument")
+		panic(err)
+	}
+
+	err = meter.RegisterCallback([]instrument.Asynchronous{memoryUsage},
+		func(ctx context.Context) {
+			// instrument.WithCallbackFunc(func(ctx context.Context) {
+			//Do Work to get the real memoryUsage
+			// mem := GatherMemory(ctx)
+			mem := 75000
+
+			memoryUsage.Observe(ctx, int64(mem))
+		})
+	if err != nil {
+		fmt.Println("Failed to register callback")
+		panic(err)
+	}
+}
+
+//nolint:govet // Meter doesn't register for go vet
+func ExampleMeter_asynchronous_multiple() {
+	meterProvider := nonrecording.NewNoopMeterProvider()
+	meter := meterProvider.Meter("go.opentelemetry.io/otel/metric#MultiAsyncExample")
+
+	// This is just a sample of memory stats to record from the Memstats
+	heapAlloc, _ := meter.AsyncInt64().UpDownCounter("heapAllocs")
+	gcCount, _ := meter.AsyncInt64().Counter("gcCount")
+	gcPause, _ := meter.SyncFloat64().Histogram("gcPause")
+
+	err := meter.RegisterCallback([]instrument.Asynchronous{
+		heapAlloc,
+		gcCount,
+	},
+		func(ctx context.Context) {
+			memStats := &runtime.MemStats{}
+			// This call does work
+			runtime.ReadMemStats(memStats)
+
+			heapAlloc.Observe(ctx, int64(memStats.HeapAlloc))
+			gcCount.Observe(ctx, int64(memStats.NumGC))
+
+			// This function synchronously records the pauses
+			computeGCPauses(ctx, gcPause, memStats.PauseNs[:])
+		},
+	)
+
+	if err != nil {
+		fmt.Println("Failed to register callback")
+		panic(err)
+	}
+}
+
+//This is just an example, see the the contrib runtime instrumentation for real implementation
+func computeGCPauses(ctx context.Context, recorder syncfloat64.Histogram, pauseBuff []uint64) {
+
+}
diff --git a/metric/global/metric.go b/metric/global/metric.go
deleted file mode 100644
index 14ba86200..000000000
--- a/metric/global/metric.go
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright The OpenTelemetry Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package global // import "go.opentelemetry.io/otel/metric/global"
-
-import (
-	"go.opentelemetry.io/otel/internal/metric/global"
-	"go.opentelemetry.io/otel/metric"
-)
-
-// Meter creates an implementation of the Meter interface from the global
-// MeterProvider. The instrumentationName must be the name of the library
-// providing instrumentation. This name may be the same as the instrumented
-// code only if that code provides built-in instrumentation. If the
-// instrumentationName is empty, then a implementation defined default name
-// will be used instead.
-//
-// This is short for MeterProvider().Meter(name)
-func Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter {
-	return GetMeterProvider().Meter(instrumentationName, opts...)
-}
-
-// GetMeterProvider returns the registered global meter provider.  If
-// none is registered then a default meter provider is returned that
-// forwards the Meter interface to the first registered Meter.
-//
-// Use the meter provider to create a named meter. E.g.
-//     meter := global.MeterProvider().Meter("example.com/foo")
-// or
-//     meter := global.Meter("example.com/foo")
-func GetMeterProvider() metric.MeterProvider {
-	return global.MeterProvider()
-}
-
-// SetMeterProvider registers `mp` as the global meter provider.
-func SetMeterProvider(mp metric.MeterProvider) {
-	global.SetMeterProvider(mp)
-}
diff --git a/metric/global/metric_test.go b/metric/global/metric_test.go
deleted file mode 100644
index dc4d9370a..000000000
--- a/metric/global/metric_test.go
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright The OpenTelemetry Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package global
-
-import (
-	"testing"
-
-	"go.opentelemetry.io/otel/metric"
-)
-
-type testMeterProvider struct{}
-
-var _ metric.MeterProvider = &testMeterProvider{}
-
-func (*testMeterProvider) Meter(_ string, _ ...metric.MeterOption) metric.Meter {
-	return metric.Meter{}
-}
-
-func TestMultipleGlobalMeterProvider(t *testing.T) {
-	p1 := testMeterProvider{}
-	p2 := metric.NewNoopMeterProvider()
-	SetMeterProvider(&p1)
-	SetMeterProvider(p2)
-
-	got := GetMeterProvider()
-	want := p2
-	if got != want {
-		t.Fatalf("MeterProvider: got %p, want %p\n", got, want)
-	}
-}
diff --git a/metric/go.mod b/metric/go.mod
index 17780384f..7d510be1f 100644
--- a/metric/go.mod
+++ b/metric/go.mod
@@ -2,6 +2,8 @@ module go.opentelemetry.io/otel/metric
 
 go 1.16
 
+require go.opentelemetry.io/otel v1.4.1
+
 replace go.opentelemetry.io/otel => ../
 
 replace go.opentelemetry.io/otel/bridge/opencensus => ../bridge/opencensus
@@ -38,13 +40,6 @@ replace go.opentelemetry.io/otel/sdk/metric => ../sdk/metric
 
 replace go.opentelemetry.io/otel/trace => ../trace
 
-require (
-	github.com/google/go-cmp v0.5.7
-	github.com/stretchr/testify v1.7.0
-	go.opentelemetry.io/otel v1.4.1
-	go.opentelemetry.io/otel/internal/metric v0.27.0
-)
-
 replace go.opentelemetry.io/otel/example/passthrough => ../example/passthrough
 
 replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../exporters/otlp/otlptrace
@@ -53,8 +48,6 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../ex
 
 replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../exporters/otlp/otlptrace/otlptracehttp
 
-replace go.opentelemetry.io/otel/internal/metric => ../internal/metric
-
 replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../exporters/otlp/otlpmetric
 
 replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../exporters/otlp/otlpmetric/otlpmetricgrpc
diff --git a/metric/go.sum b/metric/go.sum
index 531cae722..5457c7626 100644
--- a/metric/go.sum
+++ b/metric/go.sum
@@ -1,8 +1,6 @@
 github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
 github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
@@ -11,9 +9,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/metric/instrument/asyncfloat64/asyncfloat64.go b/metric/instrument/asyncfloat64/asyncfloat64.go
new file mode 100644
index 000000000..91c034fb2
--- /dev/null
+++ b/metric/instrument/asyncfloat64/asyncfloat64.go
@@ -0,0 +1,70 @@
+// Copyright The OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package asyncfloat64 // import "go.opentelemetry.io/otel/metric/instrument/asyncfloat64"
+
+import (
+	"context"
+
+	"go.opentelemetry.io/otel/attribute"
+	"go.opentelemetry.io/otel/metric/instrument"
+)
+
+// InstrumentProvider provides access to individual instruments.
+type InstrumentProvider interface {
+	// Counter creates an instrument for recording increasing values.
+	Counter(name string, opts ...instrument.Option) (Counter, error)
+
+	// UpDownCounter creates an instrument for recording changes of a value.
+	UpDownCounter(name string, opts ...instrument.Option) (UpDownCounter, error)
+
+	// Gauge creates an instrument for recording the current value.
+	Gauge(name string, opts ...instrument.Option) (Gauge, error)
+}
+
+// Counter is an instrument that records increasing values.
+type Counter interface {
+	// Observe records the state of the instrument.
+	//
+	// It is only valid to call this within a callback. If called outside of the
+	// registered callback it should have no effect on the instrument, and an
+	// error will be reported via the error handler.
+	Observe(ctx context.Context, x float64, attrs ...attribute.KeyValue)
+
+	instrument.Asynchronous
+}
+
+// UpDownCounter is an instrument that records increasing or decresing values.
+type UpDownCounter interface {
+	// Observe records the state of the instrument.
+	//
+	// It is only valid to call this within a callback. If called outside of the
+	// registered callback it should have no effect on the instrument, and an
+	// error will be reported via the error handler.
+	Observe(ctx context.Context, x float64, attrs ...attribute.KeyValue)
+
+	instrument.Asynchronous
+}
+
+// Gauge is an instrument that records independent readings.
+type Gauge interface {
+	// Observe records the state of the instrument.
+	//
+	// It is only valid to call this within a callback. If called outside of the
+	// registered callback it should have no effect on the instrument, and an
+	// error will be reported via the error handler.
+	Observe(ctx context.Context, x float64, attrs ...attribute.KeyValue)
+
+	instrument.Asynchronous
+}
diff --git a/metric/instrument/asyncint64/asyncint64.go b/metric/instrument/asyncint64/asyncint64.go
new file mode 100644
index 000000000..9dfba553d
--- /dev/null
+++ b/metric/instrument/asyncint64/asyncint64.go
@@ -0,0 +1,70 @@
+// Copyright The OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package asyncint64 // import "go.opentelemetry.io/otel/metric/instrument/asyncint64"
+
+import (
+	"context"
+
+	"go.opentelemetry.io/otel/attribute"
+	"go.opentelemetry.io/otel/metric/instrument"
+)
+
+// InstrumentProvider provides access to individual instruments.
+type InstrumentProvider interface {
+	// Counter creates an instrument for recording increasing values.
+	Counter(name string, opts ...instrument.Option) (Counter, error)
+
+	// UpDownCounter creates an instrument for recording changes of a value.
+	UpDownCounter(name string, opts ...instrument.Option) (UpDownCounter, error)
+
+	// Gauge creates an instrument for recording the current value.
+	Gauge(name string, opts ...instrument.Option) (Gauge, error)
+}
+
+// Counter is an instrument that records increasing values.
+type Counter interface {
+	// Observe records the state of the instrument.
+	//
+	// It is only valid to call this within a callback. If called outside of the
+	// registered callback it should have no effect on the instrument, and an
+	// error will be reported via the error handler.
+	Observe(ctx context.Context, x int64, attrs ...attribute.KeyValue)
+
+	instrument.Asynchronous
+}
+
+// UpDownCounter is an instrument that records increasing or decresing values.
+type UpDownCounter interface {
+	// Observe records the state of the instrument.
+	//
+	// It is only valid to call this within a callback. If called outside of the
+	// registered callback it should have no effect on the instrument, and an
+	// error will be reported via the error handler.
+	Observe(ctx context.Context, x int64, attrs ...attribute.KeyValue)
+
+	instrument.Asynchronous
+}
+
+// Gauge is an instrument that records independent readings.
+type Gauge interface {
+	// Observe records the state of the instrument.
+	//
+	// It is only valid to call this within a callback. If called outside of the
+	// registered callback it should have no effect on the instrument, and an
+	// error will be reported via the error handler.
+	Observe(ctx context.Context, x int64, attrs ...attribute.KeyValue)
+
+	instrument.Asynchronous
+}
diff --git a/metric/instrument/config.go b/metric/instrument/config.go
new file mode 100644
index 000000000..d6ea25a8d
--- /dev/null
+++ b/metric/instrument/config.go
@@ -0,0 +1,69 @@
+// Copyright The OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package instrument // import "go.opentelemetry.io/otel/metric/instrument"
+
+import "go.opentelemetry.io/otel/metric/unit"
+
+// Config contains options for metric instrument descriptors.
+type Config struct {
+	description string
+	unit        unit.Unit
+}
+
+// Description describes the instrument in human-readable terms.
+func (cfg Config) Description() string {
+	return cfg.description
+}
+
+// Unit describes the measurement unit for a instrument.
+func (cfg Config) Unit() unit.Unit {
+	return cfg.unit
+}
+
+// Option is an interface for applying metric instrument options.
+type Option interface {
+	applyInstrument(Config) Config
+}
+
+// NewConfig creates a new Config and applies all the given options.
+func NewConfig(opts ...Option) Config {
+	var config Config
+	for _, o := range opts {
+		config = o.applyInstrument(config)
+	}
+	return config
+}
+
+type optionFunc func(Config) Config
+
+func (fn optionFunc) applyInstrument(cfg Config) Config {
+	return fn(cfg)
+}
+
+// WithDescription applies provided description.
+func WithDescription(desc string) Option {
+	return optionFunc(func(cfg Config) Config {
+		cfg.description = desc
+		return cfg
+	})
+}
+
+// WithUnit applies provided unit.
+func WithUnit(unit unit.Unit) Option {
+	return optionFunc(func(cfg Config) Config {
+		cfg.unit = unit
+		return cfg
+	})
+}
diff --git a/metric/noop_test.go b/metric/instrument/instrument.go
similarity index 54%
rename from metric/noop_test.go
rename to metric/instrument/instrument.go
index e5a7528a2..e1bbb850d 100644
--- a/metric/noop_test.go
+++ b/metric/instrument/instrument.go
@@ -12,23 +12,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package metric
+package instrument // import "go.opentelemetry.io/otel/metric/instrument"
 
-import (
-	"testing"
-)
-
-func TestNewNoopMeterProvider(t *testing.T) {
-	got, want := NewNoopMeterProvider(), noopMeterProvider{}
-	if got != want {
-		t.Errorf("NewNoopMeterProvider() returned %#v, want %#v", got, want)
-	}
+// Asynchronous instruments are instruments that are updated within a Callback.
+// If an instrument is observed outside of it's callback it should be an error.
+//
+// This interface is used as a grouping mechanism.
+type Asynchronous interface {
+	asynchronous()
 }
 
-func TestNoopMeterProviderMeter(t *testing.T) {
-	mp := NewNoopMeterProvider()
-	got, want := mp.Meter(""), Meter{}
-	if got != want {
-		t.Errorf("noopMeterProvider.Meter() returned %#v, want %#v", got, want)
-	}
+// Synchronous instruments are updated in line with application code.
+//
+// This interface is used as a grouping mechanism.
+type Synchronous interface {
+	synchronous()
 }
diff --git a/metric/instrument/syncfloat64/syncfloat64.go b/metric/instrument/syncfloat64/syncfloat64.go
new file mode 100644
index 000000000..1989292ec
--- /dev/null
+++ b/metric/instrument/syncfloat64/syncfloat64.go
@@ -0,0 +1,56 @@
+// Copyright The OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package syncfloat64 // import "go.opentelemetry.io/otel/metric/instrument/syncfloat64"
+
+import (
+	"context"
+
+	"go.opentelemetry.io/otel/attribute"
+	"go.opentelemetry.io/otel/metric/instrument"
+)
+
+// InstrumentProvider provides access to individual instruments.
+type InstrumentProvider interface {
+	// Counter creates an instrument for recording increasing values.
+	Counter(name string, opts ...instrument.Option) (Counter, error)
+	// UpDownCounter creates an instrument for recording changes of a value.
+	UpDownCounter(name string, opts ...instrument.Option) (UpDownCounter, error)
+	// Histogram creates an instrument for recording a distribution of values.
+	Histogram(name string, opts ...instrument.Option) (Histogram, error)
+}
+
+// Counter is an instrument that records increasing values.
+type Counter interface {
+	// Add records a change to the counter.
+	Add(ctx context.Context, incr float64, attrs ...attribute.KeyValue)
+
+	instrument.Synchronous
+}
+
+// UpDownCounter is an instrument that records increasing or decresing values.
+type UpDownCounter interface {
+	// Add records a change to the counter.
+	Add(ctx context.Context, incr float64, attrs ...attribute.KeyValue)
+
+	instrument.Synchronous
+}
+
+// Histogram is an instrument that records a distribution of values.
+type Histogram interface {
+	// Record adds an additional value to the distribution.
+	Record(ctx context.Context, incr float64, attrs ...attribute.KeyValue)
+
+	instrument.Synchronous
+}
diff --git a/metric/instrument/syncint64/syncint64.go b/metric/instrument/syncint64/syncint64.go
new file mode 100644
index 000000000..ee882bcd9
--- /dev/null
+++ b/metric/instrument/syncint64/syncint64.go
@@ -0,0 +1,56 @@
+// Copyright The OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package syncint64 // import "go.opentelemetry.io/otel/metric/instrument/syncint64"
+
+import (
+	"context"
+
+	"go.opentelemetry.io/otel/attribute"
+	"go.opentelemetry.io/otel/metric/instrument"
+)
+
+// InstrumentProvider provides access to individual instruments.
+type InstrumentProvider interface {
+	// Counter creates an instrument for recording increasing values.
+	Counter(name string, opts ...instrument.Option) (Counter, error)
+	// UpDownCounter creates an instrument for recording changes of a value.
+	UpDownCounter(name string, opts ...instrument.Option) (UpDownCounter, error)
+	// Histogram creates an instrument for recording a distribution of values.
+	Histogram(name string, opts ...instrument.Option) (Histogram, error)
+}
+
+// Counter is an instrument that records increasing values.
+type Counter interface {
+	// Add records a change to the counter.
+	Add(ctx context.Context, incr int64, attrs ...attribute.KeyValue)
+
+	instrument.Synchronous
+}
+
+// UpDownCounter is an instrument that records increasing or decresing values.
+type UpDownCounter interface {
+	// Add records a change to the counter.
+	Add(ctx context.Context, incr int64, attrs ...attribute.KeyValue)
+
+	instrument.Synchronous
+}
+
+// Histogram is an instrument that records a distribution of values.
+type Histogram interface {
+	// Record adds an additional value to the distribution.
+	Record(ctx context.Context, incr int64, attrs ...attribute.KeyValue)
+
+	instrument.Synchronous
+}
diff --git a/metric/meter.go b/metric/meter.go
new file mode 100644
index 000000000..21fc1c499
--- /dev/null
+++ b/metric/meter.go
@@ -0,0 +1,60 @@
+// Copyright The OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package metric // import "go.opentelemetry.io/otel/metric"
+
+import (
+	"context"
+
+	"go.opentelemetry.io/otel/metric/instrument"
+	"go.opentelemetry.io/otel/metric/instrument/asyncfloat64"
+	"go.opentelemetry.io/otel/metric/instrument/asyncint64"
+	"go.opentelemetry.io/otel/metric/instrument/syncfloat64"
+	"go.opentelemetry.io/otel/metric/instrument/syncint64"
+)
+
+// MeterProvider provides access to named Meter instances, for instrumenting
+// an application or library.
+type MeterProvider interface {
+	// Meter creates an instance of a `Meter` interface. The instrumentationName
+	// must be the name of the library providing instrumentation. This name may
+	// be the same as the instrumented code only if that code provides built-in
+	// instrumentation. If the instrumentationName is empty, then a
+	// implementation defined default name will be used instead.
+	Meter(instrumentationName string, opts ...MeterOption) Meter
+}
+
+// Meter provides access to instrument instances for recording metrics.
+type Meter interface {
+	// AsyncInt64 is the namespace for the Asynchronous Integer instruments.
+	//
+	// To Observe data with instruments it must be registered in a callback.
+	AsyncInt64() asyncint64.InstrumentProvider
+
+	// AsyncFloat64 is the namespace for the Asynchronous Float instruments
+	//
+	// To Observe data with instruments it must be registered in a callback.
+	AsyncFloat64() asyncfloat64.InstrumentProvider
+
+	// RegisterCallback captures the function that will be called during Collect.
+	//
+	// It is only valid to call Observe within the scope of the passed function,
+	// and only on the instruments that were registered with this call.
+	RegisterCallback(insts []instrument.Asynchronous, function func(context.Context)) error
+
+	// SyncInt64 is the namespace for the Synchronous Integer instruments
+	SyncInt64() syncint64.InstrumentProvider
+	// SyncFloat64 is the namespace for the Synchronous Float instruments
+	SyncFloat64() syncfloat64.InstrumentProvider
+}
diff --git a/metric/metric.go b/metric/metric.go
deleted file mode 100644
index d8c5a6b3f..000000000
--- a/metric/metric.go
+++ /dev/null
@@ -1,538 +0,0 @@
-// Copyright The OpenTelemetry Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package metric // import "go.opentelemetry.io/otel/metric"
-
-import (
-	"context"
-
-	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
-)
-
-// MeterProvider supports named Meter instances.
-type MeterProvider interface {
-	// Meter creates an implementation of the Meter interface.
-	// The instrumentationName must be the name of the library providing
-	// instrumentation. This name may be the same as the instrumented code
-	// only if that code provides built-in instrumentation. If the
-	// instrumentationName is empty, then a implementation defined default
-	// name will be used instead.
-	Meter(instrumentationName string, opts ...MeterOption) Meter
-}
-
-// Meter is the creator of metric instruments.
-//
-// An uninitialized Meter is a no-op implementation.
-type Meter struct {
-	impl sdkapi.MeterImpl
-}
-
-// WrapMeterImpl constructs a `Meter` implementation from a
-// `MeterImpl` implementation.
-func WrapMeterImpl(impl sdkapi.MeterImpl) Meter {
-	return Meter{
-		impl: impl,
-	}
-}
-
-// Measurement is used for reporting a synchronous batch of metric
-// values. Instances of this type should be created by synchronous
-// instruments (e.g., Int64Counter.Measurement()).
-//
-// Note: This is an alias because it is a first-class member of the
-// API but is also part of the lower-level sdkapi interface.
-type Measurement = sdkapi.Measurement
-
-// Observation is used for reporting an asynchronous  batch of metric
-// values. Instances of this type should be created by asynchronous
-// instruments (e.g., Int64GaugeObserver.Observation()).
-//
-// Note: This is an alias because it is a first-class member of the
-// API but is also part of the lower-level sdkapi interface.
-type Observation = sdkapi.Observation
-
-// RecordBatch atomically records a batch of measurements.
-func (m Meter) RecordBatch(ctx context.Context, ls []attribute.KeyValue, ms ...Measurement) {
-	if m.impl == nil {
-		return
-	}
-	m.impl.RecordBatch(ctx, ls, ms...)
-}
-
-// NewBatchObserver creates a new BatchObserver that supports
-// making batches of observations for multiple instruments.
-func (m Meter) NewBatchObserver(callback BatchObserverFunc) BatchObserver {
-	return BatchObserver{
-		meter:  m,
-		runner: newBatchAsyncRunner(callback),
-	}
-}
-
-// NewInt64Counter creates a new integer Counter instrument with the
-// given name, customized with options.  May return an error if the
-// name is invalid (e.g., empty) or improperly registered (e.g.,
-// duplicate registration).
-func (m Meter) NewInt64Counter(name string, options ...InstrumentOption) (Int64Counter, error) {
-	return wrapInt64CounterInstrument(
-		m.newSync(name, sdkapi.CounterInstrumentKind, number.Int64Kind, options))
-}
-
-// NewFloat64Counter creates a new floating point Counter with the
-// given name, customized with options.  May return an error if the
-// name is invalid (e.g., empty) or improperly registered (e.g.,
-// duplicate registration).
-func (m Meter) NewFloat64Counter(name string, options ...InstrumentOption) (Float64Counter, error) {
-	return wrapFloat64CounterInstrument(
-		m.newSync(name, sdkapi.CounterInstrumentKind, number.Float64Kind, options))
-}
-
-// NewInt64UpDownCounter creates a new integer UpDownCounter instrument with the
-// given name, customized with options.  May return an error if the
-// name is invalid (e.g., empty) or improperly registered (e.g.,
-// duplicate registration).
-func (m Meter) NewInt64UpDownCounter(name string, options ...InstrumentOption) (Int64UpDownCounter, error) {
-	return wrapInt64UpDownCounterInstrument(
-		m.newSync(name, sdkapi.UpDownCounterInstrumentKind, number.Int64Kind, options))
-}
-
-// NewFloat64UpDownCounter creates a new floating point UpDownCounter with the
-// given name, customized with options.  May return an error if the
-// name is invalid (e.g., empty) or improperly registered (e.g.,
-// duplicate registration).
-func (m Meter) NewFloat64UpDownCounter(name string, options ...InstrumentOption) (Float64UpDownCounter, error) {
-	return wrapFloat64UpDownCounterInstrument(
-		m.newSync(name, sdkapi.UpDownCounterInstrumentKind, number.Float64Kind, options))
-}
-
-// NewInt64Histogram creates a new integer Histogram instrument with the
-// given name, customized with options.  May return an error if the
-// name is invalid (e.g., empty) or improperly registered (e.g.,
-// duplicate registration).
-func (m Meter) NewInt64Histogram(name string, opts ...InstrumentOption) (Int64Histogram, error) {
-	return wrapInt64HistogramInstrument(
-		m.newSync(name, sdkapi.HistogramInstrumentKind, number.Int64Kind, opts))
-}
-
-// NewFloat64Histogram creates a new floating point Histogram with the
-// given name, customized with options.  May return an error if the
-// name is invalid (e.g., empty) or improperly registered (e.g.,
-// duplicate registration).
-func (m Meter) NewFloat64Histogram(name string, opts ...InstrumentOption) (Float64Histogram, error) {
-	return wrapFloat64HistogramInstrument(
-		m.newSync(name, sdkapi.HistogramInstrumentKind, number.Float64Kind, opts))
-}
-
-// NewInt64GaugeObserver creates a new integer GaugeObserver instrument
-// with the given name, running a given callback, and customized with
-// options.  May return an error if the name is invalid (e.g., empty)
-// or improperly registered (e.g., duplicate registration).
-func (m Meter) NewInt64GaugeObserver(name string, callback Int64ObserverFunc, opts ...InstrumentOption) (Int64GaugeObserver, error) {
-	if callback == nil {
-		return wrapInt64GaugeObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
-	}
-	return wrapInt64GaugeObserverInstrument(
-		m.newAsync(name, sdkapi.GaugeObserverInstrumentKind, number.Int64Kind, opts,
-			newInt64AsyncRunner(callback)))
-}
-
-// NewFloat64GaugeObserver creates a new floating point GaugeObserver with
-// the given name, running a given callback, and customized with
-// options.  May return an error if the name is invalid (e.g., empty)
-// or improperly registered (e.g., duplicate registration).
-func (m Meter) NewFloat64GaugeObserver(name string, callback Float64ObserverFunc, opts ...InstrumentOption) (Float64GaugeObserver, error) {
-	if callback == nil {
-		return wrapFloat64GaugeObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
-	}
-	return wrapFloat64GaugeObserverInstrument(
-		m.newAsync(name, sdkapi.GaugeObserverInstrumentKind, number.Float64Kind, opts,
-			newFloat64AsyncRunner(callback)))
-}
-
-// NewInt64CounterObserver creates a new integer CounterObserver instrument
-// with the given name, running a given callback, and customized with
-// options.  May return an error if the name is invalid (e.g., empty)
-// or improperly registered (e.g., duplicate registration).
-func (m Meter) NewInt64CounterObserver(name string, callback Int64ObserverFunc, opts ...InstrumentOption) (Int64CounterObserver, error) {
-	if callback == nil {
-		return wrapInt64CounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
-	}
-	return wrapInt64CounterObserverInstrument(
-		m.newAsync(name, sdkapi.CounterObserverInstrumentKind, number.Int64Kind, opts,
-			newInt64AsyncRunner(callback)))
-}
-
-// NewFloat64CounterObserver creates a new floating point CounterObserver with
-// the given name, running a given callback, and customized with
-// options.  May return an error if the name is invalid (e.g., empty)
-// or improperly registered (e.g., duplicate registration).
-func (m Meter) NewFloat64CounterObserver(name string, callback Float64ObserverFunc, opts ...InstrumentOption) (Float64CounterObserver, error) {
-	if callback == nil {
-		return wrapFloat64CounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
-	}
-	return wrapFloat64CounterObserverInstrument(
-		m.newAsync(name, sdkapi.CounterObserverInstrumentKind, number.Float64Kind, opts,
-			newFloat64AsyncRunner(callback)))
-}
-
-// NewInt64UpDownCounterObserver creates a new integer UpDownCounterObserver instrument
-// with the given name, running a given callback, and customized with
-// options.  May return an error if the name is invalid (e.g., empty)
-// or improperly registered (e.g., duplicate registration).
-func (m Meter) NewInt64UpDownCounterObserver(name string, callback Int64ObserverFunc, opts ...InstrumentOption) (Int64UpDownCounterObserver, error) {
-	if callback == nil {
-		return wrapInt64UpDownCounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
-	}
-	return wrapInt64UpDownCounterObserverInstrument(
-		m.newAsync(name, sdkapi.UpDownCounterObserverInstrumentKind, number.Int64Kind, opts,
-			newInt64AsyncRunner(callback)))
-}
-
-// NewFloat64UpDownCounterObserver creates a new floating point UpDownCounterObserver with
-// the given name, running a given callback, and customized with
-// options.  May return an error if the name is invalid (e.g., empty)
-// or improperly registered (e.g., duplicate registration).
-func (m Meter) NewFloat64UpDownCounterObserver(name string, callback Float64ObserverFunc, opts ...InstrumentOption) (Float64UpDownCounterObserver, error) {
-	if callback == nil {
-		return wrapFloat64UpDownCounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
-	}
-	return wrapFloat64UpDownCounterObserverInstrument(
-		m.newAsync(name, sdkapi.UpDownCounterObserverInstrumentKind, number.Float64Kind, opts,
-			newFloat64AsyncRunner(callback)))
-}
-
-// NewInt64GaugeObserver creates a new integer GaugeObserver instrument
-// with the given name, running in a batch callback, and customized with
-// options.  May return an error if the name is invalid (e.g., empty)
-// or improperly registered (e.g., duplicate registration).
-func (b BatchObserver) NewInt64GaugeObserver(name string, opts ...InstrumentOption) (Int64GaugeObserver, error) {
-	if b.runner == nil {
-		return wrapInt64GaugeObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
-	}
-	return wrapInt64GaugeObserverInstrument(
-		b.meter.newAsync(name, sdkapi.GaugeObserverInstrumentKind, number.Int64Kind, opts, b.runner))
-}
-
-// NewFloat64GaugeObserver creates a new floating point GaugeObserver with
-// the given name, running in a batch callback, and customized with
-// options.  May return an error if the name is invalid (e.g., empty)
-// or improperly registered (e.g., duplicate registration).
-func (b BatchObserver) NewFloat64GaugeObserver(name string, opts ...InstrumentOption) (Float64GaugeObserver, error) {
-	if b.runner == nil {
-		return wrapFloat64GaugeObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
-	}
-	return wrapFloat64GaugeObserverInstrument(
-		b.meter.newAsync(name, sdkapi.GaugeObserverInstrumentKind, number.Float64Kind, opts,
-			b.runner))
-}
-
-// NewInt64CounterObserver creates a new integer CounterObserver instrument
-// with the given name, running in a batch callback, and customized with
-// options.  May return an error if the name is invalid (e.g., empty)
-// or improperly registered (e.g., duplicate registration).
-func (b BatchObserver) NewInt64CounterObserver(name string, opts ...InstrumentOption) (Int64CounterObserver, error) {
-	if b.runner == nil {
-		return wrapInt64CounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
-	}
-	return wrapInt64CounterObserverInstrument(
-		b.meter.newAsync(name, sdkapi.CounterObserverInstrumentKind, number.Int64Kind, opts, b.runner))
-}
-
-// NewFloat64CounterObserver creates a new floating point CounterObserver with
-// the given name, running in a batch callback, and customized with
-// options.  May return an error if the name is invalid (e.g., empty)
-// or improperly registered (e.g., duplicate registration).
-func (b BatchObserver) NewFloat64CounterObserver(name string, opts ...InstrumentOption) (Float64CounterObserver, error) {
-	if b.runner == nil {
-		return wrapFloat64CounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
-	}
-	return wrapFloat64CounterObserverInstrument(
-		b.meter.newAsync(name, sdkapi.CounterObserverInstrumentKind, number.Float64Kind, opts,
-			b.runner))
-}
-
-// NewInt64UpDownCounterObserver creates a new integer UpDownCounterObserver instrument
-// with the given name, running in a batch callback, and customized with
-// options.  May return an error if the name is invalid (e.g., empty)
-// or improperly registered (e.g., duplicate registration).
-func (b BatchObserver) NewInt64UpDownCounterObserver(name string, opts ...InstrumentOption) (Int64UpDownCounterObserver, error) {
-	if b.runner == nil {
-		return wrapInt64UpDownCounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
-	}
-	return wrapInt64UpDownCounterObserverInstrument(
-		b.meter.newAsync(name, sdkapi.UpDownCounterObserverInstrumentKind, number.Int64Kind, opts, b.runner))
-}
-
-// NewFloat64UpDownCounterObserver creates a new floating point UpDownCounterObserver with
-// the given name, running in a batch callback, and customized with
-// options.  May return an error if the name is invalid (e.g., empty)
-// or improperly registered (e.g., duplicate registration).
-func (b BatchObserver) NewFloat64UpDownCounterObserver(name string, opts ...InstrumentOption) (Float64UpDownCounterObserver, error) {
-	if b.runner == nil {
-		return wrapFloat64UpDownCounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
-	}
-	return wrapFloat64UpDownCounterObserverInstrument(
-		b.meter.newAsync(name, sdkapi.UpDownCounterObserverInstrumentKind, number.Float64Kind, opts,
-			b.runner))
-}
-
-// MeterImpl returns the underlying MeterImpl of this Meter.
-func (m Meter) MeterImpl() sdkapi.MeterImpl {
-	return m.impl
-}
-
-// newAsync constructs one new asynchronous instrument.
-func (m Meter) newAsync(
-	name string,
-	mkind sdkapi.InstrumentKind,
-	nkind number.Kind,
-	opts []InstrumentOption,
-	runner sdkapi.AsyncRunner,
-) (
-	sdkapi.AsyncImpl,
-	error,
-) {
-	if m.impl == nil {
-		return sdkapi.NewNoopAsyncInstrument(), nil
-	}
-	cfg := NewInstrumentConfig(opts...)
-	desc := sdkapi.NewDescriptor(name, mkind, nkind, cfg.description, cfg.unit)
-	return m.impl.NewAsyncInstrument(desc, runner)
-}
-
-// newSync constructs one new synchronous instrument.
-func (m Meter) newSync(
-	name string,
-	metricKind sdkapi.InstrumentKind,
-	numberKind number.Kind,
-	opts []InstrumentOption,
-) (
-	sdkapi.SyncImpl,
-	error,
-) {
-	if m.impl == nil {
-		return sdkapi.NewNoopSyncInstrument(), nil
-	}
-	cfg := NewInstrumentConfig(opts...)
-	desc := sdkapi.NewDescriptor(name, metricKind, numberKind, cfg.description, cfg.unit)
-	return m.impl.NewSyncInstrument(desc)
-}
-
-// MeterMust is a wrapper for Meter interfaces that panics when any
-// instrument constructor encounters an error.
-type MeterMust struct {
-	meter Meter
-}
-
-// BatchObserverMust is a wrapper for BatchObserver that panics when
-// any instrument constructor encounters an error.
-type BatchObserverMust struct {
-	batch BatchObserver
-}
-
-// Must constructs a MeterMust implementation from a Meter, allowing
-// the application to panic when any instrument constructor yields an
-// error.
-func Must(meter Meter) MeterMust {
-	return MeterMust{meter: meter}
-}
-
-// NewInt64Counter calls `Meter.NewInt64Counter` and returns the
-// instrument, panicking if it encounters an error.
-func (mm MeterMust) NewInt64Counter(name string, cos ...InstrumentOption) Int64Counter {
-	if inst, err := mm.meter.NewInt64Counter(name, cos...); err != nil {
-		panic(err)
-	} else {
-		return inst
-	}
-}
-
-// NewFloat64Counter calls `Meter.NewFloat64Counter` and returns the
-// instrument, panicking if it encounters an error.
-func (mm MeterMust) NewFloat64Counter(name string, cos ...InstrumentOption) Float64Counter {
-	if inst, err := mm.meter.NewFloat64Counter(name, cos...); err != nil {
-		panic(err)
-	} else {
-		return inst
-	}
-}
-
-// NewInt64UpDownCounter calls `Meter.NewInt64UpDownCounter` and returns the
-// instrument, panicking if it encounters an error.
-func (mm MeterMust) NewInt64UpDownCounter(name string, cos ...InstrumentOption) Int64UpDownCounter {
-	if inst, err := mm.meter.NewInt64UpDownCounter(name, cos...); err != nil {
-		panic(err)
-	} else {
-		return inst
-	}
-}
-
-// NewFloat64UpDownCounter calls `Meter.NewFloat64UpDownCounter` and returns the
-// instrument, panicking if it encounters an error.
-func (mm MeterMust) NewFloat64UpDownCounter(name string, cos ...InstrumentOption) Float64UpDownCounter {
-	if inst, err := mm.meter.NewFloat64UpDownCounter(name, cos...); err != nil {
-		panic(err)
-	} else {
-		return inst
-	}
-}
-
-// NewInt64Histogram calls `Meter.NewInt64Histogram` and returns the
-// instrument, panicking if it encounters an error.
-func (mm MeterMust) NewInt64Histogram(name string, mos ...InstrumentOption) Int64Histogram {
-	if inst, err := mm.meter.NewInt64Histogram(name, mos...); err != nil {
-		panic(err)
-	} else {
-		return inst
-	}
-}
-
-// NewFloat64Histogram calls `Meter.NewFloat64Histogram` and returns the
-// instrument, panicking if it encounters an error.
-func (mm MeterMust) NewFloat64Histogram(name string, mos ...InstrumentOption) Float64Histogram {
-	if inst, err := mm.meter.NewFloat64Histogram(name, mos...); err != nil {
-		panic(err)
-	} else {
-		return inst
-	}
-}
-
-// NewInt64GaugeObserver calls `Meter.NewInt64GaugeObserver` and
-// returns the instrument, panicking if it encounters an error.
-func (mm MeterMust) NewInt64GaugeObserver(name string, callback Int64ObserverFunc, oos ...InstrumentOption) Int64GaugeObserver {
-	if inst, err := mm.meter.NewInt64GaugeObserver(name, callback, oos...); err != nil {
-		panic(err)
-	} else {
-		return inst
-	}
-}
-
-// NewFloat64GaugeObserver calls `Meter.NewFloat64GaugeObserver` and
-// returns the instrument, panicking if it encounters an error.
-func (mm MeterMust) NewFloat64GaugeObserver(name string, callback Float64ObserverFunc, oos ...InstrumentOption) Float64GaugeObserver {
-	if inst, err := mm.meter.NewFloat64GaugeObserver(name, callback, oos...); err != nil {
-		panic(err)
-	} else {
-		return inst
-	}
-}
-
-// NewInt64CounterObserver calls `Meter.NewInt64CounterObserver` and
-// returns the instrument, panicking if it encounters an error.
-func (mm MeterMust) NewInt64CounterObserver(name string, callback Int64ObserverFunc, oos ...InstrumentOption) Int64CounterObserver {
-	if inst, err := mm.meter.NewInt64CounterObserver(name, callback, oos...); err != nil {
-		panic(err)
-	} else {
-		return inst
-	}
-}
-
-// NewFloat64CounterObserver calls `Meter.NewFloat64CounterObserver` and
-// returns the instrument, panicking if it encounters an error.
-func (mm MeterMust) NewFloat64CounterObserver(name string, callback Float64ObserverFunc, oos ...InstrumentOption) Float64CounterObserver {
-	if inst, err := mm.meter.NewFloat64CounterObserver(name, callback, oos...); err != nil {
-		panic(err)
-	} else {
-		return inst
-	}
-}
-
-// NewInt64UpDownCounterObserver calls `Meter.NewInt64UpDownCounterObserver` and
-// returns the instrument, panicking if it encounters an error.
-func (mm MeterMust) NewInt64UpDownCounterObserver(name string, callback Int64ObserverFunc, oos ...InstrumentOption) Int64UpDownCounterObserver {
-	if inst, err := mm.meter.NewInt64UpDownCounterObserver(name, callback, oos...); err != nil {
-		panic(err)
-	} else {
-		return inst
-	}
-}
-
-// NewFloat64UpDownCounterObserver calls `Meter.NewFloat64UpDownCounterObserver` and
-// returns the instrument, panicking if it encounters an error.
-func (mm MeterMust) NewFloat64UpDownCounterObserver(name string, callback Float64ObserverFunc, oos ...InstrumentOption) Float64UpDownCounterObserver {
-	if inst, err := mm.meter.NewFloat64UpDownCounterObserver(name, callback, oos...); err != nil {
-		panic(err)
-	} else {
-		return inst
-	}
-}
-
-// NewBatchObserver returns a wrapper around BatchObserver that panics
-// when any instrument constructor returns an error.
-func (mm MeterMust) NewBatchObserver(callback BatchObserverFunc) BatchObserverMust {
-	return BatchObserverMust{
-		batch: mm.meter.NewBatchObserver(callback),
-	}
-}
-
-// NewInt64GaugeObserver calls `BatchObserver.NewInt64GaugeObserver` and
-// returns the instrument, panicking if it encounters an error.
-func (bm BatchObserverMust) NewInt64GaugeObserver(name string, oos ...InstrumentOption) Int64GaugeObserver {
-	if inst, err := bm.batch.NewInt64GaugeObserver(name, oos...); err != nil {
-		panic(err)
-	} else {
-		return inst
-	}
-}
-
-// NewFloat64GaugeObserver calls `BatchObserver.NewFloat64GaugeObserver` and
-// returns the instrument, panicking if it encounters an error.
-func (bm BatchObserverMust) NewFloat64GaugeObserver(name string, oos ...InstrumentOption) Float64GaugeObserver {
-	if inst, err := bm.batch.NewFloat64GaugeObserver(name, oos...); err != nil {
-		panic(err)
-	} else {
-		return inst
-	}
-}
-
-// NewInt64CounterObserver calls `BatchObserver.NewInt64CounterObserver` and
-// returns the instrument, panicking if it encounters an error.
-func (bm BatchObserverMust) NewInt64CounterObserver(name string, oos ...InstrumentOption) Int64CounterObserver {
-	if inst, err := bm.batch.NewInt64CounterObserver(name, oos...); err != nil {
-		panic(err)
-	} else {
-		return inst
-	}
-}
-
-// NewFloat64CounterObserver calls `BatchObserver.NewFloat64CounterObserver` and
-// returns the instrument, panicking if it encounters an error.
-func (bm BatchObserverMust) NewFloat64CounterObserver(name string, oos ...InstrumentOption) Float64CounterObserver {
-	if inst, err := bm.batch.NewFloat64CounterObserver(name, oos...); err != nil {
-		panic(err)
-	} else {
-		return inst
-	}
-}
-
-// NewInt64UpDownCounterObserver calls `BatchObserver.NewInt64UpDownCounterObserver` and
-// returns the instrument, panicking if it encounters an error.
-func (bm BatchObserverMust) NewInt64UpDownCounterObserver(name string, oos ...InstrumentOption) Int64UpDownCounterObserver {
-	if inst, err := bm.batch.NewInt64UpDownCounterObserver(name, oos...); err != nil {
-		panic(err)
-	} else {
-		return inst
-	}
-}
-
-// NewFloat64UpDownCounterObserver calls `BatchObserver.NewFloat64UpDownCounterObserver` and
-// returns the instrument, panicking if it encounters an error.
-func (bm BatchObserverMust) NewFloat64UpDownCounterObserver(name string, oos ...InstrumentOption) Float64UpDownCounterObserver {
-	if inst, err := bm.batch.NewFloat64UpDownCounterObserver(name, oos...); err != nil {
-		panic(err)
-	} else {
-		return inst
-	}
-}
diff --git a/metric/metric_instrument.go b/metric/metric_instrument.go
deleted file mode 100644
index 2da24c8f2..000000000
--- a/metric/metric_instrument.go
+++ /dev/null
@@ -1,464 +0,0 @@
-// Copyright The OpenTelemetry Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package metric // import "go.opentelemetry.io/otel/metric"
-
-import (
-	"context"
-	"errors"
-
-	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
-)
-
-// ErrSDKReturnedNilImpl is returned when a new `MeterImpl` returns nil.
-var ErrSDKReturnedNilImpl = errors.New("SDK returned a nil implementation")
-
-// Int64ObserverFunc is a type of callback that integral
-// observers run.
-type Int64ObserverFunc func(context.Context, Int64ObserverResult)
-
-// Float64ObserverFunc is a type of callback that floating point
-// observers run.
-type Float64ObserverFunc func(context.Context, Float64ObserverResult)
-
-// BatchObserverFunc is a callback argument for use with any
-// Observer instrument that will be reported as a batch of
-// observations.
-type BatchObserverFunc func(context.Context, BatchObserverResult)
-
-// Int64ObserverResult is passed to an observer callback to capture
-// observations for one asynchronous integer metric instrument.
-type Int64ObserverResult struct {
-	instrument sdkapi.AsyncImpl
-	function   func([]attribute.KeyValue, ...Observation)
-}
-
-// Float64ObserverResult is passed to an observer callback to capture
-// observations for one asynchronous floating point metric instrument.
-type Float64ObserverResult struct {
-	instrument sdkapi.AsyncImpl
-	function   func([]attribute.KeyValue, ...Observation)
-}
-
-// BatchObserverResult is passed to a batch observer callback to
-// capture observations for multiple asynchronous instruments.
-type BatchObserverResult struct {
-	function func([]attribute.KeyValue, ...Observation)
-}
-
-// Observe captures a single integer value from the associated
-// instrument callback, with the given labels.
-func (ir Int64ObserverResult) Observe(value int64, labels ...attribute.KeyValue) {
-	ir.function(labels, sdkapi.NewObservation(ir.instrument, number.NewInt64Number(value)))
-}
-
-// Observe captures a single floating point value from the associated
-// instrument callback, with the given labels.
-func (fr Float64ObserverResult) Observe(value float64, labels ...attribute.KeyValue) {
-	fr.function(labels, sdkapi.NewObservation(fr.instrument, number.NewFloat64Number(value)))
-}
-
-// Observe captures a multiple observations from the associated batch
-// instrument callback, with the given labels.
-func (br BatchObserverResult) Observe(labels []attribute.KeyValue, obs ...Observation) {
-	br.function(labels, obs...)
-}
-
-var _ sdkapi.AsyncSingleRunner = (*Int64ObserverFunc)(nil)
-var _ sdkapi.AsyncSingleRunner = (*Float64ObserverFunc)(nil)
-var _ sdkapi.AsyncBatchRunner = (*BatchObserverFunc)(nil)
-
-// newInt64AsyncRunner returns a single-observer callback for integer Observer instruments.
-func newInt64AsyncRunner(c Int64ObserverFunc) sdkapi.AsyncSingleRunner {
-	return &c
-}
-
-// newFloat64AsyncRunner returns a single-observer callback for floating point Observer instruments.
-func newFloat64AsyncRunner(c Float64ObserverFunc) sdkapi.AsyncSingleRunner {
-	return &c
-}
-
-// newBatchAsyncRunner returns a batch-observer callback use with multiple Observer instruments.
-func newBatchAsyncRunner(c BatchObserverFunc) sdkapi.AsyncBatchRunner {
-	return &c
-}
-
-// AnyRunner implements AsyncRunner.
-func (*Int64ObserverFunc) AnyRunner() {}
-
-// AnyRunner implements AsyncRunner.
-func (*Float64ObserverFunc) AnyRunner() {}
-
-// AnyRunner implements AsyncRunner.
-func (*BatchObserverFunc) AnyRunner() {}
-
-// Run implements AsyncSingleRunner.
-func (i *Int64ObserverFunc) Run(ctx context.Context, impl sdkapi.AsyncImpl, function func([]attribute.KeyValue, ...Observation)) {
-	(*i)(ctx, Int64ObserverResult{
-		instrument: impl,
-		function:   function,
-	})
-}
-
-// Run implements AsyncSingleRunner.
-func (f *Float64ObserverFunc) Run(ctx context.Context, impl sdkapi.AsyncImpl, function func([]attribute.KeyValue, ...Observation)) {
-	(*f)(ctx, Float64ObserverResult{
-		instrument: impl,
-		function:   function,
-	})
-}
-
-// Run implements AsyncBatchRunner.
-func (b *BatchObserverFunc) Run(ctx context.Context, function func([]attribute.KeyValue, ...Observation)) {
-	(*b)(ctx, BatchObserverResult{
-		function: function,
-	})
-}
-
-// wrapInt64GaugeObserverInstrument converts an AsyncImpl into Int64GaugeObserver.
-func wrapInt64GaugeObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Int64GaugeObserver, error) {
-	common, err := checkNewAsync(asyncInst, err)
-	return Int64GaugeObserver{asyncInstrument: common}, err
-}
-
-// wrapFloat64GaugeObserverInstrument converts an AsyncImpl into Float64GaugeObserver.
-func wrapFloat64GaugeObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Float64GaugeObserver, error) {
-	common, err := checkNewAsync(asyncInst, err)
-	return Float64GaugeObserver{asyncInstrument: common}, err
-}
-
-// wrapInt64CounterObserverInstrument converts an AsyncImpl into Int64CounterObserver.
-func wrapInt64CounterObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Int64CounterObserver, error) {
-	common, err := checkNewAsync(asyncInst, err)
-	return Int64CounterObserver{asyncInstrument: common}, err
-}
-
-// wrapFloat64CounterObserverInstrument converts an AsyncImpl into Float64CounterObserver.
-func wrapFloat64CounterObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Float64CounterObserver, error) {
-	common, err := checkNewAsync(asyncInst, err)
-	return Float64CounterObserver{asyncInstrument: common}, err
-}
-
-// wrapInt64UpDownCounterObserverInstrument converts an AsyncImpl into Int64UpDownCounterObserver.
-func wrapInt64UpDownCounterObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Int64UpDownCounterObserver, error) {
-	common, err := checkNewAsync(asyncInst, err)
-	return Int64UpDownCounterObserver{asyncInstrument: common}, err
-}
-
-// wrapFloat64UpDownCounterObserverInstrument converts an AsyncImpl into Float64UpDownCounterObserver.
-func wrapFloat64UpDownCounterObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Float64UpDownCounterObserver, error) {
-	common, err := checkNewAsync(asyncInst, err)
-	return Float64UpDownCounterObserver{asyncInstrument: common}, err
-}
-
-// BatchObserver represents an Observer callback that can report
-// observations for multiple instruments.
-type BatchObserver struct {
-	meter  Meter
-	runner sdkapi.AsyncBatchRunner
-}
-
-// Int64GaugeObserver is a metric that captures a set of int64 values at a
-// point in time.
-type Int64GaugeObserver struct {
-	asyncInstrument
-}
-
-// Float64GaugeObserver is a metric that captures a set of float64 values
-// at a point in time.
-type Float64GaugeObserver struct {
-	asyncInstrument
-}
-
-// Int64CounterObserver is a metric that captures a precomputed sum of
-// int64 values at a point in time.
-type Int64CounterObserver struct {
-	asyncInstrument
-}
-
-// Float64CounterObserver is a metric that captures a precomputed sum of
-// float64 values at a point in time.
-type Float64CounterObserver struct {
-	asyncInstrument
-}
-
-// Int64UpDownCounterObserver is a metric that captures a precomputed sum of
-// int64 values at a point in time.
-type Int64UpDownCounterObserver struct {
-	asyncInstrument
-}
-
-// Float64UpDownCounterObserver is a metric that captures a precomputed sum of
-// float64 values at a point in time.
-type Float64UpDownCounterObserver struct {
-	asyncInstrument
-}
-
-// Observation returns an Observation, a BatchObserverFunc
-// argument, for an asynchronous integer instrument.
-// This returns an implementation-level object for use by the SDK,
-// users should not refer to this.
-func (i Int64GaugeObserver) Observation(v int64) Observation {
-	return sdkapi.NewObservation(i.instrument, number.NewInt64Number(v))
-}
-
-// Observation returns an Observation, a BatchObserverFunc
-// argument, for an asynchronous integer instrument.
-// This returns an implementation-level object for use by the SDK,
-// users should not refer to this.
-func (f Float64GaugeObserver) Observation(v float64) Observation {
-	return sdkapi.NewObservation(f.instrument, number.NewFloat64Number(v))
-}
-
-// Observation returns an Observation, a BatchObserverFunc
-// argument, for an asynchronous integer instrument.
-// This returns an implementation-level object for use by the SDK,
-// users should not refer to this.
-func (i Int64CounterObserver) Observation(v int64) Observation {
-	return sdkapi.NewObservation(i.instrument, number.NewInt64Number(v))
-}
-
-// Observation returns an Observation, a BatchObserverFunc
-// argument, for an asynchronous integer instrument.
-// This returns an implementation-level object for use by the SDK,
-// users should not refer to this.
-func (f Float64CounterObserver) Observation(v float64) Observation {
-	return sdkapi.NewObservation(f.instrument, number.NewFloat64Number(v))
-}
-
-// Observation returns an Observation, a BatchObserverFunc
-// argument, for an asynchronous integer instrument.
-// This returns an implementation-level object for use by the SDK,
-// users should not refer to this.
-func (i Int64UpDownCounterObserver) Observation(v int64) Observation {
-	return sdkapi.NewObservation(i.instrument, number.NewInt64Number(v))
-}
-
-// Observation returns an Observation, a BatchObserverFunc
-// argument, for an asynchronous integer instrument.
-// This returns an implementation-level object for use by the SDK,
-// users should not refer to this.
-func (f Float64UpDownCounterObserver) Observation(v float64) Observation {
-	return sdkapi.NewObservation(f.instrument, number.NewFloat64Number(v))
-}
-
-// syncInstrument contains a SyncImpl.
-type syncInstrument struct {
-	instrument sdkapi.SyncImpl
-}
-
-// asyncInstrument contains a AsyncImpl.
-type asyncInstrument struct {
-	instrument sdkapi.AsyncImpl
-}
-
-// AsyncImpl implements AsyncImpl.
-func (a asyncInstrument) AsyncImpl() sdkapi.AsyncImpl {
-	return a.instrument
-}
-
-// SyncImpl returns the implementation object for synchronous instruments.
-func (s syncInstrument) SyncImpl() sdkapi.SyncImpl {
-	return s.instrument
-}
-
-func (s syncInstrument) float64Measurement(value float64) Measurement {
-	return sdkapi.NewMeasurement(s.instrument, number.NewFloat64Number(value))
-}
-
-func (s syncInstrument) int64Measurement(value int64) Measurement {
-	return sdkapi.NewMeasurement(s.instrument, number.NewInt64Number(value))
-}
-
-func (s syncInstrument) directRecord(ctx context.Context, number number.Number, labels []attribute.KeyValue) {
-	s.instrument.RecordOne(ctx, number, labels)
-}
-
-// checkNewAsync receives an AsyncImpl and potential
-// error, and returns the same types, checking for and ensuring that
-// the returned interface is not nil.
-func checkNewAsync(instrument sdkapi.AsyncImpl, err error) (asyncInstrument, error) {
-	if instrument == nil {
-		if err == nil {
-			err = ErrSDKReturnedNilImpl
-		}
-		instrument = sdkapi.NewNoopAsyncInstrument()
-	}
-	return asyncInstrument{
-		instrument: instrument,
-	}, err
-}
-
-// checkNewSync receives an SyncImpl and potential
-// error, and returns the same types, checking for and ensuring that
-// the returned interface is not nil.
-func checkNewSync(instrument sdkapi.SyncImpl, err error) (syncInstrument, error) {
-	if instrument == nil {
-		if err == nil {
-			err = ErrSDKReturnedNilImpl
-		}
-		// Note: an alternate behavior would be to synthesize a new name
-		// or group all duplicately-named instruments of a certain type
-		// together and use a tag for the original name, e.g.,
-		//   name = 'invalid.counter.int64'
-		//   label = 'original-name=duplicate-counter-name'
-		instrument = sdkapi.NewNoopSyncInstrument()
-	}
-	return syncInstrument{
-		instrument: instrument,
-	}, err
-}
-
-// wrapInt64CounterInstrument converts a SyncImpl into Int64Counter.
-func wrapInt64CounterInstrument(syncInst sdkapi.SyncImpl, err error) (Int64Counter, error) {
-	common, err := checkNewSync(syncInst, err)
-	return Int64Counter{syncInstrument: common}, err
-}
-
-// wrapFloat64CounterInstrument converts a SyncImpl into Float64Counter.
-func wrapFloat64CounterInstrument(syncInst sdkapi.SyncImpl, err error) (Float64Counter, error) {
-	common, err := checkNewSync(syncInst, err)
-	return Float64Counter{syncInstrument: common}, err
-}
-
-// wrapInt64UpDownCounterInstrument converts a SyncImpl into Int64UpDownCounter.
-func wrapInt64UpDownCounterInstrument(syncInst sdkapi.SyncImpl, err error) (Int64UpDownCounter, error) {
-	common, err := checkNewSync(syncInst, err)
-	return Int64UpDownCounter{syncInstrument: common}, err
-}
-
-// wrapFloat64UpDownCounterInstrument converts a SyncImpl into Float64UpDownCounter.
-func wrapFloat64UpDownCounterInstrument(syncInst sdkapi.SyncImpl, err error) (Float64UpDownCounter, error) {
-	common, err := checkNewSync(syncInst, err)
-	return Float64UpDownCounter{syncInstrument: common}, err
-}
-
-// wrapInt64HistogramInstrument converts a SyncImpl into Int64Histogram.
-func wrapInt64HistogramInstrument(syncInst sdkapi.SyncImpl, err error) (Int64Histogram, error) {
-	common, err := checkNewSync(syncInst, err)
-	return Int64Histogram{syncInstrument: common}, err
-}
-
-// wrapFloat64HistogramInstrument converts a SyncImpl into Float64Histogram.
-func wrapFloat64HistogramInstrument(syncInst sdkapi.SyncImpl, err error) (Float64Histogram, error) {
-	common, err := checkNewSync(syncInst, err)
-	return Float64Histogram{syncInstrument: common}, err
-}
-
-// Float64Counter is a metric that accumulates float64 values.
-type Float64Counter struct {
-	syncInstrument
-}
-
-// Int64Counter is a metric that accumulates int64 values.
-type Int64Counter struct {
-	syncInstrument
-}
-
-// Measurement creates a Measurement object to use with batch
-// recording.
-func (c Float64Counter) Measurement(value float64) Measurement {
-	return c.float64Measurement(value)
-}
-
-// Measurement creates a Measurement object to use with batch
-// recording.
-func (c Int64Counter) Measurement(value int64) Measurement {
-	return c.int64Measurement(value)
-}
-
-// Add adds the value to the counter's sum. The labels should contain
-// the keys and values to be associated with this value.
-func (c Float64Counter) Add(ctx context.Context, value float64, labels ...attribute.KeyValue) {
-	c.directRecord(ctx, number.NewFloat64Number(value), labels)
-}
-
-// Add adds the value to the counter's sum. The labels should contain
-// the keys and values to be associated with this value.
-func (c Int64Counter) Add(ctx context.Context, value int64, labels ...attribute.KeyValue) {
-	c.directRecord(ctx, number.NewInt64Number(value), labels)
-}
-
-// Float64UpDownCounter is a metric instrument that sums floating
-// point values.
-type Float64UpDownCounter struct {
-	syncInstrument
-}
-
-// Int64UpDownCounter is a metric instrument that sums integer values.
-type Int64UpDownCounter struct {
-	syncInstrument
-}
-
-// Measurement creates a Measurement object to use with batch
-// recording.
-func (c Float64UpDownCounter) Measurement(value float64) Measurement {
-	return c.float64Measurement(value)
-}
-
-// Measurement creates a Measurement object to use with batch
-// recording.
-func (c Int64UpDownCounter) Measurement(value int64) Measurement {
-	return c.int64Measurement(value)
-}
-
-// Add adds the value to the counter's sum. The labels should contain
-// the keys and values to be associated with this value.
-func (c Float64UpDownCounter) Add(ctx context.Context, value float64, labels ...attribute.KeyValue) {
-	c.directRecord(ctx, number.NewFloat64Number(value), labels)
-}
-
-// Add adds the value to the counter's sum. The labels should contain
-// the keys and values to be associated with this value.
-func (c Int64UpDownCounter) Add(ctx context.Context, value int64, labels ...attribute.KeyValue) {
-	c.directRecord(ctx, number.NewInt64Number(value), labels)
-}
-
-// Float64Histogram is a metric that records float64 values.
-type Float64Histogram struct {
-	syncInstrument
-}
-
-// Int64Histogram is a metric that records int64 values.
-type Int64Histogram struct {
-	syncInstrument
-}
-
-// Measurement creates a Measurement object to use with batch
-// recording.
-func (c Float64Histogram) Measurement(value float64) Measurement {
-	return c.float64Measurement(value)
-}
-
-// Measurement creates a Measurement object to use with batch
-// recording.
-func (c Int64Histogram) Measurement(value int64) Measurement {
-	return c.int64Measurement(value)
-}
-
-// Record adds a new value to the list of Histogram's records. The
-// labels should contain the keys and values to be associated with
-// this value.
-func (c Float64Histogram) Record(ctx context.Context, value float64, labels ...attribute.KeyValue) {
-	c.directRecord(ctx, number.NewFloat64Number(value), labels)
-}
-
-// Record adds a new value to the Histogram's distribution. The
-// labels should contain the keys and values to be associated with
-// this value.
-func (c Int64Histogram) Record(ctx context.Context, value int64, labels ...attribute.KeyValue) {
-	c.directRecord(ctx, number.NewInt64Number(value), labels)
-}
diff --git a/metric/metric_test.go b/metric/metric_test.go
deleted file mode 100644
index ab8e916b8..000000000
--- a/metric/metric_test.go
+++ /dev/null
@@ -1,497 +0,0 @@
-// Copyright The OpenTelemetry Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package metric_test
-
-import (
-	"context"
-	"errors"
-	"testing"
-
-	"github.com/google/go-cmp/cmp"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-
-	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/metrictest"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
-	"go.opentelemetry.io/otel/metric/unit"
-)
-
-var Must = metric.Must
-
-var (
-	syncKinds = []sdkapi.InstrumentKind{
-		sdkapi.HistogramInstrumentKind,
-		sdkapi.CounterInstrumentKind,
-		sdkapi.UpDownCounterInstrumentKind,
-	}
-	asyncKinds = []sdkapi.InstrumentKind{
-		sdkapi.GaugeObserverInstrumentKind,
-		sdkapi.CounterObserverInstrumentKind,
-		sdkapi.UpDownCounterObserverInstrumentKind,
-	}
-	addingKinds = []sdkapi.InstrumentKind{
-		sdkapi.CounterInstrumentKind,
-		sdkapi.UpDownCounterInstrumentKind,
-		sdkapi.CounterObserverInstrumentKind,
-		sdkapi.UpDownCounterObserverInstrumentKind,
-	}
-	groupingKinds = []sdkapi.InstrumentKind{
-		sdkapi.HistogramInstrumentKind,
-		sdkapi.GaugeObserverInstrumentKind,
-	}
-
-	monotonicKinds = []sdkapi.InstrumentKind{
-		sdkapi.CounterInstrumentKind,
-		sdkapi.CounterObserverInstrumentKind,
-	}
-
-	nonMonotonicKinds = []sdkapi.InstrumentKind{
-		sdkapi.UpDownCounterInstrumentKind,
-		sdkapi.UpDownCounterObserverInstrumentKind,
-		sdkapi.HistogramInstrumentKind,
-		sdkapi.GaugeObserverInstrumentKind,
-	}
-
-	precomputedSumKinds = []sdkapi.InstrumentKind{
-		sdkapi.CounterObserverInstrumentKind,
-		sdkapi.UpDownCounterObserverInstrumentKind,
-	}
-
-	nonPrecomputedSumKinds = []sdkapi.InstrumentKind{
-		sdkapi.CounterInstrumentKind,
-		sdkapi.UpDownCounterInstrumentKind,
-		sdkapi.HistogramInstrumentKind,
-		sdkapi.GaugeObserverInstrumentKind,
-	}
-)
-
-func TestSynchronous(t *testing.T) {
-	for _, k := range syncKinds {
-		require.True(t, k.Synchronous())
-		require.False(t, k.Asynchronous())
-	}
-	for _, k := range asyncKinds {
-		require.True(t, k.Asynchronous())
-		require.False(t, k.Synchronous())
-	}
-}
-
-func TestGrouping(t *testing.T) {
-	for _, k := range groupingKinds {
-		require.True(t, k.Grouping())
-		require.False(t, k.Adding())
-	}
-	for _, k := range addingKinds {
-		require.True(t, k.Adding())
-		require.False(t, k.Grouping())
-	}
-}
-
-func TestMonotonic(t *testing.T) {
-	for _, k := range monotonicKinds {
-		require.True(t, k.Monotonic())
-	}
-	for _, k := range nonMonotonicKinds {
-		require.False(t, k.Monotonic())
-	}
-}
-
-func TestPrecomputedSum(t *testing.T) {
-	for _, k := range precomputedSumKinds {
-		require.True(t, k.PrecomputedSum())
-	}
-	for _, k := range nonPrecomputedSumKinds {
-		require.False(t, k.PrecomputedSum())
-	}
-}
-
-func checkSyncBatches(ctx context.Context, t *testing.T, labels []attribute.KeyValue, provider *metrictest.MeterProvider, nkind number.Kind, mkind sdkapi.InstrumentKind, instrument sdkapi.InstrumentImpl, expected ...float64) {
-	t.Helper()
-
-	batchesCount := len(provider.MeasurementBatches)
-	if len(provider.MeasurementBatches) != len(expected) {
-		t.Errorf("Expected %d recorded measurement batches, got %d", batchesCount, len(provider.MeasurementBatches))
-	}
-	recorded := metrictest.AsStructs(provider.MeasurementBatches)
-
-	for i, batch := range provider.MeasurementBatches {
-		if len(batch.Measurements) != 1 {
-			t.Errorf("Expected 1 measurement in batch %d, got %d", i, len(batch.Measurements))
-		}
-
-		measurement := batch.Measurements[0]
-		descriptor := measurement.Instrument.Descriptor()
-
-		expected := metrictest.Measured{
-			Name: descriptor.Name(),
-			Library: metrictest.Library{
-				InstrumentationName: "apitest",
-			},
-			Labels: metrictest.LabelsToMap(labels...),
-			Number: metrictest.ResolveNumberByKind(t, nkind, expected[i]),
-		}
-		require.Equal(t, expected, recorded[i])
-	}
-}
-
-func TestOptions(t *testing.T) {
-	type testcase struct {
-		name string
-		opts []metric.InstrumentOption
-		desc string
-		unit unit.Unit
-	}
-	testcases := []testcase{
-		{
-			name: "no opts",
-			opts: nil,
-			desc: "",
-			unit: "",
-		},
-		{
-			name: "description",
-			opts: []metric.InstrumentOption{
-				metric.WithDescription("stuff"),
-			},
-			desc: "stuff",
-			unit: "",
-		},
-		{
-			name: "description override",
-			opts: []metric.InstrumentOption{
-				metric.WithDescription("stuff"),
-				metric.WithDescription("things"),
-			},
-			desc: "things",
-			unit: "",
-		},
-		{
-			name: "unit",
-			opts: []metric.InstrumentOption{
-				metric.WithUnit("s"),
-			},
-			desc: "",
-			unit: "s",
-		},
-		{
-			name: "description override",
-			opts: []metric.InstrumentOption{
-				metric.WithDescription("stuff"),
-				metric.WithDescription("things"),
-			},
-			desc: "things",
-			unit: "",
-		},
-		{
-			name: "unit",
-			opts: []metric.InstrumentOption{
-				metric.WithUnit("s"),
-			},
-			desc: "",
-			unit: "s",
-		},
-
-		{
-			name: "unit override",
-			opts: []metric.InstrumentOption{
-				metric.WithUnit("s"),
-				metric.WithUnit("h"),
-			},
-			desc: "",
-			unit: "h",
-		},
-		{
-			name: "all",
-			opts: []metric.InstrumentOption{
-				metric.WithDescription("stuff"),
-				metric.WithUnit("s"),
-			},
-			desc: "stuff",
-			unit: "s",
-		},
-	}
-	for idx, tt := range testcases {
-		t.Logf("Testing counter case %s (%d)", tt.name, idx)
-		cfg := metric.NewInstrumentConfig(tt.opts...)
-		if diff := cmp.Diff(cfg.Description(), tt.desc); diff != "" {
-			t.Errorf("Compare Description: -got +want %s", diff)
-		}
-		if diff := cmp.Diff(cfg.Unit(), tt.unit); diff != "" {
-			t.Errorf("Compare Unit: -got +want %s", diff)
-		}
-	}
-}
-func testPair() (*metrictest.MeterProvider, metric.Meter) {
-	provider := metrictest.NewMeterProvider()
-	return provider, provider.Meter("apitest")
-}
-
-func TestCounter(t *testing.T) {
-	// N.B. the API does not check for negative
-	// values, that's the SDK's responsibility.
-	t.Run("float64 counter", func(t *testing.T) {
-		provider, meter := testPair()
-		c := Must(meter).NewFloat64Counter("test.counter.float")
-		ctx := context.Background()
-		labels := []attribute.KeyValue{attribute.String("A", "B")}
-		c.Add(ctx, 1994.1, labels...)
-		meter.RecordBatch(ctx, labels, c.Measurement(42))
-		checkSyncBatches(ctx, t, labels, provider, number.Float64Kind, sdkapi.CounterInstrumentKind, c.SyncImpl(),
-			1994.1, 42,
-		)
-	})
-	t.Run("int64 counter", func(t *testing.T) {
-		provider, meter := testPair()
-		c := Must(meter).NewInt64Counter("test.counter.int")
-		ctx := context.Background()
-		labels := []attribute.KeyValue{attribute.String("A", "B"), attribute.String("C", "D")}
-		c.Add(ctx, 42, labels...)
-		meter.RecordBatch(ctx, labels, c.Measurement(420000))
-		checkSyncBatches(ctx, t, labels, provider, number.Int64Kind, sdkapi.CounterInstrumentKind, c.SyncImpl(),
-			42, 420000,
-		)
-
-	})
-	t.Run("int64 updowncounter", func(t *testing.T) {
-		provider, meter := testPair()
-		c := Must(meter).NewInt64UpDownCounter("test.updowncounter.int")
-		ctx := context.Background()
-		labels := []attribute.KeyValue{attribute.String("A", "B"), attribute.String("C", "D")}
-		c.Add(ctx, 100, labels...)
-		meter.RecordBatch(ctx, labels, c.Measurement(42))
-		checkSyncBatches(ctx, t, labels, provider, number.Int64Kind, sdkapi.UpDownCounterInstrumentKind, c.SyncImpl(),
-			100, 42,
-		)
-	})
-	t.Run("float64 updowncounter", func(t *testing.T) {
-		provider, meter := testPair()
-		c := Must(meter).NewFloat64UpDownCounter("test.updowncounter.float")
-		ctx := context.Background()
-		labels := []attribute.KeyValue{attribute.String("A", "B"), attribute.String("C", "D")}
-		c.Add(ctx, 100.1, labels...)
-		meter.RecordBatch(ctx, labels, c.Measurement(-100.1))
-		checkSyncBatches(ctx, t, labels, provider, number.Float64Kind, sdkapi.UpDownCounterInstrumentKind, c.SyncImpl(),
-			100.1, -100.1,
-		)
-	})
-}
-
-func TestHistogram(t *testing.T) {
-	t.Run("float64 histogram", func(t *testing.T) {
-		provider, meter := testPair()
-		m := Must(meter).NewFloat64Histogram("test.histogram.float")
-		ctx := context.Background()
-		labels := []attribute.KeyValue{}
-		m.Record(ctx, 42, labels...)
-		meter.RecordBatch(ctx, labels, m.Measurement(-100.5))
-		checkSyncBatches(ctx, t, labels, provider, number.Float64Kind, sdkapi.HistogramInstrumentKind, m.SyncImpl(),
-			42, -100.5,
-		)
-	})
-	t.Run("int64 histogram", func(t *testing.T) {
-		provider, meter := testPair()
-		m := Must(meter).NewInt64Histogram("test.histogram.int")
-		ctx := context.Background()
-		labels := []attribute.KeyValue{attribute.Int("I", 1)}
-		m.Record(ctx, 173, labels...)
-		meter.RecordBatch(ctx, labels, m.Measurement(0))
-		checkSyncBatches(ctx, t, labels, provider, number.Int64Kind, sdkapi.HistogramInstrumentKind, m.SyncImpl(),
-			173, 0,
-		)
-	})
-}
-
-func TestObserverInstruments(t *testing.T) {
-	t.Run("float gauge", func(t *testing.T) {
-		labels := []attribute.KeyValue{attribute.String("O", "P")}
-		provider, meter := testPair()
-		o := Must(meter).NewFloat64GaugeObserver("test.gauge.float", func(_ context.Context, result metric.Float64ObserverResult) {
-			result.Observe(42.1, labels...)
-		})
-		provider.RunAsyncInstruments()
-		checkObserverBatch(t, labels, provider, number.Float64Kind, sdkapi.GaugeObserverInstrumentKind, o.AsyncImpl(),
-			42.1,
-		)
-	})
-	t.Run("int gauge", func(t *testing.T) {
-		labels := []attribute.KeyValue{}
-		provider, meter := testPair()
-		o := Must(meter).NewInt64GaugeObserver("test.gauge.int", func(_ context.Context, result metric.Int64ObserverResult) {
-			result.Observe(-142, labels...)
-		})
-		provider.RunAsyncInstruments()
-		checkObserverBatch(t, labels, provider, number.Int64Kind, sdkapi.GaugeObserverInstrumentKind, o.AsyncImpl(),
-			-142,
-		)
-	})
-	t.Run("float counterobserver", func(t *testing.T) {
-		labels := []attribute.KeyValue{attribute.String("O", "P")}
-		provider, meter := testPair()
-		o := Must(meter).NewFloat64CounterObserver("test.counter.float", func(_ context.Context, result metric.Float64ObserverResult) {
-			result.Observe(42.1, labels...)
-		})
-		provider.RunAsyncInstruments()
-		checkObserverBatch(t, labels, provider, number.Float64Kind, sdkapi.CounterObserverInstrumentKind, o.AsyncImpl(),
-			42.1,
-		)
-	})
-	t.Run("int counterobserver", func(t *testing.T) {
-		labels := []attribute.KeyValue{}
-		provider, meter := testPair()
-		o := Must(meter).NewInt64CounterObserver("test.counter.int", func(_ context.Context, result metric.Int64ObserverResult) {
-			result.Observe(-142, labels...)
-		})
-		provider.RunAsyncInstruments()
-		checkObserverBatch(t, labels, provider, number.Int64Kind, sdkapi.CounterObserverInstrumentKind, o.AsyncImpl(),
-			-142,
-		)
-	})
-	t.Run("float updowncounterobserver", func(t *testing.T) {
-		labels := []attribute.KeyValue{attribute.String("O", "P")}
-		provider, meter := testPair()
-		o := Must(meter).NewFloat64UpDownCounterObserver("test.updowncounter.float", func(_ context.Context, result metric.Float64ObserverResult) {
-			result.Observe(42.1, labels...)
-		})
-		provider.RunAsyncInstruments()
-		checkObserverBatch(t, labels, provider, number.Float64Kind, sdkapi.UpDownCounterObserverInstrumentKind, o.AsyncImpl(),
-			42.1,
-		)
-	})
-	t.Run("int updowncounterobserver", func(t *testing.T) {
-		labels := []attribute.KeyValue{}
-		provider, meter := testPair()
-		o := Must(meter).NewInt64UpDownCounterObserver("test..int", func(_ context.Context, result metric.Int64ObserverResult) {
-			result.Observe(-142, labels...)
-		})
-		provider.RunAsyncInstruments()
-		checkObserverBatch(t, labels, provider, number.Int64Kind, sdkapi.UpDownCounterObserverInstrumentKind, o.AsyncImpl(),
-			-142,
-		)
-	})
-}
-
-func TestBatchObserverInstruments(t *testing.T) {
-	provider, meter := testPair()
-
-	var obs1 metric.Int64GaugeObserver
-	var obs2 metric.Float64GaugeObserver
-
-	labels := []attribute.KeyValue{
-		attribute.String("A", "B"),
-		attribute.String("C", "D"),
-	}
-
-	cb := Must(meter).NewBatchObserver(
-		func(_ context.Context, result metric.BatchObserverResult) {
-			result.Observe(labels,
-				obs1.Observation(42),
-				obs2.Observation(42.0),
-			)
-		},
-	)
-	obs1 = cb.NewInt64GaugeObserver("test.gauge.int")
-	obs2 = cb.NewFloat64GaugeObserver("test.gauge.float")
-
-	provider.RunAsyncInstruments()
-
-	require.Len(t, provider.MeasurementBatches, 1)
-
-	impl1 := obs1.AsyncImpl().Implementation().(*metrictest.Async)
-	impl2 := obs2.AsyncImpl().Implementation().(*metrictest.Async)
-
-	require.NotNil(t, impl1)
-	require.NotNil(t, impl2)
-
-	got := provider.MeasurementBatches[0]
-	require.Equal(t, labels, got.Labels)
-	require.Len(t, got.Measurements, 2)
-
-	m1 := got.Measurements[0]
-	require.Equal(t, impl1, m1.Instrument.Implementation().(*metrictest.Async))
-	require.Equal(t, 0, m1.Number.CompareNumber(number.Int64Kind, metrictest.ResolveNumberByKind(t, number.Int64Kind, 42)))
-
-	m2 := got.Measurements[1]
-	require.Equal(t, impl2, m2.Instrument.Implementation().(*metrictest.Async))
-	require.Equal(t, 0, m2.Number.CompareNumber(number.Float64Kind, metrictest.ResolveNumberByKind(t, number.Float64Kind, 42)))
-}
-
-func checkObserverBatch(t *testing.T, labels []attribute.KeyValue, provider *metrictest.MeterProvider, nkind number.Kind, mkind sdkapi.InstrumentKind, observer sdkapi.AsyncImpl, expected float64) {
-	t.Helper()
-	assert.Len(t, provider.MeasurementBatches, 1)
-	if len(provider.MeasurementBatches) < 1 {
-		return
-	}
-	o := observer.Implementation().(*metrictest.Async)
-	if !assert.NotNil(t, o) {
-		return
-	}
-	got := provider.MeasurementBatches[0]
-	assert.Equal(t, labels, got.Labels)
-	assert.Len(t, got.Measurements, 1)
-	if len(got.Measurements) < 1 {
-		return
-	}
-	measurement := got.Measurements[0]
-	require.Equal(t, mkind, measurement.Instrument.Descriptor().InstrumentKind())
-	assert.Equal(t, o, measurement.Instrument.Implementation().(*metrictest.Async))
-	ft := metrictest.ResolveNumberByKind(t, nkind, expected)
-	assert.Equal(t, 0, measurement.Number.CompareNumber(nkind, ft))
-}
-
-type testWrappedMeter struct {
-}
-
-var _ sdkapi.MeterImpl = testWrappedMeter{}
-
-func (testWrappedMeter) RecordBatch(context.Context, []attribute.KeyValue, ...sdkapi.Measurement) {
-}
-
-func (testWrappedMeter) NewSyncInstrument(_ sdkapi.Descriptor) (sdkapi.SyncImpl, error) {
-	return nil, nil
-}
-
-func (testWrappedMeter) NewAsyncInstrument(_ sdkapi.Descriptor, _ sdkapi.AsyncRunner) (sdkapi.AsyncImpl, error) {
-	return nil, errors.New("Test wrap error")
-}
-
-func TestWrappedInstrumentError(t *testing.T) {
-	impl := &testWrappedMeter{}
-	meter := metric.WrapMeterImpl(impl)
-
-	histogram, err := meter.NewInt64Histogram("test.histogram")
-
-	require.Equal(t, err, metric.ErrSDKReturnedNilImpl)
-	require.NotNil(t, histogram.SyncImpl())
-
-	observer, err := meter.NewInt64GaugeObserver("test.observer", func(_ context.Context, result metric.Int64ObserverResult) {})
-
-	require.NotNil(t, err)
-	require.NotNil(t, observer.AsyncImpl())
-}
-
-func TestNilCallbackObserverNoop(t *testing.T) {
-	// Tests that a nil callback yields a no-op observer without error.
-	_, meter := testPair()
-
-	observer := Must(meter).NewInt64GaugeObserver("test.observer", nil)
-
-	impl := observer.AsyncImpl().Implementation()
-	desc := observer.AsyncImpl().Descriptor()
-	require.Equal(t, nil, impl)
-	require.Equal(t, "", desc.Name())
-}
diff --git a/metric/metrictest/meter.go b/metric/metrictest/meter.go
deleted file mode 100644
index 759bb04ba..000000000
--- a/metric/metrictest/meter.go
+++ /dev/null
@@ -1,290 +0,0 @@
-// Copyright The OpenTelemetry Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package metrictest // import "go.opentelemetry.io/otel/metric/metrictest"
-
-import (
-	"context"
-	"sync"
-	"testing"
-
-	"go.opentelemetry.io/otel/attribute"
-	internalmetric "go.opentelemetry.io/otel/internal/metric"
-	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
-)
-
-type (
-	Handle struct {
-		Instrument *Sync
-		Labels     []attribute.KeyValue
-	}
-
-	// Library is the same as "sdk/instrumentation".Library but there is
-	// a package cycle to use it.
-	Library struct {
-		InstrumentationName    string
-		InstrumentationVersion string
-		SchemaURL              string
-	}
-
-	Batch struct {
-		// Measurement needs to be aligned for 64-bit atomic operations.
-		Measurements []Measurement
-		Ctx          context.Context
-		Labels       []attribute.KeyValue
-		Library      Library
-	}
-
-	// MeterImpl is an OpenTelemetry Meter implementation used for testing.
-	MeterImpl struct {
-		library          Library
-		provider         *MeterProvider
-		asyncInstruments *internalmetric.AsyncInstrumentState
-	}
-
-	// MeterProvider is a collection of named MeterImpls used for testing.
-	MeterProvider struct {
-		lock sync.Mutex
-
-		MeasurementBatches []Batch
-		impls              []*MeterImpl
-	}
-
-	Measurement struct {
-		// Number needs to be aligned for 64-bit atomic operations.
-		Number     number.Number
-		Instrument sdkapi.InstrumentImpl
-	}
-
-	Instrument struct {
-		meter      *MeterImpl
-		descriptor sdkapi.Descriptor
-	}
-
-	Async struct {
-		Instrument
-
-		runner sdkapi.AsyncRunner
-	}
-
-	Sync struct {
-		Instrument
-	}
-)
-
-var (
-	_ sdkapi.SyncImpl  = &Sync{}
-	_ sdkapi.MeterImpl = &MeterImpl{}
-	_ sdkapi.AsyncImpl = &Async{}
-)
-
-// NewDescriptor is a test helper for constructing test metric
-// descriptors using standard options.
-func NewDescriptor(name string, ikind sdkapi.InstrumentKind, nkind number.Kind, opts ...metric.InstrumentOption) sdkapi.Descriptor {
-	cfg := metric.NewInstrumentConfig(opts...)
-	return sdkapi.NewDescriptor(name, ikind, nkind, cfg.Description(), cfg.Unit())
-}
-
-func (i Instrument) Descriptor() sdkapi.Descriptor {
-	return i.descriptor
-}
-
-func (a *Async) Implementation() interface{} {
-	return a
-}
-
-func (s *Sync) Implementation() interface{} {
-	return s
-}
-
-func (s *Sync) RecordOne(ctx context.Context, number number.Number, labels []attribute.KeyValue) {
-	s.meter.doRecordSingle(ctx, labels, s, number)
-}
-
-func (h *Handle) RecordOne(ctx context.Context, number number.Number) {
-	h.Instrument.meter.doRecordSingle(ctx, h.Labels, h.Instrument, number)
-}
-
-func (h *Handle) Unbind() {
-}
-
-func (m *MeterImpl) doRecordSingle(ctx context.Context, labels []attribute.KeyValue, instrument sdkapi.InstrumentImpl, number number.Number) {
-	m.collect(ctx, labels, []Measurement{{
-		Instrument: instrument,
-		Number:     number,
-	}})
-}
-
-// NewMeterProvider returns a MeterProvider suitable for testing.
-// When the test is complete, consult MeterProvider.MeasurementBatches.
-func NewMeterProvider() *MeterProvider {
-	return &MeterProvider{}
-}
-
-// Meter implements metric.MeterProvider.
-func (p *MeterProvider) Meter(name string, opts ...metric.MeterOption) metric.Meter {
-	p.lock.Lock()
-	defer p.lock.Unlock()
-	cfg := metric.NewMeterConfig(opts...)
-	impl := &MeterImpl{
-		library: Library{
-			InstrumentationName:    name,
-			InstrumentationVersion: cfg.InstrumentationVersion(),
-			SchemaURL:              cfg.SchemaURL(),
-		},
-		provider:         p,
-		asyncInstruments: internalmetric.NewAsyncInstrumentState(),
-	}
-	p.impls = append(p.impls, impl)
-	return metric.WrapMeterImpl(impl)
-}
-
-// NewSyncInstrument implements sdkapi.MeterImpl.
-func (m *MeterImpl) NewSyncInstrument(descriptor sdkapi.Descriptor) (sdkapi.SyncImpl, error) {
-	return &Sync{
-		Instrument{
-			descriptor: descriptor,
-			meter:      m,
-		},
-	}, nil
-}
-
-// NewAsyncInstrument implements sdkapi.MeterImpl.
-func (m *MeterImpl) NewAsyncInstrument(descriptor sdkapi.Descriptor, runner sdkapi.AsyncRunner) (sdkapi.AsyncImpl, error) {
-	a := &Async{
-		Instrument: Instrument{
-			descriptor: descriptor,
-			meter:      m,
-		},
-		runner: runner,
-	}
-	m.provider.registerAsyncInstrument(a, m, runner)
-	return a, nil
-}
-
-// RecordBatch implements sdkapi.MeterImpl.
-func (m *MeterImpl) RecordBatch(ctx context.Context, labels []attribute.KeyValue, measurements ...sdkapi.Measurement) {
-	mm := make([]Measurement, len(measurements))
-	for i := 0; i < len(measurements); i++ {
-		m := measurements[i]
-		mm[i] = Measurement{
-			Instrument: m.SyncImpl().Implementation().(*Sync),
-			Number:     m.Number(),
-		}
-	}
-	m.collect(ctx, labels, mm)
-}
-
-// CollectAsync is called from asyncInstruments.Run() with the lock held.
-func (m *MeterImpl) CollectAsync(labels []attribute.KeyValue, obs ...sdkapi.Observation) {
-	mm := make([]Measurement, len(obs))
-	for i := 0; i < len(obs); i++ {
-		o := obs[i]
-		mm[i] = Measurement{
-			Instrument: o.AsyncImpl(),
-			Number:     o.Number(),
-		}
-	}
-	m.collect(context.Background(), labels, mm)
-}
-
-// collect is called from CollectAsync() or RecordBatch() with the lock held.
-func (m *MeterImpl) collect(ctx context.Context, labels []attribute.KeyValue, measurements []Measurement) {
-	m.provider.addMeasurement(Batch{
-		Ctx:          ctx,
-		Labels:       labels,
-		Measurements: measurements,
-		Library:      m.library,
-	})
-}
-
-// registerAsyncInstrument locks the provider and registers the new Async instrument.
-func (p *MeterProvider) registerAsyncInstrument(a *Async, m *MeterImpl, runner sdkapi.AsyncRunner) {
-	p.lock.Lock()
-	defer p.lock.Unlock()
-
-	m.asyncInstruments.Register(a, runner)
-}
-
-// addMeasurement locks the provider and adds the new measurement batch.
-func (p *MeterProvider) addMeasurement(b Batch) {
-	p.lock.Lock()
-	defer p.lock.Unlock()
-	p.MeasurementBatches = append(p.MeasurementBatches, b)
-}
-
-// copyImpls locks the provider and copies the current list of *MeterImpls.
-func (p *MeterProvider) copyImpls() []*MeterImpl {
-	p.lock.Lock()
-	defer p.lock.Unlock()
-	cpy := make([]*MeterImpl, len(p.impls))
-	copy(cpy, p.impls)
-	return cpy
-}
-
-// RunAsyncInstruments is used in tests to trigger collection from
-// asynchronous instruments.
-func (p *MeterProvider) RunAsyncInstruments() {
-	for _, impl := range p.copyImpls() {
-		impl.asyncInstruments.Run(context.Background(), impl)
-	}
-}
-
-// Measured is the helper struct which provides flat representation of recorded measurements
-// to simplify testing
-type Measured struct {
-	Name    string
-	Labels  map[attribute.Key]attribute.Value
-	Number  number.Number
-	Library Library
-}
-
-// LabelsToMap converts label set to keyValue map, to be easily used in tests
-func LabelsToMap(kvs ...attribute.KeyValue) map[attribute.Key]attribute.Value {
-	m := map[attribute.Key]attribute.Value{}
-	for _, label := range kvs {
-		m[label.Key] = label.Value
-	}
-	return m
-}
-
-// AsStructs converts recorded batches to array of flat, readable Measured helper structures
-func AsStructs(batches []Batch) []Measured {
-	var r []Measured
-	for _, batch := range batches {
-		for _, m := range batch.Measurements {
-			r = append(r, Measured{
-				Name:    m.Instrument.Descriptor().Name(),
-				Labels:  LabelsToMap(batch.Labels...),
-				Number:  m.Number,
-				Library: batch.Library,
-			})
-		}
-	}
-	return r
-}
-
-// ResolveNumberByKind takes defined metric descriptor creates a concrete typed metric number
-func ResolveNumberByKind(t *testing.T, kind number.Kind, value float64) number.Number {
-	t.Helper()
-	switch kind {
-	case number.Int64Kind:
-		return number.NewInt64Number(int64(value))
-	case number.Float64Kind:
-		return number.NewFloat64Number(value)
-	}
-	panic("invalid number kind")
-}
diff --git a/metric/nonrecording/instruments.go b/metric/nonrecording/instruments.go
new file mode 100644
index 000000000..84a6aa89c
--- /dev/null
+++ b/metric/nonrecording/instruments.go
@@ -0,0 +1,138 @@
+// Copyright The OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package nonrecording // import "go.opentelemetry.io/otel/metric/nonrecording"
+
+import (
+	"context"
+
+	"go.opentelemetry.io/otel/attribute"
+	"go.opentelemetry.io/otel/metric/instrument"
+	"go.opentelemetry.io/otel/metric/instrument/asyncfloat64"
+	"go.opentelemetry.io/otel/metric/instrument/asyncint64"
+	"go.opentelemetry.io/otel/metric/instrument/syncfloat64"
+	"go.opentelemetry.io/otel/metric/instrument/syncint64"
+)
+
+type nonrecordingAsyncFloat64Instrument struct {
+	instrument.Asynchronous
+}
+
+var (
+	_ asyncfloat64.InstrumentProvider = nonrecordingAsyncFloat64Instrument{}
+	_ asyncfloat64.Counter            = nonrecordingAsyncFloat64Instrument{}
+	_ asyncfloat64.UpDownCounter      = nonrecordingAsyncFloat64Instrument{}
+	_ asyncfloat64.Gauge              = nonrecordingAsyncFloat64Instrument{}
+)
+
+func (n nonrecordingAsyncFloat64Instrument) Counter(name string, opts ...instrument.Option) (asyncfloat64.Counter, error) {
+	return n, nil
+}
+
+func (n nonrecordingAsyncFloat64Instrument) UpDownCounter(name string, opts ...instrument.Option) (asyncfloat64.UpDownCounter, error) {
+	return n, nil
+}
+
+func (n nonrecordingAsyncFloat64Instrument) Gauge(name string, opts ...instrument.Option) (asyncfloat64.Gauge, error) {
+	return n, nil
+}
+
+func (nonrecordingAsyncFloat64Instrument) Observe(context.Context, float64, ...attribute.KeyValue) {
+
+}
+
+type nonrecordingAsyncInt64Instrument struct {
+	instrument.Asynchronous
+}
+
+var (
+	_ asyncint64.InstrumentProvider = nonrecordingAsyncInt64Instrument{}
+	_ asyncint64.Counter            = nonrecordingAsyncInt64Instrument{}
+	_ asyncint64.UpDownCounter      = nonrecordingAsyncInt64Instrument{}
+	_ asyncint64.Gauge              = nonrecordingAsyncInt64Instrument{}
+)
+
+func (n nonrecordingAsyncInt64Instrument) Counter(name string, opts ...instrument.Option) (asyncint64.Counter, error) {
+	return n, nil
+}
+
+func (n nonrecordingAsyncInt64Instrument) UpDownCounter(name string, opts ...instrument.Option) (asyncint64.UpDownCounter, error) {
+	return n, nil
+}
+
+func (n nonrecordingAsyncInt64Instrument) Gauge(name string, opts ...instrument.Option) (asyncint64.Gauge, error) {
+	return n, nil
+}
+
+func (nonrecordingAsyncInt64Instrument) Observe(context.Context, int64, ...attribute.KeyValue) {
+}
+
+type nonrecordingSyncFloat64Instrument struct {
+	instrument.Synchronous
+}
+
+var (
+	_ syncfloat64.InstrumentProvider = nonrecordingSyncFloat64Instrument{}
+	_ syncfloat64.Counter            = nonrecordingSyncFloat64Instrument{}
+	_ syncfloat64.UpDownCounter      = nonrecordingSyncFloat64Instrument{}
+	_ syncfloat64.Histogram          = nonrecordingSyncFloat64Instrument{}
+)
+
+func (n nonrecordingSyncFloat64Instrument) Counter(name string, opts ...instrument.Option) (syncfloat64.Counter, error) {
+	return n, nil
+}
+
+func (n nonrecordingSyncFloat64Instrument) UpDownCounter(name string, opts ...instrument.Option) (syncfloat64.UpDownCounter, error) {
+	return n, nil
+}
+
+func (n nonrecordingSyncFloat64Instrument) Histogram(name string, opts ...instrument.Option) (syncfloat64.Histogram, error) {
+	return n, nil
+}
+
+func (nonrecordingSyncFloat64Instrument) Add(context.Context, float64, ...attribute.KeyValue) {
+
+}
+
+func (nonrecordingSyncFloat64Instrument) Record(context.Context, float64, ...attribute.KeyValue) {
+
+}
+
+type nonrecordingSyncInt64Instrument struct {
+	instrument.Synchronous
+}
+
+var (
+	_ syncint64.InstrumentProvider = nonrecordingSyncInt64Instrument{}
+	_ syncint64.Counter            = nonrecordingSyncInt64Instrument{}
+	_ syncint64.UpDownCounter      = nonrecordingSyncInt64Instrument{}
+	_ syncint64.Histogram          = nonrecordingSyncInt64Instrument{}
+)
+
+func (n nonrecordingSyncInt64Instrument) Counter(name string, opts ...instrument.Option) (syncint64.Counter, error) {
+	return n, nil
+}
+
+func (n nonrecordingSyncInt64Instrument) UpDownCounter(name string, opts ...instrument.Option) (syncint64.UpDownCounter, error) {
+	return n, nil
+}
+
+func (n nonrecordingSyncInt64Instrument) Histogram(name string, opts ...instrument.Option) (syncint64.Histogram, error) {
+	return n, nil
+}
+
+func (nonrecordingSyncInt64Instrument) Add(context.Context, int64, ...attribute.KeyValue) {
+}
+func (nonrecordingSyncInt64Instrument) Record(context.Context, int64, ...attribute.KeyValue) {
+}
diff --git a/metric/nonrecording/meter.go b/metric/nonrecording/meter.go
new file mode 100644
index 000000000..2acea49d8
--- /dev/null
+++ b/metric/nonrecording/meter.go
@@ -0,0 +1,64 @@
+// Copyright The OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package nonrecording // import "go.opentelemetry.io/otel/metric/nonrecording"
+
+import (
+	"context"
+
+	"go.opentelemetry.io/otel/metric"
+	"go.opentelemetry.io/otel/metric/instrument"
+	"go.opentelemetry.io/otel/metric/instrument/asyncfloat64"
+	"go.opentelemetry.io/otel/metric/instrument/asyncint64"
+	"go.opentelemetry.io/otel/metric/instrument/syncfloat64"
+	"go.opentelemetry.io/otel/metric/instrument/syncint64"
+)
+
+// NewNoopMeterProvider creates a MeterProvider that does not record any metrics.
+func NewNoopMeterProvider() metric.MeterProvider {
+	return noopMeterProvider{}
+}
+
+type noopMeterProvider struct{}
+
+var _ metric.MeterProvider = noopMeterProvider{}
+
+func (noopMeterProvider) Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter {
+	return noopMeter{}
+}
+
+// NewNoopMeter creates a Meter that does not record any metrics.
+func NewNoopMeter() metric.Meter {
+	return noopMeter{}
+}
+
+type noopMeter struct{}
+
+var _ metric.Meter = noopMeter{}
+
+func (noopMeter) AsyncInt64() asyncint64.InstrumentProvider {
+	return nonrecordingAsyncInt64Instrument{}
+}
+func (noopMeter) AsyncFloat64() asyncfloat64.InstrumentProvider {
+	return nonrecordingAsyncFloat64Instrument{}
+}
+func (noopMeter) SyncInt64() syncint64.InstrumentProvider {
+	return nonrecordingSyncInt64Instrument{}
+}
+func (noopMeter) SyncFloat64() syncfloat64.InstrumentProvider {
+	return nonrecordingSyncFloat64Instrument{}
+}
+func (noopMeter) RegisterCallback([]instrument.Asynchronous, func(context.Context)) error {
+	return nil
+}
diff --git a/metric/noop.go b/metric/noop.go
deleted file mode 100644
index 37c653f51..000000000
--- a/metric/noop.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright The OpenTelemetry Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package metric // import "go.opentelemetry.io/otel/metric"
-
-type noopMeterProvider struct{}
-
-// NewNoopMeterProvider returns an implementation of MeterProvider that
-// performs no operations. The Meter and Instrument created from the returned
-// MeterProvider also perform no operations.
-func NewNoopMeterProvider() MeterProvider {
-	return noopMeterProvider{}
-}
-
-var _ MeterProvider = noopMeterProvider{}
-
-func (noopMeterProvider) Meter(instrumentationName string, opts ...MeterOption) Meter {
-	return Meter{}
-}
diff --git a/sdk/export/metric/aggregation/aggregation.go b/sdk/export/metric/aggregation/aggregation.go
index 09b203060..702c5b2bc 100644
--- a/sdk/export/metric/aggregation/aggregation.go
+++ b/sdk/export/metric/aggregation/aggregation.go
@@ -15,8 +15,8 @@
 package aggregation // import "go.opentelemetry.io/otel/sdk/export/metric/aggregation"
 
 import (
-	"go.opentelemetry.io/otel/metric/number"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/number"
 )
 
 // Deprecated: use module "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
diff --git a/sdk/export/metric/go.mod b/sdk/export/metric/go.mod
index 8f7284647..415e71a87 100644
--- a/sdk/export/metric/go.mod
+++ b/sdk/export/metric/go.mod
@@ -41,7 +41,6 @@ replace go.opentelemetry.io/otel/trace => ../../../trace
 
 require (
 	go.opentelemetry.io/otel v1.4.1
-	go.opentelemetry.io/otel/metric v0.27.0
 	go.opentelemetry.io/otel/sdk/metric v0.27.0
 )
 
diff --git a/sdk/export/metric/metric.go b/sdk/export/metric/metric.go
index 00d3f67b3..b3dccccd9 100644
--- a/sdk/export/metric/metric.go
+++ b/sdk/export/metric/metric.go
@@ -18,10 +18,10 @@ import (
 	"time"
 
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/export"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 // Deprecated: use module "go.opentelemetry.io/otel/sdk/metric/export"
diff --git a/sdk/metric/aggregator/aggregator.go b/sdk/metric/aggregator/aggregator.go
index 85d2b3fbd..59d42b1a8 100644
--- a/sdk/metric/aggregator/aggregator.go
+++ b/sdk/metric/aggregator/aggregator.go
@@ -19,9 +19,9 @@ import (
 	"fmt"
 	"math"
 
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 // Aggregator implements a specific aggregation behavior, e.g., a
diff --git a/sdk/metric/aggregator/aggregator_test.go b/sdk/metric/aggregator/aggregator_test.go
index bf405b5c3..aab8393c9 100644
--- a/sdk/metric/aggregator/aggregator_test.go
+++ b/sdk/metric/aggregator/aggregator_test.go
@@ -21,13 +21,13 @@ import (
 
 	"github.com/stretchr/testify/require"
 
-	"go.opentelemetry.io/otel/metric/metrictest"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/metrictest"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 func TestInconsistentAggregatorErr(t *testing.T) {
diff --git a/sdk/metric/aggregator/aggregatortest/test.go b/sdk/metric/aggregator/aggregatortest/test.go
index 2d2a197e6..b9ea62da9 100644
--- a/sdk/metric/aggregator/aggregatortest/test.go
+++ b/sdk/metric/aggregator/aggregatortest/test.go
@@ -26,11 +26,11 @@ import (
 	"github.com/stretchr/testify/require"
 
 	ottest "go.opentelemetry.io/otel/internal/internaltest"
-	"go.opentelemetry.io/otel/metric/metrictest"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/metrictest"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 const Magnitude = 1000
diff --git a/sdk/metric/aggregator/histogram/benchmark_test.go b/sdk/metric/aggregator/histogram/benchmark_test.go
index 4902c11b7..597af3eb7 100644
--- a/sdk/metric/aggregator/histogram/benchmark_test.go
+++ b/sdk/metric/aggregator/histogram/benchmark_test.go
@@ -19,10 +19,10 @@ import (
 	"math/rand"
 	"testing"
 
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 const inputRange = 1e6
diff --git a/sdk/metric/aggregator/histogram/histogram.go b/sdk/metric/aggregator/histogram/histogram.go
index 142ca24eb..f4d94b38c 100644
--- a/sdk/metric/aggregator/histogram/histogram.go
+++ b/sdk/metric/aggregator/histogram/histogram.go
@@ -19,10 +19,10 @@ import (
 	"sort"
 	"sync"
 
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 // Note: This code uses a Mutex to govern access to the exclusive
diff --git a/sdk/metric/aggregator/histogram/histogram_test.go b/sdk/metric/aggregator/histogram/histogram_test.go
index 00acf2e77..b22bd149e 100644
--- a/sdk/metric/aggregator/histogram/histogram_test.go
+++ b/sdk/metric/aggregator/histogram/histogram_test.go
@@ -23,11 +23,11 @@ import (
 
 	"github.com/stretchr/testify/require"
 
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 const count = 100
diff --git a/sdk/metric/aggregator/lastvalue/lastvalue.go b/sdk/metric/aggregator/lastvalue/lastvalue.go
index 59a7ac823..7e88f6b8d 100644
--- a/sdk/metric/aggregator/lastvalue/lastvalue.go
+++ b/sdk/metric/aggregator/lastvalue/lastvalue.go
@@ -20,10 +20,10 @@ import (
 	"time"
 	"unsafe"
 
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 type (
diff --git a/sdk/metric/aggregator/lastvalue/lastvalue_test.go b/sdk/metric/aggregator/lastvalue/lastvalue_test.go
index 3d105a75c..16f9614c2 100644
--- a/sdk/metric/aggregator/lastvalue/lastvalue_test.go
+++ b/sdk/metric/aggregator/lastvalue/lastvalue_test.go
@@ -25,11 +25,11 @@ import (
 	"github.com/stretchr/testify/require"
 
 	ottest "go.opentelemetry.io/otel/internal/internaltest"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 const count = 100
diff --git a/sdk/metric/aggregator/sum/sum.go b/sdk/metric/aggregator/sum/sum.go
index be3c6dfff..d5c70e59b 100644
--- a/sdk/metric/aggregator/sum/sum.go
+++ b/sdk/metric/aggregator/sum/sum.go
@@ -17,10 +17,10 @@ package sum // import "go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
 import (
 	"context"
 
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 // Aggregator aggregates counter events.
diff --git a/sdk/metric/aggregator/sum/sum_test.go b/sdk/metric/aggregator/sum/sum_test.go
index 59480e2c8..c92594a46 100644
--- a/sdk/metric/aggregator/sum/sum_test.go
+++ b/sdk/metric/aggregator/sum/sum_test.go
@@ -22,10 +22,10 @@ import (
 	"github.com/stretchr/testify/require"
 
 	ottest "go.opentelemetry.io/otel/internal/internaltest"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 const count = 100
diff --git a/sdk/metric/benchmark_test.go b/sdk/metric/benchmark_test.go
index c4ecf3062..fd5f49bd1 100644
--- a/sdk/metric/benchmark_test.go
+++ b/sdk/metric/benchmark_test.go
@@ -22,11 +22,13 @@ import (
 
 	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/global"
-	"go.opentelemetry.io/otel/metric/sdkapi"
+	"go.opentelemetry.io/otel/metric/instrument"
+	"go.opentelemetry.io/otel/metric/instrument/syncfloat64"
+	"go.opentelemetry.io/otel/metric/instrument/syncint64"
 	sdk "go.opentelemetry.io/otel/sdk/metric"
 	"go.opentelemetry.io/otel/sdk/metric/export"
 	"go.opentelemetry.io/otel/sdk/metric/processor/processortest"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 type benchFixture struct {
@@ -44,7 +46,7 @@ func newFixture(b *testing.B) *benchFixture {
 	}
 
 	bf.accumulator = sdk.NewAccumulator(bf)
-	bf.meter = metric.WrapMeterImpl(bf.accumulator)
+	bf.meter = sdkapi.WrapMeterImpl(bf.accumulator)
 	return bf
 }
 
@@ -56,8 +58,33 @@ func (f *benchFixture) Meter(_ string, _ ...metric.MeterOption) metric.Meter {
 	return f.meter
 }
 
-func (f *benchFixture) meterMust() metric.MeterMust {
-	return metric.Must(f.meter)
+func (f *benchFixture) iCounter(name string) syncint64.Counter {
+	ctr, err := f.meter.SyncInt64().Counter(name)
+	if err != nil {
+		f.B.Error(err)
+	}
+	return ctr
+}
+func (f *benchFixture) fCounter(name string) syncfloat64.Counter {
+	ctr, err := f.meter.SyncFloat64().Counter(name)
+	if err != nil {
+		f.B.Error(err)
+	}
+	return ctr
+}
+func (f *benchFixture) iHistogram(name string) syncint64.Histogram {
+	ctr, err := f.meter.SyncInt64().Histogram(name)
+	if err != nil {
+		f.B.Error(err)
+	}
+	return ctr
+}
+func (f *benchFixture) fHistogram(name string) syncfloat64.Histogram {
+	ctr, err := f.meter.SyncFloat64().Histogram(name)
+	if err != nil {
+		f.B.Error(err)
+	}
+	return ctr
 }
 
 func makeLabels(n int) []attribute.KeyValue {
@@ -81,7 +108,7 @@ func benchmarkLabels(b *testing.B, n int) {
 	ctx := context.Background()
 	fix := newFixture(b)
 	labs := makeLabels(n)
-	cnt := fix.meterMust().NewInt64Counter("int64.sum")
+	cnt := fix.iCounter("int64.sum")
 
 	b.ResetTimer()
 
@@ -154,31 +181,33 @@ func BenchmarkIterator_16(b *testing.B) {
 
 // Counters
 
-func BenchmarkGlobalInt64CounterAddWithSDK(b *testing.B) {
-	// Compare with BenchmarkInt64CounterAdd() to see overhead of global
-	// package. This is in the SDK to avoid the API from depending on the
-	// SDK.
-	ctx := context.Background()
-	fix := newFixture(b)
+// TODO readd global
 
-	sdk := global.Meter("test")
-	global.SetMeterProvider(fix)
+// func BenchmarkGlobalInt64CounterAddWithSDK(b *testing.B) {
+// 	// Compare with BenchmarkInt64CounterAdd() to see overhead of global
+// 	// package. This is in the SDK to avoid the API from depending on the
+// 	// SDK.
+// 	ctx := context.Background()
+// 	fix := newFixture(b)
 
-	labs := []attribute.KeyValue{attribute.String("A", "B")}
-	cnt := Must(sdk).NewInt64Counter("int64.sum")
+// 	sdk := global.Meter("test")
+// 	global.SetMeterProvider(fix)
 
-	b.ResetTimer()
+// 	labs := []attribute.KeyValue{attribute.String("A", "B")}
+// 	cnt := Must(sdk).NewInt64Counter("int64.sum")
 
-	for i := 0; i < b.N; i++ {
-		cnt.Add(ctx, 1, labs...)
-	}
-}
+// 	b.ResetTimer()
+
+// 	for i := 0; i < b.N; i++ {
+// 		cnt.Add(ctx, 1, labs...)
+// 	}
+// }
 
 func BenchmarkInt64CounterAdd(b *testing.B) {
 	ctx := context.Background()
 	fix := newFixture(b)
 	labs := makeLabels(1)
-	cnt := fix.meterMust().NewInt64Counter("int64.sum")
+	cnt := fix.iCounter("int64.sum")
 
 	b.ResetTimer()
 
@@ -191,7 +220,7 @@ func BenchmarkFloat64CounterAdd(b *testing.B) {
 	ctx := context.Background()
 	fix := newFixture(b)
 	labs := makeLabels(1)
-	cnt := fix.meterMust().NewFloat64Counter("float64.sum")
+	cnt := fix.fCounter("float64.sum")
 
 	b.ResetTimer()
 
@@ -206,7 +235,7 @@ func BenchmarkInt64LastValueAdd(b *testing.B) {
 	ctx := context.Background()
 	fix := newFixture(b)
 	labs := makeLabels(1)
-	mea := fix.meterMust().NewInt64Histogram("int64.lastvalue")
+	mea := fix.iHistogram("int64.lastvalue")
 
 	b.ResetTimer()
 
@@ -219,7 +248,7 @@ func BenchmarkFloat64LastValueAdd(b *testing.B) {
 	ctx := context.Background()
 	fix := newFixture(b)
 	labs := makeLabels(1)
-	mea := fix.meterMust().NewFloat64Histogram("float64.lastvalue")
+	mea := fix.fHistogram("float64.lastvalue")
 
 	b.ResetTimer()
 
@@ -234,7 +263,7 @@ func BenchmarkInt64HistogramAdd(b *testing.B) {
 	ctx := context.Background()
 	fix := newFixture(b)
 	labs := makeLabels(1)
-	mea := fix.meterMust().NewInt64Histogram("int64.histogram")
+	mea := fix.iHistogram("int64.histogram")
 
 	b.ResetTimer()
 
@@ -247,7 +276,7 @@ func BenchmarkFloat64HistogramAdd(b *testing.B) {
 	ctx := context.Background()
 	fix := newFixture(b)
 	labs := makeLabels(1)
-	mea := fix.meterMust().NewFloat64Histogram("float64.histogram")
+	mea := fix.fHistogram("float64.histogram")
 
 	b.ResetTimer()
 
@@ -264,12 +293,12 @@ func BenchmarkObserverRegistration(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		names = append(names, fmt.Sprintf("test.%d.lastvalue", i))
 	}
-	cb := func(_ context.Context, result metric.Int64ObserverResult) {}
 
 	b.ResetTimer()
 
 	for i := 0; i < b.N; i++ {
-		fix.meterMust().NewInt64GaugeObserver(names[i], cb)
+		ctr, _ := fix.meter.AsyncInt64().Counter(names[i])
+		_ = fix.meter.RegisterCallback([]instrument.Asynchronous{ctr}, func(context.Context) {})
 	}
 }
 
@@ -277,11 +306,16 @@ func BenchmarkGaugeObserverObservationInt64(b *testing.B) {
 	ctx := context.Background()
 	fix := newFixture(b)
 	labs := makeLabels(1)
-	_ = fix.meterMust().NewInt64GaugeObserver("test.lastvalue", func(_ context.Context, result metric.Int64ObserverResult) {
+	ctr, _ := fix.meter.AsyncInt64().Counter("test.lastvalue")
+	err := fix.meter.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
 		for i := 0; i < b.N; i++ {
-			result.Observe((int64)(i), labs...)
+			ctr.Observe(ctx, (int64)(i), labs...)
 		}
 	})
+	if err != nil {
+		b.Errorf("could not register callback: %v", err)
+		b.FailNow()
+	}
 
 	b.ResetTimer()
 
@@ -292,11 +326,16 @@ func BenchmarkGaugeObserverObservationFloat64(b *testing.B) {
 	ctx := context.Background()
 	fix := newFixture(b)
 	labs := makeLabels(1)
-	_ = fix.meterMust().NewFloat64GaugeObserver("test.lastvalue", func(_ context.Context, result metric.Float64ObserverResult) {
+	ctr, _ := fix.meter.AsyncFloat64().Counter("test.lastvalue")
+	err := fix.meter.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
 		for i := 0; i < b.N; i++ {
-			result.Observe((float64)(i), labs...)
+			ctr.Observe(ctx, (float64)(i), labs...)
 		}
 	})
+	if err != nil {
+		b.Errorf("could not register callback: %v", err)
+		b.FailNow()
+	}
 
 	b.ResetTimer()
 
@@ -310,17 +349,18 @@ func benchmarkBatchRecord8Labels(b *testing.B, numInst int) {
 	ctx := context.Background()
 	fix := newFixture(b)
 	labs := makeLabels(numLabels)
-	var meas []sdkapi.Measurement
+	var meas []syncint64.Counter
 
 	for i := 0; i < numInst; i++ {
-		inst := fix.meterMust().NewInt64Counter(fmt.Sprintf("int64.%d.sum", i))
-		meas = append(meas, inst.Measurement(1))
+		meas = append(meas, fix.iCounter(fmt.Sprintf("int64.%d.sum", i)))
 	}
 
 	b.ResetTimer()
 
 	for i := 0; i < b.N; i++ {
-		fix.accumulator.RecordBatch(ctx, labs, meas...)
+		for _, ctr := range meas {
+			ctr.Add(ctx, 1, labs...)
+		}
 	}
 }
 
@@ -346,7 +386,7 @@ func BenchmarkRepeatedDirectCalls(b *testing.B) {
 	ctx := context.Background()
 	fix := newFixture(b)
 
-	c := fix.meterMust().NewInt64Counter("int64.sum")
+	c := fix.iCounter("int64.sum")
 	k := attribute.String("bench", "true")
 
 	b.ResetTimer()
diff --git a/sdk/metric/controller/basic/controller.go b/sdk/metric/controller/basic/controller.go
index ee6940562..de0da5484 100644
--- a/sdk/metric/controller/basic/controller.go
+++ b/sdk/metric/controller/basic/controller.go
@@ -21,12 +21,13 @@ import (
 	"time"
 
 	"go.opentelemetry.io/otel"
-	"go.opentelemetry.io/otel/internal/metric/registry"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/sdk/instrumentation"
 	sdk "go.opentelemetry.io/otel/sdk/metric"
 	controllerTime "go.opentelemetry.io/otel/sdk/metric/controller/time"
 	"go.opentelemetry.io/otel/sdk/metric/export"
+	"go.opentelemetry.io/otel/sdk/metric/registry"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/resource"
 )
 
@@ -99,7 +100,7 @@ func (c *Controller) Meter(instrumentationName string, opts ...metric.MeterOptio
 				library:      library,
 			}))
 	}
-	return metric.WrapMeterImpl(m.(*registry.UniqueInstrumentMeterImpl))
+	return sdkapi.WrapMeterImpl(m.(*registry.UniqueInstrumentMeterImpl))
 }
 
 type accumulatorCheckpointer struct {
@@ -108,6 +109,8 @@ type accumulatorCheckpointer struct {
 	library      instrumentation.Library
 }
 
+var _ sdkapi.MeterImpl = &accumulatorCheckpointer{}
+
 // New constructs a Controller using the provided checkpointer factory
 // and options (including optional exporter) to configure a metric
 // export pipeline.
diff --git a/sdk/metric/controller/basic/controller_test.go b/sdk/metric/controller/basic/controller_test.go
index 503a27814..26dcf4bb2 100644
--- a/sdk/metric/controller/basic/controller_test.go
+++ b/sdk/metric/controller/basic/controller_test.go
@@ -25,8 +25,7 @@ import (
 
 	"go.opentelemetry.io/otel/attribute"
 	ottest "go.opentelemetry.io/otel/internal/internaltest"
-	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/sdkapi"
+	"go.opentelemetry.io/otel/metric/instrument"
 	"go.opentelemetry.io/otel/sdk/instrumentation"
 	controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
 	"go.opentelemetry.io/otel/sdk/metric/controller/controllertest"
@@ -34,6 +33,7 @@ import (
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
 	processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
 	"go.opentelemetry.io/otel/sdk/metric/processor/processortest"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/resource"
 )
 
@@ -127,7 +127,7 @@ func TestControllerUsesResource(t *testing.T) {
 			ctx := context.Background()
 			require.NoError(t, cont.Start(ctx))
 
-			ctr := metric.Must(cont.Meter("named")).NewFloat64Counter("calls.sum")
+			ctr, _ := cont.Meter("named").SyncFloat64().Counter("calls.sum")
 			ctr.Add(context.Background(), 1.)
 
 			// Collect once
@@ -152,16 +152,19 @@ func TestStartNoExporter(t *testing.T) {
 	)
 	mock := controllertest.NewMockClock()
 	cont.SetClock(mock)
+	meter := cont.Meter("go.opentelemetry.io/otel/sdk/metric/controller/basic_test#StartNoExporter")
 
 	calls := int64(0)
 
-	_ = metric.Must(cont.Meter("named")).NewInt64CounterObserver("calls.lastvalue",
-		func(ctx context.Context, result metric.Int64ObserverResult) {
-			calls++
-			checkTestContext(t, ctx)
-			result.Observe(calls, attribute.String("A", "B"))
-		},
-	)
+	counterObserver, err := meter.AsyncInt64().Counter("calls.lastvalue")
+	require.NoError(t, err)
+
+	err = meter.RegisterCallback([]instrument.Asynchronous{counterObserver}, func(ctx context.Context) {
+		calls++
+		checkTestContext(t, ctx)
+		counterObserver.Observe(ctx, calls, attribute.String("A", "B"))
+	})
+	require.NoError(t, err)
 
 	// Collect() has not been called.  The controller is unstarted.
 	expect := map[string]float64{}
@@ -220,18 +223,22 @@ func TestObserverCanceled(t *testing.T) {
 		controller.WithCollectTimeout(time.Millisecond),
 		controller.WithResource(resource.Empty()),
 	)
+	meter := cont.Meter("go.opentelemetry.io/otel/sdk/metric/controller/basic_test#ObserverCanceled")
 
 	calls := int64(0)
 
-	_ = metric.Must(cont.Meter("named")).NewInt64CounterObserver("done.lastvalue",
-		func(ctx context.Context, result metric.Int64ObserverResult) {
-			<-ctx.Done()
-			calls++
-			result.Observe(calls)
-		},
-	)
+	counterObserver, err := meter.AsyncInt64().Counter("done.lastvalue")
+	require.NoError(t, err)
+
+	err = meter.RegisterCallback([]instrument.Asynchronous{counterObserver}, func(ctx context.Context) {
+		<-ctx.Done()
+		calls++
+		counterObserver.Observe(ctx, calls)
+	})
+	require.NoError(t, err)
+
 	// This relies on the context timing out
-	err := cont.Collect(context.Background())
+	err = cont.Collect(context.Background())
 	require.Error(t, err)
 	require.True(t, errors.Is(err, context.DeadlineExceeded))
 
@@ -251,14 +258,18 @@ func TestObserverContext(t *testing.T) {
 		controller.WithCollectTimeout(0),
 		controller.WithResource(resource.Empty()),
 	)
+	meter := cont.Meter("go.opentelemetry.io/otel/sdk/metric/controller/basic_test#ObserverContext")
+
+	counterObserver, err := meter.AsyncInt64().Counter("done.lastvalue")
+	require.NoError(t, err)
+
+	err = meter.RegisterCallback([]instrument.Asynchronous{counterObserver}, func(ctx context.Context) {
+		time.Sleep(10 * time.Millisecond)
+		checkTestContext(t, ctx)
+		counterObserver.Observe(ctx, 1)
+	})
+	require.NoError(t, err)
 
-	_ = metric.Must(cont.Meter("named")).NewInt64CounterObserver("done.lastvalue",
-		func(ctx context.Context, result metric.Int64ObserverResult) {
-			time.Sleep(10 * time.Millisecond)
-			checkTestContext(t, ctx)
-			result.Observe(1)
-		},
-	)
 	ctx := testContext()
 
 	require.NoError(t, cont.Collect(ctx))
@@ -314,14 +325,17 @@ func TestExportTimeout(t *testing.T) {
 	)
 	mock := controllertest.NewMockClock()
 	cont.SetClock(mock)
+	meter := cont.Meter("go.opentelemetry.io/otel/sdk/metric/controller/basic_test#ExportTimeout")
 
 	calls := int64(0)
-	_ = metric.Must(cont.Meter("named")).NewInt64CounterObserver("one.lastvalue",
-		func(ctx context.Context, result metric.Int64ObserverResult) {
-			calls++
-			result.Observe(calls)
-		},
-	)
+	counterObserver, err := meter.AsyncInt64().Counter("one.lastvalue")
+	require.NoError(t, err)
+
+	err = meter.RegisterCallback([]instrument.Asynchronous{counterObserver}, func(ctx context.Context) {
+		calls++
+		counterObserver.Observe(ctx, calls)
+	})
+	require.NoError(t, err)
 
 	require.NoError(t, cont.Start(context.Background()))
 
@@ -332,7 +346,7 @@ func TestExportTimeout(t *testing.T) {
 	// Collect after 1s, timeout
 	mock.Add(time.Second)
 
-	err := testHandler.Flush()
+	err = testHandler.Flush()
 	require.Error(t, err)
 	require.True(t, errors.Is(err, context.DeadlineExceeded))
 
@@ -369,13 +383,17 @@ func TestCollectAfterStopThenStartAgain(t *testing.T) {
 	mock := controllertest.NewMockClock()
 	cont.SetClock(mock)
 
+	meter := cont.Meter("go.opentelemetry.io/otel/sdk/metric/controller/basic_test#CollectAfterStopThenStartAgain")
+
 	calls := 0
-	_ = metric.Must(cont.Meter("named")).NewInt64CounterObserver("one.lastvalue",
-		func(ctx context.Context, result metric.Int64ObserverResult) {
-			calls++
-			result.Observe(int64(calls))
-		},
-	)
+	counterObserver, err := meter.AsyncInt64().Counter("one.lastvalue")
+	require.NoError(t, err)
+
+	err = meter.RegisterCallback([]instrument.Asynchronous{counterObserver}, func(ctx context.Context) {
+		calls++
+		counterObserver.Observe(ctx, int64(calls))
+	})
+	require.NoError(t, err)
 
 	// No collections happen (because mock clock does not advance):
 	require.NoError(t, cont.Start(context.Background()))
@@ -403,7 +421,7 @@ func TestCollectAfterStopThenStartAgain(t *testing.T) {
 	// explicit collection should still fail.
 	require.NoError(t, cont.Start(context.Background()))
 	require.True(t, cont.IsRunning())
-	err := cont.Collect(context.Background())
+	err = cont.Collect(context.Background())
 	require.Error(t, err)
 	require.Equal(t, controller.ErrControllerStarted, err)
 
@@ -452,10 +470,10 @@ func TestRegistryFunction(t *testing.T) {
 	require.NotNil(t, m1)
 	require.Equal(t, m1, m2)
 
-	c1, err := m1.NewInt64Counter("counter.sum")
+	c1, err := m1.SyncInt64().Counter("counter.sum")
 	require.NoError(t, err)
 
-	c2, err := m1.NewInt64Counter("counter.sum")
+	c2, err := m1.SyncInt64().Counter("counter.sum")
 	require.NoError(t, err)
 
 	require.Equal(t, c1, c2)
diff --git a/sdk/metric/controller/basic/pull_test.go b/sdk/metric/controller/basic/pull_test.go
index 96d1e1ada..2284e68a7 100644
--- a/sdk/metric/controller/basic/pull_test.go
+++ b/sdk/metric/controller/basic/pull_test.go
@@ -23,7 +23,6 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric"
 	controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
 	"go.opentelemetry.io/otel/sdk/metric/controller/controllertest"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
@@ -45,7 +44,8 @@ func TestPullNoCollect(t *testing.T) {
 
 	ctx := context.Background()
 	meter := puller.Meter("nocache")
-	counter := metric.Must(meter).NewInt64Counter("counter.sum")
+	counter, err := meter.SyncInt64().Counter("counter.sum")
+	require.NoError(t, err)
 
 	counter.Add(ctx, 10, attribute.String("A", "B"))
 
@@ -83,7 +83,8 @@ func TestPullWithCollect(t *testing.T) {
 
 	ctx := context.Background()
 	meter := puller.Meter("nocache")
-	counter := metric.Must(meter).NewInt64Counter("counter.sum")
+	counter, err := meter.SyncInt64().Counter("counter.sum")
+	require.NoError(t, err)
 
 	counter.Add(ctx, 10, attribute.String("A", "B"))
 
diff --git a/sdk/metric/controller/basic/push_test.go b/sdk/metric/controller/basic/push_test.go
index 0a6679b29..67bbddfdc 100644
--- a/sdk/metric/controller/basic/push_test.go
+++ b/sdk/metric/controller/basic/push_test.go
@@ -27,7 +27,6 @@ import (
 
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric"
 	controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
 	"go.opentelemetry.io/otel/sdk/metric/controller/controllertest"
 	"go.opentelemetry.io/otel/sdk/metric/export"
@@ -117,7 +116,8 @@ func TestPushTicker(t *testing.T) {
 
 	ctx := context.Background()
 
-	counter := metric.Must(meter).NewInt64Counter("counter.sum")
+	counter, err := meter.SyncInt64().Counter("counter.sum")
+	require.NoError(t, err)
 
 	require.NoError(t, p.Start(ctx))
 
@@ -197,8 +197,10 @@ func TestPushExportError(t *testing.T) {
 			ctx := context.Background()
 
 			meter := p.Meter("name")
-			counter1 := metric.Must(meter).NewInt64Counter("counter1.sum")
-			counter2 := metric.Must(meter).NewInt64Counter("counter2.sum")
+			counter1, err := meter.SyncInt64().Counter("counter1.sum")
+			require.NoError(t, err)
+			counter2, err := meter.SyncInt64().Counter("counter2.sum")
+			require.NoError(t, err)
 
 			require.NoError(t, p.Start(ctx))
 			runtime.Gosched()
diff --git a/sdk/metric/correct_test.go b/sdk/metric/correct_test.go
index 25e7fc608..1b24a209c 100644
--- a/sdk/metric/correct_test.go
+++ b/sdk/metric/correct_test.go
@@ -26,16 +26,17 @@ import (
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/sdkapi"
+	"go.opentelemetry.io/otel/metric/instrument"
+	"go.opentelemetry.io/otel/metric/instrument/asyncint64"
+	"go.opentelemetry.io/otel/metric/nonrecording"
 	metricsdk "go.opentelemetry.io/otel/sdk/metric"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/export"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
 	"go.opentelemetry.io/otel/sdk/metric/processor/processortest"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
-var Must = metric.Must
-
 type handler struct {
 	sync.Mutex
 	err error
@@ -88,7 +89,7 @@ func newSDK(t *testing.T) (metric.Meter, *metricsdk.Accumulator, *testSelector,
 	accum := metricsdk.NewAccumulator(
 		processor,
 	)
-	meter := metric.WrapMeterImpl(accum)
+	meter := sdkapi.WrapMeterImpl(accum)
 	return meter, accum, testSelector, processor
 }
 
@@ -96,7 +97,8 @@ func TestInputRangeCounter(t *testing.T) {
 	ctx := context.Background()
 	meter, sdk, _, processor := newSDK(t)
 
-	counter := Must(meter).NewInt64Counter("name.sum")
+	counter, err := meter.SyncInt64().Counter("name.sum")
+	require.NoError(t, err)
 
 	counter.Add(ctx, -1)
 	require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush())
@@ -118,7 +120,8 @@ func TestInputRangeUpDownCounter(t *testing.T) {
 	ctx := context.Background()
 	meter, sdk, _, processor := newSDK(t)
 
-	counter := Must(meter).NewInt64UpDownCounter("name.sum")
+	counter, err := meter.SyncInt64().UpDownCounter("name.sum")
+	require.NoError(t, err)
 
 	counter.Add(ctx, -1)
 	counter.Add(ctx, -1)
@@ -137,7 +140,8 @@ func TestInputRangeHistogram(t *testing.T) {
 	ctx := context.Background()
 	meter, sdk, _, processor := newSDK(t)
 
-	histogram := Must(meter).NewFloat64Histogram("name.histogram")
+	histogram, err := meter.SyncFloat64().Histogram("name.histogram")
+	require.NoError(t, err)
 
 	histogram.Record(ctx, math.NaN())
 	require.Equal(t, aggregation.ErrNaNInput, testHandler.Flush())
@@ -162,7 +166,8 @@ func TestDisabledInstrument(t *testing.T) {
 	ctx := context.Background()
 	meter, sdk, _, processor := newSDK(t)
 
-	histogram := Must(meter).NewFloat64Histogram("name.disabled")
+	histogram, err := meter.SyncFloat64().Histogram("name.disabled")
+	require.NoError(t, err)
 
 	histogram.Record(ctx, -1)
 	checkpointed := sdk.Collect(ctx)
@@ -175,7 +180,8 @@ func TestRecordNaN(t *testing.T) {
 	ctx := context.Background()
 	meter, _, _, _ := newSDK(t)
 
-	c := Must(meter).NewFloat64Counter("name.sum")
+	c, err := meter.SyncFloat64().Counter("name.sum")
+	require.NoError(t, err)
 
 	require.Nil(t, testHandler.Flush())
 	c.Add(ctx, math.NaN())
@@ -186,7 +192,8 @@ func TestSDKLabelsDeduplication(t *testing.T) {
 	ctx := context.Background()
 	meter, sdk, _, processor := newSDK(t)
 
-	counter := Must(meter).NewInt64Counter("name.sum")
+	counter, err := meter.SyncInt64().Counter("name.sum")
+	require.NoError(t, err)
 
 	const (
 		maxKeys = 21
@@ -277,48 +284,86 @@ func TestObserverCollection(t *testing.T) {
 	meter, sdk, _, processor := newSDK(t)
 	mult := 1
 
-	_ = Must(meter).NewFloat64GaugeObserver("float.gauge.lastvalue", func(_ context.Context, result metric.Float64ObserverResult) {
-		result.Observe(float64(mult), attribute.String("A", "B"))
+	gaugeF, err := meter.AsyncFloat64().Gauge("float.gauge.lastvalue")
+	require.NoError(t, err)
+	err = meter.RegisterCallback([]instrument.Asynchronous{
+		gaugeF,
+	}, func(ctx context.Context) {
+		gaugeF.Observe(ctx, float64(mult), attribute.String("A", "B"))
 		// last value wins
-		result.Observe(float64(-mult), attribute.String("A", "B"))
-		result.Observe(float64(-mult), attribute.String("C", "D"))
-	})
-	_ = Must(meter).NewInt64GaugeObserver("int.gauge.lastvalue", func(_ context.Context, result metric.Int64ObserverResult) {
-		result.Observe(int64(-mult), attribute.String("A", "B"))
-		result.Observe(int64(mult))
-		// last value wins
-		result.Observe(int64(mult), attribute.String("A", "B"))
-		result.Observe(int64(mult))
+		gaugeF.Observe(ctx, float64(-mult), attribute.String("A", "B"))
+		gaugeF.Observe(ctx, float64(-mult), attribute.String("C", "D"))
 	})
+	require.NoError(t, err)
 
-	_ = Must(meter).NewFloat64CounterObserver("float.counterobserver.sum", func(_ context.Context, result metric.Float64ObserverResult) {
-		result.Observe(float64(mult), attribute.String("A", "B"))
-		result.Observe(float64(2*mult), attribute.String("A", "B"))
-		result.Observe(float64(mult), attribute.String("C", "D"))
-	})
-	_ = Must(meter).NewInt64CounterObserver("int.counterobserver.sum", func(_ context.Context, result metric.Int64ObserverResult) {
-		result.Observe(int64(2*mult), attribute.String("A", "B"))
-		result.Observe(int64(mult))
+	gaugeI, err := meter.AsyncInt64().Gauge("int.gauge.lastvalue")
+	require.NoError(t, err)
+	err = meter.RegisterCallback([]instrument.Asynchronous{
+		gaugeI,
+	}, func(ctx context.Context) {
+		gaugeI.Observe(ctx, int64(-mult), attribute.String("A", "B"))
+		gaugeI.Observe(ctx, int64(mult))
 		// last value wins
-		result.Observe(int64(mult), attribute.String("A", "B"))
-		result.Observe(int64(mult))
+		gaugeI.Observe(ctx, int64(mult), attribute.String("A", "B"))
+		gaugeI.Observe(ctx, int64(mult))
 	})
+	require.NoError(t, err)
 
-	_ = Must(meter).NewFloat64UpDownCounterObserver("float.updowncounterobserver.sum", func(_ context.Context, result metric.Float64ObserverResult) {
-		result.Observe(float64(mult), attribute.String("A", "B"))
-		result.Observe(float64(-2*mult), attribute.String("A", "B"))
-		result.Observe(float64(mult), attribute.String("C", "D"))
+	counterF, err := meter.AsyncFloat64().Counter("float.counterobserver.sum")
+	require.NoError(t, err)
+	err = meter.RegisterCallback([]instrument.Asynchronous{
+		counterF,
+	}, func(ctx context.Context) {
+		counterF.Observe(ctx, float64(mult), attribute.String("A", "B"))
+		counterF.Observe(ctx, float64(2*mult), attribute.String("A", "B"))
+		counterF.Observe(ctx, float64(mult), attribute.String("C", "D"))
 	})
-	_ = Must(meter).NewInt64UpDownCounterObserver("int.updowncounterobserver.sum", func(_ context.Context, result metric.Int64ObserverResult) {
-		result.Observe(int64(2*mult), attribute.String("A", "B"))
-		result.Observe(int64(mult))
+	require.NoError(t, err)
+
+	counterI, err := meter.AsyncInt64().Counter("int.counterobserver.sum")
+	require.NoError(t, err)
+	err = meter.RegisterCallback([]instrument.Asynchronous{
+		counterI,
+	}, func(ctx context.Context) {
+		counterI.Observe(ctx, int64(2*mult), attribute.String("A", "B"))
+		counterI.Observe(ctx, int64(mult))
 		// last value wins
-		result.Observe(int64(mult), attribute.String("A", "B"))
-		result.Observe(int64(-mult))
+		counterI.Observe(ctx, int64(mult), attribute.String("A", "B"))
+		counterI.Observe(ctx, int64(mult))
 	})
+	require.NoError(t, err)
 
-	_ = Must(meter).NewInt64GaugeObserver("empty.gauge.sum", func(_ context.Context, result metric.Int64ObserverResult) {
+	updowncounterF, err := meter.AsyncFloat64().UpDownCounter("float.updowncounterobserver.sum")
+	require.NoError(t, err)
+	err = meter.RegisterCallback([]instrument.Asynchronous{
+		updowncounterF,
+	}, func(ctx context.Context) {
+		updowncounterF.Observe(ctx, float64(mult), attribute.String("A", "B"))
+		updowncounterF.Observe(ctx, float64(-2*mult), attribute.String("A", "B"))
+		updowncounterF.Observe(ctx, float64(mult), attribute.String("C", "D"))
 	})
+	require.NoError(t, err)
+
+	updowncounterI, err := meter.AsyncInt64().UpDownCounter("int.updowncounterobserver.sum")
+	require.NoError(t, err)
+	err = meter.RegisterCallback([]instrument.Asynchronous{
+		updowncounterI,
+	}, func(ctx context.Context) {
+		updowncounterI.Observe(ctx, int64(2*mult), attribute.String("A", "B"))
+		updowncounterI.Observe(ctx, int64(mult))
+		// last value wins
+		updowncounterI.Observe(ctx, int64(mult), attribute.String("A", "B"))
+		updowncounterI.Observe(ctx, int64(-mult))
+	})
+	require.NoError(t, err)
+
+	unused, err := meter.AsyncInt64().Gauge("empty.gauge.sum")
+	require.NoError(t, err)
+	err = meter.RegisterCallback([]instrument.Asynchronous{
+		unused,
+	}, func(ctx context.Context) {
+	})
+	require.NoError(t, err)
 
 	for mult = 0; mult < 3; mult++ {
 		processor.Reset()
@@ -333,15 +378,15 @@ func TestObserverCollection(t *testing.T) {
 			"int.gauge.lastvalue//":      mult,
 			"int.gauge.lastvalue/A=B/":   mult,
 
-			"float.counterobserver.sum/A=B/": 2 * mult,
+			"float.counterobserver.sum/A=B/": 3 * mult,
 			"float.counterobserver.sum/C=D/": mult,
-			"int.counterobserver.sum//":      mult,
-			"int.counterobserver.sum/A=B/":   mult,
+			"int.counterobserver.sum//":      2 * mult,
+			"int.counterobserver.sum/A=B/":   3 * mult,
 
-			"float.updowncounterobserver.sum/A=B/": -2 * mult,
+			"float.updowncounterobserver.sum/A=B/": -mult,
 			"float.updowncounterobserver.sum/C=D/": mult,
-			"int.updowncounterobserver.sum//":      -mult,
-			"int.updowncounterobserver.sum/A=B/":   mult,
+			"int.updowncounterobserver.sum//":      0,
+			"int.updowncounterobserver.sum/A=B/":   3 * mult,
 		}, processor.Values())
 	}
 }
@@ -351,18 +396,26 @@ func TestCounterObserverInputRange(t *testing.T) {
 	meter, sdk, _, processor := newSDK(t)
 
 	// TODO: these tests are testing for negative values, not for _descending values_. Fix.
-	_ = Must(meter).NewFloat64CounterObserver("float.counterobserver.sum", func(_ context.Context, result metric.Float64ObserverResult) {
-		result.Observe(-2, attribute.String("A", "B"))
+	counterF, _ := meter.AsyncFloat64().Counter("float.counterobserver.sum")
+	err := meter.RegisterCallback([]instrument.Asynchronous{
+		counterF,
+	}, func(ctx context.Context) {
+		counterF.Observe(ctx, -2, attribute.String("A", "B"))
 		require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush())
-		result.Observe(-1, attribute.String("C", "D"))
+		counterF.Observe(ctx, -1, attribute.String("C", "D"))
 		require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush())
 	})
-	_ = Must(meter).NewInt64CounterObserver("int.counterobserver.sum", func(_ context.Context, result metric.Int64ObserverResult) {
-		result.Observe(-1, attribute.String("A", "B"))
+	require.NoError(t, err)
+	counterI, _ := meter.AsyncInt64().Counter("int.counterobserver.sum")
+	err = meter.RegisterCallback([]instrument.Asynchronous{
+		counterI,
+	}, func(ctx context.Context) {
+		counterI.Observe(ctx, -1, attribute.String("A", "B"))
 		require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush())
-		result.Observe(-1)
+		counterI.Observe(ctx, -1)
 		require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush())
 	})
+	require.NoError(t, err)
 
 	collected := sdk.Collect(ctx)
 
@@ -377,51 +430,43 @@ func TestObserverBatch(t *testing.T) {
 	ctx := context.Background()
 	meter, sdk, _, processor := newSDK(t)
 
-	var floatGaugeObs metric.Float64GaugeObserver
-	var intGaugeObs metric.Int64GaugeObserver
-	var floatCounterObs metric.Float64CounterObserver
-	var intCounterObs metric.Int64CounterObserver
-	var floatUpDownCounterObs metric.Float64UpDownCounterObserver
-	var intUpDownCounterObs metric.Int64UpDownCounterObserver
+	floatGaugeObs, _ := meter.AsyncFloat64().Gauge("float.gauge.lastvalue")
+	intGaugeObs, _ := meter.AsyncInt64().Gauge("int.gauge.lastvalue")
+	floatCounterObs, _ := meter.AsyncFloat64().Counter("float.counterobserver.sum")
+	intCounterObs, _ := meter.AsyncInt64().Counter("int.counterobserver.sum")
+	floatUpDownCounterObs, _ := meter.AsyncFloat64().UpDownCounter("float.updowncounterobserver.sum")
+	intUpDownCounterObs, _ := meter.AsyncInt64().UpDownCounter("int.updowncounterobserver.sum")
 
-	var batch = Must(meter).NewBatchObserver(
-		func(_ context.Context, result metric.BatchObserverResult) {
-			result.Observe(
-				[]attribute.KeyValue{
-					attribute.String("A", "B"),
-				},
-				floatGaugeObs.Observation(1),
-				floatGaugeObs.Observation(-1),
-				intGaugeObs.Observation(-1),
-				intGaugeObs.Observation(1),
-				floatCounterObs.Observation(1000),
-				intCounterObs.Observation(100),
-				floatUpDownCounterObs.Observation(-1000),
-				intUpDownCounterObs.Observation(-100),
-			)
-			result.Observe(
-				[]attribute.KeyValue{
-					attribute.String("C", "D"),
-				},
-				floatGaugeObs.Observation(-1),
-				floatCounterObs.Observation(-1),
-				floatUpDownCounterObs.Observation(-1),
-			)
-			result.Observe(
-				nil,
-				intGaugeObs.Observation(1),
-				intGaugeObs.Observation(1),
-				intCounterObs.Observation(10),
-				floatCounterObs.Observation(1.1),
-				intUpDownCounterObs.Observation(10),
-			)
-		})
-	floatGaugeObs = batch.NewFloat64GaugeObserver("float.gauge.lastvalue")
-	intGaugeObs = batch.NewInt64GaugeObserver("int.gauge.lastvalue")
-	floatCounterObs = batch.NewFloat64CounterObserver("float.counterobserver.sum")
-	intCounterObs = batch.NewInt64CounterObserver("int.counterobserver.sum")
-	floatUpDownCounterObs = batch.NewFloat64UpDownCounterObserver("float.updowncounterobserver.sum")
-	intUpDownCounterObs = batch.NewInt64UpDownCounterObserver("int.updowncounterobserver.sum")
+	err := meter.RegisterCallback([]instrument.Asynchronous{
+		floatGaugeObs,
+		intGaugeObs,
+		floatCounterObs,
+		intCounterObs,
+		floatUpDownCounterObs,
+		intUpDownCounterObs,
+	}, func(ctx context.Context) {
+		ab := attribute.String("A", "B")
+		floatGaugeObs.Observe(ctx, 1, ab)
+		floatGaugeObs.Observe(ctx, -1, ab)
+		intGaugeObs.Observe(ctx, -1, ab)
+		intGaugeObs.Observe(ctx, 1, ab)
+		floatCounterObs.Observe(ctx, 1000, ab)
+		intCounterObs.Observe(ctx, 100, ab)
+		floatUpDownCounterObs.Observe(ctx, -1000, ab)
+		intUpDownCounterObs.Observe(ctx, -100, ab)
+
+		cd := attribute.String("C", "D")
+		floatGaugeObs.Observe(ctx, -1, cd)
+		floatCounterObs.Observe(ctx, -1, cd)
+		floatUpDownCounterObs.Observe(ctx, -1, cd)
+
+		intGaugeObs.Observe(ctx, 1)
+		intGaugeObs.Observe(ctx, 1)
+		intCounterObs.Observe(ctx, 10)
+		floatCounterObs.Observe(ctx, 1.1)
+		intUpDownCounterObs.Observe(ctx, 10)
+	})
+	require.NoError(t, err)
 
 	collected := sdk.Collect(ctx)
 
@@ -445,37 +490,6 @@ func TestObserverBatch(t *testing.T) {
 	}, processor.Values())
 }
 
-func TestRecordBatch(t *testing.T) {
-	ctx := context.Background()
-	meter, sdk, _, processor := newSDK(t)
-
-	counter1 := Must(meter).NewInt64Counter("int64.sum")
-	counter2 := Must(meter).NewFloat64Counter("float64.sum")
-	histogram1 := Must(meter).NewInt64Histogram("int64.histogram")
-	histogram2 := Must(meter).NewFloat64Histogram("float64.histogram")
-
-	sdk.RecordBatch(
-		ctx,
-		[]attribute.KeyValue{
-			attribute.String("A", "B"),
-			attribute.String("C", "D"),
-		},
-		counter1.Measurement(1),
-		counter2.Measurement(2),
-		histogram1.Measurement(3),
-		histogram2.Measurement(4),
-	)
-
-	sdk.Collect(ctx)
-
-	require.EqualValues(t, map[string]float64{
-		"int64.sum/A=B,C=D/":         1,
-		"float64.sum/A=B,C=D/":       2,
-		"int64.histogram/A=B,C=D/":   3,
-		"float64.histogram/A=B,C=D/": 4,
-	}, processor.Values())
-}
-
 // TestRecordPersistence ensures that a direct-called instrument that
 // is repeatedly used each interval results in a persistent record, so
 // that its encoded labels will be cached across collection intervals.
@@ -483,7 +497,9 @@ func TestRecordPersistence(t *testing.T) {
 	ctx := context.Background()
 	meter, sdk, selector, _ := newSDK(t)
 
-	c := Must(meter).NewFloat64Counter("name.sum")
+	c, err := meter.SyncFloat64().Counter("name.sum")
+	require.NoError(t, err)
+
 	uk := attribute.String("bound", "false")
 
 	for i := 0; i < 100; i++ {
@@ -497,51 +513,58 @@ func TestRecordPersistence(t *testing.T) {
 func TestIncorrectInstruments(t *testing.T) {
 	// The Batch observe/record APIs are susceptible to
 	// uninitialized instruments.
-	var counter metric.Int64Counter
-	var observer metric.Int64GaugeObserver
+	var observer asyncint64.Gauge
 
 	ctx := context.Background()
 	meter, sdk, _, processor := newSDK(t)
 
 	// Now try with uninitialized instruments.
-	meter.RecordBatch(ctx, nil, counter.Measurement(1))
-	meter.NewBatchObserver(func(_ context.Context, result metric.BatchObserverResult) {
-		result.Observe(nil, observer.Observation(1))
+	err := meter.RegisterCallback([]instrument.Asynchronous{
+		observer,
+	}, func(ctx context.Context) {
+		observer.Observe(ctx, 1)
 	})
+	require.ErrorIs(t, err, metricsdk.ErrBadInstrument)
 
 	collected := sdk.Collect(ctx)
-	require.Equal(t, metricsdk.ErrUninitializedInstrument, testHandler.Flush())
+	err = testHandler.Flush()
+	require.NoError(t, err)
 	require.Equal(t, 0, collected)
 
 	// Now try with instruments from another SDK.
-	var noopMeter metric.Meter
-	counter = metric.Must(noopMeter).NewInt64Counter("name.sum")
-	observer = metric.Must(noopMeter).NewBatchObserver(
-		func(context.Context, metric.BatchObserverResult) {},
-	).NewInt64GaugeObserver("observer")
+	noopMeter := nonrecording.NewNoopMeter()
+	observer, _ = noopMeter.AsyncInt64().Gauge("observer")
 
-	meter.RecordBatch(ctx, nil, counter.Measurement(1))
-	meter.NewBatchObserver(func(_ context.Context, result metric.BatchObserverResult) {
-		result.Observe(nil, observer.Observation(1))
-	})
+	err = meter.RegisterCallback(
+		[]instrument.Asynchronous{observer},
+		func(ctx context.Context) {
+			observer.Observe(ctx, 1)
+		},
+	)
+	require.ErrorIs(t, err, metricsdk.ErrBadInstrument)
 
 	collected = sdk.Collect(ctx)
 	require.Equal(t, 0, collected)
 	require.EqualValues(t, map[string]float64{}, processor.Values())
-	require.Equal(t, metricsdk.ErrUninitializedInstrument, testHandler.Flush())
+
+	err = testHandler.Flush()
+	require.NoError(t, err)
 }
 
 func TestSyncInAsync(t *testing.T) {
 	ctx := context.Background()
 	meter, sdk, _, processor := newSDK(t)
 
-	counter := Must(meter).NewFloat64Counter("counter.sum")
-	_ = Must(meter).NewInt64GaugeObserver("observer.lastvalue",
-		func(ctx context.Context, result metric.Int64ObserverResult) {
-			result.Observe(10)
-			counter.Add(ctx, 100)
-		},
-	)
+	counter, _ := meter.SyncFloat64().Counter("counter.sum")
+	gauge, _ := meter.AsyncInt64().Gauge("observer.lastvalue")
+
+	err := meter.RegisterCallback([]instrument.Asynchronous{
+		gauge,
+	}, func(ctx context.Context) {
+		gauge.Observe(ctx, 10)
+		counter.Add(ctx, 100)
+	})
+	require.NoError(t, err)
 
 	sdk.Collect(ctx)
 
diff --git a/sdk/metric/export/aggregation/aggregation.go b/sdk/metric/export/aggregation/aggregation.go
index 44c8c3320..ea09fa683 100644
--- a/sdk/metric/export/aggregation/aggregation.go
+++ b/sdk/metric/export/aggregation/aggregation.go
@@ -18,7 +18,7 @@ import (
 	"fmt"
 	"time"
 
-	"go.opentelemetry.io/otel/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/number"
 )
 
 // These interfaces describe the various ways to access state from an
diff --git a/sdk/metric/export/aggregation/temporality.go b/sdk/metric/export/aggregation/temporality.go
index ca71b79d0..0612fe06a 100644
--- a/sdk/metric/export/aggregation/temporality.go
+++ b/sdk/metric/export/aggregation/temporality.go
@@ -17,7 +17,7 @@
 package aggregation // import "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
 
 import (
-	"go.opentelemetry.io/otel/metric/sdkapi"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 // Temporality indicates the temporal aggregation exported by an exporter.
diff --git a/sdk/metric/export/aggregation/temporality_test.go b/sdk/metric/export/aggregation/temporality_test.go
index d5d73e9d0..69b976da7 100644
--- a/sdk/metric/export/aggregation/temporality_test.go
+++ b/sdk/metric/export/aggregation/temporality_test.go
@@ -19,9 +19,9 @@ import (
 
 	"github.com/stretchr/testify/require"
 
-	"go.opentelemetry.io/otel/metric/metrictest"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
+	"go.opentelemetry.io/otel/sdk/metric/metrictest"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 func TestTemporalityIncludes(t *testing.T) {
diff --git a/sdk/metric/export/metric.go b/sdk/metric/export/metric.go
index 5251a059e..7937995e5 100644
--- a/sdk/metric/export/metric.go
+++ b/sdk/metric/export/metric.go
@@ -20,10 +20,10 @@ import (
 	"time"
 
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/instrumentation"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/resource"
 )
 
diff --git a/sdk/metric/go.mod b/sdk/metric/go.mod
index fc70fcf87..33fda2547 100644
--- a/sdk/metric/go.mod
+++ b/sdk/metric/go.mod
@@ -42,7 +42,6 @@ require (
 	github.com/benbjohnson/clock v1.3.0
 	github.com/stretchr/testify v1.7.0
 	go.opentelemetry.io/otel v1.4.1
-	go.opentelemetry.io/otel/internal/metric v0.27.0
 	go.opentelemetry.io/otel/metric v0.27.0
 	go.opentelemetry.io/otel/sdk v1.4.1
 )
@@ -55,8 +54,6 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../..
 
 replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp
 
-replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric
-
 replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric
 
 replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc
diff --git a/sdk/metric/histogram_stress_test.go b/sdk/metric/histogram_stress_test.go
index caca662aa..abc8b967c 100644
--- a/sdk/metric/histogram_stress_test.go
+++ b/sdk/metric/histogram_stress_test.go
@@ -22,10 +22,10 @@ import (
 
 	"github.com/stretchr/testify/require"
 
-	"go.opentelemetry.io/otel/metric/metrictest"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
+	"go.opentelemetry.io/otel/sdk/metric/metrictest"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 func TestStressInt64Histogram(t *testing.T) {
diff --git a/metric/metrictest/alignment_test.go b/sdk/metric/metrictest/alignment_test.go
similarity index 100%
rename from metric/metrictest/alignment_test.go
rename to sdk/metric/metrictest/alignment_test.go
diff --git a/sdk/metric/metrictest/meter.go b/sdk/metric/metrictest/meter.go
new file mode 100644
index 000000000..8222441e4
--- /dev/null
+++ b/sdk/metric/metrictest/meter.go
@@ -0,0 +1,56 @@
+// Copyright The OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package metrictest // import "go.opentelemetry.io/otel/sdk/metric/metrictest"
+
+import (
+	"context"
+
+	"go.opentelemetry.io/otel/attribute"
+	"go.opentelemetry.io/otel/metric/instrument"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
+)
+
+type (
+
+	// Library is the same as "sdk/instrumentation".Library but there is
+	// a package cycle to use it.
+	Library struct {
+		InstrumentationName    string
+		InstrumentationVersion string
+		SchemaURL              string
+	}
+
+	Batch struct {
+		// Measurement needs to be aligned for 64-bit atomic operations.
+		Measurements []Measurement
+		Ctx          context.Context
+		Labels       []attribute.KeyValue
+		Library      Library
+	}
+
+	Measurement struct {
+		// Number needs to be aligned for 64-bit atomic operations.
+		Number     number.Number
+		Instrument sdkapi.InstrumentImpl
+	}
+)
+
+// NewDescriptor is a test helper for constructing test metric
+// descriptors using standard options.
+func NewDescriptor(name string, ikind sdkapi.InstrumentKind, nkind number.Kind, opts ...instrument.Option) sdkapi.Descriptor {
+	cfg := instrument.NewConfig(opts...)
+	return sdkapi.NewDescriptor(name, ikind, nkind, cfg.Description(), cfg.Unit())
+}
diff --git a/metric/number/doc.go b/sdk/metric/number/doc.go
similarity index 92%
rename from metric/number/doc.go
rename to sdk/metric/number/doc.go
index 0649ff875..6f947400a 100644
--- a/metric/number/doc.go
+++ b/sdk/metric/number/doc.go
@@ -20,4 +20,4 @@ This package is currently in a pre-GA phase. Backwards incompatible changes
 may be introduced in subsequent minor version releases as we work to track the
 evolving OpenTelemetry specification and user feedback.
 */
-package number // import "go.opentelemetry.io/otel/metric/number"
+package number // import "go.opentelemetry.io/otel/sdk/metric/number"
diff --git a/metric/number/kind_string.go b/sdk/metric/number/kind_string.go
similarity index 100%
rename from metric/number/kind_string.go
rename to sdk/metric/number/kind_string.go
diff --git a/metric/number/number.go b/sdk/metric/number/number.go
similarity index 99%
rename from metric/number/number.go
rename to sdk/metric/number/number.go
index 3ec95e201..5fd2f38f6 100644
--- a/metric/number/number.go
+++ b/sdk/metric/number/number.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package number // import "go.opentelemetry.io/otel/metric/number"
+package number // import "go.opentelemetry.io/otel/sdk/metric/number"
 
 //go:generate stringer -type=Kind
 
diff --git a/metric/number/number_test.go b/sdk/metric/number/number_test.go
similarity index 100%
rename from metric/number/number_test.go
rename to sdk/metric/number/number_test.go
diff --git a/sdk/metric/processor/basic/basic.go b/sdk/metric/processor/basic/basic.go
index 71101bc4b..096044c04 100644
--- a/sdk/metric/processor/basic/basic.go
+++ b/sdk/metric/processor/basic/basic.go
@@ -21,10 +21,10 @@ import (
 	"time"
 
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/export"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 type (
diff --git a/sdk/metric/processor/basic/basic_test.go b/sdk/metric/processor/basic/basic_test.go
index 1378bf60d..80d0e2a20 100644
--- a/sdk/metric/processor/basic/basic_test.go
+++ b/sdk/metric/processor/basic/basic_test.go
@@ -25,19 +25,19 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/metrictest"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
+	"go.opentelemetry.io/otel/metric/instrument"
 	"go.opentelemetry.io/otel/sdk/instrumentation"
 	sdk "go.opentelemetry.io/otel/sdk/metric"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest"
 	"go.opentelemetry.io/otel/sdk/metric/export"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/metrictest"
+	"go.opentelemetry.io/otel/sdk/metric/number"
 	"go.opentelemetry.io/otel/sdk/metric/processor/basic"
 	"go.opentelemetry.io/otel/sdk/metric/processor/processortest"
 	processorTest "go.opentelemetry.io/otel/sdk/metric/processor/processortest"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/resource"
 )
 
@@ -450,15 +450,16 @@ func TestCounterObserverEndToEnd(t *testing.T) {
 		eselector,
 	)
 	accum := sdk.NewAccumulator(proc)
-	meter := metric.WrapMeterImpl(accum)
+	meter := sdkapi.WrapMeterImpl(accum)
 
 	var calls int64
-	metric.Must(meter).NewInt64CounterObserver("observer.sum",
-		func(_ context.Context, result metric.Int64ObserverResult) {
-			calls++
-			result.Observe(calls)
-		},
-	)
+	ctr, err := meter.AsyncInt64().Counter("observer.sum")
+	require.NoError(t, err)
+	err = meter.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
+		calls++
+		ctr.Observe(ctx, calls)
+	})
+	require.NoError(t, err)
 	reader := proc.Reader()
 
 	var startTime [3]time.Time
diff --git a/sdk/metric/processor/processortest/test.go b/sdk/metric/processor/processortest/test.go
index 6dbad2624..8931fc833 100644
--- a/sdk/metric/processor/processortest/test.go
+++ b/sdk/metric/processor/processortest/test.go
@@ -22,7 +22,6 @@ import (
 	"time"
 
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/instrumentation"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
@@ -30,6 +29,7 @@ import (
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
 	"go.opentelemetry.io/otel/sdk/metric/export"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/resource"
 )
 
diff --git a/sdk/metric/processor/processortest/test_test.go b/sdk/metric/processor/processortest/test_test.go
index 4cc990c34..c157ad47e 100644
--- a/sdk/metric/processor/processortest/test_test.go
+++ b/sdk/metric/processor/processortest/test_test.go
@@ -21,33 +21,37 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric"
+	"go.opentelemetry.io/otel/metric/instrument"
 	"go.opentelemetry.io/otel/sdk/instrumentation"
 	metricsdk "go.opentelemetry.io/otel/sdk/metric"
 	"go.opentelemetry.io/otel/sdk/metric/export"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
 	"go.opentelemetry.io/otel/sdk/metric/processor/processortest"
 	processorTest "go.opentelemetry.io/otel/sdk/metric/processor/processortest"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/resource"
 )
 
-func generateTestData(proc export.Processor) {
+func generateTestData(t *testing.T, proc export.Processor) {
 	ctx := context.Background()
 	accum := metricsdk.NewAccumulator(proc)
-	meter := metric.WrapMeterImpl(accum)
+	meter := sdkapi.WrapMeterImpl(accum)
 
-	counter := metric.Must(meter).NewFloat64Counter("counter.sum")
-
-	_ = metric.Must(meter).NewInt64CounterObserver("observer.sum",
-		func(_ context.Context, result metric.Int64ObserverResult) {
-			result.Observe(10, attribute.String("K1", "V1"))
-			result.Observe(11, attribute.String("K1", "V2"))
-		},
-	)
+	counter, err := meter.SyncFloat64().Counter("counter.sum")
+	require.NoError(t, err)
 
 	counter.Add(ctx, 100, attribute.String("K1", "V1"))
 	counter.Add(ctx, 101, attribute.String("K1", "V2"))
 
+	counterObserver, err := meter.AsyncInt64().Counter("observer.sum")
+	require.NoError(t, err)
+
+	err = meter.RegisterCallback([]instrument.Asynchronous{counterObserver}, func(ctx context.Context) {
+		counterObserver.Observe(ctx, 10, attribute.String("K1", "V1"))
+		counterObserver.Observe(ctx, 11, attribute.String("K1", "V2"))
+	})
+	require.NoError(t, err)
+
 	accum.Collect(ctx)
 }
 
@@ -60,7 +64,7 @@ func TestProcessorTesting(t *testing.T) {
 			attribute.DefaultEncoder(),
 		),
 	)
-	generateTestData(checkpointer)
+	generateTestData(t, checkpointer)
 
 	res := resource.NewSchemaless(attribute.String("R", "V"))
 	expect := map[string]float64{
diff --git a/sdk/metric/processor/reducer/reducer.go b/sdk/metric/processor/reducer/reducer.go
index b7fb48940..d26fd5534 100644
--- a/sdk/metric/processor/reducer/reducer.go
+++ b/sdk/metric/processor/reducer/reducer.go
@@ -16,8 +16,8 @@ package reducer // import "go.opentelemetry.io/otel/sdk/metric/processor/reducer
 
 import (
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/export"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 type (
diff --git a/sdk/metric/processor/reducer/reducer_test.go b/sdk/metric/processor/reducer/reducer_test.go
index 3da65d313..22fc774f1 100644
--- a/sdk/metric/processor/reducer/reducer_test.go
+++ b/sdk/metric/processor/reducer/reducer_test.go
@@ -21,8 +21,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/sdkapi"
+	"go.opentelemetry.io/otel/metric/instrument"
 	"go.opentelemetry.io/otel/sdk/instrumentation"
 	metricsdk "go.opentelemetry.io/otel/sdk/metric"
 	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
@@ -30,6 +29,7 @@ import (
 	"go.opentelemetry.io/otel/sdk/metric/processor/processortest"
 	processorTest "go.opentelemetry.io/otel/sdk/metric/processor/processortest"
 	"go.opentelemetry.io/otel/sdk/metric/processor/reducer"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/resource"
 )
 
@@ -54,21 +54,22 @@ func (testFilter) LabelFilterFor(_ *sdkapi.Descriptor) attribute.Filter {
 	}
 }
 
-func generateData(impl sdkapi.MeterImpl) {
+func generateData(t *testing.T, impl sdkapi.MeterImpl) {
 	ctx := context.Background()
-	meter := metric.WrapMeterImpl(impl)
-
-	counter := metric.Must(meter).NewFloat64Counter("counter.sum")
-
-	_ = metric.Must(meter).NewInt64CounterObserver("observer.sum",
-		func(_ context.Context, result metric.Int64ObserverResult) {
-			result.Observe(10, kvs1...)
-			result.Observe(10, kvs2...)
-		},
-	)
+	meter := sdkapi.WrapMeterImpl(impl)
 
+	counter, err := meter.SyncFloat64().Counter("counter.sum")
+	require.NoError(t, err)
 	counter.Add(ctx, 100, kvs1...)
 	counter.Add(ctx, 100, kvs2...)
+
+	counterObserver, err := meter.AsyncInt64().Counter("observer.sum")
+	require.NoError(t, err)
+	err = meter.RegisterCallback([]instrument.Asynchronous{counterObserver}, func(ctx context.Context) {
+		counterObserver.Observe(ctx, 10, kvs1...)
+		counterObserver.Observe(ctx, 10, kvs2...)
+	})
+	require.NoError(t, err)
 }
 
 func TestFilterProcessor(t *testing.T) {
@@ -79,7 +80,7 @@ func TestFilterProcessor(t *testing.T) {
 	accum := metricsdk.NewAccumulator(
 		reducer.New(testFilter{}, processorTest.NewCheckpointer(testProc)),
 	)
-	generateData(accum)
+	generateData(t, accum)
 
 	accum.Collect(context.Background())
 
@@ -97,7 +98,7 @@ func TestFilterBasicProcessor(t *testing.T) {
 	)
 	exporter := processorTest.New(basicProc, attribute.DefaultEncoder())
 
-	generateData(accum)
+	generateData(t, accum)
 
 	basicProc.StartCollection()
 	accum.Collect(context.Background())
diff --git a/internal/metric/registry/doc.go b/sdk/metric/registry/doc.go
similarity index 92%
rename from internal/metric/registry/doc.go
rename to sdk/metric/registry/doc.go
index 2f17562f0..b401408be 100644
--- a/internal/metric/registry/doc.go
+++ b/sdk/metric/registry/doc.go
@@ -21,4 +21,4 @@ This package is currently in a pre-GA phase. Backwards incompatible changes
 may be introduced in subsequent minor version releases as we work to track the
 evolving OpenTelemetry specification and user feedback.
 */
-package registry // import "go.opentelemetry.io/otel/internal/metric/registry"
+package registry // import "go.opentelemetry.io/otel/sdk/metric/registry"
diff --git a/internal/metric/registry/registry.go b/sdk/metric/registry/registry.go
similarity index 86%
rename from internal/metric/registry/registry.go
rename to sdk/metric/registry/registry.go
index c929bf45c..c2870e483 100644
--- a/internal/metric/registry/registry.go
+++ b/sdk/metric/registry/registry.go
@@ -12,15 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package registry // import "go.opentelemetry.io/otel/internal/metric/registry"
+package registry // import "go.opentelemetry.io/otel/sdk/metric/registry"
 
 import (
 	"context"
 	"fmt"
 	"sync"
 
-	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric/sdkapi"
+	"go.opentelemetry.io/otel/metric/instrument"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 // UniqueInstrumentMeterImpl implements the metric.MeterImpl interface, adding
@@ -53,11 +53,6 @@ func (u *UniqueInstrumentMeterImpl) MeterImpl() sdkapi.MeterImpl {
 	return u.impl
 }
 
-// RecordBatch implements sdkapi.MeterImpl.
-func (u *UniqueInstrumentMeterImpl) RecordBatch(ctx context.Context, labels []attribute.KeyValue, ms ...sdkapi.Measurement) {
-	u.impl.RecordBatch(ctx, labels, ms...)
-}
-
 // NewMetricKindMismatchError formats an error that describes a
 // mismatched metric instrument definition.
 func NewMetricKindMismatchError(desc sdkapi.Descriptor) error {
@@ -115,10 +110,7 @@ func (u *UniqueInstrumentMeterImpl) NewSyncInstrument(descriptor sdkapi.Descript
 }
 
 // NewAsyncInstrument implements sdkapi.MeterImpl.
-func (u *UniqueInstrumentMeterImpl) NewAsyncInstrument(
-	descriptor sdkapi.Descriptor,
-	runner sdkapi.AsyncRunner,
-) (sdkapi.AsyncImpl, error) {
+func (u *UniqueInstrumentMeterImpl) NewAsyncInstrument(descriptor sdkapi.Descriptor) (sdkapi.AsyncImpl, error) {
 	u.lock.Lock()
 	defer u.lock.Unlock()
 
@@ -130,10 +122,17 @@ func (u *UniqueInstrumentMeterImpl) NewAsyncInstrument(
 		return impl.(sdkapi.AsyncImpl), nil
 	}
 
-	asyncInst, err := u.impl.NewAsyncInstrument(descriptor, runner)
+	asyncInst, err := u.impl.NewAsyncInstrument(descriptor)
 	if err != nil {
 		return nil, err
 	}
 	u.state[descriptor.Name()] = asyncInst
 	return asyncInst, nil
 }
+
+func (u *UniqueInstrumentMeterImpl) RegisterCallback(insts []instrument.Asynchronous, callback func(context.Context)) error {
+	u.lock.Lock()
+	defer u.lock.Unlock()
+
+	return u.impl.RegisterCallback(insts, callback)
+}
diff --git a/internal/metric/registry/registry_test.go b/sdk/metric/registry/registry_test.go
similarity index 71%
rename from internal/metric/registry/registry_test.go
rename to sdk/metric/registry/registry_test.go
index 048848988..5d2bc9b10 100644
--- a/internal/metric/registry/registry_test.go
+++ b/sdk/metric/registry/registry_test.go
@@ -15,16 +15,15 @@
 package registry_test
 
 import (
-	"context"
 	"errors"
 	"testing"
 
 	"github.com/stretchr/testify/require"
 
-	"go.opentelemetry.io/otel/internal/metric/registry"
 	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/metrictest"
-	"go.opentelemetry.io/otel/metric/sdkapi"
+	metricsdk "go.opentelemetry.io/otel/sdk/metric"
+	"go.opentelemetry.io/otel/sdk/metric/registry"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 type (
@@ -34,22 +33,22 @@ type (
 var (
 	allNew = map[string]newFunc{
 		"counter.int64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) {
-			return unwrap(m.NewInt64Counter(name))
+			return unwrap(m.SyncInt64().Counter(name))
 		},
 		"counter.float64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) {
-			return unwrap(m.NewFloat64Counter(name))
+			return unwrap(m.SyncFloat64().Counter(name))
 		},
 		"histogram.int64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) {
-			return unwrap(m.NewInt64Histogram(name))
+			return unwrap(m.SyncInt64().Histogram(name))
 		},
 		"histogram.float64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) {
-			return unwrap(m.NewFloat64Histogram(name))
+			return unwrap(m.SyncFloat64().Histogram(name))
 		},
 		"gaugeobserver.int64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) {
-			return unwrap(m.NewInt64GaugeObserver(name, func(context.Context, metric.Int64ObserverResult) {}))
+			return unwrap(m.AsyncInt64().Gauge(name))
 		},
 		"gaugeobserver.float64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) {
-			return unwrap(m.NewFloat64GaugeObserver(name, func(context.Context, metric.Float64ObserverResult) {}))
+			return unwrap(m.AsyncFloat64().Gauge(name))
 		},
 	}
 )
@@ -71,10 +70,11 @@ func unwrap(impl interface{}, err error) (sdkapi.InstrumentImpl, error) {
 	return nil, err
 }
 
+// TODO Replace with controller
 func testMeterWithRegistry(name string) metric.Meter {
-	return metric.WrapMeterImpl(
+	return sdkapi.WrapMeterImpl(
 		registry.NewUniqueInstrumentMeterImpl(
-			metrictest.NewMeterProvider().Meter(name).MeterImpl(),
+			metricsdk.NewAccumulator(nil),
 		),
 	)
 }
@@ -91,21 +91,6 @@ func TestRegistrySameInstruments(t *testing.T) {
 	}
 }
 
-func TestRegistryDifferentNamespace(t *testing.T) {
-	for _, nf := range allNew {
-		provider := metrictest.NewMeterProvider()
-
-		meter1 := provider.Meter("meter1")
-		meter2 := provider.Meter("meter2")
-		inst1, err1 := nf(meter1, "this")
-		inst2, err2 := nf(meter2, "this")
-
-		require.NoError(t, err1)
-		require.NoError(t, err2)
-		require.NotEqual(t, inst1, inst2)
-	}
-}
-
 func TestRegistryDiffInstruments(t *testing.T) {
 	for origName, origf := range allNew {
 		meter := testMeterWithRegistry("meter")
@@ -120,7 +105,7 @@ func TestRegistryDiffInstruments(t *testing.T) {
 
 			other, err := nf(meter, "this")
 			require.Error(t, err)
-			require.NotNil(t, other)
+			require.Nil(t, other)
 			require.True(t, errors.Is(err, registry.ErrMetricKindMismatch))
 		}
 	}
diff --git a/sdk/metric/sdk.go b/sdk/metric/sdk.go
index a04334262..db3ad3302 100644
--- a/sdk/metric/sdk.go
+++ b/sdk/metric/sdk.go
@@ -23,11 +23,11 @@ import (
 
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
-	internal "go.opentelemetry.io/otel/internal/metric"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
+	"go.opentelemetry.io/otel/metric/instrument"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/export"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 type (
@@ -44,10 +44,8 @@ type (
 		// current maps `mapkey` to *record.
 		current sync.Map
 
-		// asyncInstruments is a set of
-		// `*asyncInstrument` instances
-		asyncLock        sync.Mutex
-		asyncInstruments *internal.AsyncInstrumentState
+		callbackLock sync.Mutex
+		callbacks    map[*callback]struct{}
 
 		// currentEpoch is the current epoch number. It is
 		// incremented in `Collect()`.
@@ -58,15 +56,23 @@ type (
 
 		// collectLock prevents simultaneous calls to Collect().
 		collectLock sync.Mutex
+	}
 
-		// asyncSortSlice has a single purpose - as a temporary
-		// place for sorting during labels creation to avoid
-		// allocation.  It is cleared after use.
-		asyncSortSlice attribute.Sortable
+	callback struct {
+		insts map[*asyncInstrument]struct{}
+		f     func(context.Context)
+	}
+
+	asyncContextKey struct{}
+
+	asyncInstrument struct {
+		baseInstrument
+		instrument.Asynchronous
 	}
 
 	syncInstrument struct {
-		instrument
+		baseInstrument
+		instrument.Synchronous
 	}
 
 	// mapkey uniquely describes a metric instrument in terms of
@@ -92,16 +98,10 @@ type (
 		// supports checking for no updates during a round.
 		collectedCount int64
 
-		// storage is the stored label set for this record,
+		// labels is the stored label set for this record,
 		// except in cases where a label set is shared due to
 		// batch recording.
-		storage attribute.Set
-
-		// labels is the processed label set for this record.
-		// this may refer to the `storage` field in another
-		// record if this label set is shared resulting from
-		// `RecordBatch`.
-		labels *attribute.Set
+		labels attribute.Set
 
 		// sortSlice has a single purpose - as a temporary
 		// place for sorting during labels creation to avoid
@@ -109,7 +109,7 @@ type (
 		sortSlice attribute.Sortable
 
 		// inst is a pointer to the corresponding instrument.
-		inst *syncInstrument
+		inst *baseInstrument
 
 		// current implements the actual RecordOne() API,
 		// depending on the type of aggregation.  If nil, the
@@ -118,36 +118,23 @@ type (
 		checkpoint aggregator.Aggregator
 	}
 
-	instrument struct {
+	baseInstrument struct {
 		meter      *Accumulator
 		descriptor sdkapi.Descriptor
 	}
-
-	asyncInstrument struct {
-		instrument
-		// recorders maps ordered labels to the pair of
-		// labelset and recorder
-		recorders map[attribute.Distinct]*labeledRecorder
-	}
-
-	labeledRecorder struct {
-		observedEpoch int64
-		labels        *attribute.Set
-		observed      aggregator.Aggregator
-	}
 )
 
 var (
 	_ sdkapi.MeterImpl = &Accumulator{}
-	_ sdkapi.AsyncImpl = &asyncInstrument{}
-	_ sdkapi.SyncImpl  = &syncInstrument{}
 
 	// ErrUninitializedInstrument is returned when an instrument is used when uninitialized.
 	ErrUninitializedInstrument = fmt.Errorf("use of an uninitialized instrument")
+
+	ErrBadInstrument = fmt.Errorf("use of a instrument from another SDK")
 )
 
-func (inst *instrument) Descriptor() sdkapi.Descriptor {
-	return inst.descriptor
+func (b *baseInstrument) Descriptor() sdkapi.Descriptor {
+	return b.descriptor
 }
 
 func (a *asyncInstrument) Implementation() interface{} {
@@ -158,77 +145,24 @@ func (s *syncInstrument) Implementation() interface{} {
 	return s
 }
 
-func (a *asyncInstrument) observe(num number.Number, labels *attribute.Set) {
-	if err := aggregator.RangeTest(num, &a.descriptor); err != nil {
-		otel.Handle(err)
-		return
-	}
-	recorder := a.getRecorder(labels)
-	if recorder == nil {
-		// The instrument is disabled according to the
-		// AggregatorSelector.
-		return
-	}
-	if err := recorder.Update(context.Background(), num, &a.descriptor); err != nil {
-		otel.Handle(err)
-		return
-	}
-}
-
-func (a *asyncInstrument) getRecorder(labels *attribute.Set) aggregator.Aggregator {
-	lrec, ok := a.recorders[labels.Equivalent()]
-	if ok {
-		// Note: SynchronizedMove(nil) can't return an error
-		_ = lrec.observed.SynchronizedMove(nil, &a.descriptor)
-		lrec.observedEpoch = a.meter.currentEpoch
-		a.recorders[labels.Equivalent()] = lrec
-		return lrec.observed
-	}
-	var rec aggregator.Aggregator
-	a.meter.processor.AggregatorFor(&a.descriptor, &rec)
-	if a.recorders == nil {
-		a.recorders = make(map[attribute.Distinct]*labeledRecorder)
-	}
-	// This may store nil recorder in the map, thus disabling the
-	// asyncInstrument for the labelset for good. This is intentional,
-	// but will be revisited later.
-	a.recorders[labels.Equivalent()] = &labeledRecorder{
-		observed:      rec,
-		labels:        labels,
-		observedEpoch: a.meter.currentEpoch,
-	}
-	return rec
-}
-
 // acquireHandle gets or creates a `*record` corresponding to `kvs`,
-// the input labels.  The second argument `labels` is passed in to
-// support re-use of the orderedLabels computed by a previous
-// measurement in the same batch.   This performs two allocations
-// in the common case.
-func (s *syncInstrument) acquireHandle(kvs []attribute.KeyValue, labelPtr *attribute.Set) *record {
-	var rec *record
-	var equiv attribute.Distinct
+// the input labels.
+func (b *baseInstrument) acquireHandle(kvs []attribute.KeyValue) *record {
 
-	if labelPtr == nil {
-		// This memory allocation may not be used, but it's
-		// needed for the `sortSlice` field, to avoid an
-		// allocation while sorting.
-		rec = &record{}
-		rec.storage = attribute.NewSetWithSortable(kvs, &rec.sortSlice)
-		rec.labels = &rec.storage
-		equiv = rec.storage.Equivalent()
-	} else {
-		equiv = labelPtr.Equivalent()
-	}
+	// This memory allocation may not be used, but it's
+	// needed for the `sortSlice` field, to avoid an
+	// allocation while sorting.
+	rec := &record{}
+	rec.labels = attribute.NewSetWithSortable(kvs, &rec.sortSlice)
 
 	// Create lookup key for sync.Map (one allocation, as this
 	// passes through an interface{})
 	mk := mapkey{
-		descriptor: &s.descriptor,
-		ordered:    equiv,
+		descriptor: &b.descriptor,
+		ordered:    rec.labels.Equivalent(),
 	}
 
-	if actual, ok := s.meter.current.Load(mk); ok {
+	if actual, ok := b.meter.current.Load(mk); ok {
 		// Existing record case.
 		existingRec := actual.(*record)
 		if existingRec.refMapped.ref() {
@@ -239,19 +173,15 @@ func (s *syncInstrument) acquireHandle(kvs []attribute.KeyValue, labelPtr *attri
 		// This entry is no longer mapped, try to add a new entry.
 	}
 
-	if rec == nil {
-		rec = &record{}
-		rec.labels = labelPtr
-	}
 	rec.refMapped = refcountMapped{value: 2}
-	rec.inst = s
+	rec.inst = b
 
-	s.meter.processor.AggregatorFor(&s.descriptor, &rec.current, &rec.checkpoint)
+	b.meter.processor.AggregatorFor(&b.descriptor, &rec.current, &rec.checkpoint)
 
 	for {
 		// Load/Store: there's a memory allocation to place `mk` into
 		// an interface here.
-		if actual, loaded := s.meter.current.LoadOrStore(mk, rec); loaded {
+		if actual, loaded := b.meter.current.LoadOrStore(mk, rec); loaded {
 			// Existing record case. Cannot change rec here because if fail
 			// will try to add rec again to avoid new allocations.
 			oldRec := actual.(*record)
@@ -278,11 +208,22 @@ func (s *syncInstrument) acquireHandle(kvs []attribute.KeyValue, labelPtr *attri
 	}
 }
 
+// RecordOne captures a single synchronous metric event.
+//
 // The order of the input array `kvs` may be sorted after the function is called.
 func (s *syncInstrument) RecordOne(ctx context.Context, num number.Number, kvs []attribute.KeyValue) {
-	h := s.acquireHandle(kvs, nil)
+	h := s.acquireHandle(kvs)
 	defer h.unbind()
-	h.RecordOne(ctx, num)
+	h.captureOne(ctx, num)
+}
+
+// ObserveOne captures a single asynchronous metric event.
+
+// The order of the input array `kvs` may be sorted after the function is called.
+func (a *asyncInstrument) ObserveOne(ctx context.Context, num number.Number, attrs []attribute.KeyValue) {
+	h := a.acquireHandle(attrs)
+	defer h.unbind()
+	h.captureOne(ctx, num)
 }
 
 // NewAccumulator constructs a new Accumulator for the given
@@ -296,15 +237,17 @@ func (s *syncInstrument) RecordOne(ctx context.Context, num number.Number, kvs [
 // own periodic collection.
 func NewAccumulator(processor export.Processor) *Accumulator {
 	return &Accumulator{
-		processor:        processor,
-		asyncInstruments: internal.NewAsyncInstrumentState(),
+		processor: processor,
+		callbacks: map[*callback]struct{}{},
 	}
 }
 
+var _ sdkapi.MeterImpl = &Accumulator{}
+
 // NewSyncInstrument implements sdkapi.MetricImpl.
 func (m *Accumulator) NewSyncInstrument(descriptor sdkapi.Descriptor) (sdkapi.SyncImpl, error) {
 	return &syncInstrument{
-		instrument: instrument{
+		baseInstrument: baseInstrument{
 			descriptor: descriptor,
 			meter:      m,
 		},
@@ -312,19 +255,40 @@ func (m *Accumulator) NewSyncInstrument(descriptor sdkapi.Descriptor) (sdkapi.Sy
 }
 
 // NewAsyncInstrument implements sdkapi.MetricImpl.
-func (m *Accumulator) NewAsyncInstrument(descriptor sdkapi.Descriptor, runner sdkapi.AsyncRunner) (sdkapi.AsyncImpl, error) {
+func (m *Accumulator) NewAsyncInstrument(descriptor sdkapi.Descriptor) (sdkapi.AsyncImpl, error) {
 	a := &asyncInstrument{
-		instrument: instrument{
+		baseInstrument: baseInstrument{
 			descriptor: descriptor,
 			meter:      m,
 		},
 	}
-	m.asyncLock.Lock()
-	defer m.asyncLock.Unlock()
-	m.asyncInstruments.Register(a, runner)
 	return a, nil
 }
 
+func (m *Accumulator) RegisterCallback(insts []instrument.Asynchronous, f func(context.Context)) error {
+	cb := &callback{
+		insts: map[*asyncInstrument]struct{}{},
+		f:     f,
+	}
+	for _, inst := range insts {
+		impl, ok := inst.(sdkapi.AsyncImpl)
+		if !ok {
+			return ErrBadInstrument
+		}
+
+		ai, err := m.fromAsync(impl)
+		if err != nil {
+			return err
+		}
+		cb.insts[ai] = struct{}{}
+	}
+
+	m.callbackLock.Lock()
+	defer m.callbackLock.Unlock()
+	m.callbacks[cb] = struct{}{}
+	return nil
+}
+
 // Collect traverses the list of active records and observers and
 // exports data for each active instrument.  Collect() may not be
 // called concurrently.
@@ -337,14 +301,14 @@ func (m *Accumulator) Collect(ctx context.Context) int {
 	m.collectLock.Lock()
 	defer m.collectLock.Unlock()
 
-	checkpointed := m.observeAsyncInstruments(ctx)
-	checkpointed += m.collectSyncInstruments()
+	m.runAsyncCallbacks(ctx)
+	checkpointed := m.collectInstruments()
 	m.currentEpoch++
 
 	return checkpointed
 }
 
-func (m *Accumulator) collectSyncInstruments() int {
+func (m *Accumulator) collectInstruments() int {
 	checkpointed := 0
 
 	m.current.Range(func(key interface{}, value interface{}) bool {
@@ -387,35 +351,17 @@ func (m *Accumulator) collectSyncInstruments() int {
 	return checkpointed
 }
 
-// CollectAsync implements internal.AsyncCollector.
-// The order of the input array `kvs` may be sorted after the function is called.
-func (m *Accumulator) CollectAsync(kv []attribute.KeyValue, obs ...sdkapi.Observation) {
-	labels := attribute.NewSetWithSortable(kv, &m.asyncSortSlice)
+func (m *Accumulator) runAsyncCallbacks(ctx context.Context) {
+	m.callbackLock.Lock()
+	defer m.callbackLock.Unlock()
 
-	for _, ob := range obs {
-		if a := m.fromAsync(ob.AsyncImpl()); a != nil {
-			a.observe(ob.Number(), &labels)
-		}
+	ctx = context.WithValue(ctx, asyncContextKey{}, m)
+
+	for cb := range m.callbacks {
+		cb.f(ctx)
 	}
 }
 
-func (m *Accumulator) observeAsyncInstruments(ctx context.Context) int {
-	m.asyncLock.Lock()
-	defer m.asyncLock.Unlock()
-
-	asyncCollected := 0
-
-	m.asyncInstruments.Run(ctx, m)
-
-	for _, inst := range m.asyncInstruments.Instruments() {
-		if a := m.fromAsync(inst); a != nil {
-			asyncCollected += m.checkpointAsync(a)
-		}
-	}
-
-	return asyncCollected
-}
-
 func (m *Accumulator) checkpointRecord(r *record) int {
 	if r.current == nil {
 		return 0
@@ -426,7 +372,7 @@ func (m *Accumulator) checkpointRecord(r *record) int {
 		return 0
 	}
 
-	a := export.NewAccumulation(&r.inst.descriptor, r.labels, r.checkpoint)
+	a := export.NewAccumulation(&r.inst.descriptor, &r.labels, r.checkpoint)
 	err = m.processor.Process(a)
 	if err != nil {
 		otel.Handle(err)
@@ -434,63 +380,7 @@ func (m *Accumulator) checkpointRecord(r *record) int {
 	return 1
 }
 
-func (m *Accumulator) checkpointAsync(a *asyncInstrument) int {
-	if len(a.recorders) == 0 {
-		return 0
-	}
-	checkpointed := 0
-	for encodedLabels, lrec := range a.recorders {
-		lrec := lrec
-		epochDiff := m.currentEpoch - lrec.observedEpoch
-		if epochDiff == 0 {
-			if lrec.observed != nil {
-				a := export.NewAccumulation(&a.descriptor, lrec.labels, lrec.observed)
-				err := m.processor.Process(a)
-				if err != nil {
-					otel.Handle(err)
-				}
-				checkpointed++
-			}
-		} else if epochDiff > 1 {
-			// This is second collection cycle with no
-			// observations for this labelset. Remove the
-			// recorder.
-			delete(a.recorders, encodedLabels)
-		}
-	}
-	if len(a.recorders) == 0 {
-		a.recorders = nil
-	}
-	return checkpointed
-}
-
-// RecordBatch enters a batch of metric events.
-// The order of the input array `kvs` may be sorted after the function is called.
-func (m *Accumulator) RecordBatch(ctx context.Context, kvs []attribute.KeyValue, measurements ...sdkapi.Measurement) {
-	// Labels will be computed the first time acquireHandle is
-	// called.  Subsequent calls to acquireHandle will re-use the
-	// previously computed value instead of recomputing the
-	// ordered labels.
-	var labelsPtr *attribute.Set
-	for i, meas := range measurements {
-		s := m.fromSync(meas.SyncImpl())
-		if s == nil {
-			continue
-		}
-		h := s.acquireHandle(kvs, labelsPtr)
-
-		// Re-use labels for the next measurement.
-		if i == 0 {
-			labelsPtr = h.labels
-		}
-
-		defer h.unbind()
-		h.RecordOne(ctx, meas.Number())
-	}
-}
-
-// RecordOne implements sdkapi.SyncImpl.
-func (r *record) RecordOne(ctx context.Context, num number.Number) {
+func (r *record) captureOne(ctx context.Context, num number.Number) {
 	if r.current == nil {
 		// The instrument is disabled according to the AggregatorSelector.
 		return
@@ -519,26 +409,16 @@ func (r *record) mapkey() mapkey {
 	}
 }
 
-// fromSync gets a sync implementation object, checking for
-// uninitialized instruments and instruments created by another SDK.
-func (m *Accumulator) fromSync(sync sdkapi.SyncImpl) *syncInstrument {
-	if sync != nil {
-		if inst, ok := sync.Implementation().(*syncInstrument); ok {
-			return inst
-		}
-	}
-	otel.Handle(ErrUninitializedInstrument)
-	return nil
-}
-
 // fromSync gets an async implementation object, checking for
 // uninitialized instruments and instruments created by another SDK.
-func (m *Accumulator) fromAsync(async sdkapi.AsyncImpl) *asyncInstrument {
-	if async != nil {
-		if inst, ok := async.Implementation().(*asyncInstrument); ok {
-			return inst
-		}
+func (m *Accumulator) fromAsync(async sdkapi.AsyncImpl) (*asyncInstrument, error) {
+	if async == nil {
+		return nil, ErrUninitializedInstrument
 	}
-	otel.Handle(ErrUninitializedInstrument)
-	return nil
+	inst, ok := async.Implementation().(*asyncInstrument)
+	if !ok {
+		return nil, ErrBadInstrument
+	}
+	return inst, nil
+
 }
diff --git a/metric/sdkapi/descriptor.go b/sdk/metric/sdkapi/descriptor.go
similarity index 94%
rename from metric/sdkapi/descriptor.go
rename to sdk/metric/sdkapi/descriptor.go
index 14eb0532e..f86e44734 100644
--- a/metric/sdkapi/descriptor.go
+++ b/sdk/metric/sdkapi/descriptor.go
@@ -12,11 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package sdkapi // import "go.opentelemetry.io/otel/metric/sdkapi"
+package sdkapi // import "go.opentelemetry.io/otel/sdk/metric/sdkapi"
 
 import (
-	"go.opentelemetry.io/otel/metric/number"
 	"go.opentelemetry.io/otel/metric/unit"
+	"go.opentelemetry.io/otel/sdk/metric/number"
 )
 
 // Descriptor contains all the settings that describe an instrument,
diff --git a/metric/sdkapi/descriptor_test.go b/sdk/metric/sdkapi/descriptor_test.go
similarity index 96%
rename from metric/sdkapi/descriptor_test.go
rename to sdk/metric/sdkapi/descriptor_test.go
index 6b6927075..1f0844725 100644
--- a/metric/sdkapi/descriptor_test.go
+++ b/sdk/metric/sdkapi/descriptor_test.go
@@ -19,8 +19,8 @@ import (
 
 	"github.com/stretchr/testify/require"
 
-	"go.opentelemetry.io/otel/metric/number"
 	"go.opentelemetry.io/otel/metric/unit"
+	"go.opentelemetry.io/otel/sdk/metric/number"
 )
 
 func TestDescriptorGetters(t *testing.T) {
diff --git a/metric/sdkapi/instrumentkind.go b/sdk/metric/sdkapi/instrumentkind.go
similarity index 97%
rename from metric/sdkapi/instrumentkind.go
rename to sdk/metric/sdkapi/instrumentkind.go
index 64aa5ead1..c7406a3e4 100644
--- a/metric/sdkapi/instrumentkind.go
+++ b/sdk/metric/sdkapi/instrumentkind.go
@@ -14,7 +14,7 @@
 
 //go:generate stringer -type=InstrumentKind
 
-package sdkapi // import "go.opentelemetry.io/otel/metric/sdkapi"
+package sdkapi // import "go.opentelemetry.io/otel/sdk/metric/sdkapi"
 
 // InstrumentKind describes the kind of instrument.
 type InstrumentKind int8
diff --git a/metric/sdkapi/instrumentkind_string.go b/sdk/metric/sdkapi/instrumentkind_string.go
similarity index 100%
rename from metric/sdkapi/instrumentkind_string.go
rename to sdk/metric/sdkapi/instrumentkind_string.go
diff --git a/metric/sdkapi/instrumentkind_test.go b/sdk/metric/sdkapi/instrumentkind_test.go
similarity index 96%
rename from metric/sdkapi/instrumentkind_test.go
rename to sdk/metric/sdkapi/instrumentkind_test.go
index 0b2901383..cd1db02a8 100644
--- a/metric/sdkapi/instrumentkind_test.go
+++ b/sdk/metric/sdkapi/instrumentkind_test.go
@@ -19,7 +19,7 @@ import (
 
 	"github.com/stretchr/testify/require"
 
-	"go.opentelemetry.io/otel/metric/sdkapi"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 func TestInstrumentKinds(t *testing.T) {
diff --git a/metric/sdkapi/noop.go b/sdk/metric/sdkapi/noop.go
similarity index 71%
rename from metric/sdkapi/noop.go
rename to sdk/metric/sdkapi/noop.go
index f22895dae..6b2374b68 100644
--- a/metric/sdkapi/noop.go
+++ b/sdk/metric/sdkapi/noop.go
@@ -12,20 +12,34 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package sdkapi // import "go.opentelemetry.io/otel/metric/sdkapi"
+package sdkapi // import "go.opentelemetry.io/otel/sdk/metric/sdkapi"
 
 import (
 	"context"
 
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric/number"
-)
+	"go.opentelemetry.io/otel/metric/instrument"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+) // import (
+// 	"context"
+
+// 	"go.opentelemetry.io/otel/attribute"
+// 	"go.opentelemetry.io/otel/sdk/metric/number"
+// )
 
 type noopInstrument struct {
 	descriptor Descriptor
 }
-type noopSyncInstrument struct{ noopInstrument }
-type noopAsyncInstrument struct{ noopInstrument }
+type noopSyncInstrument struct {
+	noopInstrument
+
+	instrument.Synchronous
+}
+type noopAsyncInstrument struct {
+	noopInstrument
+
+	instrument.Asynchronous
+}
 
 var _ SyncImpl = noopSyncInstrument{}
 var _ AsyncImpl = noopAsyncInstrument{}
@@ -34,7 +48,7 @@ var _ AsyncImpl = noopAsyncInstrument{}
 // synchronous instrument interface.
 func NewNoopSyncInstrument() SyncImpl {
 	return noopSyncInstrument{
-		noopInstrument{
+		noopInstrument: noopInstrument{
 			descriptor: Descriptor{
 				instrumentKind: CounterInstrumentKind,
 			},
@@ -46,7 +60,7 @@ func NewNoopSyncInstrument() SyncImpl {
 // asynchronous instrument interface.
 func NewNoopAsyncInstrument() AsyncImpl {
 	return noopAsyncInstrument{
-		noopInstrument{
+		noopInstrument: noopInstrument{
 			descriptor: Descriptor{
 				instrumentKind: CounterObserverInstrumentKind,
 			},
@@ -64,3 +78,6 @@ func (n noopInstrument) Descriptor() Descriptor {
 
 func (noopSyncInstrument) RecordOne(context.Context, number.Number, []attribute.KeyValue) {
 }
+
+func (noopAsyncInstrument) ObserveOne(ctx context.Context, number number.Number, labels []attribute.KeyValue) {
+}
diff --git a/metric/sdkapi/sdkapi.go b/sdk/metric/sdkapi/sdkapi.go
similarity index 90%
rename from metric/sdkapi/sdkapi.go
rename to sdk/metric/sdkapi/sdkapi.go
index 36836364b..4345358ff 100644
--- a/metric/sdkapi/sdkapi.go
+++ b/sdk/metric/sdkapi/sdkapi.go
@@ -12,21 +12,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package sdkapi // import "go.opentelemetry.io/otel/metric/sdkapi"
+package sdkapi // import "go.opentelemetry.io/otel/sdk/metric/sdkapi"
 
 import (
 	"context"
 
 	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric/number"
+	"go.opentelemetry.io/otel/metric/instrument"
+	"go.opentelemetry.io/otel/sdk/metric/number"
 )
 
 // MeterImpl is the interface an SDK must implement to supply a Meter
 // implementation.
 type MeterImpl interface {
-	// RecordBatch atomically records a batch of measurements.
-	RecordBatch(ctx context.Context, labels []attribute.KeyValue, measurement ...Measurement)
-
 	// NewSyncInstrument returns a newly constructed
 	// synchronous instrument implementation or an error, should
 	// one occur.
@@ -35,10 +33,10 @@ type MeterImpl interface {
 	// NewAsyncInstrument returns a newly constructed
 	// asynchronous instrument implementation or an error, should
 	// one occur.
-	NewAsyncInstrument(
-		descriptor Descriptor,
-		runner AsyncRunner,
-	) (AsyncImpl, error)
+	NewAsyncInstrument(descriptor Descriptor) (AsyncImpl, error)
+
+	// Etc.
+	RegisterCallback(insts []instrument.Asynchronous, callback func(context.Context)) error
 }
 
 // InstrumentImpl is a common interface for synchronous and
@@ -57,6 +55,7 @@ type InstrumentImpl interface {
 // synchronous instrument (e.g., Histogram and Counter instruments).
 type SyncImpl interface {
 	InstrumentImpl
+	instrument.Synchronous
 
 	// RecordOne captures a single synchronous metric event.
 	RecordOne(ctx context.Context, number number.Number, labels []attribute.KeyValue)
@@ -66,6 +65,10 @@ type SyncImpl interface {
 // asynchronous instrument (e.g., Observer instruments).
 type AsyncImpl interface {
 	InstrumentImpl
+	instrument.Asynchronous
+
+	// ObserveOne captures a single synchronous metric event.
+	ObserveOne(ctx context.Context, number number.Number, labels []attribute.KeyValue)
 }
 
 // AsyncRunner is expected to convert into an AsyncSingleRunner or an
diff --git a/metric/sdkapi/sdkapi_test.go b/sdk/metric/sdkapi/sdkapi_test.go
similarity index 96%
rename from metric/sdkapi/sdkapi_test.go
rename to sdk/metric/sdkapi/sdkapi_test.go
index 9c80f89bd..69fec0fe6 100644
--- a/metric/sdkapi/sdkapi_test.go
+++ b/sdk/metric/sdkapi/sdkapi_test.go
@@ -19,7 +19,7 @@ import (
 
 	"github.com/stretchr/testify/require"
 
-	"go.opentelemetry.io/otel/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/number"
 )
 
 func TestMeasurementGetters(t *testing.T) {
diff --git a/sdk/metric/sdkapi/wrap.go b/sdk/metric/sdkapi/wrap.go
new file mode 100644
index 000000000..9a4d5b0de
--- /dev/null
+++ b/sdk/metric/sdkapi/wrap.go
@@ -0,0 +1,181 @@
+// Copyright The OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sdkapi // import "go.opentelemetry.io/otel/sdk/metric/sdkapi"
+
+import (
+	"context"
+
+	"go.opentelemetry.io/otel/attribute"
+	"go.opentelemetry.io/otel/metric"
+	"go.opentelemetry.io/otel/metric/instrument"
+	"go.opentelemetry.io/otel/metric/instrument/asyncfloat64"
+	"go.opentelemetry.io/otel/metric/instrument/asyncint64"
+	"go.opentelemetry.io/otel/metric/instrument/syncfloat64"
+	"go.opentelemetry.io/otel/metric/instrument/syncint64"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+)
+
+type (
+	meter   struct{ MeterImpl }
+	sfMeter struct{ meter }
+	siMeter struct{ meter }
+	afMeter struct{ meter }
+	aiMeter struct{ meter }
+
+	iAdder    struct{ SyncImpl }
+	fAdder    struct{ SyncImpl }
+	iRecorder struct{ SyncImpl }
+	fRecorder struct{ SyncImpl }
+	iObserver struct{ AsyncImpl }
+	fObserver struct{ AsyncImpl }
+)
+
+func WrapMeterImpl(impl MeterImpl) metric.Meter {
+	return meter{impl}
+}
+
+func UnwrapMeterImpl(m metric.Meter) MeterImpl {
+	mm, ok := m.(meter)
+	if !ok {
+		return nil
+	}
+	return mm.MeterImpl
+}
+
+func (m meter) AsyncFloat64() asyncfloat64.InstrumentProvider {
+	return afMeter{m}
+}
+
+func (m meter) AsyncInt64() asyncint64.InstrumentProvider {
+	return aiMeter{m}
+}
+
+func (m meter) SyncFloat64() syncfloat64.InstrumentProvider {
+	return sfMeter{m}
+}
+
+func (m meter) SyncInt64() syncint64.InstrumentProvider {
+	return siMeter{m}
+}
+
+func (m meter) RegisterCallback(insts []instrument.Asynchronous, cb func(ctx context.Context)) error {
+	return m.MeterImpl.RegisterCallback(insts, cb)
+}
+
+func (m meter) newSync(name string, ikind InstrumentKind, nkind number.Kind, opts []instrument.Option) (SyncImpl, error) {
+	cfg := instrument.NewConfig(opts...)
+	return m.NewSyncInstrument(NewDescriptor(name, ikind, nkind, cfg.Description(), cfg.Unit()))
+}
+
+func (m meter) newAsync(name string, ikind InstrumentKind, nkind number.Kind, opts []instrument.Option) (AsyncImpl, error) {
+	cfg := instrument.NewConfig(opts...)
+	return m.NewAsyncInstrument(NewDescriptor(name, ikind, nkind, cfg.Description(), cfg.Unit()))
+}
+
+func (m afMeter) Counter(name string, opts ...instrument.Option) (asyncfloat64.Counter, error) {
+	inst, err := m.newAsync(name, CounterObserverInstrumentKind, number.Float64Kind, opts)
+	return fObserver{inst}, err
+}
+
+func (m afMeter) UpDownCounter(name string, opts ...instrument.Option) (asyncfloat64.UpDownCounter, error) {
+	inst, err := m.newAsync(name, UpDownCounterObserverInstrumentKind, number.Float64Kind, opts)
+	return fObserver{inst}, err
+}
+
+func (m afMeter) Gauge(name string, opts ...instrument.Option) (asyncfloat64.Gauge, error) {
+	inst, err := m.newAsync(name, GaugeObserverInstrumentKind, number.Float64Kind, opts)
+	return fObserver{inst}, err
+}
+
+func (m aiMeter) Counter(name string, opts ...instrument.Option) (asyncint64.Counter, error) {
+	inst, err := m.newAsync(name, CounterObserverInstrumentKind, number.Int64Kind, opts)
+	return iObserver{inst}, err
+}
+
+func (m aiMeter) UpDownCounter(name string, opts ...instrument.Option) (asyncint64.UpDownCounter, error) {
+	inst, err := m.newAsync(name, UpDownCounterObserverInstrumentKind, number.Int64Kind, opts)
+	return iObserver{inst}, err
+}
+
+func (m aiMeter) Gauge(name string, opts ...instrument.Option) (asyncint64.Gauge, error) {
+	inst, err := m.newAsync(name, GaugeObserverInstrumentKind, number.Int64Kind, opts)
+	return iObserver{inst}, err
+}
+
+func (m sfMeter) Counter(name string, opts ...instrument.Option) (syncfloat64.Counter, error) {
+	inst, err := m.newSync(name, CounterInstrumentKind, number.Float64Kind, opts)
+	return fAdder{inst}, err
+}
+
+func (m sfMeter) UpDownCounter(name string, opts ...instrument.Option) (syncfloat64.UpDownCounter, error) {
+	inst, err := m.newSync(name, UpDownCounterInstrumentKind, number.Float64Kind, opts)
+	return fAdder{inst}, err
+}
+
+func (m sfMeter) Histogram(name string, opts ...instrument.Option) (syncfloat64.Histogram, error) {
+	inst, err := m.newSync(name, HistogramInstrumentKind, number.Float64Kind, opts)
+	return fRecorder{inst}, err
+}
+
+func (m siMeter) Counter(name string, opts ...instrument.Option) (syncint64.Counter, error) {
+	inst, err := m.newSync(name, CounterInstrumentKind, number.Int64Kind, opts)
+	return iAdder{inst}, err
+}
+
+func (m siMeter) UpDownCounter(name string, opts ...instrument.Option) (syncint64.UpDownCounter, error) {
+	inst, err := m.newSync(name, UpDownCounterInstrumentKind, number.Int64Kind, opts)
+	return iAdder{inst}, err
+}
+
+func (m siMeter) Histogram(name string, opts ...instrument.Option) (syncint64.Histogram, error) {
+	inst, err := m.newSync(name, HistogramInstrumentKind, number.Int64Kind, opts)
+	return iRecorder{inst}, err
+}
+
+func (a fAdder) Add(ctx context.Context, value float64, attrs ...attribute.KeyValue) {
+	if a.SyncImpl != nil {
+		a.SyncImpl.RecordOne(ctx, number.NewFloat64Number(value), attrs)
+	}
+}
+
+func (a iAdder) Add(ctx context.Context, value int64, attrs ...attribute.KeyValue) {
+	if a.SyncImpl != nil {
+		a.SyncImpl.RecordOne(ctx, number.NewInt64Number(value), attrs)
+	}
+}
+
+func (a fRecorder) Record(ctx context.Context, value float64, attrs ...attribute.KeyValue) {
+	if a.SyncImpl != nil {
+		a.SyncImpl.RecordOne(ctx, number.NewFloat64Number(value), attrs)
+	}
+}
+
+func (a iRecorder) Record(ctx context.Context, value int64, attrs ...attribute.KeyValue) {
+	if a.SyncImpl != nil {
+		a.SyncImpl.RecordOne(ctx, number.NewInt64Number(value), attrs)
+	}
+}
+
+func (a fObserver) Observe(ctx context.Context, value float64, attrs ...attribute.KeyValue) {
+	if a.AsyncImpl != nil {
+		a.AsyncImpl.ObserveOne(ctx, number.NewFloat64Number(value), attrs)
+	}
+}
+
+func (a iObserver) Observe(ctx context.Context, value int64, attrs ...attribute.KeyValue) {
+	if a.AsyncImpl != nil {
+		a.AsyncImpl.ObserveOne(ctx, number.NewInt64Number(value), attrs)
+	}
+}
diff --git a/sdk/metric/selector/simple/simple.go b/sdk/metric/selector/simple/simple.go
index 5b3e1c5fd..5451072f6 100644
--- a/sdk/metric/selector/simple/simple.go
+++ b/sdk/metric/selector/simple/simple.go
@@ -15,12 +15,12 @@
 package simple // import "go.opentelemetry.io/otel/sdk/metric/selector/simple"
 
 import (
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
 	"go.opentelemetry.io/otel/sdk/metric/export"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 )
 
 type (
diff --git a/sdk/metric/selector/simple/simple_test.go b/sdk/metric/selector/simple/simple_test.go
index a25291b51..9e946864e 100644
--- a/sdk/metric/selector/simple/simple_test.go
+++ b/sdk/metric/selector/simple/simple_test.go
@@ -19,14 +19,14 @@ import (
 
 	"github.com/stretchr/testify/require"
 
-	"go.opentelemetry.io/otel/metric/metrictest"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
 	"go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
 	"go.opentelemetry.io/otel/sdk/metric/export"
+	"go.opentelemetry.io/otel/sdk/metric/metrictest"
+	"go.opentelemetry.io/otel/sdk/metric/number"
+	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
 	"go.opentelemetry.io/otel/sdk/metric/selector/simple"
 )
 
diff --git a/sdk/metric/stress_test.go b/sdk/metric/stress_test.go
deleted file mode 100644
index f6b9eb4ba..000000000
--- a/sdk/metric/stress_test.go
+++ /dev/null
@@ -1,502 +0,0 @@
-// Copyright The OpenTelemetry Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// This test is too large for the race detector.  This SDK uses no locks
-// that the race detector would help with, anyway.
-//go:build !race
-// +build !race
-
-package metric
-
-import (
-	"context"
-	"fmt"
-	"math"
-	"math/rand"
-	"runtime"
-	"sort"
-	"strings"
-	"sync"
-	"sync/atomic"
-	"testing"
-	"time"
-
-	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric"
-	"go.opentelemetry.io/otel/metric/number"
-	"go.opentelemetry.io/otel/metric/sdkapi"
-	"go.opentelemetry.io/otel/sdk/metric/export"
-	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
-	"go.opentelemetry.io/otel/sdk/metric/processor/processortest"
-)
-
-const (
-	concurrencyPerCPU = 100
-	reclaimPeriod     = time.Millisecond * 100
-	testRun           = 5 * time.Second
-	epsilon           = 1e-10
-)
-
-var Must = metric.Must
-
-type (
-	testFixture struct {
-		// stop has to be aligned for 64-bit atomic operations.
-		stop     int64
-		expected sync.Map
-		received sync.Map // Note: doesn't require synchronization
-		wg       sync.WaitGroup
-		impl     testImpl
-		T        *testing.T
-
-		export.AggregatorSelector
-
-		lock  sync.Mutex
-		lused map[string]bool
-
-		dupCheck  map[testKey]int
-		totalDups int64
-	}
-
-	testKey struct {
-		labels     string
-		descriptor *sdkapi.Descriptor
-	}
-
-	testImpl struct {
-		newInstrument  func(meter metric.Meter, name string) SyncImpler
-		getUpdateValue func() number.Number
-		operate        func(interface{}, context.Context, number.Number, []attribute.KeyValue)
-		newStore       func() interface{}
-
-		// storeCollect and storeExpect are the same for
-		// counters, different for lastValues, to ensure we are
-		// testing the timestamps correctly.
-		storeCollect func(store interface{}, value number.Number, ts time.Time)
-		storeExpect  func(store interface{}, value number.Number)
-		readStore    func(store interface{}) number.Number
-		equalValues  func(a, b number.Number) bool
-	}
-
-	SyncImpler interface {
-		SyncImpl() sdkapi.SyncImpl
-	}
-
-	// lastValueState supports merging lastValue values, for the case
-	// where a race condition causes duplicate records.  We always
-	// take the later timestamp.
-	lastValueState struct {
-		// raw has to be aligned for 64-bit atomic operations.
-		raw number.Number
-		ts  time.Time
-	}
-)
-
-func concurrency() int {
-	return concurrencyPerCPU * runtime.NumCPU()
-}
-
-func canonicalizeLabels(ls []attribute.KeyValue) string {
-	copy := append(ls[0:0:0], ls...)
-	sort.SliceStable(copy, func(i, j int) bool {
-		return copy[i].Key < copy[j].Key
-	})
-	var b strings.Builder
-	for _, kv := range copy {
-		b.WriteString(string(kv.Key))
-		b.WriteString("=")
-		b.WriteString(kv.Value.Emit())
-		b.WriteString("$")
-	}
-	return b.String()
-}
-
-func getPeriod() time.Duration {
-	dur := math.Max(
-		float64(reclaimPeriod)/10,
-		float64(reclaimPeriod)*(1+0.1*rand.NormFloat64()),
-	)
-	return time.Duration(dur)
-}
-
-func (f *testFixture) someLabels() []attribute.KeyValue {
-	n := 1 + rand.Intn(3)
-	l := make([]attribute.KeyValue, n)
-
-	for {
-		oused := map[string]bool{}
-		for i := 0; i < n; i++ {
-			var k string
-			for {
-				k = fmt.Sprint("k", rand.Intn(1000000000))
-				if !oused[k] {
-					oused[k] = true
-					break
-				}
-			}
-			l[i] = attribute.String(k, fmt.Sprint("v", rand.Intn(1000000000)))
-		}
-		lc := canonicalizeLabels(l)
-		f.lock.Lock()
-		avail := !f.lused[lc]
-		if avail {
-			f.lused[lc] = true
-			f.lock.Unlock()
-			return l
-		}
-		f.lock.Unlock()
-	}
-}
-
-func (f *testFixture) startWorker(impl *Accumulator, meter metric.Meter, wg *sync.WaitGroup, i int) {
-	ctx := context.Background()
-	name := fmt.Sprint("test_", i)
-	instrument := f.impl.newInstrument(meter, name)
-	var descriptor *sdkapi.Descriptor
-	if ii, ok := instrument.SyncImpl().(*syncInstrument); ok {
-		descriptor = &ii.descriptor
-	}
-	kvs := f.someLabels()
-	clabs := canonicalizeLabels(kvs)
-	dur := getPeriod()
-	key := testKey{
-		labels:     clabs,
-		descriptor: descriptor,
-	}
-	for {
-		sleep := time.Duration(rand.ExpFloat64() * float64(dur))
-		time.Sleep(sleep)
-		value := f.impl.getUpdateValue()
-		f.impl.operate(instrument, ctx, value, kvs)
-
-		actual, _ := f.expected.LoadOrStore(key, f.impl.newStore())
-
-		f.impl.storeExpect(actual, value)
-
-		if atomic.LoadInt64(&f.stop) != 0 {
-			wg.Done()
-			return
-		}
-	}
-}
-
-func (f *testFixture) assertTest(numCollect int) {
-	var allErrs []func()
-	csize := 0
-	f.received.Range(func(key, gstore interface{}) bool {
-		csize++
-		gvalue := f.impl.readStore(gstore)
-
-		estore, loaded := f.expected.Load(key)
-		if !loaded {
-			allErrs = append(allErrs, func() {
-				f.T.Error("Could not locate expected key: ", key)
-			})
-			return true
-		}
-		evalue := f.impl.readStore(estore)
-
-		if !f.impl.equalValues(evalue, gvalue) {
-			allErrs = append(allErrs, func() {
-				f.T.Error("Expected value mismatch: ",
-					evalue, "!=", gvalue, " for ", key)
-			})
-		}
-		return true
-	})
-	rsize := 0
-	f.expected.Range(func(key, value interface{}) bool {
-		rsize++
-		if _, loaded := f.received.Load(key); !loaded {
-			allErrs = append(allErrs, func() {
-				f.T.Error("Did not receive expected key: ", key)
-			})
-		}
-		return true
-	})
-	if rsize != csize {
-		f.T.Error("Did not receive the correct set of metrics: Received != Expected", rsize, csize)
-	}
-
-	for _, anErr := range allErrs {
-		anErr()
-	}
-
-	// Note: It's useful to know the test triggers this condition,
-	// but we can't assert it.  Infrequently no duplicates are
-	// found, and we can't really force a race to happen.
-	//
-	// fmt.Printf("Test duplicate records seen: %.1f%%\n",
-	// 	float64(100*f.totalDups/int64(numCollect*concurrency())))
-}
-
-func (f *testFixture) preCollect() {
-	// Collect calls Process in a single-threaded context. No need
-	// to lock this struct.
-	f.dupCheck = map[testKey]int{}
-}
-
-func (*testFixture) Reader() export.Reader {
-	return nil
-}
-
-func (f *testFixture) Process(accumulation export.Accumulation) error {
-	labels := accumulation.Labels().ToSlice()
-	key := testKey{
-		labels:     canonicalizeLabels(labels),
-		descriptor: accumulation.Descriptor(),
-	}
-	if f.dupCheck[key] == 0 {
-		f.dupCheck[key]++
-	} else {
-		f.totalDups++
-	}
-
-	actual, _ := f.received.LoadOrStore(key, f.impl.newStore())
-
-	agg := accumulation.Aggregator()
-	switch accumulation.Descriptor().InstrumentKind() {
-	case sdkapi.CounterInstrumentKind:
-		sum, err := agg.(aggregation.Sum).Sum()
-		if err != nil {
-			f.T.Fatal("Sum error: ", err)
-		}
-		f.impl.storeCollect(actual, sum, time.Time{})
-	case sdkapi.HistogramInstrumentKind:
-		lv, ts, err := agg.(aggregation.LastValue).LastValue()
-		if err != nil && err != aggregation.ErrNoData {
-			f.T.Fatal("Last value error: ", err)
-		}
-		f.impl.storeCollect(actual, lv, ts)
-	default:
-		panic("Not used in this test")
-	}
-	return nil
-}
-
-func stressTest(t *testing.T, impl testImpl) {
-	ctx := context.Background()
-	t.Parallel()
-	fixture := &testFixture{
-		T:                  t,
-		impl:               impl,
-		lused:              map[string]bool{},
-		AggregatorSelector: processortest.AggregatorSelector(),
-	}
-	cc := concurrency()
-
-	sdk := NewAccumulator(fixture)
-	meter := metric.WrapMeterImpl(sdk)
-	fixture.wg.Add(cc + 1)
-
-	for i := 0; i < cc; i++ {
-		go fixture.startWorker(sdk, meter, &fixture.wg, i)
-	}
-
-	numCollect := 0
-
-	go func() {
-		for {
-			time.Sleep(reclaimPeriod)
-			fixture.preCollect()
-			sdk.Collect(ctx)
-			numCollect++
-			if atomic.LoadInt64(&fixture.stop) != 0 {
-				fixture.wg.Done()
-				return
-			}
-		}
-	}()
-
-	time.Sleep(testRun)
-	atomic.StoreInt64(&fixture.stop, 1)
-	fixture.wg.Wait()
-	fixture.preCollect()
-	sdk.Collect(ctx)
-	numCollect++
-
-	fixture.assertTest(numCollect)
-}
-
-func int64sEqual(a, b number.Number) bool {
-	return a.AsInt64() == b.AsInt64()
-}
-
-func float64sEqual(a, b number.Number) bool {
-	diff := math.Abs(a.AsFloat64() - b.AsFloat64())
-	return diff < math.Abs(a.AsFloat64())*epsilon
-}
-
-// Counters
-
-func intCounterTestImpl() testImpl {
-	return testImpl{
-		newInstrument: func(meter metric.Meter, name string) SyncImpler {
-			return Must(meter).NewInt64Counter(name + ".sum")
-		},
-		getUpdateValue: func() number.Number {
-			for {
-				x := int64(rand.Intn(100))
-				if x != 0 {
-					return number.NewInt64Number(x)
-				}
-			}
-		},
-		operate: func(inst interface{}, ctx context.Context, value number.Number, labels []attribute.KeyValue) {
-			counter := inst.(metric.Int64Counter)
-			counter.Add(ctx, value.AsInt64(), labels...)
-		},
-		newStore: func() interface{} {
-			n := number.NewInt64Number(0)
-			return &n
-		},
-		storeCollect: func(store interface{}, value number.Number, _ time.Time) {
-			store.(*number.Number).AddInt64Atomic(value.AsInt64())
-		},
-		storeExpect: func(store interface{}, value number.Number) {
-			store.(*number.Number).AddInt64Atomic(value.AsInt64())
-		},
-		readStore: func(store interface{}) number.Number {
-			return store.(*number.Number).AsNumberAtomic()
-		},
-		equalValues: int64sEqual,
-	}
-}
-
-func TestStressInt64Counter(t *testing.T) {
-	stressTest(t, intCounterTestImpl())
-}
-
-func floatCounterTestImpl() testImpl {
-	return testImpl{
-		newInstrument: func(meter metric.Meter, name string) SyncImpler {
-			return Must(meter).NewFloat64Counter(name + ".sum")
-		},
-		getUpdateValue: func() number.Number {
-			for {
-				x := rand.Float64()
-				if x != 0 {
-					return number.NewFloat64Number(x)
-				}
-			}
-		},
-		operate: func(inst interface{}, ctx context.Context, value number.Number, labels []attribute.KeyValue) {
-			counter := inst.(metric.Float64Counter)
-			counter.Add(ctx, value.AsFloat64(), labels...)
-		},
-		newStore: func() interface{} {
-			n := number.NewFloat64Number(0.0)
-			return &n
-		},
-		storeCollect: func(store interface{}, value number.Number, _ time.Time) {
-			store.(*number.Number).AddFloat64Atomic(value.AsFloat64())
-		},
-		storeExpect: func(store interface{}, value number.Number) {
-			store.(*number.Number).AddFloat64Atomic(value.AsFloat64())
-		},
-		readStore: func(store interface{}) number.Number {
-			return store.(*number.Number).AsNumberAtomic()
-		},
-		equalValues: float64sEqual,
-	}
-}
-
-func TestStressFloat64Counter(t *testing.T) {
-	stressTest(t, floatCounterTestImpl())
-}
-
-// LastValue
-
-func intLastValueTestImpl() testImpl {
-	return testImpl{
-		newInstrument: func(meter metric.Meter, name string) SyncImpler {
-			return Must(meter).NewInt64Histogram(name + ".lastvalue")
-		},
-		getUpdateValue: func() number.Number {
-			r1 := rand.Int63()
-			return number.NewInt64Number(rand.Int63() - r1)
-		},
-		operate: func(inst interface{}, ctx context.Context, value number.Number, labels []attribute.KeyValue) {
-			histogram := inst.(metric.Int64Histogram)
-			histogram.Record(ctx, value.AsInt64(), labels...)
-		},
-		newStore: func() interface{} {
-			return &lastValueState{
-				raw: number.NewInt64Number(0),
-			}
-		},
-		storeCollect: func(store interface{}, value number.Number, ts time.Time) {
-			gs := store.(*lastValueState)
-
-			if !ts.Before(gs.ts) {
-				gs.ts = ts
-				gs.raw.SetInt64Atomic(value.AsInt64())
-			}
-		},
-		storeExpect: func(store interface{}, value number.Number) {
-			gs := store.(*lastValueState)
-			gs.raw.SetInt64Atomic(value.AsInt64())
-		},
-		readStore: func(store interface{}) number.Number {
-			gs := store.(*lastValueState)
-			return gs.raw.AsNumberAtomic()
-		},
-		equalValues: int64sEqual,
-	}
-}
-
-func TestStressInt64LastValue(t *testing.T) {
-	stressTest(t, intLastValueTestImpl())
-}
-
-func floatLastValueTestImpl() testImpl {
-	return testImpl{
-		newInstrument: func(meter metric.Meter, name string) SyncImpler {
-			return Must(meter).NewFloat64Histogram(name + ".lastvalue")
-		},
-		getUpdateValue: func() number.Number {
-			return number.NewFloat64Number((-0.5 + rand.Float64()) * 100000)
-		},
-		operate: func(inst interface{}, ctx context.Context, value number.Number, labels []attribute.KeyValue) {
-			histogram := inst.(metric.Float64Histogram)
-			histogram.Record(ctx, value.AsFloat64(), labels...)
-		},
-		newStore: func() interface{} {
-			return &lastValueState{
-				raw: number.NewFloat64Number(0),
-			}
-		},
-		storeCollect: func(store interface{}, value number.Number, ts time.Time) {
-			gs := store.(*lastValueState)
-
-			if !ts.Before(gs.ts) {
-				gs.ts = ts
-				gs.raw.SetFloat64Atomic(value.AsFloat64())
-			}
-		},
-		storeExpect: func(store interface{}, value number.Number) {
-			gs := store.(*lastValueState)
-			gs.raw.SetFloat64Atomic(value.AsFloat64())
-		},
-		readStore: func(store interface{}) number.Number {
-			gs := store.(*lastValueState)
-			return gs.raw.AsNumberAtomic()
-		},
-		equalValues: float64sEqual,
-	}
-}
-
-func TestStressFloat64LastValue(t *testing.T) {
-	stressTest(t, floatLastValueTestImpl())
-}