-
Notifications
You must be signed in to change notification settings - Fork 3
Open
Labels
Description
Context
Generally it is recommended to disable instrumentations/preconditions in production systems:
- It results in slower performance
- It can result in an unfriendly UX
- UIs shouldn't break due to a spec failure
- Particularly when specs might be more strict than necessary
However:
- one shouldn't lose their benefits altogether in production
- We can catch bugs before the user is impacted at all
- Not all production systems are latency-critical
- e.g. async message processors (background jobs, Kafka handlers, etc)
Task
Implement two 'spec directives':
^::speced/always
Marks a defn/etc to be always check!ed, disregarding *assert* .
^::speced/warn
Instructs all defns/defprotocols, etc to observe *assert*as per usual. However a non-throwing checking will still be performed. If this checking fails, the report will be printlned against speced/*warn* (analog to *out* and *err*).
Consumers could bind speced/*warn* to arbitrary adapters, e.g. Timbre, Rollbar, etc.
Acceptance criteria
- I can selectively attach the flags to whichever defns/defprotocols/... I please
alwaysandwarncompose well- I can attach the flags in any likely position, avoiding silent no-ops:
(^here speced/defn ^here-too foo ^and-here [args] ....)
- Typos in flags are caught.
- individual arguments can have their own flags
- these always take precedence over the surrounding defn's flag (if any)
check!can also be flagged- for
alwayswould be a no-op, however serves as documentation (else I have to write a comment:;; this (check!) is enabled in production too, do not wrap in (assert))
- for
Examples
;; A simple case:
(speced/defn ^::speced/always foo [^::age age]
)
;; This case is equivalent to the previous one:
(speced/defn foo [^::speced/always ^::age age]
)
;; This case is different. Only `age` will be unconditionally checked
(speced/defn foo [^::speced/always ^::age age, ^::temperature temperature]
)
(speced/defn foo [^::speced/always age] ;; illegal flag, because there's no spec to check
)
;; `let` and other constructs get analog rules and syntax
(speced/let [^::speced/always ^::age age (compute-age ...)])
(^::speced/always speced/let [^::age age (compute-age ...)])
(speced/let ^::speced/always [^::age age (compute-age ...)])thumbnail and jwkoelewijn