agentleFS
Sign inSign up

physicsnemo / rules

NVIDIA/physicsnemo/.cursor/rules/mod-009-avoid-string-based-class-selection.mdc

Avoid string-based class selection with many options (>3 choices) in model constructors; prefer dependency injection with instances for better type safety and clearer APIs.

Cursor rule3.3k starsChanged 10 months ago
---
description: Avoid string-based class selection with many options (>3 choices) in model constructors; prefer dependency injection with instances for better type safety and clearer APIs.
alwaysApply: false
---

When designing model constructor APIs, rule MOD-009 should be followed. Explicitly reference "Following rule MOD-009, which discourages string-based class selection when there are many choices..." when deciding constructor parameter design.

## MOD-009: Avoid string-based class selection in model constructors

**Description:**

Passing a string that represents a class name, which is then used to instantiate
an internal submodule, should be avoided unless there are only a few choices (2
or 3 maximum) for the class name.

When there are more than 2-3 choices, the recommended practice is to pass an
already instantiated instance of a submodule instead of a string primitive for
dependency injection. This promotes better type safety, clearer APIs, and easier
testing.

**Rationale:**

String-based class selection makes code harder to type-check, debug, and test.
It obscures dependencies and makes it difficult for static analysis tools to
understand the code. Direct instance injection provides better IDE support,
type safety, and makes testing easier by allowing mock object injection.

**Example:**

```python
# Good: Limited choices (2-3 max) - string selection acceptable
class MyModel(Module):
    def __init__(
        self,
        activation: Literal["relu", "gelu"] = "relu"
    ):
        if activation == "relu":
            self.act = nn.ReLU()
        elif activation == "gelu":
            self.act = nn.GELU()

# Good: Many choices - use instance injection
class MyModel(Module):
    def __init__(
        self,
        encoder: Module,  # Pass instance, not string
        decoder: Module   # Pass instance, not string
    ):
        self.encoder = encoder
        self.decoder = decoder

# Usage:
model = MyModel(
    encoder=MyCustomEncoder(dim=128),
    decoder=MyCustomDecoder(dim=128)
)
```

**Anti-pattern:**

```python
# WRONG: String selection with many choices
class MyModel(Module):
    def __init__(
        self,
        encoder_type: str = "transformer"  # Many possible values
    ):
        # String-based factory pattern with 10+ choices
        if encoder_type == "transformer":
            self.encoder = TransformerEncoder()
        elif encoder_type == "cnn":
            self.encoder = CNNEncoder()
        # ... many more options
        # WRONG: Should accept encoder instance instead
```

Discussion

Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.

Posts are public.Sign in to post

No one has posted yet. Be the first.