skills/metabase/clojure-review/SKILL.md
Review Clojure and ClojureScript code changes for compliance with Metabase coding standards, style violations, and code quality issues. Use when reviewing pull requests or diffs containing Clojure/ClojureScript code.
npx skillsauth add aiskillstore/marketplace clojure-reviewInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This guide covers Clojure and ClojureScript coding conventions for Metabase. See also: CLOJURE_STYLE_GUIDE.adoc for the Community Clojure Style Guide.
General Naming:
acc, i, pred, coll, n, s, k, fkebab-case for all variables, functions, and constantsFunction Naming:
age not calculate-age or get-age)!Destructuring:
snake_case keysDocstrings:
src or enterprise/backend/src must have docstring[[other-var]] not backticksComments:
TODO format: ;; TODO (Name M/D/YY) -- descriptionVisibility:
^:private unless it is used elsewheredeclare (put public functions near the end)Size and Structure:
let/cond)Keywords and Metadata:
:query-type/normal not :normal:arglists metadata if they're functions but wouldn't otherwise have itOrganization:
deftest forms for logically separate test cases-test or -test-<number>Performance:
^:parallelOSS Modules:
metabase.<module>.* patternsrc/metabase/<module>/Enterprise Modules:
metabase-enterprise.<module>.* patternenterprise/backend/src/metabase_enterprise/<module>/Module Structure:
<module>.api or <module>.api.* namespaces<module>.core using Potemkin imports<module>.models.*<module>.settings<module>.schemaModule Linters:
:clj-kondo/ignore [:metabase/modules]Required Elements:
:- <schema> after route string)Naming Conventions:
snake_case/api/dashboard/:id)Behavior:
GET endpoints should not have side effects (except analytics)defendpoint forms should be small wrappers around Toucan model codeRestrictions:
lib, lib-be, or query-processor modulesNaming:
snake_case identifiersBest Practices:
t2/select-one-fn instead of fetching entire rows for one columnDocumentation:
docs/developers-guide/driver-changelog.mdImplementation:
driver argument to other driver multimethodsread-column-thunk in JDBC-based driversExamples:
Linter Suppressions:
#_:clj-kondo/ignore (keyword form)Configurable Options:
:internal defsetting instead./bin/mage kondo-updated master (or whatever target branch)
./bin/mage kondo <file or files>
./bin/mage kondo-updated HEAD./bin/mage cljfmt-files [path]./bin/mage run-tests namespace/test-name./bin/mage run-tests namespace./bin/mage run-tests test/metabase/notification Because the module lives in that directory.Note: the ./bin/mage run-tests command accepts multiple args, so you can pass
./bin/mage run-tests namespace/test-name namespace/other-test namespace/third-test
to run 3 tests, or
./bin/mage run-tests test/metabase/module1 test/metabase/module2 to run 2 modules.
./bin/mage -check-readable <file> [line-number]
Note: If you have
clojure-mcptools available (check for tools likeclojure_eval), always prefer those over./bin/mage -repl. The MCP tools provide better integration, richer feedback, and avoid shell escaping issues. Only use./bin/mage -replas a fallback when clojure-mcp is not available.
./bin/mage -repl '<code>'
./bin/mage -repl '(+ 1 1)' where (+ 1 1) is your Clojure code.
./bin/mage -repl -h for more details.To call your.namespace/your-function on arg1 and arg2:
./bin/mage -repl --namespace your.namespace '(your-function arg1 arg2)'
DO NOT use "require", "load-file" etc in the code string argument.
The ./bin/mage -repl command returns three separate, independent outputs:
value: The return value of the last expression (best for data structures)stdout: Any printed output from println etc. (best for messages)stderr: Any error messages (best for warnings and errors)Example call:
./bin/mage -repl '(println "Hello, world!") '\''({0 1, 1 3, 2 0, 3 2} {0 2, 1 0, 2 3, 3 1})'
Example response:
ns: user
session: 32a35206-871c-4553-9bc9-f49491173d1c
value: ({0 1, 1 3, 2 0, 3 2} {0 2, 1 0, 2 3, 3 1})
stdout: Hello, world!
stderr:
For effective REPL usage:
println for human-readable messagesWhat to flag:
CLOJURE_STYLE_GUIDE.adoc exists in the working directory, also check compliance with the community Clojure style guideWhat NOT to post:
Example bad code review comments to avoid:
This TODO comment is properly formatted with author and date - nice work!
Good addition of limit 1 to the query - this makes the test more efficient without changing its behavior.
The kondo ignore comment is appropriately placed here
Test name properly ends with -test as required by the style guide.
Special cases:
Use this to scan through changes efficiently:
tbl, zs')kebab-case for all variables and functions!src or enterprise/backend/src have useful docstrings[[other-var]] not backticksTODO comments include author and date: ;; TODO (Name 1/1/25) -- description^:private unless used elsewheredeclare when avoidable (public functions near end)let/cond)deftest forms for distinct test cases^:parallel-test or -test-<number>metabase.<module>.*, EE: metabase-enterprise.<module>.*)<module>.api namespaces<module>.core with Potemkin:clj-kondo/ignore [:metabase/modules]:- <schema>)snake_case/api/dashboard/:id)GET has no side effects (except analytics)lib, lib-be, or query-processor modulest2/select-one-fn instead of selecting full rows for one columndocs/developers-guide/driver-changelog.mddriver argument to other driver methods (no hardcoded driver names)read-column-thunk#_:clj-kondo/ignore keyword form)Quick scan for common issues:
| Pattern | Issue |
| -------------------------------------------- | ----------------------------------------------------------- |
| calculate-age, get-user | Pure functions should be nouns: age, user |
| update-db, save-model | Missing ! for side effects: update-db!, save-model! |
| snake_case_var | Should use kebab-case |
| Public var without docstring | Add docstring explaining purpose |
| ;; TODO fix this | Missing author/date: ;; TODO (Name 1/1/25) -- description |
| (defn foo ...) in namespace used elsewhere | Should be (defn ^:private foo ...) |
| Function > 20 lines | Consider breaking up into smaller functions |
| /api/dashboards/:id | Use singular: /api/dashboard/:id |
| Query params with snake_case | Use kebab-case for query params |
| New API endpoint without tests | Add tests for the endpoint |
For style violations:
This pure function should be named as a noun describing its return value. Consider
userinstead ofget-user.
For missing documentation:
This public var needs a docstring explaining its purpose, inputs, and outputs.
For organization issues:
This function is only used in this namespace, so it should be marked
^:private.
For API conventions:
Query parameters should use kebab-case. Change
user_idtouser-id.
development
Apple Human Interface Guidelines for content display components. Use this skill when the user asks about charts component, collection view, image view, web view, color well, image well, activity view, lockup, data visualization, content display, displaying images, rendering web content, color pickers, or presenting collections of items in Apple apps. Also use when the user says how should I display charts, what's the best way to show images, should I use a web view, how do I build a grid of items, what component shows media, or how do I present a share sheet. Cross-references: hig-foundations for color/typography/accessibility, hig-patterns for data visualization patterns, hig-components-layout for structural containers, hig-platforms for platform-specific component behavior.
tools
Automate HelpDesk tasks via Rube MCP (Composio): list tickets, manage views, use canned responses, and configure custom fields. Always search tools first for current schemas.
testing
Expert Haskell engineer specializing in advanced type systems, pure functional design, and high-reliability software. Use PROACTIVELY for type-level programming, concurrency, and architecture guidance.
tools
GraphQL gives clients exactly the data they need - no more, no less. One endpoint, typed schema, introspection. But the flexibility that makes it powerful also makes it dangerous. Without proper controls, clients can craft queries that bring down your server. This skill covers schema design, resolvers, DataLoader for N+1 prevention, federation for microservices, and client integration with Apollo/urql. Key insight: GraphQL is a contract. The schema is the API documentation. Design it carefully.