Help us improve
Share bugs, ideas, or general feedback.
From ocaml-dev
Enforces OCaml coding style including naming conventions, modularity, refactoring patterns like Option/Result combinators and monadic syntax, plus error handling to improve code quality and reduce complexity.
npx claudepluginhub avsm/ocaml-claude-marketplace --plugin ocaml-devHow this skill is triggered — by the user, by Claude, or both
Slash command
/ocaml-dev:code-styleThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
1. **Interface-First**: Design `.mli` first. Clean interface > clever implementation.
Guides OCaml development with best practices for writing code, designing .mli interfaces, result-based error handling, dune builds, and libraries like eio, fmt, logs, cmdliner, yojson, cohttp-eio.
Indexes pedantic-coder skills for universal code quality principles including naming precision, casing law, import discipline, declaration order, symmetry, and dead code intolerance. Use for code reviews, refactoring, or greenfield projects.
Simplifies and refines code for clarity, consistency, and maintainability while preserving functionality. Applies project-specific best practices to recently modified code.
Share bugs, ideas, or general feedback.
.mli first. Clean interface > clever implementation.| Element | Convention | Example |
|---|---|---|
| Files | lowercase_underscores | user_profile.ml |
| Modules | Snake_case | User_profile |
| Types | snake_case, primary type is t | type user_profile, type t |
| Values | snake_case | find_user, create_channel |
| Variants | Snake_case | Waiting_for_input, Processing_data |
Function naming:
find_* returns option (may not exist)get_* returns value directly (must exist)Avoid: Long names with many underscores (get_user_profile_data_from_database_by_id).
(* Before *)
match get_value () with Some x -> Some (x + 1) | None -> None
(* After *)
Option.map (fun x -> x + 1) (get_value ())
Prefer: Option.map, Option.bind, Option.value, Result.map, Result.bind
(* Before - nested matches *)
match fetch_user id with
| Ok user -> (match fetch_perms user with Ok p -> Ok (user, p) | Error e -> Error e)
| Error e -> Error e
(* After *)
let open Result.Syntax in
let* user = fetch_user id in
let+ perms = fetch_perms user in
(user, perms)
(* Before *)
if x > 0 then if x < 10 then "small" else "large" else "negative"
(* After *)
match x with
| x when x < 0 -> "negative"
| x when x < 10 -> "small"
| _ -> "large"
Keep functions small: Under 50 lines. One purpose per function.
Avoid deep nesting: Max 4 levels of match/if. Extract helpers.
High complexity signal: Many branches = split into focused helpers.
(* Bad - high complexity *)
let check x y z =
if x > 0 then if y > 0 then if z > 0 then ... else ... else ... else ...
(* Good - factored *)
let all_positive x y z = x > 0 && y > 0 && z > 0
let check x y z = if not (all_positive x y z) then "invalid" else ...
Use result for recoverable errors. Exceptions only for programming errors.
Never catch-all:
(* Bad *)
try f () with _ -> default
(* Good *)
try f () with Failure _ -> default
Don't silence warnings: Fix the issue, don't use [@warning "-nn"].
| Instead of | Use | Why |
|---|---|---|
Str | Re | Better API, no global state |
Printf | Fmt | Composable, type-safe |
yojson (manual) | jsont | Type-safe codecs |
Abstract types: Keep type t abstract. Expose smart constructors.
(* Good - .mli *)
type t
val create : name:string -> t
val name : t -> string
val pp : t Fmt.t
Avoid generic names: Not Util, Helpers. Use String_ext, Json_codec.
Avoid boolean blindness:
(* Bad *)
let create_widget visible bordered = ...
let w = create_widget true false (* What does this mean? *)
(* Good *)
type visibility = Visible | Hidden
let create_widget ~visibility ~border = ...
Some v -> Some (f v) | None -> Nonepp function on typesObj.magic anywhere