2 pointsby rrepo9 hours ago5 comments
  • rrepo9 hours ago
    Why Common Lisp?

    In many ecosystems, frameworks define architecture for you. In Lisp, you define your own abstractions. The biggest difference was macros. Rather than repeating patterns such as JSON parsing,Error normalization, API response formatting and Exception mapping. I abstracted them into macros that standardized the flow across the entire backend. This allowed controllers to focus purely on business logic. For example, API responses are wrapped automatically (defmacro with-api-response (result) `(let ((res ,result)) (cond ((null res) `(200 (:content-type "application/json") (,(jonathan:to-json '(:status "success" :data ())))))

           ((eq res :invalid)
            `(400 (:content-type "application/json")
                  (,(jonathan:to-json '(:status "error")))))
    
           (t
            `(200 (:content-type "application/json")
                  (,(jonathan:to-json
                     (list :status "success" :data res))))))))
    
    Instead of writing error handling and serialization logic repeatedly, I could define the contract once.
  • rrepo9 hours ago
    Hot Reload Without Restarting

    I also implemented a lightweight file reload mechanism. Instead of restarting the server on every change, the system checks file timestamps on access and reloads only what changed. This gave me a workflow similar to frontend hot reloading, but implemented manually.

    (defun reload-dev () (dolist (file '("controllers/controllers-package")) (let* ((pathname (asdf:system-relative-pathname "mindmap" (format nil "~A.lisp" file))) (new-time (file-write-date pathname)) (old-time (gethash file file-mod-times 0))) (when (> new-time old-time) (load pathname) (setf (gethash file file-mod-times) new-time))))) Because everything is just code and macros, the system remains transparent and hackable.

  • rrepo9 hours ago
    What Lisp Changed What surprised me most wasn’t performance. It was cohesion.

    The combination of: Macros for abstraction, Symbolic error flow, Minimal external, framework constraints and Explicit layering. resulted in a backend that feels small, consistent, and expressive. It reminded me somewhat of Go’s explicit design philosophy, but with a far more powerful metaprogramming layer. Lisp did not give me more libraries. It gave me more control over structure.

  • rrepo9 hours ago
    Standardizing Error Flow Another macro normalizes all exceptions into a single symbolic state:

    (defmacro with-invalid (&body body) `(handler-case (progn ,@body) (error (e) (format error-output "ERROR: ~A~%" e) :invalid)))

    Every layer speaks the same language: either valid data or :invalid. This made the layering extremely clear and reduced cognitive load.

  • rrepo9 hours ago
    Closing Thoughts Building the server, reload mechanism, and API abstractions from scratch was far more educational than using a prebuilt framework. The codebase is currently closed, but I’m considering open-sourcing it. If you’re interested in using Common Lisp in a modern web backend, I’d love to hear your thoughts.