add Claude skill to review code for strong type safety (RFD 643)#10125
add Claude skill to review code for strong type safety (RFD 643)#10125davepacheco wants to merge 4 commits intomainfrom
Conversation
|
I'm working on pasting example output here but spending a while on tweaks to the skill to get the output to be pasteable Markdown. |
|
Below is example output for the prompt "Review the code in @nexus/types/src/deployment.rs for use of strong types." I've run this several times for testing and of course it produces different output. Below is probably one of the weaker ones. Interestingly, other runs have reported the use of Broadly, I feel like the suggestions here have been useful. Type Safety ReviewModeTargeted review of FindingsSUGGESTION: Multiple representations — deployment.rs:259, 1295Problem: Fix: Use pub cockroachdb_fingerprint: Option<String>,The display code can then use SUGGESTION: Multiple representations — deployment.rs:284–288, 1329–1338Problem: Fix: For pub comment: Option<String>, For pub enum BlueprintCreator {
Nexus(OmicronZoneUuid),
Rss,
Test,
}If the set of creators is genuinely open, SUGGESTION: Missing full-struct destructuring — deployment.rs:1601Problem: Fix: Destructure at the top of the method: pub fn into_in_service_sled_config(self) -> OmicronSledConfig {
// This destructuring exists to cause a compile error if fields are
// added to BlueprintSledConfig; update the conversion below if needed.
let BlueprintSledConfig {
state: _,
subnet: _,
last_allocated_ip_subnet_offset: _,
sled_agent_generation,
disks,
datasets,
zones,
remove_mupdate_override,
host_phase_2,
measurements,
} = self;
OmicronSledConfig { generation: sled_agent_generation, ... }
} SUGGESTION: Missing full-struct destructuring — deployment.rs:3182Problem: Fix: impl From<BlueprintDatasetConfig> for DatasetConfig {
fn from(config: BlueprintDatasetConfig) -> Self {
// This destructuring is here so that new fields in
// BlueprintDatasetConfig cause a compile error here. Update
// accordingly. `disposition` and `address` are intentionally
// not passed to sled-agent.
let BlueprintDatasetConfig {
disposition: _,
id,
pool,
kind,
address: _,
quota,
reservation,
compression,
} = config;
Self {
id,
name: DatasetName::new(pool, kind),
inner: SharedDatasetConfig { quota, reservation, compression },
}
}
} SUGGESTION: Missing full-struct destructuring — deployment.rs:3201Problem: Fix: Add a destructuring at the top: fn as_strings(&self) -> Vec<String> {
let BlueprintDatasetConfig {
disposition, id, pool, kind, address: _, quota, reservation, compression,
} = self;
vec![
DatasetName::new(*pool, kind.clone()).full_name(),
id.to_string(),
disposition.to_string(),
...
]
} SUGGESTION: Missing full-struct destructuring — deployment.rs:2562Problem: Fix: Destructure SUGGESTION: Weak enum/bool usage — deployment.rs:3265Problem: pub enum BlueprintTargetExecution {
Enabled,
Disabled,
}This is a publicly-exported API type, so clarity matters. Fix: pub struct BlueprintTarget {
pub target_id: BlueprintUuid,
pub execution: BlueprintTargetExecution,
pub time_made_target: chrono::DateTime<chrono::Utc>,
}SUGGESTION: Missing newtype for domain value — deployment.rs:2499Problem: Fix: pub struct MgsSlotId(pub u16);
pub struct PendingMgsUpdate {
...
pub slot_id: MgsSlotId,
...
} SUGGESTION: Multiple representations — deployment.rs:3280–3298Problem: Fix: Remove pub struct CollectionDatasetIdentifier {
name: String,
id: DatasetUuid,
} If backwards compatibility with old serialized data is the concern, this can be No issues found in:
Summary0 blocking issues, 9 suggestions. The most impactful are the three missing |
jgallagher
left a comment
There was a problem hiding this comment.
I'll defer to the more Claude-aware folks for the wording and structure of this; the examples LGTM (with maybe one exception, noted below).
| } | ||
| ``` | ||
|
|
||
| ### Example: Inline enum variant data vs. named struct |
There was a problem hiding this comment.
I wish we could use this one more, but I don't think this pattern works for any types that end up in OpenAPI specs, right? I don't remember what the exact error is off the top of my head, but I always end up having to switch back to Variant { fields... }.
There was a problem hiding this comment.
Maybe? I'd rather run into that and then update the guidance than remove it prematurely, since it's still useful outside of OpenAPI documents and I'm guessing can be made to work with those, too.
|
I added a note about Vecs vs sets and asked it to review the file again with "Review the code in @nexus/types/src/deployment.rs for type safety. Right now, I'm only interested in the category related to Vec vs. BTreeSet." Result is below. Type Safety ReviewModeTargeted review of FindingsBLOCKING Vec when uniqueness matters — deployment.rs:3313
Problem: Fix: Implement impl IdOrdItem for Blueprint {
type Key<'a> = BlueprintUuid;
fn key(&self) -> BlueprintUuid { self.id }
}
pub blueprints: IdOrdMap<Blueprint>, The consumer in BLOCKING Vec when uniqueness matters — deployment.rs:3311
Problem: Fix: Implement impl IdOrdItem for Collection {
type Key<'a> = CollectionUuid;
fn key(&self) -> CollectionUuid { self.id }
}
pub collections: IdOrdMap<Collection>,SUGGESTION Vec when uniqueness matters — deployment.rs:3316
Problem: Silo names are unique identifiers — no system should have two silos Fix: Use pub silo_names: BTreeSet<omicron_common::api::external::Name>,SUGGESTION Vec when uniqueness matters — deployment.rs:3317
Problem: DNS zone names in the database are unique. A Fix: Use pub external_dns_zone_names: BTreeSet<String>,No issues found in:
Summary2 blocking issues ( |
|
This looks pretty good to me at a glance — will try it and see what it does. I like moving the references out to another file, though I wonder if it should be clear that it should always look at them. If not, it could use a little more guidance about when to look at them — but I suspect that if you tried to do that you'd find the answer is "always." The skill is about 3k tokens and the reference is slightly more, which is not a big deal, especially if this is skill is run in a subagent, which wouldn't clutter the top-level conversation. |
This skill enables Claude to review either existing code or a specific PR for the code's use of types to enable strong static type checking. It covers the patterns described in RFD 643 using examples from some of @jgallagher's recent PRs that clean up these kinds of problems.
This is my first real Claude skill and I'd welcome feedback. It's also Claude-written. I know from experience that can result in work that's deceptively time-consuming to review. Do feel free to stop and say "go back to the drawing board because this needs more serious thought before we review it more carefully".
There's one thing I'm struggling to resolve here, which is that ideally, the "source of truth" for the anti-patterns that this looks for would be the same as the one we refer people to: RFD 643. But RFD 643 includes a lot more context about each item, which I think is helpful for humans but maybe less useful for the LLM.