Skip to content

Production modes #70

@vemv

Description

@vemv

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
  • always and warn compose 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
    • foralways would 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))

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 ...)])

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions