In Clojure, we’ve internalized, as Stuart Halloway said, to send things to the REPL, not type into the REPL. The (sub)expressions we send to the REPL live in actual files saved on disk, vs. being ephemeral and tied to a REPL connection. Sometimes, those (sub)expressions come from existing project namespaces. Sometimes, those (sub)expressions don’t fit any particular namespace. In that case, Stu’s suggestion is to simply append them to everything.clj and evaluate from there....
Exception Translation
Can a typical “Internal Server Error” be improved a bit? Let’s find out. In fact, let’s consider more than just a response that is typically associated with internal server errors. Those types of errors usually originate in the outermost try/catch expression. There we might find code to: respond with HTTP status code 500 increment a metric that an unknown exception occurred log the event 1 save the exception to the database 2 etc....
Errors From Libraries
At this point, we’ve defined the error model and know how to use it. Until now, we focused on custom validations in the project. However, what if the errors originate in the libraries you might be using for some validation? Open-source libraries cannot possibly know about your own error model. They have their way of representing and returning errors. We hinted at the solution already - convert library errors into your own model....
Using the Error Model
Let’s see how to use the error model we defined in part 2. The one that works well enough: error is a hash map errors is a vector of error Before we get started: As before, we’ll concentrate on the functions that validate something. Given the error model above, we’ll want every validation function to return errors. We’ll build on top of the implementation and existing operations. The focus here won’t be on the individual error - what goes in the hash map....
Error Model
In part 1 we talked about the various tasks that can result in an error. Libraries can help with those tasks. However, there is still a whole bunch that the project will have to take care of. Let’s continue from there. Here we’ll concentrate on the error model - the way the errors are represented. What is that about? Each library pulled in to help with the validation tasks represents errors in its own way....
What Is a Validation?
Validate this Write a validation for that Once validated, it should … What does it mean “to validate”? What we usually mean is to make sure that only valid data get accepted by the system. For example, a “create user” endpoint should not be able to receive a PDF invoice as input and create a valid user as a result 1. In this post, let’s focus on the steps involved to get to valid data....
Configuration
Which library do you use for project configuration? There are so many to choose from. Take a look at The Clojure Toolbox under “Configuration”. Choosing the right one mostly comes down to that you want out of the configuration library. Here are my requirements: It should not maintain it’s own state. So environ is out. It should allow the project to control configuration sources (e.g. from environmental variables, java properties, command line arguments, file (edn?...
Clojure Core Extensions
There are plenty of high quality libraries that add missing functions from clojure.core. Just take a look at the clojure toolbox under “Misc. Functions” or “String Manipulation”. Which ones are you using? I tried many of them, but mostly don’t use them in my projects. Instead, I usually write my own clojure.core extensions. Here are some of the benefits as I see them: IMO it’s better 1 to be thinking “what patterns do I see in the project” than “what can this library do for me”....
Integration Tests for SQL Statement and Lock Timeout
If you’re using (and have configured) SQL statement and lock timeout, it would be good to verify how the service behaves when those timeouts happen. Integration tests can help us verify the behavior. However, the problem with integration tests is that they make most sense in the context of your project. Without the supporting code they are devoid of information that makes them tick. Let’s try it out anyway. Here are the assumptions for the tests below:...
Configuring SQL Data Source
What are you using to configure an SQL data source? Clojure wrappers? Own abstractions? Plain implementation? Perhaps you’re seeing something weird in production and want to investigate? Do questions such as these come up now and again: How many concurrent connections does the pool allow? Is this ok time to do RDS snapshot? What happens to existing connections if RDS restarts? How long is the connection timeout? How long is the statement timeout?...
Clojure Debugging Helpers
I highly recommend Repl Driven Development by Stuart Halloway if you haven’t watched it already. It’s a real eye opener when it comes to the topic of REPL and how to use it to your advantage. Two sentence summary of that presentation could easily be: Don’t type into REPL! Send things to the REPL! As soon as you begin to understand that message, a whole new world opens up where some things become so incredibly simple....
Routing Slf4j Events To Mulog
mulog plays to Clojure’s strengths such as declaring, using and manipulating data vs. composing and parsing strings. That’s why I like using it as a logging library. However, backend services might use other Java libraries that only use what’s available in the JVM ecosystem. Best case scenario is that backend ends up using at least two logging libraries: mulog and slf4j. Each one has to be configured to work properly. Even if they are, some questions still remain:...
Avoiding Type Problems When Logging to ElasticSearch
If you’re sending your log events to ElasticSearch, you might notice that ElasticSearch sometimes fails to index some events due to type problems. A single ES index cannot contain two documents having the same key but different value types. For example, {"user": 1} (integer) and {"user": "jane@example.com"} (string) cannot be written to the same ES index. mulog is an excellent logging library for Clojure. It can also send your events to ElasticSearch....
Mulog Publisher for Nicer Local Development
mulog has a couple of console publishers ready for use: Simple console publisher: output in EDN format Advanced console publisher: output in JSON format Both can be used as publishers for local development (either running locally or executing tests. The problem is that their output is not something pleasant to look at. Even if the output is pretty printed. Here is a contrived example, with just 5 events. {:mulog/event-name :company.system.slf4j-init/slf4j, :mulog/timestamp 1668079374515, :mulog/trace-id #mulog/flake "4mNpLawLr6vG7UiZo44Uxi4U-MgZ1ugS", :mulog/namespace "company....
Make Mulog Play Nice With Kaocha
mulog is an excellent logging library for Clojure. It uses agents to process events asynchronously. This makes sense for deployed services. But what about executing (integration, functional, etc.) tests? Some tests runners (e.g. Kaocha) try to capture the test output while the test is executing and provide that output in case the test failed. Do those two libraries play well together? Not really. The output captured cannot be relied upon:...
Start the System Before Executing Integration Tests in Kaocha
Kaocha is a very good tests runner for Clojure. The documentation is really good. What I wanted to do recently is to start (and stop) the system before (and after) running the integration tests. How to do that with hooks? If the project has unit tests separated from integration tests then this solution should work. tests.edn #kaocha/v1 {:tests [{:id :unit :test-paths ["test/unit"]} {:id :integration :test-paths ["test/integration"]}] :plugins [:hooks] :kaocha.hooks/wrap-run [kaocha-hooks/wrap-tests]} test/kaocha_hooks....