Skip to content

refactor(op-devstack): introduce unified ID type system (Phase 1)#18872

Merged
teddyknox merged 1 commit intodevelopfrom
teddyknox/id-refactor-phase1
Feb 4, 2026
Merged

refactor(op-devstack): introduce unified ID type system (Phase 1)#18872
teddyknox merged 1 commit intodevelopfrom
teddyknox/id-refactor-phase1

Conversation

@teddyknox
Copy link
Copy Markdown
Contributor

This PR introduces a new unified ID type system for op-devstack that addresses the parallel type system problem where 19 separate ID types each require ~150 lines of boilerplate code. The new system uses Go generics to provide type safety with a single underlying implementation.

This is Phase 1 of a multi-phase refactor. It introduces the new types alongside the existing system, with no breaking changes.

Problem

The current ID system has several issues:

  1. Code Duplication: 19 ID types × ~150 lines each = ~2,850 lines of nearly identical boilerplate
  2. Parallel Type Systems: Each ID type (e.g., L2BatcherID) has separate marshal/unmarshal, String(), sorting functions
  3. Polymorphic Lookup Complexity: RollupBoostNode is-a L2ELNode, but their ID types are separate, requiring manual conversion in lookups
  4. No Unified Querying: Can't easily query "all components on chain X" without checking each registry

See the design document for the full rationale.

Solution

Introduce a unified ComponentID type with a generic wrapper ID[T] for type safety:

// Single underlying type for all IDs
type ComponentID struct {
    kind    ComponentKind
    shape   IDShape
    key     string
    chainID eth.ChainID
}

// Type-safe wrapper using generics
type ID[T KindMarker] struct {
    ComponentID
}

// Marker types provide compile-time type safety
type L2BatcherMarker struct{}
func (L2BatcherMarker) componentKind() ComponentKind { return KindL2Batcher }

// Type alias for backward-compatible naming
type L2BatcherID2 = ID[L2BatcherMarker]

Changes

New Files

File Description
stack/component_id.go Unified ID system (~500 lines)
stack/component_id_test.go Comprehensive test suite (~330 lines)

Key Features

  • Single Implementation: One MarshalText/UnmarshalText implementation for all ID types
  • Type Safety: L2BatcherID2 and L2ELNodeID2 are distinct types at compile time
  • IDShape: Handles three ID formats (key+chain, chain-only, key-only) cleanly
  • Conversion Helpers: ConvertL2BatcherID(old) L2BatcherID2 for incremental migration
  • Serialization Compatible: Produces identical output to old system ("L2Batcher-mynode-420")

Non-Breaking

  • Old ID types (L2BatcherID, etc.) remain unchanged
  • New types use "2" suffix (L2BatcherID2, NewL2BatcherID2())
  • Both systems can coexist during migration

Test Plan

  • All new ID type constructors create correct kind/key/chainID
  • Marshal/Unmarshal round-trip works for all three ID shapes
  • Type safety verified (different ID types are not interchangeable)
  • Kind mismatch during unmarshal produces clear error
  • Conversion helpers preserve all data
  • Serialization output matches old system exactly
  • IDs work as map keys
  • Sorting works correctly
  • Existing stack tests pass

Future Phases

  • Phase 2: Unified Registry to replace 12+ separate RWMap instances
  • Phase 3: Capability interfaces (L2ELCapable) for polymorphic lookups
  • Phase 4: Remove old ID types, simplify interfaces

How to Review

  1. Start with stack/component_id.go:

    • ComponentID struct and its methods (lines 1-200)
    • KindMarker interface and marker types (lines 200-260)
    • Type aliases and constructors (lines 260-360)
    • ID[T] methods (lines 360-400)
    • Conversion helpers (lines 400-500)
  2. Review stack/component_id_test.go to understand the expected behavior

  3. Check the design document for the full rationale

@teddyknox teddyknox requested review from a team as code owners January 20, 2026 19:54
@teddyknox teddyknox requested a review from mds1 January 20, 2026 19:54
@teddyknox teddyknox changed the title op-devstack: introduce unified ID type system (Phase 1) refactor(op-devstack): introduce unified ID type system (Phase 1) Jan 21, 2026
Copy link
Copy Markdown
Contributor

@ajsutton ajsutton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

                                                                                                                                                                                                                                                                                                                           
Introduce a new unified ID type system using Go generics to address the                                                                                                                                                                                                                                                  
parallel type system problem in op-devstack. The current system has 19                                                                                                                                                                                                                                                   
separate ID types, each with ~150 lines of boilerplate for marshal/unmarshal,                                                                                                                                                                                                                                            
String(), sorting, and matcher methods.                                                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                                                                         
The new system provides:                                                                                                                                                                                                                                                                                                 
- ComponentID: single underlying struct for all IDs                                                                                                                                                                                                                                                                      
- ID[T]: generic wrapper with KindMarker constraint for type safety                                                                                                                                                                                                                                                      
- Marker types (L2BatcherMarker, etc.) to avoid circular dependencies                                                                                                                                                                                                                                                    
- IDShape enum to handle three ID formats (key+chain, chain-only, key-only)                                                                                                                                                                                                                                              
- Conversion helpers for incremental migration from old to new types                                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                                                         
New types use a "2" suffix (L2BatcherID2, NewL2BatcherID2) to coexist with                                                                                                                                                                                                                                               
the existing system during migration. Serialization output is identical to                                                                                                                                                                                                                                               
the old system for compatibility.                                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                                         
This is Phase 1 of a multi-phase refactor. Future phases will introduce a                                                                                                                                                                                                                                                
unified registry and capability interfaces for polymorphic lookups.                                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                                                                         
See docs/design/id-type-system-refactor.md for the full design document.
@teddyknox teddyknox force-pushed the teddyknox/id-refactor-phase1 branch from 6c97f16 to e52819a Compare February 4, 2026 19:41
@teddyknox teddyknox enabled auto-merge February 4, 2026 19:41
@teddyknox teddyknox added this pull request to the merge queue Feb 4, 2026
Merged via the queue into develop with commit 70bef69 Feb 4, 2026
79 checks passed
@teddyknox teddyknox deleted the teddyknox/id-refactor-phase1 branch February 4, 2026 20:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants