What to Do With Evaluated REPL Expressions?

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

May 20, 2024 · Miro Bezjak

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

February 10, 2023 · Miro Bezjak

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

January 13, 2023 · Miro Bezjak

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

January 1, 2023 · Miro Bezjak

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

December 10, 2022 · Miro Bezjak

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

November 30, 2022 · Miro Bezjak

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?...

November 23, 2022 · Miro Bezjak

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”....

November 21, 2022 · Miro Bezjak

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:...

November 21, 2022 · Miro Bezjak

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?...

November 21, 2022 · Miro Bezjak

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

November 11, 2022 · Miro Bezjak

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:...

November 10, 2022 · Miro Bezjak

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

November 9, 2022 · Miro Bezjak

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

November 9, 2022 · Miro Bezjak

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:...

November 4, 2022 · Miro Bezjak

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

November 4, 2022 · Miro Bezjak