diff --git a/3d-viewer/3d_canvas/eda_3d_canvas.cpp b/3d-viewer/3d_canvas/eda_3d_canvas.cpp
index 56e3a23e2d..ad18a048f2 100644
--- a/3d-viewer/3d_canvas/eda_3d_canvas.cpp
+++ b/3d-viewer/3d_canvas/eda_3d_canvas.cpp
@@ -738,13 +738,11 @@ void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent& event )
         RAY                mouseRay = getRayAtCurrentMousePosition();
         BOARD_ITEM*        rollOverItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
 
-        auto printNetInfo =
-                []( BOARD_CONNECTED_ITEM* aItem )
-                {
-                    return wxString::Format( _( "Net %s\tNet class %s" ),
-                                             aItem->GetNet()->GetNetname(),
-                                             aItem->GetNet()->GetNetClass()->GetName() );
-                };
+        auto printNetInfo = []( BOARD_CONNECTED_ITEM* aItem )
+        {
+            return wxString::Format( _( "Net %s\tNet class %s" ), aItem->GetNet()->GetNetname(),
+                                     aItem->GetNet()->GetNetClass()->GetHumanReadableName() );
+        };
 
         if( rollOverItem )
         {
diff --git a/common/netclass.cpp b/common/netclass.cpp
index 8e59adf1e4..c35a12e9c5 100644
--- a/common/netclass.cpp
+++ b/common/netclass.cpp
@@ -283,7 +283,7 @@ bool NETCLASS::ContainsNetclassWithName( const wxString& netclass ) const
 }
 
 
-const wxString NETCLASS::GetName() const
+const wxString NETCLASS::GetHumanReadableName() const
 {
     if( m_constituents.size() == 1 )
         return m_Name;
@@ -311,19 +311,19 @@ const wxString NETCLASS::GetName() const
 }
 
 
-const wxString NETCLASS::GetVariableSubstitutionName() const
+const wxString NETCLASS::GetName() const
 {
     if( m_constituents.size() == 1 )
         return m_Name;
 
     wxASSERT( m_constituents.size() >= 2 );
 
-    wxString name = m_constituents[0]->GetName();
+    wxString name = m_constituents[0]->m_Name;
 
     for( std::size_t i = 1; i < m_constituents.size(); ++i )
     {
         name += ",";
-        name += m_constituents[i]->GetName();
+        name += m_constituents[i]->m_Name;
     }
 
     return name;
diff --git a/common/project/net_settings.cpp b/common/project/net_settings.cpp
index afc22c6d11..8c55cd0a8d 100644
--- a/common/project/net_settings.cpp
+++ b/common/project/net_settings.cpp
@@ -600,8 +600,7 @@ void NET_SETTINGS::ClearCacheForNet( const wxString& netName )
 {
     if( m_effectiveNetclassCache.count( netName ) )
     {
-        wxString compositeNetclassName =
-                m_effectiveNetclassCache[netName]->GetVariableSubstitutionName();
+        wxString compositeNetclassName = m_effectiveNetclassCache[netName]->GetName();
         m_compositeNetClasses.erase( compositeNetclassName );
         m_effectiveNetclassCache.erase( netName );
     }
@@ -764,7 +763,7 @@ std::shared_ptr<NETCLASS> NET_SETTINGS::GetEffectiveNetClass( const wxString& aN
     {
         effectiveNetclass->SetConstituentNetclasses( std::move( netclassPtrs ) );
 
-        m_compositeNetClasses[effectiveNetclass->GetVariableSubstitutionName()] = effectiveNetclass;
+        m_compositeNetClasses[effectiveNetclass->GetName()] = effectiveNetclass;
         m_effectiveNetclassCache[aNetName] = effectiveNetclass;
 
         return effectiveNetclass;
diff --git a/eeschema/sch_bus_entry.cpp b/eeschema/sch_bus_entry.cpp
index 95ced61d2d..985c633e94 100644
--- a/eeschema/sch_bus_entry.cpp
+++ b/eeschema/sch_bus_entry.cpp
@@ -539,7 +539,8 @@ void SCH_BUS_ENTRY_BASE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame,
         conn->AppendInfoToMsgPanel( aList );
 
         if( !conn->IsBus() )
-            aList.emplace_back( _( "Resolved Netclass" ), GetEffectiveNetClass()->GetName() );
+            aList.emplace_back( _( "Resolved Netclass" ),
+                                GetEffectiveNetClass()->GetHumanReadableName() );
     }
 }
 
diff --git a/eeschema/sch_junction.cpp b/eeschema/sch_junction.cpp
index 2e61841712..e228d5134a 100644
--- a/eeschema/sch_junction.cpp
+++ b/eeschema/sch_junction.cpp
@@ -317,7 +317,7 @@ void SCH_JUNCTION::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANE
         if( !conn->IsBus() )
         {
             aList.emplace_back( _( "Resolved Netclass" ),
-                                UnescapeString( GetEffectiveNetClass()->GetName() ) );
+                                UnescapeString( GetEffectiveNetClass()->GetHumanReadableName() ) );
         }
     }
 }
diff --git a/eeschema/sch_label.cpp b/eeschema/sch_label.cpp
index 88ea62ff04..68f26cc977 100644
--- a/eeschema/sch_label.cpp
+++ b/eeschema/sch_label.cpp
@@ -754,7 +754,7 @@ bool SCH_LABEL_BASE::ResolveTextVar( const SCH_SHEET_PATH* aPath, wxString* toke
         *token = wxEmptyString;
 
         if( connection )
-            *token = GetEffectiveNetClass()->GetVariableSubstitutionName();
+            *token = GetEffectiveNetClass()->GetName();
 
         return true;
     }
@@ -1218,7 +1218,7 @@ void SCH_LABEL_BASE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PA
         if( !conn->IsBus() )
         {
             aList.emplace_back( _( "Resolved Netclass" ),
-                                UnescapeString( GetEffectiveNetClass()->GetName() ) );
+                                UnescapeString( GetEffectiveNetClass()->GetHumanReadableName() ) );
         }
     }
 }
@@ -1324,9 +1324,9 @@ void SCH_LABEL_BASE::Plot( PLOTTER* aPlotter, bool aBackground, const SCH_PLOT_O
                 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ), _( "Net" ),
                                                            connection->Name() ) );
 
-                properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
-                                                           _( "Resolved netclass" ),
-                                                           GetEffectiveNetClass()->GetName() ) );
+                properties.emplace_back(
+                        wxString::Format( wxT( "!%s = %s" ), _( "Resolved netclass" ),
+                                          GetEffectiveNetClass()->GetHumanReadableName() ) );
             }
 
             for( const SCH_FIELD& field : GetFields() )
diff --git a/eeschema/sch_line.cpp b/eeschema/sch_line.cpp
index 9be6c2af44..96cc050574 100644
--- a/eeschema/sch_line.cpp
+++ b/eeschema/sch_line.cpp
@@ -895,9 +895,9 @@ void SCH_LINE::Plot( PLOTTER* aPlotter, bool aBackground, const SCH_PLOT_OPTS& a
                                                            _( "Net" ),
                                                            connection->Name() ) );
 
-                properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
-                                                           _( "Resolved netclass" ),
-                                                           GetEffectiveNetClass()->GetName() ) );
+                properties.emplace_back(
+                        wxString::Format( wxT( "!%s = %s" ), _( "Resolved netclass" ),
+                                          GetEffectiveNetClass()->GetHumanReadableName() ) );
             }
         }
         else if( GetLayer() == LAYER_BUS )
@@ -955,7 +955,7 @@ void SCH_LINE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_IT
         if( !conn->IsBus() )
         {
             aList.emplace_back( _( "Resolved Netclass" ),
-                                UnescapeString( GetEffectiveNetClass()->GetName() ) );
+                                UnescapeString( GetEffectiveNetClass()->GetHumanReadableName() ) );
         }
     }
 }
diff --git a/include/netclass.h b/include/netclass.h
index e193490837..0a2bbed1fa 100644
--- a/include/netclass.h
+++ b/include/netclass.h
@@ -80,10 +80,6 @@ public:
     /// @brief Sets the netclasses which make up this netclass
     void SetConstituentNetclasses( std::vector<NETCLASS*>&& constituents );
 
-    /// @brief Gets the name of this (maybe aggregate) netclass in a format for label variable
-    ///        substitutions
-    const wxString GetVariableSubstitutionName() const;
-
     /// @brief Determines if the given netclass name is a constituent of this (maybe aggregate)
     /// netclass
     bool ContainsNetclassWithName( const wxString& netclass ) const;
@@ -91,9 +87,9 @@ public:
     /// @ brief Determines if this is marked as the default netclass
     bool IsDefault() const { return m_isDefault; }
 
-    /// @brief Gets the consolidated name of this netclass (which may be an aggregate)
-    const wxString GetName() const;
-    void           SetName( const wxString& aName )
+    /// @brief Set the name of this netclass. Only relevant for root netclasses (i.e. those which
+    /// are not an aggregate)
+    void SetName( const wxString& aName )
     {
         m_Name = aName;
 
@@ -101,6 +97,16 @@ public:
             m_isDefault = true;
     }
 
+    /// @brief Gets the name of this (maybe aggregate) netclass in a format for internal usage or
+    /// for export to external tools / netlists. WARNING: Do not use this to display a netclass
+    /// name to a user. Use GetHumanReadableName instead.
+    const wxString GetName() const;
+
+    /// @brief Gets the consolidated name of this netclass (which may be an aggregate). This is
+    /// intended for display to users (e.g. in infobars or messages). WARNING: Do not use this
+    /// to compare equivalence, or to export to other tools)
+    const wxString GetHumanReadableName() const;
+
     const wxString& GetDescription() const  { return m_Description; }
     void  SetDescription( const wxString& aDesc ) { m_Description = aDesc; }
 
diff --git a/pcbnew/board_connected_item.cpp b/pcbnew/board_connected_item.cpp
index f461ea0f95..41f1066130 100644
--- a/pcbnew/board_connected_item.cpp
+++ b/pcbnew/board_connected_item.cpp
@@ -113,7 +113,7 @@ wxString BOARD_CONNECTED_ITEM::GetNetClassName() const
 
 wxString BOARD_CONNECTED_ITEM::GetNetClassVariableSubstitutionName() const
 {
-    return GetEffectiveNetClass()->GetVariableSubstitutionName();
+    return GetEffectiveNetClass()->GetName();
 }
 
 
diff --git a/pcbnew/dialogs/dialog_global_edit_teardrops.cpp b/pcbnew/dialogs/dialog_global_edit_teardrops.cpp
index 68bc8e2356..418321b5ea 100644
--- a/pcbnew/dialogs/dialog_global_edit_teardrops.cpp
+++ b/pcbnew/dialogs/dialog_global_edit_teardrops.cpp
@@ -264,8 +264,7 @@ bool DIALOG_GLOBAL_EDIT_TEARDROPS::TransferDataToWindow()
     if( g_filterByNetclass && m_netclassFilter->SetStringSelection( g_netclassFilter ) )
         m_netclassFilterOpt->SetValue( true );
     else if( item )
-        m_netclassFilter->SetStringSelection(
-                item->GetNet()->GetNetClass()->GetVariableSubstitutionName() );
+        m_netclassFilter->SetStringSelection( item->GetNet()->GetNetClass()->GetName() );
 
     if( g_filterByNet && m_brd->FindNet( g_netFilter ) != nullptr )
     {
diff --git a/pcbnew/dialogs/dialog_global_edit_tracks_and_vias.cpp b/pcbnew/dialogs/dialog_global_edit_tracks_and_vias.cpp
index e5f54236fc..7fb8420e4d 100644
--- a/pcbnew/dialogs/dialog_global_edit_tracks_and_vias.cpp
+++ b/pcbnew/dialogs/dialog_global_edit_tracks_and_vias.cpp
@@ -198,8 +198,7 @@ bool DIALOG_GLOBAL_EDIT_TRACKS_AND_VIAS::TransferDataToWindow()
     }
     else if( item )
     {
-        m_netclassFilter->SetStringSelection(
-                item->GetNet()->GetNetClass()->GetVariableSubstitutionName() );
+        m_netclassFilter->SetStringSelection( item->GetNet()->GetNetClass()->GetName() );
     }
 
     if( g_filterByNet && m_brd->FindNet( g_netFilter ) != nullptr )
diff --git a/pcbnew/drc/drc_engine.cpp b/pcbnew/drc/drc_engine.cpp
index 34304d3b7c..c4f923985b 100644
--- a/pcbnew/drc/drc_engine.cpp
+++ b/pcbnew/drc/drc_engine.cpp
@@ -241,173 +241,170 @@ void DRC_ENGINE::loadImplicitRules()
     std::vector<std::shared_ptr<DRC_RULE>> netclassClearanceRules;
     std::vector<std::shared_ptr<DRC_RULE>> netclassItemSpecificRules;
 
-    auto makeNetclassRules =
-            [&]( const std::shared_ptr<NETCLASS>& nc, bool isDefault )
+    auto makeNetclassRules = [&]( const std::shared_ptr<NETCLASS>& nc, bool isDefault )
+    {
+        wxString ncName = nc->GetName();
+        wxString expr;
+
+        ncName.Replace( "'", "\\'" );
+
+        if( nc->HasClearance() )
+        {
+            std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
+            netclassRule->m_Name = wxString::Format(
+                    _( "netclass '%s'" ), nc->GetClearanceParent()->GetHumanReadableName() );
+            netclassRule->m_Implicit = true;
+
+            expr = wxString::Format( wxT( "A.NetClass == '%s'" ), ncName );
+            netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
+            netclassClearanceRules.push_back( netclassRule );
+
+            DRC_CONSTRAINT constraint( CLEARANCE_CONSTRAINT );
+            constraint.Value().SetMin( nc->GetClearance() );
+            netclassRule->AddConstraint( constraint );
+        }
+
+        if( nc->HasTrackWidth() )
+        {
+            std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
+            netclassRule->m_Name = wxString::Format(
+                    _( "netclass '%s'" ), nc->GetTrackWidthParent()->GetHumanReadableName() );
+            netclassRule->m_Implicit = true;
+
+            expr = wxString::Format( wxT( "A.NetClass == '%s'" ), ncName );
+            netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
+            netclassClearanceRules.push_back( netclassRule );
+
+            DRC_CONSTRAINT constraint( TRACK_WIDTH_CONSTRAINT );
+            constraint.Value().SetMin( bds.m_TrackMinWidth );
+            constraint.Value().SetOpt( nc->GetTrackWidth() );
+            netclassRule->AddConstraint( constraint );
+        }
+
+        if( nc->HasDiffPairWidth() )
+        {
+            std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
+            netclassRule->m_Name =
+                    wxString::Format( _( "netclass '%s' (diff pair)" ),
+                                      nc->GetDiffPairWidthParent()->GetHumanReadableName() );
+            netclassRule->m_Implicit = true;
+
+            expr = wxString::Format( wxT( "A.NetClass == '%s' && A.inDiffPair('*')" ), ncName );
+            netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
+            netclassItemSpecificRules.push_back( netclassRule );
+
+            DRC_CONSTRAINT constraint( TRACK_WIDTH_CONSTRAINT );
+            constraint.Value().SetMin( bds.m_TrackMinWidth );
+            constraint.Value().SetOpt( nc->GetDiffPairWidth() );
+            netclassRule->AddConstraint( constraint );
+        }
+
+        if( nc->HasDiffPairGap() )
+        {
+            std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
+            netclassRule->m_Name =
+                    wxString::Format( _( "netclass '%s' (diff pair)" ),
+                                      nc->GetDiffPairGapParent()->GetHumanReadableName() );
+            netclassRule->m_Implicit = true;
+
+            expr = wxString::Format( wxT( "A.NetClass == '%s'" ), ncName );
+            netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
+            netclassItemSpecificRules.push_back( netclassRule );
+
+            DRC_CONSTRAINT constraint( DIFF_PAIR_GAP_CONSTRAINT );
+            constraint.Value().SetMin( bds.m_MinClearance );
+            constraint.Value().SetOpt( nc->GetDiffPairGap() );
+            netclassRule->AddConstraint( constraint );
+
+            // A narrower diffpair gap overrides the netclass min clearance
+            if( nc->GetDiffPairGap() < nc->GetClearance() )
             {
-                wxString ncName = nc->GetVariableSubstitutionName();
-                wxString friendlyName = nc->GetName();
-                wxString expr;
+                netclassRule = std::make_shared<DRC_RULE>();
+                netclassRule->m_Name =
+                        wxString::Format( _( "netclass '%s' (diff pair)" ),
+                                          nc->GetDiffPairGapParent()->GetHumanReadableName() );
+                netclassRule->m_Implicit = true;
 
-                ncName.Replace( "'", "\\'" );
+                expr = wxString::Format( wxT( "A.NetClass == '%s' && AB.isCoupledDiffPair()" ),
+                                         ncName );
+                netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
+                netclassItemSpecificRules.push_back( netclassRule );
 
-                if( nc->HasClearance())
-                {
-                    std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
-                    netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ),
-                                                        nc->GetClearanceParent()->GetName() );
-                    netclassRule->m_Implicit = true;
+                DRC_CONSTRAINT min_clearanceConstraint( CLEARANCE_CONSTRAINT );
+                min_clearanceConstraint.Value().SetMin( nc->GetDiffPairGap() );
+                netclassRule->AddConstraint( min_clearanceConstraint );
+            }
+        }
 
-                    expr = wxString::Format( wxT( "A.NetClass == '%s'" ), ncName );
-                    netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
-                    netclassClearanceRules.push_back( netclassRule );
+        if( nc->HasViaDiameter() )
+        {
+            std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
+            netclassRule->m_Name = wxString::Format(
+                    _( "netclass '%s'" ), nc->GetViaDiameterParent()->GetHumanReadableName() );
+            netclassRule->m_Implicit = true;
 
-                    DRC_CONSTRAINT constraint( CLEARANCE_CONSTRAINT );
-                    constraint.Value().SetMin( nc->GetClearance() );
-                    netclassRule->AddConstraint( constraint );
-                }
+            expr = wxString::Format( wxT( "A.NetClass == '%s' && A.Via_Type != 'Micro'" ), ncName );
+            netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
+            netclassItemSpecificRules.push_back( netclassRule );
 
-                if( nc->HasTrackWidth() )
-                {
-                    std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
-                    netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ),
-                                                        nc->GetTrackWidthParent()->GetName() );
-                    netclassRule->m_Implicit = true;
+            DRC_CONSTRAINT constraint( VIA_DIAMETER_CONSTRAINT );
+            constraint.Value().SetMin( bds.m_ViasMinSize );
+            constraint.Value().SetOpt( nc->GetViaDiameter() );
+            netclassRule->AddConstraint( constraint );
+        }
 
-                    expr = wxString::Format( wxT( "A.NetClass == '%s'" ), ncName );
-                    netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
-                    netclassClearanceRules.push_back( netclassRule );
+        if( nc->HasViaDrill() )
+        {
+            std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
+            netclassRule->m_Name = wxString::Format(
+                    _( "netclass '%s'" ), nc->GetViaDrillParent()->GetHumanReadableName() );
+            netclassRule->m_Implicit = true;
 
-                    DRC_CONSTRAINT constraint( TRACK_WIDTH_CONSTRAINT );
-                    constraint.Value().SetMin( bds.m_TrackMinWidth );
-                    constraint.Value().SetOpt( nc->GetTrackWidth() );
-                    netclassRule->AddConstraint( constraint );
-                }
+            expr = wxString::Format( wxT( "A.NetClass == '%s' && A.Via_Type != 'Micro'" ), ncName );
+            netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
+            netclassItemSpecificRules.push_back( netclassRule );
 
-                if( nc->HasDiffPairWidth() )
-                {
-                    std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
-                    netclassRule->m_Name = wxString::Format( _( "netclass '%s' (diff pair)" ),
-                                                        nc->GetDiffPairWidthParent()->GetName() );
-                    netclassRule->m_Implicit = true;
+            DRC_CONSTRAINT constraint( HOLE_SIZE_CONSTRAINT );
+            constraint.Value().SetMin( bds.m_MinThroughDrill );
+            constraint.Value().SetOpt( nc->GetViaDrill() );
+            netclassRule->AddConstraint( constraint );
+        }
 
-                    expr = wxString::Format( wxT( "A.NetClass == '%s' && A.inDiffPair('*')" ),
-                                             ncName );
-                    netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
-                    netclassItemSpecificRules.push_back( netclassRule );
+        if( nc->HasuViaDiameter() )
+        {
+            std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
+            netclassRule->m_Name =
+                    wxString::Format( _( "netclass '%s' (uvia)" ),
+                                      nc->GetuViaDiameterParent()->GetHumanReadableName() );
+            netclassRule->m_Implicit = true;
 
-                    DRC_CONSTRAINT constraint( TRACK_WIDTH_CONSTRAINT );
-                    constraint.Value().SetMin( bds.m_TrackMinWidth );
-                    constraint.Value().SetOpt( nc->GetDiffPairWidth() );
-                    netclassRule->AddConstraint( constraint );
-                }
+            expr = wxString::Format( wxT( "A.NetClass == '%s' && A.Via_Type == 'Micro'" ), ncName );
+            netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
+            netclassItemSpecificRules.push_back( netclassRule );
 
-                if( nc->HasDiffPairGap() )
-                {
-                    std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
-                    netclassRule->m_Name = wxString::Format( _( "netclass '%s' (diff pair)" ),
-                                                        nc->GetDiffPairGapParent()->GetName() );
-                    netclassRule->m_Implicit = true;
+            DRC_CONSTRAINT constraint( VIA_DIAMETER_CONSTRAINT );
+            constraint.Value().SetMin( bds.m_MicroViasMinSize );
+            constraint.Value().SetMin( nc->GetuViaDiameter() );
+            netclassRule->AddConstraint( constraint );
+        }
 
-                    expr = wxString::Format( wxT( "A.NetClass == '%s'" ), ncName );
-                    netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
-                    netclassItemSpecificRules.push_back( netclassRule );
+        if( nc->HasuViaDrill() )
+        {
+            std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
+            netclassRule->m_Name = wxString::Format(
+                    _( "netclass '%s' (uvia)" ), nc->GetuViaDrillParent()->GetHumanReadableName() );
+            netclassRule->m_Implicit = true;
 
-                    DRC_CONSTRAINT constraint( DIFF_PAIR_GAP_CONSTRAINT );
-                    constraint.Value().SetMin( bds.m_MinClearance );
-                    constraint.Value().SetOpt( nc->GetDiffPairGap() );
-                    netclassRule->AddConstraint( constraint );
+            expr = wxString::Format( wxT( "A.NetClass == '%s' && A.Via_Type == 'Micro'" ), ncName );
+            netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
+            netclassItemSpecificRules.push_back( netclassRule );
 
-                    // A narrower diffpair gap overrides the netclass min clearance
-                    if( nc->GetDiffPairGap() < nc->GetClearance() )
-                    {
-                        netclassRule = std::make_shared<DRC_RULE>();
-                        netclassRule->m_Name = wxString::Format( _( "netclass '%s' (diff pair)" ),
-                                                        nc->GetDiffPairGapParent()->GetName() );
-                        netclassRule->m_Implicit = true;
-
-                        expr = wxString::Format( wxT( "A.NetClass == '%s' && AB.isCoupledDiffPair()" ),
-                                                 ncName );
-                        netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
-                        netclassItemSpecificRules.push_back( netclassRule );
-
-                        DRC_CONSTRAINT min_clearanceConstraint( CLEARANCE_CONSTRAINT );
-                        min_clearanceConstraint.Value().SetMin( nc->GetDiffPairGap() );
-                        netclassRule->AddConstraint( min_clearanceConstraint );
-                    }
-                }
-
-                if( nc->HasViaDiameter() )
-                {
-                    std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
-                    netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ),
-                                                        nc->GetViaDiameterParent()->GetName() );
-                    netclassRule->m_Implicit = true;
-
-                    expr = wxString::Format( wxT( "A.NetClass == '%s' && A.Via_Type != 'Micro'" ),
-                                             ncName );
-                    netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
-                    netclassItemSpecificRules.push_back( netclassRule );
-
-                    DRC_CONSTRAINT constraint( VIA_DIAMETER_CONSTRAINT );
-                    constraint.Value().SetMin( bds.m_ViasMinSize );
-                    constraint.Value().SetOpt( nc->GetViaDiameter() );
-                    netclassRule->AddConstraint( constraint );
-                }
-
-                if( nc->HasViaDrill() )
-                {
-                    std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
-                    netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ),
-                                                        nc->GetViaDrillParent()->GetName() );
-                    netclassRule->m_Implicit = true;
-
-                    expr = wxString::Format( wxT( "A.NetClass == '%s' && A.Via_Type != 'Micro'" ),
-                                             ncName );
-                    netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
-                    netclassItemSpecificRules.push_back( netclassRule );
-
-                    DRC_CONSTRAINT constraint( HOLE_SIZE_CONSTRAINT );
-                    constraint.Value().SetMin( bds.m_MinThroughDrill );
-                    constraint.Value().SetOpt( nc->GetViaDrill() );
-                    netclassRule->AddConstraint( constraint );
-                }
-
-                if( nc->HasuViaDiameter() )
-                {
-                    std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
-                    netclassRule->m_Name = wxString::Format( _( "netclass '%s' (uvia)" ),
-                                                        nc->GetuViaDiameterParent()->GetName() );
-                    netclassRule->m_Implicit = true;
-
-                    expr = wxString::Format( wxT( "A.NetClass == '%s' && A.Via_Type == 'Micro'" ),
-                                             ncName );
-                    netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
-                    netclassItemSpecificRules.push_back( netclassRule );
-
-                    DRC_CONSTRAINT constraint( VIA_DIAMETER_CONSTRAINT );
-                    constraint.Value().SetMin( bds.m_MicroViasMinSize );
-                    constraint.Value().SetMin( nc->GetuViaDiameter() );
-                    netclassRule->AddConstraint( constraint );
-                }
-
-                if( nc->HasuViaDrill() )
-                {
-                    std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
-                    netclassRule->m_Name = wxString::Format( _( "netclass '%s' (uvia)" ),
-                                                        nc->GetuViaDrillParent()->GetName() );
-                    netclassRule->m_Implicit = true;
-
-                    expr = wxString::Format( wxT( "A.NetClass == '%s' && A.Via_Type == 'Micro'" ),
-                                             ncName );
-                    netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
-                    netclassItemSpecificRules.push_back( netclassRule );
-
-                    DRC_CONSTRAINT constraint( HOLE_SIZE_CONSTRAINT );
-                    constraint.Value().SetMin( bds.m_MicroViasMinDrill );
-                    constraint.Value().SetOpt( nc->GetuViaDrill() );
-                    netclassRule->AddConstraint( constraint );
-                }
-            };
+            DRC_CONSTRAINT constraint( HOLE_SIZE_CONSTRAINT );
+            constraint.Value().SetMin( bds.m_MicroViasMinDrill );
+            constraint.Value().SetOpt( nc->GetuViaDrill() );
+            netclassRule->AddConstraint( constraint );
+        }
+    };
 
     m_board->SynchronizeNetsAndNetClasses( false );
     makeNetclassRules( bds.m_NetSettings->GetDefaultNetclass(), true );
diff --git a/pcbnew/generators/pcb_tuning_pattern.cpp b/pcbnew/generators/pcb_tuning_pattern.cpp
index add47dd544..5ef958c462 100644
--- a/pcbnew/generators/pcb_tuning_pattern.cpp
+++ b/pcbnew/generators/pcb_tuning_pattern.cpp
@@ -2131,7 +2131,8 @@ void PCB_TUNING_PATTERN::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame,
     }
 
     if( netclass )
-        aList.emplace_back( _( "Resolved Netclass" ), UnescapeString( netclass->GetName() ) );
+        aList.emplace_back( _( "Resolved Netclass" ),
+                            UnescapeString( netclass->GetHumanReadableName() ) );
 
     aList.emplace_back( _( "Layer" ), layerMaskDescribe() );
 
diff --git a/pcbnew/pad.cpp b/pcbnew/pad.cpp
index e0255cb13d..efb8a8d2a2 100644
--- a/pcbnew/pad.cpp
+++ b/pcbnew/pad.cpp
@@ -1293,7 +1293,7 @@ void PAD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>&
         aList.emplace_back( _( "Net" ), UnescapeString( GetNetname() ) );
 
         aList.emplace_back( _( "Resolved Netclass" ),
-                            UnescapeString( GetEffectiveNetClass()->GetName() ) );
+                            UnescapeString( GetEffectiveNetClass()->GetHumanReadableName() ) );
 
         if( IsLocked() )
             aList.emplace_back( _( "Status" ), _( "Locked" ) );
diff --git a/pcbnew/pcb_track.cpp b/pcbnew/pcb_track.cpp
index f055f78646..324ce8d427 100644
--- a/pcbnew/pcb_track.cpp
+++ b/pcbnew/pcb_track.cpp
@@ -1665,7 +1665,7 @@ void PCB_TRACK::GetMsgPanelInfoBase_Common( EDA_DRAW_FRAME* aFrame,
     aList.emplace_back( _( "Net" ), UnescapeString( GetNetname() ) );
 
     aList.emplace_back( _( "Resolved Netclass" ),
-                        UnescapeString( GetEffectiveNetClass()->GetName() ) );
+                        UnescapeString( GetEffectiveNetClass()->GetHumanReadableName() ) );
 
 #if 0   // Enable for debugging
     if( GetBoard() )
diff --git a/pcbnew/pcbexpr_evaluator.cpp b/pcbnew/pcbexpr_evaluator.cpp
index 4d7b5a703d..18baa1bbfe 100644
--- a/pcbnew/pcbexpr_evaluator.cpp
+++ b/pcbnew/pcbexpr_evaluator.cpp
@@ -155,7 +155,7 @@ public:
     const wxString& AsString() const override
     {
         const_cast<PCBEXPR_NETCLASS_VALUE*>( this )->Set(
-                m_item->GetEffectiveNetClass()->GetVariableSubstitutionName() );
+                m_item->GetEffectiveNetClass()->GetName() );
         return LIBEVAL::VALUE::AsString();
     }
 
diff --git a/pcbnew/router/router_tool.cpp b/pcbnew/router/router_tool.cpp
index 4ccac07dc3..a416a00762 100644
--- a/pcbnew/router/router_tool.cpp
+++ b/pcbnew/router/router_tool.cpp
@@ -2685,9 +2685,10 @@ void ROUTER_TOOL::UpdateMessagePanel()
             NETCLASS* netclassB = netB->GetNetClass();
 
             if( *netclassA == *netclassB )
-                netclass = netclassA->GetName();
+                netclass = netclassA->GetHumanReadableName();
             else
-                netclass = netclassA->GetName() + wxT( ", " ) + netclassB->GetName();
+                netclass = netclassA->GetHumanReadableName() + wxT( ", " )
+                           + netclassB->GetHumanReadableName();
 
             secondary = wxString::Format( _( "Resolved Netclass: %s" ),
                                           UnescapeString( netclass ) );
@@ -2699,8 +2700,9 @@ void ROUTER_TOOL::UpdateMessagePanel()
             description = wxString::Format( _( "Routing Track: %s" ),
                                             net->GetNetname() );
 
-            secondary = wxString::Format( _( "Resolved Netclass: %s" ),
-                                          UnescapeString( net->GetNetClass()->GetName() ) );
+            secondary = wxString::Format(
+                    _( "Resolved Netclass: %s" ),
+                    UnescapeString( net->GetNetClass()->GetHumanReadableName() ) );
         }
         else
         {
diff --git a/pcbnew/specctra_import_export/specctra_export.cpp b/pcbnew/specctra_import_export/specctra_export.cpp
index 3603c417b8..7848fefa5d 100644
--- a/pcbnew/specctra_import_export/specctra_export.cpp
+++ b/pcbnew/specctra_import_export/specctra_export.cpp
@@ -1737,7 +1737,7 @@ void SPECCTRA_DB::exportNETCLASS( const std::shared_ptr<NETCLASS>& aNetClass, BO
 
     for( NETINFO_ITEM* net : aBoard->GetNetInfo() )
     {
-        if( net->GetNetClass()->GetVariableSubstitutionName() == clazz->m_class_id )
+        if( net->GetNetClass()->GetName() == clazz->m_class_id )
             clazz->m_net_ids.push_back( TO_UTF8( net->GetNetname() ) );
     }
 
diff --git a/pcbnew/tools/board_inspection_tool.cpp b/pcbnew/tools/board_inspection_tool.cpp
index 63c34bee6b..0e4032b71c 100644
--- a/pcbnew/tools/board_inspection_tool.cpp
+++ b/pcbnew/tools/board_inspection_tool.cpp
@@ -197,8 +197,9 @@ wxString BOARD_INSPECTION_TOOL::getItemDescription( BOARD_ITEM* aItem )
     {
         BOARD_CONNECTED_ITEM* cItem = static_cast<BOARD_CONNECTED_ITEM*>( aItem );
 
-        msg += wxS( " " ) + wxString::Format( _( "[netclass %s]" ),
-                                              cItem->GetEffectiveNetClass()->GetName() );
+        msg += wxS( " " )
+               + wxString::Format( _( "[netclass %s]" ),
+                                   cItem->GetEffectiveNetClass()->GetHumanReadableName() );
     }
 
     return msg;
diff --git a/pcbnew/tools/pcb_control.cpp b/pcbnew/tools/pcb_control.cpp
index 6df8e7ec83..e2f23e647d 100644
--- a/pcbnew/tools/pcb_control.cpp
+++ b/pcbnew/tools/pcb_control.cpp
@@ -1855,8 +1855,8 @@ int PCB_CONTROL::UpdateMessagePanel( const TOOL_EVENT& aEvent )
                     if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
                     {
                         netNames.insert( UnescapeString( bci->GetNetname() ) );
-                        netClasses.insert(
-                                UnescapeString( bci->GetEffectiveNetClass()->GetName() ) );
+                        netClasses.insert( UnescapeString(
+                                bci->GetEffectiveNetClass()->GetHumanReadableName() ) );
 
                         if( netNames.size() > 1 && netClasses.size() > 1 )
                             break;
diff --git a/pcbnew/widgets/pcb_net_inspector_panel_data_model.h b/pcbnew/widgets/pcb_net_inspector_panel_data_model.h
index 1d69af2e7f..6375e7c737 100644
--- a/pcbnew/widgets/pcb_net_inspector_panel_data_model.h
+++ b/pcbnew/widgets/pcb_net_inspector_panel_data_model.h
@@ -53,7 +53,7 @@ public:
     {
         wxASSERT( aNet );
         m_net_name = UnescapeString( aNet->GetNetname() );
-        m_net_class = UnescapeString( aNet->GetNetClass()->GetName() );
+        m_net_class = UnescapeString( aNet->GetNetClass()->GetHumanReadableName() );
         m_column_changed.resize( COLUMN_LAST_STATIC_COL + 1 + 2, 0 );
     }
 
diff --git a/pcbnew/zone.cpp b/pcbnew/zone.cpp
index 949e63c039..671de58b2e 100644
--- a/pcbnew/zone.cpp
+++ b/pcbnew/zone.cpp
@@ -805,7 +805,7 @@ void ZONE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>&
             aList.emplace_back( _( "Net" ), UnescapeString( GetNetname() ) );
 
             aList.emplace_back( _( "Resolved Netclass" ),
-                                UnescapeString( GetEffectiveNetClass()->GetName() ) );
+                                UnescapeString( GetEffectiveNetClass()->GetHumanReadableName() ) );
         }
 
         // Display priority level
diff --git a/qa/tests/eeschema/test_sch_netclass.cpp b/qa/tests/eeschema/test_sch_netclass.cpp
index 9240c7a117..2336e75c66 100644
--- a/qa/tests/eeschema/test_sch_netclass.cpp
+++ b/qa/tests/eeschema/test_sch_netclass.cpp
@@ -59,29 +59,28 @@ BOOST_AUTO_TEST_CASE( TestMultiNetclasses )
     std::shared_ptr<NET_SETTINGS>& netSettings = m_schematic.Prj().GetProjectFile().m_NetSettings;
 
     std::shared_ptr<NETCLASS> nc = netSettings->GetEffectiveNetClass( "/BUS.SIGNAL" );
-    BOOST_CHECK_EQUAL( nc->GetVariableSubstitutionName(), "CLASS2,CLASS1,Default" );
+    BOOST_CHECK_EQUAL( nc->GetName(), "CLASS2,CLASS1,Default" );
 
     nc = netSettings->GetEffectiveNetClass( "/BUS.A0" );
-    BOOST_CHECK_EQUAL( nc->GetVariableSubstitutionName(), "CLASS1,CLASS3,Default" );
+    BOOST_CHECK_EQUAL( nc->GetName(), "CLASS1,CLASS3,Default" );
 
     nc = netSettings->GetEffectiveNetClass( "/BUS.A1" );
-    BOOST_CHECK_EQUAL( nc->GetVariableSubstitutionName(), "CLASS1,Default" );
+    BOOST_CHECK_EQUAL( nc->GetName(), "CLASS1,Default" );
 
     nc = netSettings->GetEffectiveNetClass( "/BUS.A2" );
-    wxString name = nc->GetName();
-    BOOST_CHECK_EQUAL( nc->GetVariableSubstitutionName(), "CLASS1,CLASS4,Default" );
+    BOOST_CHECK_EQUAL( nc->GetName(), "CLASS1,CLASS4,Default" );
 
     nc = netSettings->GetEffectiveNetClass( "/NET_1" );
-    BOOST_CHECK_EQUAL( nc->GetVariableSubstitutionName(), "CLASS2,CLASS3,Default" );
+    BOOST_CHECK_EQUAL( nc->GetName(), "CLASS2,CLASS3,Default" );
 
     nc = netSettings->GetEffectiveNetClass( "/NET_2" );
-    BOOST_CHECK_EQUAL( nc->GetVariableSubstitutionName(), "CLASS_COMPLETE" );
+    BOOST_CHECK_EQUAL( nc->GetName(), "CLASS_COMPLETE" );
 
     nc = netSettings->GetEffectiveNetClass( "/NET_3" );
-    BOOST_CHECK_EQUAL( nc->GetVariableSubstitutionName(), "CLASS_COMPLETE,CLASS3,CLASS4" );
+    BOOST_CHECK_EQUAL( nc->GetName(), "CLASS_COMPLETE,CLASS3,CLASS4" );
 
     nc = netSettings->GetEffectiveNetClass( "/NET_4" );
-    BOOST_CHECK_EQUAL( nc->GetVariableSubstitutionName(), "CLASS_COMPLETE,CLASS3,CLASS4" );
+    BOOST_CHECK_EQUAL( nc->GetName(), "CLASS_COMPLETE,CLASS3,CLASS4" );
 }
 
 BOOST_AUTO_TEST_SUITE_END()